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

feat: prepare load testing #2031

Merged
merged 1 commit into from
Jan 16, 2024
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
1 change: 0 additions & 1 deletion packages/app/.env.development
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,4 @@ SECURITY_CHARON_URL="https://egapro-charon.dev.fabrique.social.gouv.fr"
# old EGAPRO_SENTRY_DSN
SENTRY_DSN=""
# old EGAPRO_FLAVOUR
SENTRY_ENVIRONMENT="development"
SENTRY_AUTH_TOKEN=3b4a6e7f1e2346cebd6ad7fa390d66c800dfce8331fc4982a12aafefed1cc47f
1 change: 0 additions & 1 deletion packages/app/.env.production
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,4 @@ SECURITY_CHARON_URL="https://egapro-charon.dev.fabrique.social.gouv.fr"
# old EGAPRO_SENTRY_DSN
SENTRY_DSN=""
# old EGAPRO_FLAVOUR
SENTRY_ENVIRONMENT="prod"
SENTRY_AUTH_TOKEN=3b4a6e7f1e2346cebd6ad7fa390d66c800dfce8331fc4982a12aafefed1cc47f
2 changes: 0 additions & 2 deletions packages/app/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,3 @@ SECURITY_ALLOWED_IPS=""
# old EGAPRO_SENTRY_DSN
SENTRY_DSN=""
# old EGAPRO_FLAVOUR
SENTRY_ENVIRONMENT="test"

2 changes: 1 addition & 1 deletion packages/app/sentry.client.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as Sentry from "@sentry/nextjs";

Sentry.init({
dsn: "https://[email protected]/99",
environment: process.env.SENTRY_ENVIRONMENT,
environment: process.env.EGAPRO_ENV || "dev",
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,

Expand Down
2 changes: 1 addition & 1 deletion packages/app/sentry.edge.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as Sentry from "@sentry/nextjs";

Sentry.init({
dsn: "https://[email protected]/99",
environment: process.env.SENTRY_ENVIRONMENT,
environment: process.env.EGAPRO_ENV || "dev",
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,

Expand Down
2 changes: 1 addition & 1 deletion packages/app/sentry.server.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as Sentry from "@sentry/nextjs";

Sentry.init({
dsn: "https://[email protected]/99",
environment: process.env.SENTRY_ENVIRONMENT,
environment: process.env.EGAPRO_ENV || "dev",

// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
Expand Down
138 changes: 138 additions & 0 deletions packages/app/src/app/(default)/load-test/IndexList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
"use client";

import Table from "@codegouvfr/react-dsfr/Table";
import { CompanyWorkforceRange } from "@common/core-domain/domain/valueObjects/declaration/CompanyWorkforceRange";
import { type DeclarationDTO } from "@common/core-domain/dtos/DeclarationDTO";
import { type DeclarationOpmcDTO } from "@common/core-domain/dtos/DeclarationOpmcDTO";
import { formatIsoToFr } from "@common/utils/date";
import { Heading, Link } from "@design-system";
import { isBefore, sub } from "date-fns";
import { capitalize, upperCase } from "lodash";

import { buildHelpersObjectifsMesures } from "../index-egapro/objectifs-mesures/[siren]/[year]/ObjectifsMesuresForm";

//Note: For 2022, first year of OPMC, we consider that the duration to be frozen is 2 years, but for next years, it will be 1 year like isFrozenDeclaration.
const OPMC_FROZEN_DURATION = { years: 2 };

const isFrozenDeclarationForOPMC = (declaration: DeclarationOpmcDTO) =>
declaration?.["declaration-existante"]?.date
? isBefore(new Date(declaration?.["declaration-existante"]?.date), sub(new Date(), OPMC_FROZEN_DURATION))
: false;

enum declarationOpmcStatus {
ALREADY_FILLED = "Déjà renseignés",
COMPLETED = "Renseignés",
INDEX_OVER_85 = "Index supérieur à 85",
NOT_APPLICABLE = "Non applicable",
NOT_MODIFIABLE = "Déclaration non modifiable",
NOT_MODIFIABLE_CORRECT = "Déclaration non modifiable sur données correctes",
NOT_MODIFIABLE_INCORRECT = "Déclaration non modifiable sur données incorrectes",
TO_COMPLETE = "À renseigner",
YEAR_NOT_APPLICABLE = "Année non applicable",
}

const getDeclarationOpmcStatus = (declaration?: DeclarationOpmcDTO) => {
if (!declaration) return declarationOpmcStatus.NOT_APPLICABLE;
const { after2021, index, initialValuesObjectifsMesures, objectifsMesuresSchema } =
buildHelpersObjectifsMesures(declaration);

if (!declaration["resultat-global"] || index === undefined) return declarationOpmcStatus.NOT_APPLICABLE;
if (!after2021) return declarationOpmcStatus.YEAR_NOT_APPLICABLE;
if (index >= 85) return declarationOpmcStatus.INDEX_OVER_85;

try {
objectifsMesuresSchema.parse(initialValuesObjectifsMesures);
if (isFrozenDeclarationForOPMC(declaration)) return declarationOpmcStatus.NOT_MODIFIABLE_CORRECT;
} catch (e) {
if (isFrozenDeclarationForOPMC(declaration)) return declarationOpmcStatus.NOT_MODIFIABLE_INCORRECT;
return declarationOpmcStatus.TO_COMPLETE;
}
return declarationOpmcStatus.COMPLETED;
};

const formatDeclarationOpmcStatus = (status: declarationOpmcStatus, siren: string, year: number) => {
const withLink = (text: string) => (
<Link key={`${siren}-objectifs-mesures`} href={`/index-egapro/objectifs-mesures/${siren}/${year}`}>
{text}
</Link>
);

switch (status) {
case declarationOpmcStatus.COMPLETED:
return withLink(declarationOpmcStatus.COMPLETED);
case declarationOpmcStatus.INDEX_OVER_85:
return declarationOpmcStatus.INDEX_OVER_85;
case declarationOpmcStatus.NOT_APPLICABLE:
return declarationOpmcStatus.NOT_APPLICABLE;
case declarationOpmcStatus.NOT_MODIFIABLE_CORRECT:
return withLink(declarationOpmcStatus.ALREADY_FILLED);
case declarationOpmcStatus.NOT_MODIFIABLE_INCORRECT:
return withLink(declarationOpmcStatus.NOT_MODIFIABLE);
case declarationOpmcStatus.TO_COMPLETE:
return withLink(declarationOpmcStatus.TO_COMPLETE);
case declarationOpmcStatus.YEAR_NOT_APPLICABLE:
return declarationOpmcStatus.YEAR_NOT_APPLICABLE;
default:
return declarationOpmcStatus.NOT_APPLICABLE;
}
};

const formatTableData = (declarations: DeclarationDTO[], declarationOpmcList: DeclarationOpmcDTO[]) =>
declarations.map(declaration => {
const rowYear = declaration.commencer?.annéeIndicateurs;
const rowSiren = declaration.commencer?.siren;
return [
<Link key={declaration.commencer?.siren} href={`/index-egapro/declaration/${rowSiren}/${rowYear}`}>
{declaration.commencer?.siren}
</Link>,
rowYear,
declaration.entreprise?.type === "ues"
? upperCase(declaration.entreprise?.type)
: capitalize(declaration.entreprise?.type),
declaration.entreprise?.tranche ? CompanyWorkforceRange.Label[declaration.entreprise.tranche] : undefined,
formatIsoToFr(declaration["declaration-existante"].date || ""),
declaration["resultat-global"]?.index || <span title="Non calculable">NC</span>,
formatDeclarationOpmcStatus(
getDeclarationOpmcStatus(
declarationOpmcList.find(declarationOpmc => declarationOpmc.commencer?.annéeIndicateurs === rowYear),
),
rowSiren || "",
rowYear || 0,
),
<Link
key={`${rowSiren}-pdf`}
href={`/index-egapro/declaration/${rowSiren}/${rowYear}/pdf`}
download={`declaration_egapro_${rowSiren}_${Number(rowYear) + 1}.pdf`}
>
Télécharger
</Link>,
];
});

export const IndexList = ({
declarations,
declarationOpmcList,
}: {
declarationOpmcList: DeclarationOpmcDTO[];
declarations: DeclarationDTO[];
}) => {
const headers = [
"SIREN",
"ANNÉE INDICATEUR",
"STRUCTURE",
"TRANCHE D'EFFECTIF",
"DATE DE DÉCLARATION",
"INDEX",
"OBJECTIFS ET MESURES",
"RÉCAPITULATIF",
];

return (
<div>
<Heading as="h1" variant="h5" text="Liste des déclarations transmises - Index Égalité Professionnelle" />
{(declarations.length > 0 && (
<Table headers={headers} data={formatTableData(declarations, declarationOpmcList)} />
)) || <p>Aucune déclaration transmise.</p>}
</div>
);
};
62 changes: 62 additions & 0 deletions packages/app/src/app/(default)/load-test/RepeqList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Table from "@codegouvfr/react-dsfr/Table";
import { type RepresentationEquilibreeDTO } from "@common/core-domain/dtos/RepresentationEquilibreeDTO";
import { formatIsoToFr } from "@common/utils/date";
import { Heading, Link } from "@design-system";

const getPercent = (
filterReasonKey: string,
percentKey: string,
representationEquilibree: RepresentationEquilibreeDTO,
) =>
(!(filterReasonKey in representationEquilibree) &&
representationEquilibree[percentKey as keyof RepresentationEquilibreeDTO]?.toString()) ||
"NC";

const formatTableData = (representationEquilibrees: RepresentationEquilibreeDTO[]) =>
representationEquilibrees.map(representationEquilibree => [
<Link
key={representationEquilibree.siren}
href={`/representation-equilibree/${representationEquilibree.siren}/${representationEquilibree.year}`}
>
{representationEquilibree.siren}
</Link>,
representationEquilibree.year,
formatIsoToFr(representationEquilibree.declaredAt),
getPercent("notComputableReasonExecutives", "executiveWomenPercent", representationEquilibree),
getPercent("notComputableReasonExecutives", "executiveMenPercent", representationEquilibree),
getPercent("notComputableReasonMembers", "memberWomenPercent", representationEquilibree),
getPercent("notComputableReasonMembers", "memberMenPercent", representationEquilibree),
<Link
key={`${representationEquilibree.siren}-pdf`}
href={`/representation-equilibree/${representationEquilibree.siren}/${representationEquilibree.year}/pdf`}
download={`declaration_egapro_${representationEquilibree.siren}_${Number(representationEquilibree.year) + 1}.pdf`}
>
Télécharger
</Link>,
]);

export const RepeqList = ({
representationEquilibrees,
}: {
representationEquilibrees: RepresentationEquilibreeDTO[];
}) => {
const headers = [
"SIREN",
"ANNÉE ÉCARTS",
"DATE DE DÉCLARATION",
"% FEMMES CADRES",
"% HOMMES CADRES",
"% FEMMES MEMBRES",
"% HOMMES MEMBRES",
"RÉCAPITULATIF",
];

return (
<div>
<Heading as="h1" variant="h5" text="Liste des déclarations transmises - Représentation Équilibrée" />
{(representationEquilibrees.length > 0 && (
<Table headers={headers} data={formatTableData(representationEquilibrees)} />
)) || <p>Aucune déclaration transmise.</p>}
</div>
);
};
80 changes: 80 additions & 0 deletions packages/app/src/app/(default)/load-test/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import Alert from "@codegouvfr/react-dsfr/Alert";
import { type DeclarationDTO } from "@common/core-domain/dtos/DeclarationDTO";
import { type DeclarationOpmcDTO } from "@common/core-domain/dtos/DeclarationOpmcDTO";
import { type RepresentationEquilibreeDTO } from "@common/core-domain/dtos/RepresentationEquilibreeDTO";
import { type NextServerPageProps } from "@common/utils/next";
import { Box, Heading } from "@design-system";
import { MessageProvider } from "@design-system/client";

import {
getAllDeclarationOpmcSirenAndYear,
getAllDeclarationsBySiren,
getAllRepresentationEquilibreeBySiren,
} from "../mon-espace/actions";
import { IndexList } from "./IndexList";
import { RepeqList } from "./RepeqList";

const InfoText = () => (
<>
<p>
Dans ce menu, vous avez accès à la liste des déclarations de l’index de l’égalité professionnelle et, si vous êtes
assujetti, de la représentation équilibrée qui ont été transmises à l’administration, en sélectionnant au
préalable dans la liste déroulante le numéro Siren de l'entreprise (ou de l’entreprise ayant déclaré l'index pour
le compte de l’unité économique et sociale) concernée si vous gérez plusieurs entreprises.
</p>
<br />
<p>
Vous pouvez ainsi télécharger le récapitulatif de la déclaration à la colonne « <b>RÉCAPITULATIF</b> », et en
cliquant sur le Siren, vous accédez à la déclaration transmise. A la colonne « <b>OBJECTIFS ET MESURES</b> », vous
avez accès à la déclaration des mesures de correction lorsque l’index est inférieur à 75 points et des objectifs
de progression lorsque l’index est inférieur à 85 points.
</p>
</>
);

const LoadTestPage = async ({ searchParams }: NextServerPageProps<never, { key: string; siren: string }>) => {
try {
if (!searchParams || !searchParams.siren || !searchParams.key) throw new Error("Missing search params");
if (searchParams.key !== "egapro-load-test") throw new Error("Invalid key");
} catch (e) {
throw new Error("Load test error");
}

const selectedSiren = searchParams.siren;

let declarations: DeclarationDTO[] = [];
let repEq: RepresentationEquilibreeDTO[] = [];
const declarationOpmcList: DeclarationOpmcDTO[] = [];

if (selectedSiren) {
declarations = await getAllDeclarationsBySiren(selectedSiren);
repEq = await getAllRepresentationEquilibreeBySiren(selectedSiren);

for (const declaration of declarations) {
if (declaration.commencer?.annéeIndicateurs) {
const result = await getAllDeclarationOpmcSirenAndYear(
declaration.commencer?.siren || "",
declaration.commencer?.annéeIndicateurs || 0,
);
if (result) {
declarationOpmcList.push(result);
}
}
}
}

return (
<MessageProvider>
<Heading as="h1" text="Load Testing" />
<Alert severity="info" small description={<InfoText />} />
<Box mt="10w">
<IndexList declarations={declarations} declarationOpmcList={declarationOpmcList} />
</Box>
<Box mt="10w" mb="8w">
<RepeqList representationEquilibrees={repEq} />
</Box>
</MessageProvider>
);
};

export default LoadTestPage;
Loading