Skip to content

Commit

Permalink
feat(backend/fiches): ajout d'une route de synthèse (#3347)
Browse files Browse the repository at this point in the history
  • Loading branch information
dthib authored Oct 10, 2024
1 parent 04c1150 commit 376d9cc
Show file tree
Hide file tree
Showing 24 changed files with 920 additions and 2 deletions.
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AuthModule } from './auth/auth.module';
import { CollectivitesModule } from './collectivites/collectivites.module';
import { CommonModule } from './common/common.module';
import { validateBackendConfiguration } from './common/services/backend-configuration.service';
import { FichesActionModule } from './fiches/fiches-action.module';
import { IndicateursModule } from './indicateurs/indicateurs.module';
import { SheetModule } from './spreadsheets/sheet.module';
import { TrpcRouter } from './trpc.router';
Expand All @@ -21,6 +22,7 @@ import { TrpcModule } from './trpc/trpc.module';
CollectivitesModule,
IndicateursModule,
AuthModule,
FichesActionModule,
],
controllers: [],
exports: [TrpcRouter],
Expand Down
3 changes: 2 additions & 1 deletion backend/src/common/controllers/version.controller.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { createZodDto } from '@anatine/zod-nestjs';
import { Controller, Get } from '@nestjs/common';
import { ApiOkResponse } from '@nestjs/swagger';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { PublicEndpoint } from '../../auth/decorators/public-endpoint.decorator';
import { versionResponseSchema } from '../models/version.models';

/**
* Création des classes de réponse à partir du schema pour générer automatiquement la documentation OpenAPI
*/
@ApiTags('Application')
export class VersionResponseClass extends createZodDto(versionResponseSchema) {}
@Controller()
export class VersionController {
Expand Down
16 changes: 16 additions & 0 deletions backend/src/common/models/count-synthese.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { extendApi, extendZodWithOpenApi } from '@anatine/zod-openapi';
import { z } from 'zod';
extendZodWithOpenApi(z);

export const countSyntheseValeurSchema = extendApi(
z.object({
count: z.number().int(),
valeur: z.union([z.string(), z.number(), z.boolean(), z.null()]),
}),
);
export type CountSyntheseValeurType = z.infer<typeof countSyntheseValeurSchema>;

export const countSyntheseSchema = extendApi(
z.record(z.string(), countSyntheseValeurSchema),
);
export type CountSyntheseType = z.infer<typeof countSyntheseSchema>;
32 changes: 32 additions & 0 deletions backend/src/common/models/modified-since.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { DateTime } from 'luxon';
import { z } from 'zod';

export enum ModifiedSinceEnum {
LAST_90_DAYS = 'last-90-days',
LAST_60_DAYS = 'last-60-days',
LAST_30_DAYS = 'last-30-days',
LAST_15_DAYS = 'last-15-days',
}

export const modifiedSinceSchema = z.enum([
ModifiedSinceEnum.LAST_90_DAYS,
ModifiedSinceEnum.LAST_60_DAYS,
ModifiedSinceEnum.LAST_30_DAYS,
ModifiedSinceEnum.LAST_15_DAYS,
]);

export const getModifiedSinceDate = (
modifiedSince: ModifiedSinceEnum,
): Date => {
const now = DateTime.now();
switch (modifiedSince) {
case ModifiedSinceEnum.LAST_90_DAYS:
return now.minus({ days: 90 }).toJSDate();
case ModifiedSinceEnum.LAST_60_DAYS:
return now.minus({ days: 60 }).toJSDate();
case ModifiedSinceEnum.LAST_30_DAYS:
return now.minus({ days: 30 }).toJSDate();
case ModifiedSinceEnum.LAST_15_DAYS:
return now.minus({ days: 15 }).toJSDate();
}
};
61 changes: 61 additions & 0 deletions backend/src/fiches/controllers/fiches-action.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { createZodDto } from '@anatine/zod-nestjs';
import { Controller, Get, Param, Query } from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { TokenInfo } from '../../auth/decorators/token-info.decorators';
import { SupabaseJwtPayload } from '../../auth/models/auth.models';
import { getFichesActionSyntheseSchema } from '../models/get-fiches-action-synthese.response';
import { getFichesActionFilterRequestSchema } from '../models/get-fiches-actions-filter.request';
import FichesActionSyntheseService from '../services/fiches-action-synthese.service';

/**
* Création des classes de réponse à partir du schema pour générer automatiquement la documentation OpenAPI
*/
export class GetFichesActionSyntheseResponseClass extends createZodDto(
getFichesActionSyntheseSchema,
) {}
export class GetFichesActionFilterRequestClass extends createZodDto(
getFichesActionFilterRequestSchema,
) {}

@ApiTags('Fiches action')
@Controller('collectivites/:collectivite_id/fiches-action')
export class FichesActionController {
constructor(
private readonly fichesActionSyntheseService: FichesActionSyntheseService,
) {}

@Get('synthese')
@ApiOkResponse({
type: GetFichesActionSyntheseResponseClass,
description:
"Récupération de la sythèse des fiches action d'une collectivité (ex: nombre par statut)",
})
async getFichesActionSynthese(
@Param('collectivite_id') collectiviteId: number,
@Query() request: GetFichesActionFilterRequestClass,
@TokenInfo() tokenInfo: SupabaseJwtPayload,
) {
return this.fichesActionSyntheseService.getFichesActionSynthese(
collectiviteId,
request,
tokenInfo,
);
}

@Get('')
// TODO: type it for documentation
@ApiOkResponse({
description: "Récupération des fiches action d'une collectivité",
})
async getFichesAction(
@Param('collectivite_id') collectiviteId: number,
@Query() request: GetFichesActionFilterRequestClass,
@TokenInfo() tokenInfo: SupabaseJwtPayload,
) {
return this.fichesActionSyntheseService.getFichesAction(
collectiviteId,
request,
tokenInfo,
);
}
}
14 changes: 14 additions & 0 deletions backend/src/fiches/fiches-action.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { AuthModule } from '../auth/auth.module';
import { CollectivitesModule } from '../collectivites/collectivites.module';
import { CommonModule } from '../common/common.module';
import { FichesActionController } from './controllers/fiches-action.controller';
import FichesActionSyntheseService from './services/fiches-action-synthese.service';

@Module({
imports: [CommonModule, AuthModule, CollectivitesModule],
providers: [FichesActionSyntheseService],
exports: [FichesActionSyntheseService],
controllers: [FichesActionController],
})
export class FichesActionModule {}
32 changes: 32 additions & 0 deletions backend/src/fiches/models/axe.table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { InferInsertModel } from 'drizzle-orm';
import {
AnyPgColumn,
integer,
pgTable,
serial,
text,
timestamp,
uuid,
} from 'drizzle-orm/pg-core';
import { collectiviteTable } from '../../collectivites/models/collectivite.models';
import { planActionTypeTable } from './plan-action-type.table';

export const axeTable = pgTable('axe', {
id: serial('id').primaryKey(),
nom: text('nom'),
collectivite_id: integer('collectivite_id')
.notNull()
.references(() => collectiviteTable.id),
parent: integer('parent').references((): AnyPgColumn => axeTable.id),
plan: integer('plan').references((): AnyPgColumn => axeTable.id),
type: integer('type').references(() => planActionTypeTable.id),
created_at: timestamp('created_at', { withTimezone: true })
.notNull()
.defaultNow(),
modified_at: timestamp('modified_at', { withTimezone: true })
.notNull()
.defaultNow(),
modified_by: uuid('modified_by'), // TODO references auth.uid
panier_id: integer('panier_id'), // TODO references panier
});
export type CreateAxeType = InferInsertModel<typeof axeTable>;
16 changes: 16 additions & 0 deletions backend/src/fiches/models/fiche-action-axe.table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { integer, pgTable, primaryKey } from 'drizzle-orm/pg-core';
import { axeTable } from './axe.table';
import { ficheActionTable } from './fiche-action.table';

export const ficheActionAxeTable = pgTable(
'fiche_action_axe',
{
fiche_id: integer('fiche_id').references(() => ficheActionTable.id),
axe_id: integer('axe_id').references(() => axeTable.id),
},
(table) => {
return {
pk: primaryKey({ columns: [table.fiche_id, table.axe_id] }),
};
},
);
18 changes: 18 additions & 0 deletions backend/src/fiches/models/fiche-action-partenaire-tag.table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { integer, pgTable, primaryKey } from 'drizzle-orm/pg-core';
import { partenaireTagTable } from '../../taxonomie/models/partenaire-tag.table';
import { ficheActionTable } from './fiche-action.table';

export const ficheActionPartenaireTagTable = pgTable(
'fiche_action_partenaire_tag',
{
fiche_id: integer('fiche_id').references(() => ficheActionTable.id),
partenaire_tag_id: integer('partenaire_tag_id').references(
() => partenaireTagTable.id,
),
},
(table) => {
return {
pk: primaryKey({ columns: [table.fiche_id, table.partenaire_tag_id] }),
};
},
);
24 changes: 24 additions & 0 deletions backend/src/fiches/models/fiche-action-pilote.table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { integer, pgTable, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
import { personneTagTable } from '../../taxonomie/models/personne-tag.table';
import { ficheActionTable } from './fiche-action.table';

export const ficheActionPiloteTable = pgTable(
'fiche_action_pilote',
{
fiche_id: integer('fiche_id').references(() => ficheActionTable.id),
tag_id: integer('tag_id').references(() => personneTagTable.id),
user_id: uuid('user_id'), // references dcp
},
(table) => {
return {
one_user_per_fiche: uniqueIndex('one_user_per_fiche ').on(
table.fiche_id,
table.user_id,
),
one_tag_per_fiche: uniqueIndex('one_tag_per_fiche ').on(
table.fiche_id,
table.tag_id,
),
};
},
);
19 changes: 19 additions & 0 deletions backend/src/fiches/models/fiche-action-referent.table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { integer, pgTable, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
import { personneTagTable } from '../../taxonomie/models/personne-tag.table';
import { ficheActionTable } from './fiche-action.table';

export const ficheActionReferentTable = pgTable(
'fiche_action_referent',
{
fiche_id: integer('fiche_id').references(() => ficheActionTable.id),
tag_id: integer('tag_id').references(() => personneTagTable.id),
user_id: uuid('user_id'), // references dcp
},
(table) => {
return {
fiche_action_referent_fiche_id_user_id_tag_id_key: uniqueIndex(
'fiche_action_referent_fiche_id_user_id_tag_id_key ',
).on(table.fiche_id, table.user_id, table.tag_id),
};
},
);
18 changes: 18 additions & 0 deletions backend/src/fiches/models/fiche-action-service.table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { integer, pgTable, primaryKey } from 'drizzle-orm/pg-core';
import { serviceTagTable } from '../../taxonomie/models/service-tag.table';
import { ficheActionTable } from './fiche-action.table';

export const ficheActionServiceTagTable = pgTable(
'fiche_action_service_tag',
{
fiche_id: integer('fiche_id').references(() => ficheActionTable.id),
service_tag_id: integer('service_tag_id').references(
() => serviceTagTable.id,
),
},
(table) => {
return {
pk: primaryKey({ columns: [table.fiche_id, table.service_tag_id] }),
};
},
);
Loading

0 comments on commit 376d9cc

Please sign in to comment.