Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: ajout des filtres sur les missions locales #3947

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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());
Loading