Skip to content

Commit

Permalink
feat: add comment to model voie (#492)
Browse files Browse the repository at this point in the history
* feat: add comment to model voie

* correct

* add export comentaire voie

* return Jules

* delete cookie

* correct

* correct test

* correct

* correct lint

* factorise find voir metas
  • Loading branch information
fufeck authored Dec 18, 2024
1 parent 9501e64 commit e4a1c49
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 98 deletions.
30 changes: 28 additions & 2 deletions apps/api/src/modules/base_locale/base_locale.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import { ExtentedToponymeDTO } from '@/modules/toponyme/dto/extended_toponyme.dt
import { CreateToponymeDTO } from '@/modules/toponyme/dto/create_toponyme.dto';
import { filterSensitiveFields } from '@/modules/base_locale/utils/base_locale.utils';
import { ExtendedBaseLocaleDTO } from './dto/extended_base_locale.dto';
import { ExtendedVoieDTO } from '../voie/dto/extended_voie.dto';
import { ExtendedVoieDTO, VoieMetas } from '../voie/dto/extended_voie.dto';
import { UpdateBaseLocaleDTO } from './dto/update_base_locale.dto';
import { UpdateBaseLocaleDemoDTO } from './dto/update_base_locale_demo.dto';
import { CreateDemoBaseLocaleDTO } from './dto/create_demo_base_locale.dto';
Expand All @@ -68,6 +68,7 @@ import { BatchNumeroResponseDTO } from '../numeros/dto/batch_numero_response.dto
import { isSuperAdmin } from '@/lib/utils/is-admin.utils';
import { SearchNumeroDTO } from '../numeros/dto/search_numero.dto';
import { Numero } from '@/shared/entities/numero.entity';
import { filterComments } from '@/shared/utils/filter.utils';

@ApiTags('bases-locales')
@Controller('bases-locales')
Expand Down Expand Up @@ -595,11 +596,36 @@ export class BaseLocaleController {
const voies: Voie[] = await this.voieService.findMany({
balId: req.baseLocale.id,
});

const extendedVoie: ExtendedVoieDTO[] = await this.voieService.extendVoies(
req.baseLocale.id,
voies,
);
res.status(HttpStatus.OK).json(extendedVoie);
const voiesFiltered: ExtendedVoieDTO[] = extendedVoie.map((v) =>
filterComments(v, !req.isAdmin),
);
res.status(HttpStatus.OK).json(voiesFiltered);
}

@Get(':baseLocaleId/voies/metas')
@ApiOperation({
summary: 'Find all Metas Voie in Bal',
operationId: 'findVoieMetasByBal',
})
@ApiParam({ name: 'baseLocaleId', required: true, type: String })
@ApiResponse({
status: HttpStatus.OK,
type: VoieMetas,
isArray: true,
})
@ApiBearerAuth('admin-token')
@UseGuards(AdminGuard)
async findVoieMetasByBal(@Req() req: CustomRequest, @Res() res: Response) {
const voiesMetas: VoieMetas[] = await this.voieService.findVoiesMetas(
req.baseLocale.id,
);

res.status(HttpStatus.OK).json(voiesMetas);
}

@Post(':baseLocaleId/voies')
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/modules/numeros/numero.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import {
} from '@nestjs/swagger';

import { Numero } from '@/shared/entities/numero.entity';
import { filterSensitiveFields } from '@/shared/utils/numero.utils';

import { CustomRequest } from '@/lib/types/request.type';
import { AdminGuard } from '@/lib/guards/admin.guard';
import { NumeroService } from '@/modules/numeros/numero.service';
import { UpdateNumeroDTO } from '@/modules/numeros/dto/update_numero.dto';
import { filterComments } from '@/shared/utils/filter.utils';

@ApiTags('numeros')
@Controller('numeros')
Expand All @@ -41,7 +41,7 @@ export class NumeroController {
@ApiResponse({ status: HttpStatus.OK, type: Numero })
@ApiBearerAuth('admin-token')
find(@Req() req: CustomRequest, @Res() res: Response) {
const numero: Numero = filterSensitiveFields(req.numero, !req.isAdmin);
const numero: Numero = filterComments(req.numero, !req.isAdmin);
res.status(HttpStatus.OK).json(numero);
}

Expand Down
25 changes: 0 additions & 25 deletions apps/api/src/modules/numeros/numero.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,31 +88,6 @@ export class NumeroService {
});
}

async countVoiesNumeroAndCertifie(balId: string): Promise<
{
voieId: string;
nbNumeros: string;
nbNumerosCertifies: string;
comments: string[];
}[]
> {
const query = this.numerosRepository
.createQueryBuilder('numeros')
.select('numeros.voie_id', 'voieId')
.addSelect('count(numeros.id)', 'nbNumeros')
.addSelect(
'count(CASE WHEN numeros.certifie THEN true END)',
'nbNumerosCertifies',
)
.addSelect(
`array_remove(array_agg(CASE WHEN numeros.comment IS NOT NULL THEN concat(numeros.numero, numeros.suffixe, ' - ', numeros.comment) END), NULL)`,
'comments',
)
.where('numeros.bal_id = :balId', { balId })
.groupBy('numeros.voie_id');
return query.getRawMany();
}

async countBalNumeroAndCertifie(balId: string): Promise<{
nbNumeros: string;
nbNumerosCertifies: string;
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/modules/toponyme/toponyme.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
} from '@nestjs/swagger';

import { Toponyme } from '@/shared/entities/toponyme.entity';
import { filterSensitiveFields } from '@/shared/utils/numero.utils';

import { CustomRequest } from '@/lib/types/request.type';
import { AdminGuard } from '@/lib/guards/admin.guard';
Expand All @@ -31,6 +30,7 @@ import { ExtentedToponymeDTO } from '@/modules/toponyme/dto/extended_toponyme.dt
import { UpdateToponymeDTO } from '@/modules/toponyme/dto/update_toponyme.dto';
import { NumeroService } from '@/modules/numeros/numero.service';
import { Numero } from '@/shared/entities/numero.entity';
import { filterComments } from '@/shared/utils/filter.utils';

@ApiTags('toponymes')
@Controller('toponymes')
Expand Down Expand Up @@ -133,7 +133,7 @@ export class ToponymeController {
null,
{ voie: true },
);
const result = numeros.map((n) => filterSensitiveFields(n, !req.isAdmin));
const result = numeros.map((n) => filterComments(n, !req.isAdmin));
res.status(HttpStatus.OK).json(result);
}
}
6 changes: 6 additions & 0 deletions apps/api/src/modules/voie/dto/create_voie.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ValidateNested,
IsNotEmptyObject,
IsEnum,
MaxLength,
} from 'class-validator';

import { TypeNumerotationEnum } from '@/shared/entities/voie.entity';
Expand Down Expand Up @@ -37,4 +38,9 @@ export class CreateVoieDTO {
nullable: false,
})
trace: LineString;

@IsOptional()
@MaxLength(5000)
@ApiProperty({ required: false, nullable: true })
comment?: string;
}
14 changes: 11 additions & 3 deletions apps/api/src/modules/voie/dto/extended_voie.dto.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { ApiProperty } from '@nestjs/swagger';
import { ApiProperty, IntersectionType } from '@nestjs/swagger';

import { Voie } from '@/shared/entities/voie.entity';

export class ExtendedVoieDTO extends Voie {
export class VoieMetas {
@ApiProperty()
id: string;

@ApiProperty()
nbNumeros?: number;

Expand All @@ -13,5 +16,10 @@ export class ExtendedVoieDTO extends Voie {
isAllCertified?: boolean;

@ApiProperty()
comments?: string[];
comment?: string;

@ApiProperty()
commentedNumeros?: string[];
}

export class ExtendedVoieDTO extends IntersectionType(Voie, VoieMetas) {}
6 changes: 6 additions & 0 deletions apps/api/src/modules/voie/dto/update_voie.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ValidateNested,
IsNotEmptyObject,
IsEnum,
MaxLength,
} from 'class-validator';

import { TypeNumerotationEnum } from '@/shared/entities/voie.entity';
Expand Down Expand Up @@ -38,4 +39,9 @@ export class UpdateVoieDTO {
nullable: false,
})
trace: LineString;

@IsOptional()
@MaxLength(5000)
@ApiProperty({ required: false, nullable: true })
comment?: string;
}
36 changes: 31 additions & 5 deletions apps/api/src/modules/voie/voie.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@ import {

import { Voie } from '@/shared/entities/voie.entity';
import { Numero } from '@/shared/entities/numero.entity';
import { filterSensitiveFields } from '@/shared/utils/numero.utils';
import { Toponyme } from '@/shared/entities/toponyme.entity';

import { CustomRequest } from '@/lib/types/request.type';
import { AdminGuard } from '@/lib/guards/admin.guard';
import { VoieService } from '@/modules/voie/voie.service';
import { ExtendedVoieDTO } from '@/modules/voie/dto/extended_voie.dto';
import {
ExtendedVoieDTO,
VoieMetas,
} from '@/modules/voie/dto/extended_voie.dto';
import { UpdateVoieDTO } from '@/modules/voie/dto/update_voie.dto';
import { RestoreVoieDTO } from '@/modules/voie/dto/restore_voie.dto';
import { CreateNumeroDTO } from '@/modules/numeros/dto/create_numero.dto';
import { NumeroService } from '@/modules/numeros/numero.service';
import { filterComments } from '@/shared/utils/filter.utils';

@ApiTags('voies')
@Controller('voies')
Expand All @@ -52,12 +55,35 @@ export class VoieController {
@ApiResponse({ status: HttpStatus.OK, type: ExtendedVoieDTO })
@ApiBearerAuth('admin-token')
async find(@Req() req: CustomRequest, @Res() res: Response) {
const voieExtended: ExtendedVoieDTO = await this.voieService.extendVoie(
req.voie,
const voieMetas: VoieMetas = await this.voieService.findVoieMetas(
req.voie.id,
);
const voieExtended: ExtendedVoieDTO = filterComments(
{
...req.voie,
...voieMetas,
},
!req.isAdmin,
);
res.status(HttpStatus.OK).json(voieExtended);
}

@Get(':voieId/metas')
@ApiOperation({
summary: 'Find Voie Metas by id',
operationId: 'findVoieMetas',
})
@ApiParam({ name: 'voieId', required: true, type: String })
@ApiResponse({ status: HttpStatus.OK, type: VoieMetas })
@ApiBearerAuth('admin-token')
@UseGuards(AdminGuard)
async findMetas(@Req() req: CustomRequest, @Res() res: Response) {
const voieMetas: VoieMetas = await this.voieService.findVoieMetas(
req.voie.id,
);
res.status(HttpStatus.OK).json(voieMetas);
}

@Put(':voieId')
@ApiOperation({ summary: 'Update Voie by id', operationId: 'updateVoie' })
@ApiParam({ name: 'voieId', required: true, type: String })
Expand Down Expand Up @@ -141,7 +167,7 @@ export class VoieController {
},
},
);
const result = numeros.map((n) => filterSensitiveFields(n, !req.isAdmin));
const result = numeros.map((n) => filterComments(n, !req.isAdmin));
res.status(HttpStatus.OK).json(result);
}

Expand Down
98 changes: 43 additions & 55 deletions apps/api/src/modules/voie/voie.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
FindOptionsWhere,
In,
Repository,
SelectQueryBuilder,
UpdateResult,
} from 'typeorm';
import { keyBy } from 'lodash';
Expand All @@ -27,7 +28,10 @@ import { Voie, TypeNumerotationEnum } from '@/shared/entities/voie.entity';
import { Toponyme } from '@/shared/entities/toponyme.entity';

import { cleanNom, cleanNomAlt, getNomAltDefault } from '@/lib/utils/nom.util';
import { ExtendedVoieDTO } from '@/modules/voie/dto/extended_voie.dto';
import {
ExtendedVoieDTO,
VoieMetas,
} from '@/modules/voie/dto/extended_voie.dto';
import { UpdateVoieDTO } from '@/modules/voie/dto/update_voie.dto';
import { CreateVoieDTO } from '@/modules/voie/dto/create_voie.dto';
import { RestoreVoieDTO } from '@/modules/voie/dto/restore_voie.dto';
Expand Down Expand Up @@ -142,6 +146,7 @@ export class VoieService {
nomAlt: createVoieDto.nomAlt ? cleanNomAlt(createVoieDto.nomAlt) : null,
centroid: null,
bbox: null,
comment: createVoieDto.comment,
};
// Calculer le centroid si la trace et le type de numerotation est metrique
if (voie.trace && voie.typeNumerotation === TypeNumerotationEnum.METRIQUE) {
Expand Down Expand Up @@ -327,60 +332,9 @@ export class VoieService {
balId: string,
voies: Voie[],
): Promise<ExtendedVoieDTO[]> {
const voiesMetas =
await this.numeroService.countVoiesNumeroAndCertifie(balId);
const voiesMetasIndex = keyBy(voiesMetas, 'voieId');

return voies.map((voie) =>
this.extendVoieWithMeta(voie, voiesMetasIndex[voie.id]),
);
}

private extendVoieWithMeta(
voie: Voie,
voieMeta?: {
voieId: string;
nbNumeros: string;
nbNumerosCertifies: string;
comments: string[];
},
): ExtendedVoieDTO {
const nbNumeros: number = Number(voieMeta?.nbNumeros) || 0;
const nbNumerosCertifies: number =
Number(voieMeta?.nbNumerosCertifies) || 0;
return {
...voie,
nbNumeros,
nbNumerosCertifies,
isAllCertified: nbNumeros > 0 ? nbNumeros === nbNumerosCertifies : false,
comments: voieMeta?.comments || [],
};
}

public async extendVoie(voie: Voie): Promise<ExtendedVoieDTO> {
const numeros = await this.numeroService.findMany({
voieId: voie.id,
});

const nbNumerosCertifies = numeros.filter(
(n) => n.certifie === true,
).length;

return {
...voie,
nbNumeros: numeros.length,
nbNumerosCertifies: nbNumerosCertifies,
isAllCertified:
numeros.length > 0 && numeros.length === nbNumerosCertifies,
comments: numeros
.filter(
(n) =>
n.comment !== undefined && n.comment !== null && n.comment !== '',
)
.map(
({ numero, suffixe, comment }) => `${numero}${suffixe} - ${comment}`,
),
};
const voiesMetas = await this.findVoiesMetas(balId);
const voiesMetasIndex = keyBy(voiesMetas, 'id');
return voies.map((voie) => ({ ...voie, ...voiesMetasIndex[voie.id] }));
}

public async touch(voieId: string, updatedAt: Date = new Date()) {
Expand Down Expand Up @@ -447,4 +401,38 @@ export class VoieService {
trace: JSON.parse(f.trace),
}));
}

createQueryVoieMetas: SelectQueryBuilder<Voie> = this.voiesRepository
.createQueryBuilder('voies')
.select('voies.id', 'id')
.addSelect('count(numeros.id)::int', 'nbNumeros')
.addSelect(
'count(CASE WHEN numeros.certifie THEN true END)::int',
'nbNumerosCertifies',
)
.addSelect(
'CASE WHEN count(numeros.id) > 0 AND count(CASE WHEN numeros.certifie THEN true END) = count(numeros.id) THEN true ELSE false END',
'isAllCertified',
)
.addSelect('voies.comment', 'comment')
.addSelect(
`array_remove(array_agg(CASE WHEN numeros.comment IS NOT NULL THEN concat(numeros.numero, numeros.suffixe, ' - ', numeros.comment) END), NULL)`,
'commentedNumeros',
)
.leftJoin('voies.numeros', 'numeros')
.groupBy('voies.id, voies.comment');

async findVoieMetas(voieId: string): Promise<VoieMetas> {
const query = this.createQueryVoieMetas.where('voies.id = :voieId', {
voieId,
});
return query.getRawOne();
}

async findVoiesMetas(balId: string): Promise<VoieMetas[]> {
const query = this.createQueryVoieMetas.where('voies.bal_id = :balId', {
balId,
});
return query.getRawMany();
}
}
Loading

0 comments on commit e4a1c49

Please sign in to comment.