diff --git a/src/notion/publish/v2/createStatBlock.ts b/src/notion/publish/v2/createStatBlock.ts new file mode 100644 index 0000000..6617433 --- /dev/null +++ b/src/notion/publish/v2/createStatBlock.ts @@ -0,0 +1,51 @@ +import { BlockObjectRequest } from "@notionhq/client/build/src/api-endpoints"; +import { ValueFormatOptions } from "../../../format/ValueFormatOptions"; +import { + StatDesc, + StatType, + isStatGroupListDesc, + isStatGroupDesc, + isSingleValueStatDesc, + StatsData, + isMultiValueStatDesc, +} from "../../../statistiques/v2/desc/StatsDesc"; +import { createMultiValueStatListItemBlock } from "./createMultiValueStatListItemBlock"; +import { createSingleValueStatListItemBlock } from "./createSingleValueStatListItemBlock"; +import { createStatGroupListItemBlock } from "./createStatGroupListItemBlock"; +import { createStatGroupListListItemBlock } from "./createStatGroupListListItemBlock"; + +export function createStatBlock( + statDesc: T, + statData: StatType +): BlockObjectRequest { + if (isStatGroupListDesc(statDesc)) { + return createStatGroupListListItemBlock( + statDesc, + statData as StatType + ); + } + + if (isStatGroupDesc(statDesc)) { + return createStatGroupListItemBlock( + statDesc, + statData as StatType + ); + } + + if (isSingleValueStatDesc(statDesc)) { + return createSingleValueStatListItemBlock( + statDesc.label, + statDesc as ValueFormatOptions, + statData as StatsData + ); + } + + if (isMultiValueStatDesc(statDesc)) { + return createMultiValueStatListItemBlock( + statDesc, + statData as Record + ); + } + + throw new Error("Unsupported stat descriptor type"); +} diff --git a/src/notion/publish/v2/createStatGroupListItemBlock.ts b/src/notion/publish/v2/createStatGroupListItemBlock.ts index 82e64c6..70fb578 100644 --- a/src/notion/publish/v2/createStatGroupListItemBlock.ts +++ b/src/notion/publish/v2/createStatGroupListItemBlock.ts @@ -1,21 +1,15 @@ import { - isMultiValueStatDesc, - isSingleValueStatDesc, - isStatGroupDesc, StatGroupDesc, - StatsData, - StatsType, + StatType, } from "../../../statistiques/v2/desc/StatsDesc"; -import { createSingleValueStatListItemBlock } from "./createSingleValueStatListItemBlock"; import { BulletedListItemBlockObjectRequest } from "../blocks/BulletedListItemBlockObjectRequest"; import { BulletedListItemChildren } from "../blocks/BulletedListItemChildren"; -import { createMultiValueStatListItemBlock } from "./createMultiValueStatListItemBlock"; -import { ValueFormatOptions } from "../../../format/ValueFormatOptions"; import { BlockObjectRequest } from "@notionhq/client/build/src/api-endpoints"; +import { createStatBlock } from "./createStatBlock"; export function createStatGroupListItemBlock( descriptor: D, - stats: StatsType + stats: StatType ): BulletedListItemBlockObjectRequest { return { bulleted_list_item: { @@ -36,32 +30,12 @@ export function createStatGroupListItemBlock( export function createStatGroupChildrenListItemBlock( descriptor: D, - stats: StatsType + stats: StatType ): BlockObjectRequest[] { return Object.keys(descriptor.stats).map((statName) => { const childStatDesc = descriptor.stats[statName]; const childStatData = stats[statName]; - if (isStatGroupDesc(childStatDesc)) { - return createStatGroupListItemBlock( - childStatDesc, - childStatData as StatsType - ); - } - if (isSingleValueStatDesc(childStatDesc)) { - return createSingleValueStatListItemBlock( - childStatDesc.label, - childStatDesc as ValueFormatOptions, - childStatData as StatsData - ); - } - - if (isMultiValueStatDesc(childStatDesc)) { - return createMultiValueStatListItemBlock( - childStatDesc, - childStatData as Record - ); - } - throw "Mussing case"; + return createStatBlock(childStatDesc, childStatData); }); } diff --git a/src/notion/publish/v2/createStatGroupListListItemBlock.ts b/src/notion/publish/v2/createStatGroupListListItemBlock.ts new file mode 100644 index 0000000..6c11f2f --- /dev/null +++ b/src/notion/publish/v2/createStatGroupListListItemBlock.ts @@ -0,0 +1,45 @@ +import { + StatGroupListDesc, + StatType, +} from "../../../statistiques/v2/desc/StatsDesc"; +import { BulletedListItemBlockObjectRequest } from "../blocks/BulletedListItemBlockObjectRequest"; +import { BulletedListItemChildren } from "../blocks/BulletedListItemChildren"; +import { createStatBlock } from "./createStatBlock"; + +export function createStatGroupListListItemBlock( + descriptor: D, + statsDictionnaryData: StatType +): BulletedListItemBlockObjectRequest { + return { + bulleted_list_item: { + rich_text: [ + { + text: { + content: descriptor.label, + }, + }, + ], + children: Object.entries(statsDictionnaryData).map( + ([keyName, statData]) => { + return { + bulleted_list_item: { + rich_text: [ + { + text: { + content: keyName, + }, + }, + ], + children: Object.keys(descriptor.stats).map((statName) => { + const childStatDesc = descriptor.stats[statName]; + const childStatData = statData[statName]; + + return createStatBlock(childStatDesc, childStatData); + }) as BulletedListItemChildren, + }, + }; + } + ) as BulletedListItemChildren, + }, + }; +} diff --git a/src/notion/publish/v2/publishStatsToPage.ts b/src/notion/publish/v2/publishStatsToPage.ts index 4abeb58..3dfbf05 100644 --- a/src/notion/publish/v2/publishStatsToPage.ts +++ b/src/notion/publish/v2/publishStatsToPage.ts @@ -1,7 +1,7 @@ import { Client } from "@notionhq/client"; import { StatGroupDesc, - StatsType, + StatType, } from "../../../statistiques/v2/desc/StatsDesc"; import { createStatGroupChildrenListItemBlock } from "./createStatGroupListItemBlock"; import { updatePageContent } from "./updatePageContent"; @@ -12,7 +12,7 @@ export async function publishStatsToPage( statsPageId: string, pageHeader: string, descriptor: D, - stats: StatsType + stats: StatType ) { const statsBlocks = createStatGroupChildrenListItemBlock(descriptor, stats); diff --git a/src/statistiques/v2/ELStatsV2.ts b/src/statistiques/v2/ELStatsV2.ts index eea6347..ed7e9f1 100644 --- a/src/statistiques/v2/ELStatsV2.ts +++ b/src/statistiques/v2/ELStatsV2.ts @@ -1,5 +1,5 @@ import { statsPenalesDesc } from "./penales/StatsPenales"; -import { StatsType } from "./desc/StatsDesc"; +import { StatType } from "./desc/StatsDesc"; export const eLStatsV2Desc = { label: "Stats v2", @@ -8,4 +8,4 @@ export const eLStatsV2Desc = { }, }; -export type ELStatsV2 = StatsType; +export type ELStatsV2 = StatType; diff --git a/src/statistiques/v2/autres/StatsAutres.ts b/src/statistiques/v2/autres/StatsAutres.ts index f0e2429..736625a 100644 --- a/src/statistiques/v2/autres/StatsAutres.ts +++ b/src/statistiques/v2/autres/StatsAutres.ts @@ -1,18 +1,18 @@ -import { StatsType } from "../desc/StatsDesc"; +import { StatType } from "../desc/StatsDesc"; export const statsAutresDesc = { label: "Stats Sociales", stats: { nbFamillesSuppressionCAF: { - label: "Nb Familles avec une suppression CAF" + label: "Nb Familles avec une suppression CAF", }, nbFamillesControleFiscal: { -label: "Nb Familles ayant eu un contrôle fiscal" + label: "Nb Familles ayant eu un contrôle fiscal", }, nbFamillesControleURSAFF: { -label: "Nb Familles ayant eu un contrôle URSAFF" + label: "Nb Familles ayant eu un contrôle URSAFF", }, }, } as const; -export type StatsAutres = StatsType; +export type StatsAutres = StatType; diff --git a/src/statistiques/v2/desc/StatsDesc copy.ts b/src/statistiques/v2/desc/StatsDesc copy.ts deleted file mode 100644 index d265c3e..0000000 --- a/src/statistiques/v2/desc/StatsDesc copy.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ValueFormatOptions } from "../../../format/ValueFormatOptions"; - -type StatDescTypes = StatGroupDesc | StatDesc | MultiValueStatDesc; - -/** - * Descripteur d'une Section de stat - */ -export type StatGroupDesc = { - label: string; - desc?: string; - stats: { [propName: string]: StatDescTypes }; -}; - -export function isStatGroupDesc(x: StatDescTypes): x is StatGroupDesc { - if (!("stats" in x) || x.stats === null || typeof x.stats !== "object") { - return false; - } - - return true; -} - -/** - * Descripteur d'une valeur unique - */ -export type StatDesc = { - label: string; -} & ValueFormatOptions; - -export function isSingleValueStatDesc(x: StatDescTypes): x is StatDesc { - return !isStatGroupDesc(x) && !isMultiValueStatDesc(x); -} - -export type MultiValueStatDesc = { - label: string; - type: "multi"; -} & ValueFormatOptions; - -export function isMultiValueStatDesc( - x: StatDescTypes -): x is MultiValueStatDesc { - if (!("label" in x) || x.label === null || typeof x.label !== "string") { - return false; - } - return "type" in x && x.type === "multi"; -} - -export type StatsType = { - [Property in keyof T["stats"]]: T["stats"][Property] extends StatGroupDesc - ? StatsType - : T["stats"][Property] extends MultiValueStatDesc - ? Record - : number; -}; diff --git a/src/statistiques/v2/desc/StatsDesc.ts b/src/statistiques/v2/desc/StatsDesc.ts index 6fb0833..ffb31dc 100644 --- a/src/statistiques/v2/desc/StatsDesc.ts +++ b/src/statistiques/v2/desc/StatsDesc.ts @@ -1,14 +1,29 @@ import { ValueFormatOptions } from "../../../format/ValueFormatOptions"; -type StatDesc = StatGroupDesc | SingleValueStatDesc | MultiValueStatDesc; +export type StatDesc = + | StatGroupListDesc + | StatGroupDesc + | SingleValueStatDesc + | MultiValueStatDesc; + +type NamedStatsDescriptors = { + [propName: string]: StatDesc; +}; /** - * Descripteur d'une Section de stat + * Descripteur d'un groupe de stat */ export type StatGroupDesc = { label: string; desc?: string; - stats: { [propName: string]: StatDesc }; + stats: NamedStatsDescriptors; +}; + +export type StatGroupListDesc = { + label: string; + desc?: string; + type: "group-list"; + stats: NamedStatsDescriptors; }; export function isStatGroupDesc(x: StatDesc): x is StatGroupDesc { @@ -19,6 +34,14 @@ export function isStatGroupDesc(x: StatDesc): x is StatGroupDesc { return true; } +export function isStatGroupListDesc(x: StatDesc): x is StatGroupListDesc { + if (!("stats" in x) || x.stats === null || typeof x.stats !== "object") { + return false; + } + + return "type" in x && x.type === "group-list"; +} + /** * Descripteur d'une valeur unique */ @@ -44,10 +67,21 @@ export function isMultiValueStatDesc(x: StatDesc): x is MultiValueStatDesc { export type StatsData = number | { value: number; relatedPageIds: string[] }; -export type StatsType = { - [Property in keyof T["stats"]]: T["stats"][Property] extends StatGroupDesc - ? StatsType - : T["stats"][Property] extends MultiValueStatDesc - ? Record - : StatsData; +export type StatType = T extends StatGroupListDesc + ? Record< + string, + { + [Property in keyof T["stats"]]: StatType; + } + > + : T extends StatGroupDesc + ? { + [Property in keyof T["stats"]]: StatType; + } + : T extends MultiValueStatDesc + ? Record + : StatsData; + +export type NamedStatsType = { + [Property in keyof T]: StatType; }; diff --git a/src/statistiques/v2/generales/StatsGenerales.ts b/src/statistiques/v2/generales/StatsGenerales.ts index fd70623..79a59ae 100644 --- a/src/statistiques/v2/generales/StatsGenerales.ts +++ b/src/statistiques/v2/generales/StatsGenerales.ts @@ -1,4 +1,4 @@ -import { StatsType } from "../desc/StatsDesc"; +import { StatType } from "../desc/StatsDesc"; export const statsGeneralesDesc = { label: "Stats Générales", @@ -56,4 +56,4 @@ export const statsGeneralesDesc = { }, } as const; -export type StatsGenerales = StatsType; +export type StatsGenerales = StatType; diff --git a/src/statistiques/v2/penales/StatsPenales.ts b/src/statistiques/v2/penales/StatsPenales.ts index 0e1e1c2..7924ee9 100644 --- a/src/statistiques/v2/penales/StatsPenales.ts +++ b/src/statistiques/v2/penales/StatsPenales.ts @@ -1,4 +1,4 @@ -import { StatsType } from "../desc/StatsDesc"; +import { StatType } from "../desc/StatsDesc"; export const statsTribunalCorrectionnelDesc = { nbFamillesConvoquees: { @@ -181,7 +181,33 @@ export const statsPenalesDesc = { }, }, }, + parDepartements: { + label: "Par départements", + type: "group-list", + stats: { + nbFamilles: { + label: "Nb familles", + }, + pourcentageMED: { + label: "% familles avec une Mise en demeure", + unit: "%", + }, + pourcentageProcedurePenaleHorsGendarmerie: { + label: + "% familles avec une Procédure Pénale différent de Gendarmerie (Procureur, Tribunal...)", + unit: "%", + }, + pourcentageTribunalCorrectionnel: { + label: "% familles avec un evt Tribunal correctionnel", + unit: "%", + }, + pourcentageTribunalPolice: { + label: "% familles avec un evt Tribunal de Police", + unit: "%", + }, + }, + }, }, } as const; -export type StatsPenales = StatsType; +export type StatsPenales = StatType; diff --git a/src/statistiques/v2/penales/computeStatsPenales.ts b/src/statistiques/v2/penales/computeStatsPenales.ts index 4eeb703..21c3993 100644 --- a/src/statistiques/v2/penales/computeStatsPenales.ts +++ b/src/statistiques/v2/penales/computeStatsPenales.ts @@ -22,6 +22,9 @@ import { FamilleAvecInfosProceduresPenales as FamilleAvecInfoProceduresPenales, } from "./computeFamilleAvecInfosProceduresPenales"; import { computeIntervalMedGendarmerieOuProcureur } from "./intervals/computeIntervalMedGendarmerieOuProcureur"; +import { groupBy } from "lodash"; +import { percent } from "../../../utils/math/percent"; +import { isEvtTypeProcedurePenaleHorsGendarmerie } from "../../../data/TypeEvenementsPenal"; export type FamilleAvecInfoTribunaux = Famille & { infoTribunaux: InfoTribunalCorrectionnel[]; @@ -64,6 +67,12 @@ export function computeStatsPenales(familles: Famille[]): StatsPenales { }; }); + const famillesParDepartement = groupBy( + famillesResistantesOuEx, + (f) => f.Departement + ); + const departements = Object.keys(famillesParDepartement).sort(); + const famillesAvecInfoProceduresPenales: FamilleAvecInfoProceduresPenales[] = famillesResistantesOuEx.map(computeFamilleAvecInfosProceduresPenales); @@ -206,10 +215,51 @@ export function computeStatsPenales(familles: Famille[]): StatsPenales { ), }, }, + parDepartements: Object.fromEntries( + departements.map((d) => [ + d, + computeStatDepartement(famillesParDepartement[d]), + ]) + ), }; 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"] { diff --git a/src/statistiques/v2/sociales/StatsSociales.ts b/src/statistiques/v2/sociales/StatsSociales.ts index e3512e1..beb0cc6 100644 --- a/src/statistiques/v2/sociales/StatsSociales.ts +++ b/src/statistiques/v2/sociales/StatsSociales.ts @@ -1,4 +1,4 @@ -import { StatsType } from "../desc/StatsDesc"; +import { StatType } from "../desc/StatsDesc"; export const statsSocialesDesc = { label: "Stats Sociales", @@ -72,4 +72,4 @@ export const statsSocialesDesc = { }, } as const; -export type StatsSociales = StatsType; +export type StatsSociales = StatType;