Skip to content

Commit

Permalink
fix: Merge branch 'master' into TM-1752-Dashboard-Mission-Locales
Browse files Browse the repository at this point in the history
  • Loading branch information
Pomarom committed Dec 17, 2024
2 parents cdaef24 + 9ded7ab commit 223d3f2
Show file tree
Hide file tree
Showing 48 changed files with 895 additions and 386 deletions.
4 changes: 2 additions & 2 deletions .infra/files/configs/mongodb/seed.gpg
Git LFS file not shown
2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"init:dev": "yarn cli init:dev",
"build": "tsup-node --env.NODE_ENV production",
"typecheck": "tsc --noEmit",
"generate:import": "tsx scripts/generate-import-effectif.ts generate"
"generate:import": "tsx scripts/index.ts generate"
},
"dependencies": {
"@asteasolutions/zod-to-openapi": "^6.4.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { writeFileSync } from "fs";

import { faker as fakerEn, Faker, fr } from "@faker-js/faker";
import { program } from "commander";
import { config } from "dotenv";
import { program, type Command } from "commander";
import { MongoClient } from "mongodb";
import { IFormationCatalogue } from "shared/models/data/formationsCatalogue.model";
import { IOrganisme } from "shared/models/data/organismes.model";
import { write, utils } from "xlsx";

import { getMongodbUri } from "@/common/mongodb";

config({ path: ".env" });
config({ path: ".env.local", override: true });

const fakerFr = new Faker({ locale: [fr] });

function optional(data) {
Expand All @@ -29,15 +23,17 @@ function fakeEffectif(formateur: IOrganisme, formation: IFormationCatalogue) {
{ value: 3, weight: 10 },
]);

const dateInscription = fakerFr.date.past({
years: anneeFormation,
});
const dateEntree = fakerEn.date.soon({
const dateEntree = formation.date_debut?.at(-1) ?? null;
const dateFin = formation.date_fin?.at(-1) ?? null;

if (!dateEntree || !dateFin) {
throw new Error('Formation "date_debut" or "date_fin" is missing');
}

const dateInscription = fakerFr.date.recent({
days: 90,
refDate: dateInscription,
refDate: new Date(dateEntree),
});
const dateFin = new Date(dateInscription);
dateFin.setFullYear(dateInscription.getFullYear() + anneeFormation);

return {
nom_apprenant: lastName,
Expand Down Expand Up @@ -84,13 +80,13 @@ function fakeEffectif(formateur: IOrganisme, formation: IFormationCatalogue) {
etablissement_lieu_de_formation_siret: formateur.siret,
etablissement_lieu_de_formation_adresse: fakerFr.location.streetAddress(),
etablissement_lieu_de_formation_code_postal: fakerFr.helpers.fromRegExp("[0-9]{5}"),
annee_scolaire: fakerFr.helpers.arrayElement(["2023-2024", "2024-2024"]),
annee_scolaire: fakerFr.helpers.arrayElement(["2024-2025", "2025-2025"]),
annee_formation: anneeFormation,
formation_rncp: optional(formation.rncp_code),
formation_cfd: formation.cfd,
date_inscription_formation: dateInscription,
date_entree_formation: dateEntree,
date_fin_formation: dateFin,
date_entree_formation: new Date(dateEntree),
date_fin_formation: new Date(dateFin),
duree_theorique_formation_mois: Number(formation.duree) * 12,
libelle_court_formation: formation.intitule_court ?? formation.intitule_long,
obtention_diplome_formation: "",
Expand Down Expand Up @@ -124,59 +120,46 @@ function fakeEffectif(formateur: IOrganisme, formation: IFormationCatalogue) {
};
}

let client: MongoClient | null = null;

program
.configureHelp({
sortSubcommands: true,
})
.showSuggestionAfterError()
.hook("preAction", async () => {
client = new MongoClient(getMongodbUri() ?? "");
await client.connect();
})
.hook("postAction", async () => {
await client?.close();
})
.command("generate <siret>")
.option("-c, --count <number>", "Count", "50")
.option("-n, --name <string>", "Name", "import")
.action(async (siret, { count, name }) => {
if (!client) {
throw new Error("Missing mongodb client");
}

const formateur = await client.db("flux-retour-cfas").collection<IOrganisme>("organismes").findOne({ siret });

if (formateur === null) {
throw new Error("OF not found");
}

if (formateur.relatedFormations == null) {
throw new Error("Not related formation");
}

const formationCles = formateur.relatedFormations.map((f) => f.cle_ministere_educatif);
const formations: IFormationCatalogue[] = (await client
.db("flux-retour-cfas")
.collection("formationsCatalogue")
.find({ cle_ministere_educatif: { $in: formationCles } })
.toArray()) as any;

if (formations.length === 0) {
throw new Error("Formation not found");
}

const data: any[] = [];
for (let i = 0; i < Number(count); i += 1) {
data.push(fakeEffectif(formateur, fakerEn.helpers.arrayElement(formations)));
}

const worksheet = utils.json_to_sheet(data);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Sheet");

writeFileSync(`./outputs/${name}.xlsx`, write(workbook, { type: "buffer" }));
});
export default function registerCommand(program: Command, getClient: () => MongoClient) {
program
.command("generate <siret>")
.option("-c, --count <number>", "Count", "50")
.option("-n, --name <string>", "Name", "import")
.action(async (siret, { count, name }) => {
const client = getClient();

const formateur = await client.db("flux-retour-cfas").collection<IOrganisme>("organismes").findOne({ siret });

if (formateur === null) {
throw new Error("OF not found");
}

if (formateur.relatedFormations == null) {
throw new Error("Not related formation");
}

const formationCles = formateur.relatedFormations.map((f) => f.cle_ministere_educatif);
const formations: IFormationCatalogue[] = (await client
.db("flux-retour-cfas")
.collection("formationsCatalogue")
.find({ cle_ministere_educatif: { $in: formationCles } })
.toArray()) as any;

if (formations.length === 0) {
throw new Error("Formation not found");
}

const data: any[] = [];
for (let i = 0; i < Number(count); i += 1) {
data.push(fakeEffectif(formateur, fakerEn.helpers.arrayElement(formations)));
}

const worksheet = utils.json_to_sheet(data);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Sheet");

writeFileSync(`./outputs/${name}.xlsx`, write(workbook, { type: "buffer" }));
});
}

await program.parseAsync(process.argv);
7 changes: 7 additions & 0 deletions server/scripts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { config } from "dotenv";

config({ path: ".env" });
config({ path: ".env.local", override: true });

// Dynamic import to start server after env are loaded
import("./main");
31 changes: 31 additions & 0 deletions server/scripts/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { program } from "commander";
import { MongoClient } from "mongodb";

import { getMongodbUri } from "@/common/mongodb";

import registerCommand from "./commands/generate-import-effectif";

let client: MongoClient | null = null;

const getClient = (): MongoClient => {
if (!client) {
throw new Error("Missing mongodb client");
}
return client;
};

program
.configureHelp({
sortSubcommands: true,
})
.showSuggestionAfterError()
.hook("preAction", async () => {
client = new MongoClient(getMongodbUri() ?? "");
await client.connect();
})
.hook("postAction", async () => {
await client?.close();
});

registerCommand(program, getClient);
await program.parseAsync(process.argv);
19 changes: 13 additions & 6 deletions server/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,19 @@ program
.option("-q, --queued", "Run job asynchronously", false)
.action(createJobAction("send-reminder-emails"));

program
.command("hydrate:contrats-deca-raw")
.description("Manually trigger the creation of deca effectifs")
.option("-q, --queued", "Run job asynchronously", false)
.action(createJobAction("hydrate:contrats-deca-raw"));

program
.command("hydrate:effectifs:update_computed_statut")
.description("Manually trigger the update of the computed")
.option("--id <string>", "Id de l'organisme", (organismeId) => organismeId, null)
.option("-q, --queued", "Run job asynchronously", false)
.action(createJobAction("hydrate:effectifs:update_computed_statut"));

program
.command("job:run")
.description("Run a job")
Expand All @@ -304,12 +317,6 @@ program
return createJobAction(name)(options);
});

program
.command("hydrate:contrats-deca-raw")
.description("Manually trigger the creation of deca effectifs")
.option("-q, --queued", "Run job asynchronously", false)
.action(createJobAction("hydrate:contrats-deca-raw"));

export async function startCLI() {
await program.parseAsync(process.argv);
}
1 change: 1 addition & 0 deletions server/src/common/actions/effectifs.statut.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export function createComputedStatutObject(
function createUpdateObject(effectif: IEffectif, evaluationDate: Date): UpdateFilter<IEffectif> {
return {
$set: {
updated_at: new Date(),
"_computed.statut": createComputedStatutObject(effectif, evaluationDate),
},
};
Expand Down
5 changes: 4 additions & 1 deletion server/src/common/actions/erp.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import Boom from "boom";
import { ObjectId } from "mongodb";

import { erpDb } from "../model/collections";
import { slugify } from "../utils/stringUtils";

export const createERP = async (name: string) => {
const uniqueId = slugify(name);

return erpDb().insertOne({
_id: new ObjectId(),
name,
created_at: new Date(),
apiV3: true,
unique_id: name.toLowerCase(),
unique_id: uniqueId,
});
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ObjectId } from "mongodb";
import { TypeEffectifNominatif } from "shared/constants/indicateurs";
import { Acl } from "shared/constants/permissions";
import { IOrganisation } from "shared/models";
import { IOrganisation, type IndicateursEffectifsAvecFormation } from "shared/models";

import { effectifsDECADb, effectifsDb } from "@/common/model/collections";
import { AuthContext } from "@/common/model/internal/AuthContext";
Expand Down Expand Up @@ -113,7 +113,7 @@ export const getOrganismeIndicateursEffectifsParFormation = async (
ctx: AuthContext,
organismeId: ObjectId,
filters: FullEffectifsFilters
) => {
): Promise<IndicateursEffectifsAvecFormation[]> => {
const indicateurs = [
...(await getOrganismeIndicateursEffectifsParFormationGenerique(ctx, organismeId, filters, effectifsDb())),
...(await getOrganismeIndicateursEffectifsParFormationGenerique(
Expand All @@ -125,29 +125,31 @@ export const getOrganismeIndicateursEffectifsParFormation = async (
)),
];

const mapRNCP = indicateurs.reduce((acc, { rncp_code, ...rest }) => {
// TODO: get niveau from CFD if not found
const rncp = rncp_code ?? "null";
return acc[rncp]
? {
...acc,
[rncp]: {
rncp_code,
apprentis: acc[rncp].apprentis + rest.apprentis,
abandons: acc[rncp].abandons + rest.abandons,
inscrits: acc[rncp].inscrits + rest.inscrits,
apprenants: acc[rncp].apprenants + rest.apprenants,
rupturants: acc[rncp].rupturants + rest.rupturants,
},
}
: {
...acc,
[rncp]: {
rncp_code,
...rest,
},
const mapRNCP = indicateurs.reduce<Record<string, IndicateursEffectifsAvecFormation>>(
(acc, { rncp_code, cfd_code, ...rest }) => {
const id = `${rncp_code ?? "null"}-${cfd_code ?? "null"}`;

if (acc[id] == null) {
acc[id] = {
rncp_code,
cfd_code,
...rest,
};
}, {});
} else {
acc[id] = {
...acc[id],
apprentis: acc[id].apprentis + rest.apprentis,
abandons: acc[id].abandons + rest.abandons,
inscrits: acc[id].inscrits + rest.inscrits,
apprenants: acc[id].apprenants + rest.apprenants,
rupturants: acc[id].rupturants + rest.rupturants,
};
}

return acc;
},
{}
);

return Object.values(mapRNCP);
};
Loading

0 comments on commit 223d3f2

Please sign in to comment.