feat: stats departementales tabulaire

This commit is contained in:
Sébastien Arod 2025-06-23 09:28:57 +02:00
parent 8d64d79983
commit c82ae0833b
12 changed files with 566 additions and 285 deletions

4
src/data/Departement.ts Normal file
View file

@ -0,0 +1,4 @@
export type Departement = {
notionId: string;
name: string;
};

View file

@ -1,5 +1,5 @@
import { Client } from "@notionhq/client";
import { writeFileSync } from "fs";
import { stat, Stats, writeFileSync } from "fs";
import { fetchFamiliesWithEventsFromNotion } from "./notion/fetch/fetchFamiliesWithEventsFromNotion";
import { publishStatsToPage } from "./notion/publish/v2/publishStatsToPage";
import { computeStatsPenales } from "./statistiques/v2/penales/computeStatsPenales";
@ -19,6 +19,8 @@ import { nettoyerDonneesFamilles } from "./data/nettoyage/familles/preparerDonne
import { statsAutresDesc } from "./statistiques/v2/autres/StatsAutres";
import { computeStatsAutres } from "./statistiques/v2/autres/computeStatsAutres";
import { updateUpdateDate as updateRootPageUpdateDate } from "./notion/publish/updateUpdateDate";
import { computeStatsDepartementales } from "./statistiques/v2/departemental/computeStatDepartement";
import { publishStatsDepartementales } from "./notion/publish/publishStatsDepartementales";
type ProcessOptions = {
dryRun: boolean;
@ -51,12 +53,12 @@ function buildProcessOptions(): ProcessOptions {
await checkDbSchemas(notionClient);
console.log("Téléchargement des données...");
const donneesFamillesBrutes = await fetchFamiliesWithEventsFromNotion(
notionClient
);
const donneesNotion = await fetchFamiliesWithEventsFromNotion(notionClient);
console.log("Nettoyage des données brutes...");
const { familles, messages } = nettoyerDonneesFamilles(donneesFamillesBrutes);
const { familles, messages } = nettoyerDonneesFamilles(
donneesNotion.familles
);
const donneesBloquantes = messages.filter(
(m) => m.severite === "Donnée incohérente bloquante"
@ -107,6 +109,10 @@ function buildProcessOptions(): ProcessOptions {
const statsSociales = computeStatsSociales(familles);
const statsAutres = computeStatsAutres(familles);
const statsGeneralesMensuelles = computeStatsGeneralesMensuelles(familles);
const statsDepartementales = computeStatsDepartementales(
familles,
donneesNotion.departements
);
const mermaidDiagramStatsGeneralesMensuellesCode =
mermaidDiagramStatsGeneralesMensuelles(statsGeneralesMensuelles);
writeFileSync(
@ -131,6 +137,7 @@ function buildProcessOptions(): ProcessOptions {
sociales: statsSociales,
statsAutres: statsAutres,
StatsGeneralesMensuelles: statsGeneralesMensuelles,
statsDepartementales: statsDepartementales,
},
null,
" "
@ -177,5 +184,10 @@ function buildProcessOptions(): ProcessOptions {
statsAutresDesc,
statsAutres
);
await publishStatsDepartementales(
notionClient,
"21a168be9f1980a7a61df867770483c3",
statsDepartementales
);
}
})();

View file

@ -0,0 +1,40 @@
import { Client, isFullPage } from "@notionhq/client";
import { Contact } from "../../data/Contact";
import { checkboxPropertyToBoolean } from "../utils/properties/checkboxPropertyToBoolean";
import { relationPropertyToPageId } from "../utils/properties/relationPropertyToPageId";
import { titlePropertyToText } from "../utils/properties/titlePropertyToText";
import { queryAllDbResults } from "../utils/queryAllDbResults";
import { contactsDbId } from "./dbIds";
export async function fetchContacts(
notionClient: Client,
cacheConfig: boolean | { ttl: number },
missions: Readonly<{
notionId: string;
Nom: string;
Equipe: string;
ContactsNotionIds: string[];
}>[]
) {
const contactPages = (
await queryAllDbResults(
notionClient,
{
database_id: contactsDbId,
},
{ cache: cacheConfig }
)
).filter(isFullPage);
const contacts: Contact[] = contactPages.map((page) => ({
notionId: page.id,
notionIdFamille: relationPropertyToPageId(page.properties, "Famille")!,
Nom: titlePropertyToText(page.properties, "Nom"),
AExercéUneMission: checkboxPropertyToBoolean(
page.properties,
"A exercé une mission"
),
Missions: missions.filter((m) => m.ContactsNotionIds.includes(page.id)),
}));
return contacts;
}

View file

@ -0,0 +1,26 @@
import { Client, isFullPage } from "@notionhq/client";
import { Departement } from "../../data/Departement";
import { titlePropertyToText } from "../utils/properties/titlePropertyToText";
import { queryAllDbResults } from "../utils/queryAllDbResults";
import { departementsDbId } from "./dbIds";
export async function fetchDepartements(
notionClient: Client,
cacheConfig: boolean | { ttl: number }
) {
const departementPages = (
await queryAllDbResults(
notionClient,
{
database_id: departementsDbId,
},
{ cache: cacheConfig }
)
).filter(isFullPage);
const departements: Departement[] = departementPages.map((page) => ({
notionId: page.id,
name: titlePropertyToText(page.properties, "Nom"),
}));
return departements;
}

View file

@ -0,0 +1,53 @@
import { Client, isFullPage } from "@notionhq/client";
import { Amende } from "../../data/Amende";
import { queryAllDbResults } from "../utils/queryAllDbResults";
import { familEventsDbId } from "./dbIds";
import { PageObjectResponse } from "@notionhq/client/build/src/api-endpoints";
import { EvenementFamille } from "../../data/EvenementFamille";
import { TypeEvenement } from "../../data/TypeEvenement";
import { datePropertyToDate } from "../utils/properties/datePropertyToDate";
import { relationPropertyToPageId } from "../utils/properties/relationPropertyToPageId";
import { selectPropertyToText } from "../utils/properties/selectPropertyToText";
import { titlePropertyToText } from "../utils/properties/titlePropertyToText";
import { richTextPropertyToPlainText } from "../utils/text/richTextPropertyToPlainText";
export async function fetchEvenements(
notionClient: Client,
cacheConfig: boolean | { ttl: number },
amendes: Amende[]
) {
const eventPages = (
await queryAllDbResults(
notionClient,
{
database_id: familEventsDbId,
sorts: [{ property: "Date", direction: "ascending" }],
},
{ cache: cacheConfig }
)
).filter(isFullPage);
const familyEvents = eventPages.map((pageObjectResponse) => {
return buildFamilyEvent(pageObjectResponse, amendes);
});
return familyEvents;
}
export function buildFamilyEvent(
page: PageObjectResponse,
amendes: Amende[]
): EvenementFamille {
const pageProperties = page.properties;
const familyEvent: EvenementFamille = {
notionId: page.id,
Évènement: titlePropertyToText(pageProperties, "Évènement"),
Type: selectPropertyToText(pageProperties, "Type")! as TypeEvenement,
"Enfants concernés": richTextPropertyToPlainText(
pageProperties,
"Enfants concernés"
),
Date: datePropertyToDate(pageProperties, "Date"),
notionIdFamille: relationPropertyToPageId(pageProperties, "Famille")!,
Amendes: amendes.filter((a) => a.notionIdEvenement === page.id),
};
return familyEvent;
}

View file

@ -1,47 +1,26 @@
import { Client, isFullPage } from "@notionhq/client";
import { PageObjectResponse } from "@notionhq/client/build/src/api-endpoints";
import { ContexteEntreeDC } from "../../data/ContexteEntreeDC";
import { EvenementFamille } from "../../data/EvenementFamille";
import { Client } from "@notionhq/client";
import { Famille } from "../../data/Famille";
import { StatutFamille } from "../../data/StatutFamille";
import { TypeEvenement } from "../../data/TypeEvenement";
import { datePropertyToDate } from "../utils/properties/datePropertyToDate";
import { relationPropertyToPageId } from "../utils/properties/relationPropertyToPageId";
import { selectPropertyToText } from "../utils/properties/selectPropertyToText";
import { statusPropertyToText } from "../utils/properties/statusPropertyToText";
import { titlePropertyToText } from "../utils/properties/titlePropertyToText";
import { CacheConfig, queryAllDbResults } from "../utils/queryAllDbResults";
import { richTextPropertyToPlainText } from "../utils/text/richTextPropertyToPlainText";
import {
contactsDbId,
departementsDbId,
familEventsDbId,
missionsDbId,
} from "./dbIds";
import {
propContexteEntree,
familiesDbId,
propDerniereModification,
propPenal,
propSocial,
} from "./dbfamilleDesc";
import { StatutPenal } from "../../data/StatutPenal";
import { StatutSocial } from "../../data/StatutSocial";
import { CacheConfig } from "../utils/queryAllDbResults";
import { Contact } from "../../data/Contact";
import { Mission } from "../../data/Mission";
import { relationPropertyToPageIds } from "../utils/properties/relationPropertyToPageIds";
import { checkboxPropertyToBoolean } from "../utils/properties/checkboxPropertyToBoolean";
import { Amende } from "../../data/Amende";
import { fetchAmendes } from "./fetchAmendes";
import { Departement } from "../../data/Departement";
import { fetchEvenements } from "./fetchEvenements";
import { fetchContacts } from "./fetchContacts";
import { fetchMissions } from "./fetchMissions";
import { fetchDepartements } from "./fetchDepartements";
import { fetchFamilles } from "./fetchFamilles";
type Departement = {
notionId: string;
name: string;
export type FetchedData = {
familles: Famille[];
departements: Departement[];
};
export async function fetchFamiliesWithEventsFromNotion(
notionClient: Client,
cacheConfig: CacheConfig = { ttl: 3600 * 1000 }
): Promise<Famille[]> {
): Promise<FetchedData> {
const departements: Departement[] = await fetchDepartements(
notionClient,
cacheConfig
@ -61,213 +40,5 @@ export async function fetchFamiliesWithEventsFromNotion(
departements,
contacts
);
return familles;
}
async function fetchFamilles(
notionClient: Client,
cacheConfig: boolean | { ttl: number },
familyEvents: Readonly<{
notionId: string;
notionIdFamille: string;
Évènement: string;
Date: Date | null;
Type: TypeEvenement;
Amendes: Amende[];
"Enfants concernés": string;
}>[],
departements: Departement[],
contacts: Readonly<{
notionId: string;
notionIdFamille: string;
Nom: string;
Missions: Mission[];
AExercéUneMission: boolean;
}>[]
) {
const familyPages = (
await queryAllDbResults(
notionClient,
{
database_id: familiesDbId,
},
{ cache: cacheConfig }
)
).filter(isFullPage);
const familles: Famille[] = await Promise.all(
familyPages.map((pageObjectResponse) => {
return buildFamily(
pageObjectResponse,
familyEvents,
departements,
contacts
);
})
);
return familles;
}
async function fetchEvenements(
notionClient: Client,
cacheConfig: boolean | { ttl: number },
amendes: Amende[]
) {
const eventPages = (
await queryAllDbResults(
notionClient,
{
database_id: familEventsDbId,
sorts: [{ property: "Date", direction: "ascending" }],
},
{ cache: cacheConfig }
)
).filter(isFullPage);
const familyEvents = eventPages.map((pageObjectResponse) => {
return buildFamilyEvent(pageObjectResponse, amendes);
});
return familyEvents;
}
async function fetchContacts(
notionClient: Client,
cacheConfig: boolean | { ttl: number },
missions: Readonly<{
notionId: string;
Nom: string;
Equipe: string;
ContactsNotionIds: string[];
}>[]
) {
const contactPages = (
await queryAllDbResults(
notionClient,
{
database_id: contactsDbId,
},
{ cache: cacheConfig }
)
).filter(isFullPage);
const contacts: Contact[] = contactPages.map((page) => ({
notionId: page.id,
notionIdFamille: relationPropertyToPageId(page.properties, "Famille")!,
Nom: titlePropertyToText(page.properties, "Nom"),
AExercéUneMission: checkboxPropertyToBoolean(
page.properties,
"A exercé une mission"
),
Missions: missions.filter((m) => m.ContactsNotionIds.includes(page.id)),
}));
return contacts;
}
async function fetchMissions(
notionClient: Client,
cacheConfig: boolean | { ttl: number }
) {
const missionsPages = (
await queryAllDbResults(
notionClient,
{
database_id: missionsDbId,
},
{ cache: cacheConfig }
)
).filter(isFullPage);
const missions: Mission[] = missionsPages.map((page) => ({
notionId: page.id,
Nom: titlePropertyToText(page.properties, "Nom"),
Equipe: selectPropertyToText(page.properties, "Équipe")!,
ContactsNotionIds: relationPropertyToPageIds(
page.properties,
"📔 Contacts"
),
}));
return missions;
}
async function fetchDepartements(
notionClient: Client,
cacheConfig: boolean | { ttl: number }
) {
const departementPages = (
await queryAllDbResults(
notionClient,
{
database_id: departementsDbId,
},
{ cache: cacheConfig }
)
).filter(isFullPage);
const departements: Departement[] = departementPages.map((page) => ({
notionId: page.id,
name: titlePropertyToText(page.properties, "Nom"),
}));
return departements;
}
function buildFamilyEvent(
page: PageObjectResponse,
amendes: Amende[]
): EvenementFamille {
const pageProperties = page.properties;
const familyEvent: EvenementFamille = {
notionId: page.id,
Évènement: titlePropertyToText(pageProperties, "Évènement"),
Type: selectPropertyToText(pageProperties, "Type")! as TypeEvenement,
"Enfants concernés": richTextPropertyToPlainText(
pageProperties,
"Enfants concernés"
),
Date: datePropertyToDate(pageProperties, "Date"),
notionIdFamille: relationPropertyToPageId(pageProperties, "Famille")!,
Amendes: amendes.filter((a) => a.notionIdEvenement === page.id),
};
return familyEvent;
}
function buildFamily(
page: PageObjectResponse,
familyEvents: EvenementFamille[],
departements: Departement[],
contacts: Contact[]
): Famille {
const pageProperties = page.properties;
const departementId = relationPropertyToPageId(pageProperties, "Département");
const departement = departementId
? departements.find((d) => d.notionId === departementId)
: null;
const family: Famille = {
notionId: page.id,
Titre: titlePropertyToText(pageProperties, ""),
Statut: statusPropertyToText(pageProperties, "Statut") as StatutFamille,
ContexteEntree: selectPropertyToText(
pageProperties,
propContexteEntree
) as ContexteEntreeDC,
Integration: datePropertyToDate(pageProperties, "Intégration"),
Sortie: datePropertyToDate(pageProperties, "Sortie"),
Evenements: familyEvents.filter((fe) => fe.notionIdFamille === page.id),
// Ces 4 propriétés seront peuplés après le data consistency check
EvenementsDates: [],
EvenementsEL: [],
EvenementsAvantEL: [],
EvenementsApresEL: [],
Departement: departement?.name || null,
DerniereModification: datePropertyToDate(
pageProperties,
propDerniereModification
)!,
Penal: statusPropertyToText(pageProperties, propPenal) as StatutPenal,
Social: statusPropertyToText(pageProperties, propSocial) as StatutSocial,
Missions: contacts
.filter((c) => c.notionIdFamille === page.id)
.flatMap((c) => c.Missions),
Contacts: contacts.filter((c) => c.notionIdFamille === page.id),
};
return family;
return { familles, departements };
}

View file

@ -0,0 +1,112 @@
import { Client, isFullPage } from "@notionhq/client";
import { Amende } from "../../data/Amende";
import { Departement } from "../../data/Departement";
import { Famille } from "../../data/Famille";
import { Mission } from "../../data/Mission";
import { TypeEvenement } from "../../data/TypeEvenement";
import { queryAllDbResults } from "../utils/queryAllDbResults";
import {
familiesDbId,
propContexteEntree,
propDerniereModification,
propPenal,
propSocial,
} from "./dbfamilleDesc";
import { PageObjectResponse } from "@notionhq/client/build/src/api-endpoints";
import { EvenementFamille } from "../../data/EvenementFamille";
import { Contact } from "../../data/Contact";
import { relationPropertyToPageId } from "../utils/properties/relationPropertyToPageId";
import { titlePropertyToText } from "../utils/properties/titlePropertyToText";
import { statusPropertyToText } from "../utils/properties/statusPropertyToText";
import { selectPropertyToText } from "../utils/properties/selectPropertyToText";
import { StatutFamille } from "../../data/StatutFamille";
import { ContexteEntreeDC } from "../../data/ContexteEntreeDC";
import { datePropertyToDate } from "../utils/properties/datePropertyToDate";
import { StatutPenal } from "../../data/StatutPenal";
import { StatutSocial } from "../../data/StatutSocial";
export async function fetchFamilles(
notionClient: Client,
cacheConfig: boolean | { ttl: number },
familyEvents: Readonly<{
notionId: string;
notionIdFamille: string;
Évènement: string;
Date: Date | null;
Type: TypeEvenement;
Amendes: Amende[];
"Enfants concernés": string;
}>[],
departements: Departement[],
contacts: Readonly<{
notionId: string;
notionIdFamille: string;
Nom: string;
Missions: Mission[];
AExercéUneMission: boolean;
}>[]
) {
const familyPages = (
await queryAllDbResults(
notionClient,
{
database_id: familiesDbId,
},
{ cache: cacheConfig }
)
).filter(isFullPage);
const familles: Famille[] = await Promise.all(
familyPages.map((pageObjectResponse) => {
return buildFamily(
pageObjectResponse,
familyEvents,
departements,
contacts
);
})
);
return familles;
}
function buildFamily(
page: PageObjectResponse,
familyEvents: EvenementFamille[],
departements: Departement[],
contacts: Contact[]
): Famille {
const pageProperties = page.properties;
const departementId = relationPropertyToPageId(pageProperties, "Département");
const departement = departementId
? departements.find((d) => d.notionId === departementId)
: null;
const family: Famille = {
notionId: page.id,
Titre: titlePropertyToText(pageProperties, ""),
Statut: statusPropertyToText(pageProperties, "Statut") as StatutFamille,
ContexteEntree: selectPropertyToText(
pageProperties,
propContexteEntree
) as ContexteEntreeDC,
Integration: datePropertyToDate(pageProperties, "Intégration"),
Sortie: datePropertyToDate(pageProperties, "Sortie"),
Evenements: familyEvents.filter((fe) => fe.notionIdFamille === page.id),
// Ces 4 propriétés seront peuplés après le data consistency check
EvenementsDates: [],
EvenementsEL: [],
EvenementsAvantEL: [],
EvenementsApresEL: [],
Departement: departement?.name || null,
DerniereModification: datePropertyToDate(
pageProperties,
propDerniereModification
)!,
Penal: statusPropertyToText(pageProperties, propPenal) as StatutPenal,
Social: statusPropertyToText(pageProperties, propSocial) as StatutSocial,
Missions: contacts
.filter((c) => c.notionIdFamille === page.id)
.flatMap((c) => c.Missions),
Contacts: contacts.filter((c) => c.notionIdFamille === page.id),
};
return family;
}

View file

@ -0,0 +1,32 @@
import { Client, isFullPage } from "@notionhq/client";
import { Mission } from "../../data/Mission";
import { relationPropertyToPageIds } from "../utils/properties/relationPropertyToPageIds";
import { selectPropertyToText } from "../utils/properties/selectPropertyToText";
import { titlePropertyToText } from "../utils/properties/titlePropertyToText";
import { queryAllDbResults } from "../utils/queryAllDbResults";
import { missionsDbId } from "./dbIds";
export async function fetchMissions(
notionClient: Client,
cacheConfig: boolean | { ttl: number }
) {
const missionsPages = (
await queryAllDbResults(
notionClient,
{
database_id: missionsDbId,
},
{ cache: cacheConfig }
)
).filter(isFullPage);
const missions: Mission[] = missionsPages.map((page) => ({
notionId: page.id,
Nom: titlePropertyToText(page.properties, "Nom"),
Equipe: selectPropertyToText(page.properties, "Équipe")!,
ContactsNotionIds: relationPropertyToPageIds(
page.properties,
"📔 Contacts"
),
}));
return missions;
}

View file

@ -0,0 +1,119 @@
import { Client } from "@notionhq/client";
import {
StatDepartement,
StatDepartementales,
} from "../../statistiques/v2/departemental/computeStatDepartement";
import {
CreatePageResponse,
QueryDatabaseResponse,
UpdatePageParameters,
} from "@notionhq/client/build/src/api-endpoints";
export async function publishStatsDepartementales(
notionClient: Client,
deptStatsDbId: string,
statsDepartementales: StatDepartementales
): Promise<void> {
console.log("Publication des statistiques départementales...");
Object.entries(statsDepartementales).forEach(
async ([deptName, stat]: [string, StatDepartement]) => {
const deptStatPageId: string =
(await findDeptStatPageId(notionClient, deptStatsDbId, deptName)) ||
(await createEmptyDeptStatPageId(
notionClient,
deptStatsDbId,
deptName
));
await updateDeptStatPage(notionClient, deptStatPageId, stat);
}
);
}
async function updateDeptStatPage(
notionClient: Client,
deptStatPageId: string,
stat: StatDepartement
): Promise<void> {
const props = {
"Nb Familles": {
number: stat.nbFamilles,
},
"Pénale ?": {
checkbox: stat.hasProcedurePenale,
},
"% MED": {
number: stat.pourcentageMED,
},
"% Pénale hors Gendarmerie": {
number: stat.pourcentageProcedurePenaleHorsGendarmerie,
},
"% TC": {
number: stat.pourcentageTC,
},
"% TP": {
number: stat.pourcentageTP,
},
"Civile ?": {
checkbox: stat.hasProcedureCivile,
},
"% IP": {
number: stat.pourcentageIP,
},
"% MJIE ou AEMO": {
number: stat.pourcentageMJIEouAEMO,
},
} as UpdatePageParameters["properties"];
await notionClient.pages.update({
page_id: deptStatPageId,
properties: props,
});
}
async function findDeptStatPageId(
notionClient: Client,
deptStatsDbId: string,
deptName: string
): Promise<string | undefined> {
const res: QueryDatabaseResponse = await notionClient.databases.query({
database_id: deptStatsDbId,
filter: {
property: "Département",
rich_text: {
equals: deptName,
},
},
});
if (res.results.length === 0) {
return undefined;
} else if (res.results.length === 1) {
return res.results[0].id;
} else {
throw new Error(
`Plusieurs pages trouvées pour le département ${deptName} dans la base de données des statistiques départementales.`
);
}
}
async function createEmptyDeptStatPageId(
notionClient: Client,
deptStatsDbId: string,
deptName: string
): Promise<string> {
const res: CreatePageResponse = await notionClient.pages.create({
parent: {
database_id: deptStatsDbId,
},
properties: {
Département: {
title: [
{
text: {
content: deptName,
},
},
],
},
},
});
return res.id;
}

View file

@ -0,0 +1,106 @@
import { groupBy } from "lodash";
import { Departement } from "../../../data/Departement";
import {
isInformationPreoccupante,
isProcedureCivile,
} from "../../../data/EvenementFamille";
import { Famille } from "../../../data/Famille";
import {
isEvtTypeProcedurePenale,
isEvtTypeProcedurePenaleHorsGendarmerie,
} from "../../../data/TypeEvenementsPenal";
import { filterFamillesWithOneOfEvenements } from "../filterFamillesWithOneOfEvenements";
import { filterFamillesWithOneOfEvenementsOfType } from "../filterFamillesWithOneOfEvenementsOfType";
import { percent } from "../math/percent";
export type StatDepartementales = {
[departement: string]: StatDepartement;
};
export function computeStatsDepartementales(
familles: Famille[],
departements: Departement[]
): StatDepartementales {
const famillesByDepartement: Record<string, Famille[]> = groupBy(
familles,
(f) => f.Departement
);
return Object.fromEntries(
departements.map((d) => [
d.name,
computeStatsDepartement(famillesByDepartement[d.name] || []),
])
);
}
export type StatDepartement = {
nbFamilles: number;
hasProcedurePenale: boolean;
pourcentageMED: number | undefined;
pourcentageProcedurePenaleHorsGendarmerie: number | undefined;
pourcentageTC: number | undefined;
pourcentageTP: number | undefined;
hasProcedureCivile: boolean;
pourcentageIP: number | undefined;
pourcentageMJIEouAEMO: number | undefined;
};
function computeStatsDepartement(
famillesDepartements: Famille[]
): StatDepartement {
return {
nbFamilles: famillesDepartements.length,
hasProcedurePenale:
filterFamillesWithOneOfEvenements(famillesDepartements, (e) =>
isEvtTypeProcedurePenale(e.Type)
).length > 0,
pourcentageMED: percent(
filterFamillesWithOneOfEvenementsOfType(
famillesDepartements,
"Mise en demeure de scolarisation"
).length,
famillesDepartements.length
),
pourcentageProcedurePenaleHorsGendarmerie: percent(
filterFamillesWithOneOfEvenements(famillesDepartements, (e) =>
isEvtTypeProcedurePenaleHorsGendarmerie(e.Type)
).length,
famillesDepartements.length
),
pourcentageTC: percent(
filterFamillesWithOneOfEvenements(
famillesDepartements,
(e) => e.Type === "Tribunal de police judiciaire"
).length,
famillesDepartements.length
),
pourcentageTP: percent(
filterFamillesWithOneOfEvenements(
famillesDepartements,
(e) => e.Type === "Tribunal de police judiciaire"
).length,
famillesDepartements.length
),
hasProcedureCivile:
filterFamillesWithOneOfEvenements(famillesDepartements, (e) =>
isProcedureCivile(e)
).length > 0,
pourcentageIP: percent(
filterFamillesWithOneOfEvenements(famillesDepartements, (e) =>
isInformationPreoccupante(e)
).length,
famillesDepartements.length
),
pourcentageMJIEouAEMO: percent(
famillesDepartements.filter(
(f) =>
f.Social === "MJIE" ||
f.Social === "AEMO" ||
f.EvenementsEL.some(
(e) => e.Type === "Classement suite AEMO MIJE ou..."
)
).length,
famillesDepartements.length
),
};
}

View file

@ -0,0 +1,42 @@
import { Famille } from "../../../data/Famille";
import { isEvtTypeProcedurePenaleHorsGendarmerie } from "../../../data/TypeEvenementsPenal";
import { filterFamillesWithOneOfEvenements } from "../filterFamillesWithOneOfEvenements";
import { filterFamillesWithOneOfEvenementsOfType } from "../filterFamillesWithOneOfEvenementsOfType";
import { percent } from "../math/percent";
import { nbFamillesAvecPagesLiees } from "../nbFamillesAvecPagesLiees";
import { StatsPenales } from "./StatsPenales";
export function computeStatDepartement(
famillesDepartements: Famille[]
): StatsPenales["parDepartements"][string] {
return {
nbFamilles: nbFamillesAvecPagesLiees(famillesDepartements),
pourcentageMED: percent(
filterFamillesWithOneOfEvenementsOfType(
famillesDepartements,
"Mise en demeure de scolarisation"
).length,
famillesDepartements.length
),
pourcentageProcedurePenaleHorsGendarmerie: percent(
filterFamillesWithOneOfEvenements(famillesDepartements, (e) =>
isEvtTypeProcedurePenaleHorsGendarmerie(e.Type)
).length,
famillesDepartements.length
),
pourcentageTribunalCorrectionnel: percent(
filterFamillesWithOneOfEvenements(
famillesDepartements,
(e) => e.Type === "Tribunal correctionnel"
).length,
famillesDepartements.length
),
pourcentageTribunalPolice: percent(
filterFamillesWithOneOfEvenements(
famillesDepartements,
(e) => e.Type === "Tribunal de police judiciaire"
).length,
famillesDepartements.length
),
};
}

View file

@ -23,9 +23,8 @@ import {
} from "./computeFamilleAvecInfosProceduresPenales";
import { computeIntervalMedGendarmerieOuProcureur } from "./intervals/computeIntervalMedGendarmerieOuProcureur";
import { groupBy } from "lodash";
import { percent } from "../math/percent";
import { isEvtTypeProcedurePenaleHorsGendarmerie } from "../../../data/TypeEvenementsPenal";
import { computeStatsAmendes } from "./computeStatsAmendes";
import { computeStatDepartement } from "./computeStatDepartement";
export type FamilleAvecInfoTribunaux = Famille & {
infoTribunaux: InfoTribunalCorrectionnel[];
@ -180,41 +179,6 @@ export function computeStatsPenales(familles: Famille[]): StatsPenales {
return statsPenales;
}
function computeStatDepartement(
famillesDepartements: Famille[]
): StatsPenales["parDepartements"][string] {
return {
nbFamilles: nbFamillesAvecPagesLiees(famillesDepartements),
pourcentageMED: percent(
filterFamillesWithOneOfEvenementsOfType(
famillesDepartements,
"Mise en demeure de scolarisation"
).length,
famillesDepartements.length
),
pourcentageProcedurePenaleHorsGendarmerie: percent(
filterFamillesWithOneOfEvenements(famillesDepartements, (e) =>
isEvtTypeProcedurePenaleHorsGendarmerie(e.Type)
).length,
famillesDepartements.length
),
pourcentageTribunalCorrectionnel: percent(
filterFamillesWithOneOfEvenements(
famillesDepartements,
(e) => e.Type === "Tribunal correctionnel"
).length,
famillesDepartements.length
),
pourcentageTribunalPolice: percent(
filterFamillesWithOneOfEvenements(
famillesDepartements,
(e) => e.Type === "Tribunal de police judiciaire"
).length,
famillesDepartements.length
),
};
}
function computeCrpc(
famillesResistantesOuEx: Famille[]
): StatsPenales["procureur"]["crpc"] {