Skip to content

Commit

Permalink
feat(backend/personnalisations,badkend/referentiels): ajout du calcul…
Browse files Browse the repository at this point in the history
… de score
  • Loading branch information
dthib committed Oct 2, 2024
1 parent 6e79eef commit ec838e8
Show file tree
Hide file tree
Showing 74 changed files with 24,457 additions and 58 deletions.
4 changes: 4 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { CollectivitesModule } from './collectivites/collectivites.module';
import { CommonModule } from './common/common.module';
import { validateBackendConfiguration } from './common/services/backend-configuration.service';
import { IndicateursModule } from './indicateurs/indicateurs.module';
import { PersonnalisationsModule } from './personnalisations/personnalisations.module';
import { ReferentielsModule } from './referentiels/referentiels.module';
import { SheetModule } from './spreadsheets/sheet.module';
import { TrpcRouter } from './trpc.router';
import { TrpcModule } from './trpc/trpc.module';
Expand All @@ -21,6 +23,8 @@ import { TrpcModule } from './trpc/trpc.module';
CollectivitesModule,
IndicateursModule,
AuthModule,
PersonnalisationsModule,
ReferentielsModule,
],
controllers: [],
exports: [TrpcRouter],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Reflector } from '@nestjs/core';

export const AllowAnonymousAccess = Reflector.createDecorator<boolean>();
3 changes: 3 additions & 0 deletions backend/src/auth/decorators/allow-public-access.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Reflector } from '@nestjs/core';

export const AllowPublicAccess = Reflector.createDecorator<boolean>();
3 changes: 0 additions & 3 deletions backend/src/auth/decorators/public-endpoint.decorator.ts

This file was deleted.

28 changes: 19 additions & 9 deletions backend/src/auth/guards/auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';
import BackendConfigurationService from '../../common/services/backend-configuration.service';
import { getErrorMessage } from '../../common/services/errors.helper';
import { PublicEndpoint } from '../decorators/public-endpoint.decorator';
import { AllowAnonymousAccess } from '../decorators/allow-anonymous-access.decorator';
import { AllowPublicAccess } from '../decorators/allow-public-access.decorator';
import { SupabaseJwtPayload } from '../models/auth.models';

@Injectable()
Expand All @@ -19,18 +20,18 @@ export class AuthGuard implements CanActivate {
constructor(
private jwtService: JwtService,
private reflector: Reflector,
private backendConfigurationService: BackendConfigurationService,
private backendConfigurationService: BackendConfigurationService
) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();

const publicEndpoint = this.reflector.get(
PublicEndpoint,
context.getHandler(),
const allowPublicAccess = this.reflector.get(
AllowPublicAccess,
context.getHandler()
);

if (publicEndpoint) {
if (allowPublicAccess) {
this.logger.log(`Public endpoint`);
return true;
}
Expand All @@ -46,15 +47,24 @@ export class AuthGuard implements CanActivate {
secret:
this.backendConfigurationService.getBackendConfiguration()
.SUPABASE_JWT_SECRET,
},
}
);
// 💡 We're assigning the payload to the request object here
// so that we can access it in our route handlers
request['tokenInfo'] = payload;
this.logger.log(`Token validated for user ${payload.sub}`);
if (payload.is_anonymous) {
this.logger.error(`Anonymous user is not allowed`);
throw new UnauthorizedException();
const allowAnonymousAccess = this.reflector.get(
AllowAnonymousAccess,
context.getHandler()
);
if (allowAnonymousAccess) {
this.logger.log(`Anonymous user is allowed`);
return true;
} else {
this.logger.error(`Anonymous user is not allowed`);
throw new UnauthorizedException();
}
}
} catch (err) {
this.logger.error(`Failed to validate token: ${getErrorMessage(err)}`);
Expand Down
3 changes: 2 additions & 1 deletion backend/src/collectivites/collectivites.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Module } from '@nestjs/common';
import { CommonModule } from '../common/common.module';
import { CollectiviteController } from './controllers/collectivite.controller';
import CollectivitesService from './services/collectivites.service';
import GroupementsService from './services/groupements.service';

@Module({
imports: [CommonModule],
providers: [CollectivitesService, GroupementsService],
exports: [CollectivitesService, GroupementsService],
controllers: [],
controllers: [CollectiviteController],
})
export class CollectivitesModule {}
24 changes: 24 additions & 0 deletions backend/src/collectivites/controllers/collectivite.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Controller, Get, Param } from '@nestjs/common';
import { ApiOkResponse } from '@nestjs/swagger';
import { AllowPublicAccess } from '../../auth/decorators/allow-public-access.decorator';
import CollectivitesService from '../services/collectivites.service';

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

@Controller()
export class CollectiviteController {
constructor(private readonly collectiviteService: CollectivitesService) {}

@AllowPublicAccess()
@Get('collectivites/:collectivite_id')
@ApiOkResponse({
//type: VersionResponseClass,
description: "Récupération des informations d'une collectivite",
})
async getCollectivite(@Param('collectivite_id') collectiviteId: number) {
return this.collectiviteService.getCollectivite(collectiviteId);
}
}
88 changes: 88 additions & 0 deletions backend/src/collectivites/models/collectivite.models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
boolean,
integer,
pgEnum,
pgSchema,
pgTable,
serial,
timestamp,
Expand All @@ -13,6 +14,15 @@ import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
// TODO: create domain siren as varchar(9) check ( value ~ '^\d{9}$' );
// TODO: create domain codegeo as varchar(5);

export enum CollectiviteTypeEnum {
EPCI = 'EPCI',
COMMUNE = 'commune',
}

export enum CollectiviteSousTypeEnum {
SYNDICAT = 'syndicat',
}

export const epciNatureEnum = pgEnum('nature', [
'SMF',
'CU',
Expand Down Expand Up @@ -66,3 +76,81 @@ export const communeTable = pgTable('commune', {
});
export type CommuneType = InferSelectModel<typeof communeTable>;
export type CreateCommuneType = InferInsertModel<typeof communeTable>;

export type CollectiviteAvecType = Omit<
CollectiviteType &
Partial<CommuneType> &
Partial<EpciType> & {
type: CollectiviteTypeEnum;
soustype: CollectiviteSousTypeEnum | null;
population_tags: CollectivitePopulationTypeEnum[];
drom: boolean;
},
'collectivite_id'
>;

export enum CollectivitePopulationTypeEnum {
MOINS_DE_5000 = 'moins_de_5000',
MOINS_DE_10000 = 'moins_de_10000',
MOINS_DE_20000 = 'moins_de_20000',
MOINS_DE_50000 = 'moins_de_50000',
MOINS_DE_100000 = 'moins_de_100000',
PLUS_DE_20000 = 'plus_de_20000',
PLUS_DE_100000 = 'plus_de_100000',
}
export const typePopulationEnum = pgEnum('type_population', [
CollectivitePopulationTypeEnum.MOINS_DE_5000,
CollectivitePopulationTypeEnum.MOINS_DE_10000,
CollectivitePopulationTypeEnum.MOINS_DE_20000,
CollectivitePopulationTypeEnum.MOINS_DE_50000,
CollectivitePopulationTypeEnum.MOINS_DE_100000,
CollectivitePopulationTypeEnum.PLUS_DE_20000,
CollectivitePopulationTypeEnum.PLUS_DE_100000,
]);

export enum CollectiviteLocalisationTypeEnum {
DOM = 'DOM',
METROPOLE = 'Metropole',
}
export const typeLocalisationEnum = pgEnum('type_localisation', [
CollectiviteLocalisationTypeEnum.DOM,
CollectiviteLocalisationTypeEnum.METROPOLE,
]);

export interface IdentiteCollectivite {
type: CollectiviteTypeEnum;
soustype: CollectiviteSousTypeEnum | null;
population_tags: CollectivitePopulationTypeEnum[];
drom: boolean;
}

export const importsSchema = pgSchema('imports');

export const regionTable = importsSchema.table('region', {
code: varchar('code', { length: 2 }),
population: integer('population').notNull(),
libelle: varchar('libelle', { length: 30 }),
drom: boolean('drom').notNull(),
});
export type RegionType = InferSelectModel<typeof regionTable>;
export type CreateRegionType = InferInsertModel<typeof regionTable>;

export const banaticTable = importsSchema.table('banatic', {
siren: varchar('siren', { length: 9 }).primaryKey(),
libelle: varchar('libelle', { length: 250 }),
region_code: varchar('region_code', { length: 2 }),
departement_code: varchar('departement_code', { length: 3 }),
nature: epciNatureEnum('nature').notNull(),
population: integer('population').notNull(),
});

export type BanaticType = InferSelectModel<typeof banaticTable>;
export type CreateBanaticType = InferInsertModel<typeof banaticTable>;

export const importCommuneTable = importsSchema.table('commune', {
code: varchar('code', { length: 5 }).primaryKey(),
region_code: varchar('region_code', { length: 2 }),
departement_code: varchar('departement_code', { length: 3 }),
libelle: varchar('libelle', { length: 30 }),
population: integer('population').notNull(),
});
96 changes: 95 additions & 1 deletion backend/src/collectivites/services/collectivites.service.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,103 @@
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { eq } from 'drizzle-orm';
import { aliasedTable, eq, or } from 'drizzle-orm';
import DatabaseService from '../../common/services/database.service';
import {
banaticTable,
CollectiviteAvecType,
CollectivitePopulationTypeEnum,
CollectiviteSousTypeEnum,
collectiviteTable,
CollectiviteTypeEnum,
communeTable,
CommuneType,
epciTable,
EpciType,
importCommuneTable,
regionTable,
} from '../models/collectivite.models';

@Injectable()
export default class CollectivitesService {
private readonly logger = new Logger(CollectivitesService.name);

private readonly POPULATION_BORNES_SUP = [10000, 20000, 50000, 100000];
private readonly POPULATION_BORNES_INF = [20000, 100000];

constructor(private readonly databaseService: DatabaseService) {}

getPopulationTags(population?: number): CollectivitePopulationTypeEnum[] {
const populationTags: CollectivitePopulationTypeEnum[] = [];
if (!population) {
return populationTags;
}
this.POPULATION_BORNES_SUP.forEach((borneSup) => {
if (population < borneSup) {
populationTags.push(
`moins_de_${borneSup}` as CollectivitePopulationTypeEnum,
);
}
});
this.POPULATION_BORNES_INF.forEach((borneInf) => {
if (population > borneInf) {
populationTags.push(
`plus_de_${borneInf}` as CollectivitePopulationTypeEnum,
);
}
});
return populationTags;
}

getCollectiviteSousType(collectivite: {
epci: EpciType | null;
commune: CommuneType | null;
}): CollectiviteSousTypeEnum | null {
if (collectivite.epci) {
if (
collectivite.epci.nature === 'SMF' ||
collectivite.epci.nature === 'SIVOM' ||
collectivite.epci.nature === 'SMO' ||
collectivite.epci.nature === 'SIVU'
) {
return CollectiviteSousTypeEnum.SYNDICAT;
}
}
return null;
}

async getCollectiviteAvecType(
collectiviteId: number,
): Promise<CollectiviteAvecType> {
const collectivite = await this.getCollectivite(collectiviteId);
return {
...collectivite.banatic,
// @ts-ignore: TODO: fix this, pourquoi manque dans drizzle à cause de la jointure?
...collectivite.import_commune,
...collectivite.commune,
...collectivite.epci,
...collectivite.collectivite,
type: collectivite.epci
? CollectiviteTypeEnum.EPCI
: CollectiviteTypeEnum.COMMUNE,
soustype: this.getCollectiviteSousType(collectivite),
population_tags: this.getPopulationTags(
collectivite.commune?.population ||
// @ts-ignore: TODO: fix this, pourquoi manque dans drizzle à cause de la jointure?
collectivite.import_commune?.population ||
collectivite.banatic?.population,
),
drom: collectivite.region?.drom || false,
};
}

async getCollectivite(collectiviteId: number) {
this.logger.log(
`Récupération de la collectivite avec l'identifiant ${collectiviteId}`,
);

const importCommuneAliasedTable = aliasedTable(
importCommuneTable,
'import_commune',
);
const collectiviteByIdResult = await this.databaseService.db
.select()
.from(collectiviteTable)
Expand All @@ -25,7 +106,20 @@ export default class CollectivitesService {
eq(communeTable.collectivite_id, collectiviteTable.id),
)
.leftJoin(epciTable, eq(epciTable.collectivite_id, collectiviteTable.id))
.leftJoin(banaticTable, eq(banaticTable.siren, epciTable.siren))
.leftJoin(
importCommuneAliasedTable,
eq(importCommuneAliasedTable.code, communeTable.code),
)
.leftJoin(
regionTable,
or(
eq(regionTable.code, banaticTable.region_code),
eq(regionTable.code, importCommuneAliasedTable.region_code),
),
)
.where(eq(collectiviteTable.id, collectiviteId));

if (!collectiviteByIdResult?.length) {
throw new NotFoundException(
`Commune avec l'identifiant de collectivite ${collectiviteId} introuvable`,
Expand Down
4 changes: 2 additions & 2 deletions backend/src/common/controllers/version.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createZodDto } from '@anatine/zod-nestjs';
import { Controller, Get } from '@nestjs/common';
import { ApiOkResponse } from '@nestjs/swagger';
import { PublicEndpoint } from '../../auth/decorators/public-endpoint.decorator';
import { AllowPublicAccess } from '../../auth/decorators/allow-public-access.decorator';
import { versionResponseSchema } from '../models/version.models';

/**
Expand All @@ -10,7 +10,7 @@ import { versionResponseSchema } from '../models/version.models';
export class VersionResponseClass extends createZodDto(versionResponseSchema) {}
@Controller()
export class VersionController {
@PublicEndpoint()
@AllowPublicAccess()
@Get('version')
@ApiOkResponse({
type: VersionResponseClass,
Expand Down
7 changes: 7 additions & 0 deletions backend/src/common/services/number.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function roundTo(num: number | null, precision: number): number | null {
if (!num) {
return num;
}
const factor = Math.pow(10, precision);
return Math.round((num + Number.EPSILON) * factor) / factor;
}
Loading

0 comments on commit ec838e8

Please sign in to comment.