Skip to content

Commit

Permalink
fix: ajout des filtres sur les missions locales (#3947)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pomarom authored Dec 20, 2024
1 parent 218f3ce commit d1101f6
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 14 deletions.
50 changes: 49 additions & 1 deletion server/src/common/actions/helpers/filters.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Filter, RootFilterOperators } from "mongodb";
import { z } from "zod";
import { STATUT_APPRENANT } from "shared/constants";
import { zBooleanStringSchema } from "shared/models/parts/zodPrimitives";
import { z, ZodRawShape } from "zod";

export const organismeLookup = {
from: "organismes",
Expand Down Expand Up @@ -73,3 +75,49 @@ export function combineFilters<T>(...filters: Filter<T>[]): RootFilterOperators<
$and: nonEmptyFilters,
};
}

export const effectifsFiltersMissionLocaleSchema = {
statut: z
.array(z.enum([STATUT_APPRENANT.ABANDON, STATUT_APPRENANT.RUPTURANT, STATUT_APPRENANT.INSCRIT]).optional())
.optional(),
rqth: zBooleanStringSchema.optional(),
mineur: zBooleanStringSchema.optional(),
niveaux: z.array(z.string()).optional(),
code_insee: z.array(z.string()).optional(),
};

export type IEffectifsFiltersMissionLocale = z.infer<z.ZodObject<typeof effectifsFiltersMissionLocaleSchema>>;

export const paginationFiltersSchema = {
page: z.number().int().positive().optional(),
limit: z.number().int().positive().optional(),
sort: z.string().optional(),
order: z.enum(["asc", "desc"]).optional(),
};

export const withPaginationSchema = (schema: ZodRawShape) => {
return {
...schema,
...paginationFiltersSchema,
};
};

export type IPaginationFilters = z.infer<z.ZodObject<typeof paginationFiltersSchema>>;

export type WithPagination<T> = T & IPaginationFilters;

export const buildSortFilter = (sort: string, order: "asc" | "desc") => {
return {
[sort]: order === "asc" ? 1 : -1,
};
};

export const buildMineurFilter = (mineur: boolean) => {
return mineur
? {
"apprenant.date_de_naissance": { $gte: new Date(new Date().setFullYear(new Date().getFullYear() - 18)) },
}
: {
"apprenant.date_de_naissance": { $lt: new Date(new Date().setFullYear(new Date().getFullYear() - 18)) },
};
};
45 changes: 35 additions & 10 deletions server/src/common/actions/mission-locale/mission-locale.actions.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { STATUT_APPRENANT } from "shared/constants";
import { STATUT_APPRENANT, StatutApprenant } from "shared/constants";
import { IEffecifMissionLocale, IEffectif, IOrganisation, IUsersMigration } from "shared/models";
import { getAnneesScolaireListFromDate } from "shared/utils";

import { effectifsDb } from "@/common/model/collections";

import { buildEffectifForMissionLocale } from "../effectifs.actions";
import { DateFilters } from "../helpers/filters";
import { buildSortFilter, DateFilters, IEffectifsFiltersMissionLocale, WithPagination } from "../helpers/filters";
import { buildIndicateursEffectifsPipeline, filterByDernierStatutPipeline } from "../indicateurs/indicateurs.actions";

export const EFF_MISSION_LOCALE_FILTER = [
Expand All @@ -19,22 +19,45 @@ export const EFF_MISSION_LOCALE_FILTER = [
},
];

export const buildFiltersForMissionLocale = (effectifFilters: IEffectifsFiltersMissionLocale) => {
const { statut = null, rqth = null, mineur = null, niveaux = null, code_insee = null } = effectifFilters;
const filter = [
...filterByDernierStatutPipeline(
(statut as Array<StatutApprenant>) ?? [
STATUT_APPRENANT.ABANDON,
STATUT_APPRENANT.RUPTURANT,
STATUT_APPRENANT.INSCRIT,
],
new Date()
),
{
$match: {
...(rqth !== null ? { "apprenant.rqth": rqth } : {}),
...(mineur !== null
? { "apprenant.date_de_naissance": { $gte: new Date(new Date().setFullYear(new Date().getFullYear() - 18)) } }
: {}),
...(niveaux ? { "formation.niveau": { $in: niveaux } } : {}),
...(code_insee ? { "apprenant.adresse.code_insee": { $in: code_insee } } : {}),
},
},
];

return filter;
};

export const getPaginatedEffectifsByMissionLocaleId = async (
missionLocaleId: number,
page: number = 1,
limit: number = 20
effectifsFiltersMissionLocale: WithPagination<IEffectifsFiltersMissionLocale>
) => {
const { page = 1, limit = 20, sort = "nom", order = "asc", ...effectifFilters } = effectifsFiltersMissionLocale;
const aggregation = [
{
$match: {
"apprenant.adresse.mission_locale_id": missionLocaleId,
annee_scolaire: { $in: getAnneesScolaireListFromDate(new Date()) },
},
},
...filterByDernierStatutPipeline(
[STATUT_APPRENANT.ABANDON, STATUT_APPRENANT.RUPTURANT, STATUT_APPRENANT.INSCRIT],
new Date()
),
...buildFiltersForMissionLocale(effectifFilters),
...EFF_MISSION_LOCALE_FILTER,
{ $addFields: { stringify_organisme_id: { $toString: "$organisme_id" } } },
{
Expand All @@ -59,6 +82,9 @@ export const getPaginatedEffectifsByMissionLocaleId = async (
as: "cfa_users",
},
},
{
$sort: buildSortFilter(sort, order),
},
{
$facet: {
pagination: [{ $count: "total" }, { $addFields: { page, limit } }],
Expand All @@ -67,13 +93,12 @@ export const getPaginatedEffectifsByMissionLocaleId = async (
},
{ $unwind: { path: "$pagination", preserveNullAndEmptyArrays: true } },
];

const result = (await effectifsDb().aggregate(aggregation).next()) as {
pagination: any;
data: Array<IEffectif & { organisation: IOrganisation } & { cfa_users: Array<IUsersMigration> }>;
};

if (!result) {
if (!result || result?.data.length === 0) {
return { pagination: { total: 0, page, limit }, data: [] };
}
const { pagination, data } = result;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import express from "express";
import { IOrganisationMissionLocale } from "shared/models";

import { dateFiltersSchema } from "@/common/actions/helpers/filters";
import {
dateFiltersSchema,
effectifsFiltersMissionLocaleSchema,
withPaginationSchema,
} from "@/common/actions/helpers/filters";
import {
getEffectifIndicateursForMissionLocaleId,
getPaginatedEffectifsByMissionLocaleId,
Expand All @@ -22,7 +26,8 @@ const getIndicateursMissionLocale = async (req, { locals }) => {
return await getEffectifIndicateursForMissionLocaleId(filters, missionLocale.ml_id);
};

const getEffectifsMissionLocale = async (_req, { locals }) => {
const getEffectifsMissionLocale = async ({ query }, { locals }) => {
const filters = await validateFullZodObjectSchema(query, withPaginationSchema(effectifsFiltersMissionLocaleSchema));
const missionLocale = locals.missionLocale as IOrganisationMissionLocale;
return await getPaginatedEffectifsByMissionLocaleId(missionLocale.ml_id);
return await getPaginatedEffectifsByMissionLocaleId(missionLocale.ml_id, filters);
};
9 changes: 9 additions & 0 deletions shared/models/parts/zodPrimitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,3 +484,12 @@ export const primitivesV3 = {
})
),
};

export const zBooleanStringSchema = z.preprocess((v) => {
if (typeof v == "boolean") return v;
if (v === "true") {
return true;
} else if (v === "false") {
return false;
}
}, z.boolean());

0 comments on commit d1101f6

Please sign in to comment.