remove proc pénale

wip-related-pages
sebastien.arod@gmail.com 2024-09-05 09:23:27 +02:00
parent 21f50efb5a
commit b4d1a7edad
32 changed files with 501 additions and 65 deletions

3
.gitignore vendored
View File

@ -16,5 +16,4 @@
dist dist
test-coverage test-coverage
node_modules node_modules
el-stats-par-anciennete.json el-stats*.json
el-stats.json

View File

@ -90,6 +90,7 @@ export function isEvenementInPeriod(
export function isEvenementBefore(evt: EvenementFamille, date: Date): boolean { export function isEvenementBefore(evt: EvenementFamille, date: Date): boolean {
if (evt.Date === null) { if (evt.Date === null) {
// Assume lack of date are oldest events
return true; return true;
} }
return evt.Date < date; return evt.Date < date;
@ -101,3 +102,18 @@ export function isValidEvenementFamille(str: string | null): boolean {
Object.prototype.hasOwnProperty.call(categorieEvenement, str) Object.prototype.hasOwnProperty.call(categorieEvenement, str)
); );
} }
export function isGendarmerie(e: EvenementFamille): boolean {
return (
e.Type === "Audition gendarmerie / police" ||
e.Type === "Gendarmerie/Forces de l'ordre" ||
e.Type === "Récidive gendarmerie" ||
e.Type === "Passage police municipale"
);
}
export function isEvtProcureur(e: EvenementFamille): boolean {
return (
e.Type === "Audition procureur" ||
e.Type === "Audience CRPC" ||
e.Type === "Convocation CRPC"
);
}

View File

@ -13,6 +13,7 @@ export type Famille = {
Integration: Date | null; Integration: Date | null;
ContexteEntree: ContexteEntreeDC; ContexteEntree: ContexteEntreeDC;
Sortie: Date | null; Sortie: Date | null;
// sorted by date asc
Evenements: EvenementFamille[]; Evenements: EvenementFamille[];
}; };

View File

@ -0,0 +1,4 @@
export type EvolFormatOptions = {
evolMaxFractioDigits?: number;
evolPctMaxFractioDigits?: number;
};

View File

@ -0,0 +1,4 @@
export type ValueFormatOptions = {
unit?: string;
valueMaxFractioDigits?: number;
};

View File

@ -3,19 +3,17 @@ import { formatValue } from "./formatValue";
describe("formatValue", () => { describe("formatValue", () => {
test("format with default options", () => { test("format with default options", () => {
expect(formatValue(42.42, { notionPropName: "whatever" })).toBe("42,4"); expect(formatValue(42.42, {})).toBe("42,4");
expect(formatValue(42, { notionPropName: "whatever" })).toBe("42"); expect(formatValue(42, {})).toBe("42");
}); });
test("format with valueMaxFractioDigits: 2", () => { test("format with valueMaxFractioDigits: 2", () => {
expect( expect(
formatValue(42.4242, { formatValue(42.4242, {
notionPropName: "whatever",
valueMaxFractioDigits: 2, valueMaxFractioDigits: 2,
}) })
).toBe("42,42"); ).toBe("42,42");
expect( expect(
formatValue(42, { formatValue(42, {
notionPropName: "whatever",
valueMaxFractioDigits: 2, valueMaxFractioDigits: 2,
}) })
).toBe("42"); ).toBe("42");
@ -24,7 +22,6 @@ describe("formatValue", () => {
test("format with unit", () => { test("format with unit", () => {
expect( expect(
formatValue(42, { formatValue(42, {
notionPropName: "whatever",
unit: "%", unit: "%",
}) })
).toBe("42%"); ).toBe("42%");

View File

@ -1,6 +1,6 @@
import { StatPublishOptions } from "../../statPublishOptions"; import { ValueFormatOptions } from "./ValueFormatOptions";
export function formatValue(value: number, publishOptions: StatPublishOptions) { export function formatValue(value: number, publishOptions: ValueFormatOptions) {
const valueStr = value.toLocaleString("fr-FR", { const valueStr = value.toLocaleString("fr-FR", {
useGrouping: false, useGrouping: false,
maximumFractionDigits: maximumFractionDigits:

View File

@ -1,5 +1,5 @@
import { ValueWithEvol } from "../../../statistiques/ELStats"; import { StatPublishOptions } from "../notion/publish/v1/statPublishOptions";
import { StatPublishOptions } from "../../statPublishOptions"; import { ValueWithEvol } from "../statistiques/v1/ELStats";
import { formatValue } from "./formatValue"; import { formatValue } from "./formatValue";
export function formatValueWithEvol( export function formatValueWithEvol(

View File

@ -2,9 +2,12 @@ import { Client } from "@notionhq/client";
import { writeFileSync } from "fs"; import { writeFileSync } from "fs";
import { checkDataConsistency } from "./data/checkDataConsistency"; import { checkDataConsistency } from "./data/checkDataConsistency";
import { fetchFamiliesWithEventsFromNotion } from "./notion/fetch/fetchFamiliesWithEventsFromNotion"; import { fetchFamiliesWithEventsFromNotion } from "./notion/fetch/fetchFamiliesWithEventsFromNotion";
import { publishStatisticsToNotion } from "./notion/publish/publishStatisticsToNotion"; import { publishStatisticsToNotion } from "./notion/publish/v1/publishStatisticsToNotion";
import { computeELStats } from "./statistiques/computeELStats"; import { publishStatsToPage } from "./notion/publish/v2/publishStatsToPage";
import { computeStatsParAnciennete } from "./statistiques/computeEvenementsParAnciennete"; import { computeELStats } from "./statistiques/v1/computeELStats";
import { computeStatsParAnciennete } from "./statistiques/v1/computeEvenementsParAnciennete";
import { computeStatsPenales } from "./statistiques/v2/penales/computeStatsPenales";
import { statsPenalesDesc } from "./statistiques/v2/penales/StatsPenales";
type ProcessOptions = { type ProcessOptions = {
dryRun: boolean; dryRun: boolean;
@ -65,13 +68,26 @@ function buildProcessOptions(): ProcessOptions {
"./el-stats-par-anciennete.json", "./el-stats-par-anciennete.json",
JSON.stringify(statsParAnciennete, null, " ") JSON.stringify(statsParAnciennete, null, " ")
); );
const statsV2 = computeStatsPenales(familles);
if (options.dryRun) { if (options.dryRun) {
console.log( console.log(
"Dry run => Skip Publishing. Stats are dumped in file el-stats.json" "Dry run => Skip Publishing. Stats are dumped in file el-stats-xxx.json"
); );
writeFileSync("./el-stats.json", JSON.stringify(elStats, null, " ")); writeFileSync("./el-stats-v1.json", JSON.stringify(elStats, null, " "));
writeFileSync("./el-stats-v2.json", JSON.stringify(statsV2, null, " "));
} else { } else {
console.log("Publishing statistics..."); console.log("Publishing statistics...");
publishStatisticsToNotion(elStats, currentDate, notionClient); await publishStatisticsToNotion(elStats, currentDate, notionClient);
await publishStatsToPage(
notionClient,
"969eac5c-a4eb-49d4-b4ad-c341c9c4c785",
statsPenalesDesc,
statsV2
);
} }
})(); })();

View File

@ -22,6 +22,11 @@ export async function fetchFamiliesWithEventsFromNotion(
const eventPages = ( const eventPages = (
await queryAllDbResults(notionClient, { await queryAllDbResults(notionClient, {
database_id: familEventsDbId, database_id: familEventsDbId,
sorts: [
{property: "Date",
direction: "ascending"
}
]
}) })
).filter(isFullPage); ).filter(isFullPage);
const familyPages = ( const familyPages = (

View File

@ -3,17 +3,17 @@ import {
PageObjectResponse, PageObjectResponse,
UpdateDatabaseParameters, UpdateDatabaseParameters,
} from "@notionhq/client/build/src/api-endpoints"; } from "@notionhq/client/build/src/api-endpoints";
import { formatValueWithEvol } from "../../../format/formatValueWithEvol";
import { import {
ELStatsPeriod, ELStatsPeriod,
PeriodStatsValues, PeriodStatsValues,
ValueWithEvol, ValueWithEvol,
} from "../../statistiques/ELStats"; } from "../../../statistiques/v1/ELStats";
import { StatPublishOptions, statPublishOptions } from "../statPublishOptions"; import { titlePropertyToText } from "../../utils/properties/titlePropertyToText";
import { titlePropertyToText } from "../utils/properties/titlePropertyToText"; import { queryAllDbResults } from "../../utils/queryAllDbResults";
import { queryAllDbResults } from "../utils/queryAllDbResults"; import { removeBlocks } from "../../utils/removeBlocks";
import { removeBlocks } from "../utils/removeBlocks"; import { CreatePageProperties } from "../../utils/types/CreatePageProperties";
import { CreatePageProperties } from "../utils/types/CreatePageProperties"; import { StatPublishOptions, statPublishOptions } from "./statPublishOptions";
import { formatValueWithEvol } from "./format/formatValueWithEvol";
const periodeDbPropertyName = "Période"; const periodeDbPropertyName = "Période";

View File

@ -1,7 +1,7 @@
import { Client, isFullBlock } from "@notionhq/client"; import { Client, isFullBlock } from "@notionhq/client";
import { ELStats } from "../../statistiques/ELStats"; import { ELStats } from "../../../statistiques/v1/ELStats";
import { listAllChildrenBlocks } from "../utils/listAllChildrenBlocks"; import { listAllChildrenBlocks } from "../../utils/listAllChildrenBlocks";
import { richTextToPlainText } from "../utils/text/richTextToPlainText"; import { richTextToPlainText } from "../../utils/text/richTextToPlainText";
import { publishPeriodStats } from "./publishPeriodStats"; import { publishPeriodStats } from "./publishPeriodStats";
import { publishStatsActuelles } from "./publishStatsActuelles"; import { publishStatsActuelles } from "./publishStatsActuelles";

View File

@ -1,12 +1,12 @@
import { Client, isFullBlock } from "@notionhq/client"; import { Client, isFullBlock } from "@notionhq/client";
import { BlockObjectRequest } from "@notionhq/client/build/src/api-endpoints"; import { BlockObjectRequest } from "@notionhq/client/build/src/api-endpoints";
import { ELStatsAtDate } from "../../statistiques/ELStats"; import { formatValue } from "../../../format/formatValue";
import { StatPublishOptions, statPublishOptions } from "../statPublishOptions"; import { ELStatsAtDate } from "../../../statistiques/v1/ELStats";
import { listAllChildrenBlocks } from "../utils/listAllChildrenBlocks"; import { listAllChildrenBlocks } from "../../utils/listAllChildrenBlocks";
import { removeBlocks } from "../utils/removeBlocks"; import { removeBlocks } from "../../utils/removeBlocks";
import { richTextToPlainText } from "../utils/text/richTextToPlainText"; import { richTextToPlainText } from "../../utils/text/richTextToPlainText";
import { formatValue } from "./format/formatValue";
import { currentStatsHeading, statsPageId } from "./publishStatisticsToNotion"; import { currentStatsHeading, statsPageId } from "./publishStatisticsToNotion";
import { StatPublishOptions, statPublishOptions } from "./statPublishOptions";
export async function publishStatsActuelles( export async function publishStatsActuelles(
notionClient: Client, notionClient: Client,

View File

@ -1,4 +1,6 @@
import { AllStatsPropNames } from "../statistiques/ELStats"; import { EvolFormatOptions } from "../../../format/EvolFormatOptions";
import { ValueFormatOptions } from "../../../format/ValueFormatOptions";
import { AllStatsPropNames } from "../../../statistiques/v1/ELStats";
export function statPublishOptions( export function statPublishOptions(
statJsPropName: AllStatsPropNames statJsPropName: AllStatsPropNames
@ -185,8 +187,5 @@ const statPropsPublishOptions: {
}; };
export type StatPublishOptions = { export type StatPublishOptions = {
notionPropName: string; notionPropName: string;
unit?: string; } & ValueFormatOptions &
valueMaxFractioDigits?: number; EvolFormatOptions;
evolMaxFractioDigits?: number;
evolPctMaxFractioDigits?: number;
};

View File

@ -0,0 +1,97 @@
import { Client, isFullBlock } from "@notionhq/client";
import { BlockObjectRequest } from "@notionhq/client/build/src/api-endpoints";
import { formatValue } from "../../../format/formatValue";
import {
isStatGroupDesc,
StatDesc,
StatGroupDesc,
StatsType,
} from "../../../statistiques/v2/StatsDesc";
import { listAllChildrenBlocks } from "../../utils/listAllChildrenBlocks";
import { removeBlocks } from "../../utils/removeBlocks";
export async function publishStatsToPage<D extends StatGroupDesc>(
notionClient: Client,
statsPageId: string,
descriptor: D,
stats: StatsType<D>
) {
const childrenBlocks = (
await listAllChildrenBlocks(notionClient, {
block_id: statsPageId,
})
).filter(isFullBlock);
const blocksIdsToRemove = childrenBlocks.map((b) => b.id);
await removeBlocks(notionClient, blocksIdsToRemove);
const newBlocks = createStatGroupChildrenListItemBlock(descriptor, stats);
await notionClient.blocks.children.append({
block_id: statsPageId,
children: [...newBlocks],
});
}
function createStatGroupListItemBlock<D extends StatGroupDesc>(
descriptor: D,
stats: StatsType<D>
): BulletedListItemBlockObjectRequest {
return {
bulleted_list_item: {
rich_text: [
{
text: {
content: descriptor.label,
},
},
],
children: createStatGroupChildrenListItemBlock(
descriptor,
stats
) as BulletedListItemChildren,
},
};
}
type BulletedListItemBlockObjectRequest = Extract<
BlockObjectRequest,
{ bulleted_list_item: object }
>;
type BulletedListItemChildren =
BulletedListItemBlockObjectRequest["bulleted_list_item"]["children"];
function createStatGroupChildrenListItemBlock<D extends StatGroupDesc>(
descriptor: D,
stats: StatsType<D>
): BulletedListItemBlockObjectRequest[] {
return Object.keys(descriptor.stats).map((statName) => {
const childStatDesc = descriptor.stats[statName];
const childStatValue = stats[statName];
return isStatGroupDesc(childStatDesc)
? createStatGroupListItemBlock(
childStatDesc,
childStatValue as StatsType<typeof childStatDesc>
)
: createStatListItemBlock(childStatDesc, childStatValue as number);
});
}
function createStatListItemBlock(
descriptor: StatDesc,
statValue: number
): BulletedListItemBlockObjectRequest {
return {
bulleted_list_item: {
rich_text: [
{
text: {
content:
descriptor.label + ": " + formatValue(statValue, descriptor),
},
},
],
},
};
}

View File

View File

@ -1,6 +1,6 @@
import { Famille } from "../data/Famille"; import { Famille } from "../../data/Famille";
import { IdentifiedPeriod } from "../period/IdentifiedPeriod"; import { IdentifiedPeriod } from "../../period/IdentifiedPeriod";
import { Period } from "../period/Period"; import { Period } from "../../period/Period";
import { import {
ELStatsOverPeriod, ELStatsOverPeriod,
ELStatsPeriod, ELStatsPeriod,

View File

@ -1,7 +1,7 @@
import { Famille } from "../data/Famille"; import { Famille } from "../../data/Famille";
import { ELStats } from "./ELStats";
import { computeELPeriodStats } from "./computeELPeriodStats"; import { computeELPeriodStats } from "./computeELPeriodStats";
import { computeELStatsAtDate } from "./computeELStatsAtDate"; import { computeELStatsAtDate } from "./computeELStatsAtDate";
import { ELStats } from "./ELStats";
import { generateELMonths } from "./generateELMonths"; import { generateELMonths } from "./generateELMonths";
import { generateELYears } from "./generateELYears"; import { generateELYears } from "./generateELYears";

View File

@ -4,17 +4,17 @@ import {
isEvenementBefore, isEvenementBefore,
isProcedureCivile, isProcedureCivile,
isProcedurePenale, isProcedurePenale,
} from "../data/EvenementFamille"; } from "../../data/EvenementFamille";
import { import {
Famille, Famille,
dureeResistanceInDays, dureeResistanceInDays,
isExResistant, isExResistant,
isResistant, isResistant,
} from "../data/Famille"; } from "../../data/Famille";
import { average } from "../utils/math/average"; import { average } from "../../utils/math/average";
import { median } from "../utils/math/median"; import { median } from "../../utils/math/median";
import { percent } from "../utils/math/percent"; import { percent } from "../../utils/math/percent";
import { notNull } from "../utils/notNull"; import { notNull } from "../../utils/notNull";
import { ELStatsAtDate } from "./ELStats"; import { ELStatsAtDate } from "./ELStats";
import { computePourcentageEntreeApresMiseEnDemeure } from "./computePourcentageEntreeApresMiseEnDemeure"; import { computePourcentageEntreeApresMiseEnDemeure } from "./computePourcentageEntreeApresMiseEnDemeure";

View File

@ -1,14 +1,14 @@
import { import {
isEvenementInPeriod, isEvenementInPeriod,
isProcedurePenale, isProcedurePenale,
} from "../data/EvenementFamille"; } from "../../data/EvenementFamille";
import { import {
Famille, Famille,
isResistantOverPeriod, isResistantOverPeriod,
periodOfResistance, periodOfResistance,
} from "../data/Famille"; } from "../../data/Famille";
import { Period } from "../period/Period"; import { Period } from "../../period/Period";
import { isPeriodContaining } from "../period/isPeriodContaining"; import { isPeriodContaining } from "../../period/isPeriodContaining";
import { ELStatsOverPeriod } from "./ELStats"; import { ELStatsOverPeriod } from "./ELStats";
import { computePourcentageEntreeApresMiseEnDemeure } from "./computePourcentageEntreeApresMiseEnDemeure"; import { computePourcentageEntreeApresMiseEnDemeure } from "./computePourcentageEntreeApresMiseEnDemeure";

View File

@ -1,5 +1,8 @@
import { isProcedureCivile, isProcedurePenale } from "../data/EvenementFamille"; import {
import { Famille } from "../data/Famille"; isProcedureCivile,
isProcedurePenale,
} from "../../data/EvenementFamille";
import { Famille } from "../../data/Famille";
import { computeFamillesWithEventsConditionInEarlyPeriod } from "./computeFamilleWithEventAfterDurationOfDC"; import { computeFamillesWithEventsConditionInEarlyPeriod } from "./computeFamilleWithEventAfterDurationOfDC";
export function computeStatsParAnciennete(familles: Famille[]) { export function computeStatsParAnciennete(familles: Famille[]) {

View File

@ -1,7 +1,10 @@
import { addMonths } from "date-fns"; import { addMonths } from "date-fns";
import { EvenementFamille, isEvenementBefore } from "../data/EvenementFamille"; import {
import { Famille } from "../data/Famille"; EvenementFamille,
import { percent } from "../utils/math/percent"; isEvenementBefore,
} from "../../data/EvenementFamille";
import { Famille } from "../../data/Famille";
import { percent } from "../../utils/math/percent";
type FamillesWithEventsConditionInEarlyPeriod = { type FamillesWithEventsConditionInEarlyPeriod = {
[name: string]: { [name: string]: {

View File

@ -1,5 +1,5 @@
import { Famille } from "../data/Famille"; import { Famille } from "../../data/Famille";
import { percent } from "../utils/math/percent"; import { percent } from "../../utils/math/percent";
export function computePourcentageEntreeApresMiseEnDemeure( export function computePourcentageEntreeApresMiseEnDemeure(
familles: Famille[] familles: Famille[]

View File

@ -1,6 +1,6 @@
import { getMonth, getYear, setMonth, setYear } from "date-fns"; import { getMonth, getYear, setMonth, setYear } from "date-fns";
import { IdentifiedPeriod } from "../period/IdentifiedPeriod"; import { IdentifiedPeriod } from "../../period/IdentifiedPeriod";
import { generateConsecutiveIdentifiedPeriods } from "../period/generateConsecutiveIdentifiedPeriods"; import { generateConsecutiveIdentifiedPeriods } from "../../period/generateConsecutiveIdentifiedPeriods";
export function generateELMonths(): IdentifiedPeriod[] { export function generateELMonths(): IdentifiedPeriod[] {
const months = generateConsecutiveIdentifiedPeriods({ const months = generateConsecutiveIdentifiedPeriods({

View File

@ -1,6 +1,6 @@
import { addYears } from "date-fns/fp"; import { addYears } from "date-fns/fp";
import { IdentifiedPeriod } from "../period/IdentifiedPeriod"; import { IdentifiedPeriod } from "../../period/IdentifiedPeriod";
import { generateConsecutiveIdentifiedPeriods } from "../period/generateConsecutiveIdentifiedPeriods"; import { generateConsecutiveIdentifiedPeriods } from "../../period/generateConsecutiveIdentifiedPeriods";
export function generateELYears(): IdentifiedPeriod[] { export function generateELYears(): IdentifiedPeriod[] {
return generateConsecutiveIdentifiedPeriods({ return generateConsecutiveIdentifiedPeriods({

View File

@ -0,0 +1,11 @@
import { statsPenalesDesc } from "./penales/StatsPenales";
import { StatsType } from "./StatsDesc";
export const eLStatsV2Desc = {
label: "Stats v2",
stats: {
penales: statsPenalesDesc,
},
};
export type ELStatsV2 = StatsType<typeof eLStatsV2Desc>;

View File

@ -0,0 +1,32 @@
import { ValueFormatOptions } from "../../format/ValueFormatOptions";
export type StatGroupDesc = {
label: string;
desc?: string;
stats: { [propName: string]: StatDesc | StatGroupDesc };
};
export type StatDesc = {
label: string;
} & ValueFormatOptions;
export function isStatGroupDesc(x: unknown): x is StatGroupDesc {
if (typeof x !== "object" || Array.isArray(x) || x === null) {
return false;
}
if (!("label" in x) || x.label === null || typeof x.label !== "string") {
return false;
}
if (!("stats" in x) || x.stats === null || typeof x.stats !== "object") {
return false;
}
return true;
}
export type StatsType<T extends StatGroupDesc> = {
[Property in keyof T["stats"]]: T["stats"][Property] extends StatGroupDesc
? StatsType<T["stats"][Property]>
: number;
};

View File

@ -0,0 +1,11 @@
import { EvenementFamille } from "../../data/EvenementFamille";
import { Famille } from "../../data/Famille";
export function filterFamillesWithOneOfEvenements(
familles: Famille[],
evenementtPredicated: (evt: EvenementFamille) => boolean
): Famille[] {
return familles.filter(
(f) => f.Evenements.find((e) => evenementtPredicated(e)) !== undefined
);
}

View File

@ -0,0 +1,13 @@
import { Famille } from "../../data/Famille";
import { TypeEvenement } from "../../data/TypeEvenement";
import { filterFamillesWithOneOfEvenements } from "./filterFamillesWithOneOfEvenements";
export function filterFamillesWithOneOfEvenementsOfType(
familles: Famille[],
eventType: TypeEvenement
): Famille[] {
return filterFamillesWithOneOfEvenements(
familles,
(e) => e.Type === eventType
);
}

View File

@ -0,0 +1,66 @@
import { StatsType } from "../StatsDesc";
export const statsPenalesDesc = {
label: "Stats Pénales",
stats: {
nbFamillesMisesEnDemeure: {
label: "Nb familles mises en demeure",
},
nbFamillesAvecGendarmerie: {
label: "Nb familles avec un evenement lié à la Gendarmerie",
},
compositionPenales: {
label: "Compositions Pénales",
stats: {
nbFamilles: {
label: "Nb familles concernées",
},
acceptees: {
label: "Nb familles ayant acceptées",
},
refusees: {
label: "Nb familles ayant refusées",
},
},
},
crpc: {
label: "CRPC",
stats: {
nbFamilles: {
label: "Nb familles concernées",
},
acceptees: {
label: "Nb familles ayant acceptées",
},
refusees: {
label: "Nb familles ayant refusées",
},
},
},
tribunalCorrectionnel: {
label: "Tribunal Correctionnel",
stats: {
nbFamillesPassees: {
label: "Nb familles passées",
},
nbFamillesProgrammees: {
label: "Nb familles programmées",
},
nbFamillesRecidive: {
label: "Nb familles recidive",
},
},
},
intervalGendarmerieProcureur: {
label: "Délai moyen entre Gendarmerie et Procureur",
unit: " jours",
},
intervalProcureurTribunalCorrectionnel: {
label: "Délai moyen entre Procureur et Tribunal Correctionnel",
unit: " jours",
},
},
} as const;
export type StatsPenales = StatsType<typeof statsPenalesDesc>;

View File

@ -0,0 +1,159 @@
import { differenceInDays } from "date-fns";
import {
isCompositionPenale,
isCRPC,
isEvenementBefore,
isEvtProcureur,
isGendarmerie,
} from "../../../data/EvenementFamille";
import { Famille } from "../../../data/Famille";
import { average } from "../../../utils/math/average";
import { filterFamillesWithOneOfEvenements } from "../filterFamillesWithOneOfEvenements";
import { filterFamillesWithOneOfEvenementsOfType } from "../filterFamillesWithOneOfEvenementsOfType";
import { StatsPenales } from "./StatsPenales";
export function computeStatsPenales(familles: Famille[]): StatsPenales {
const famillesMisesEnDemeure = filterFamillesWithOneOfEvenementsOfType(
familles,
"Mise en demeure de scolarisation"
);
const famillesAvecGendarmerie = filterFamillesWithOneOfEvenements(
familles,
isGendarmerie
);
const statsPenales: StatsPenales = {
nbFamillesMisesEnDemeure: famillesMisesEnDemeure.length,
nbFamillesAvecGendarmerie: famillesAvecGendarmerie.length,
compositionPenales: computeCompositionPenales(familles),
crpc: computeCrpc(familles),
tribunalCorrectionnel: computeTribunalCorrectionnel(familles),
intervalGendarmerieProcureur: computeIntervalGendarmerieProcureur(familles),
intervalProcureurTribunalCorrectionnel:
computeIntervalProcureurTribunalCorrectionnel(familles),
};
return statsPenales;
}
function computeCrpc(familles: Famille[]): StatsPenales["crpc"] {
const famillesConcernees = filterFamillesWithOneOfEvenements(familles, (e) =>
isCRPC(e)
);
const acceptees = filterFamillesWithOneOfEvenementsOfType(
familles,
"Acceptation CRPC"
);
const refusees = filterFamillesWithOneOfEvenementsOfType(
familles,
"Refus CRPC"
);
return {
nbFamilles: famillesConcernees.length,
acceptees: acceptees.length,
refusees: refusees.length,
};
}
function computeCompositionPenales(
familles: Famille[]
): StatsPenales["compositionPenales"] {
const famillesConcernees = filterFamillesWithOneOfEvenements(familles, (e) =>
isCompositionPenale(e)
);
const acceptees = filterFamillesWithOneOfEvenementsOfType(
familles,
"Composition pénale acceptée"
);
const refusees = filterFamillesWithOneOfEvenementsOfType(
familles,
"Composition pénale refusée"
);
return {
nbFamilles: famillesConcernees.length,
acceptees: acceptees.length,
refusees: refusees.length,
};
}
function computeTribunalCorrectionnel(
familles: Famille[]
): StatsPenales["tribunalCorrectionnel"] {
const now = new Date();
const famillesPassees = filterFamillesWithOneOfEvenements(
familles,
(e) => e.Type === "Tribunal correctionnel" && isEvenementBefore(e, now)
);
const famillesProgrammees = filterFamillesWithOneOfEvenements(
familles,
(e) => e.Type === "Tribunal correctionnel" && !isEvenementBefore(e, now)
);
const famillesRecidiveTribunal = familles.filter((f) => {
return (
f.Evenements.filter((e) => e.Type === "Tribunal correctionnel").length > 1
);
});
return {
nbFamillesPassees: famillesPassees.length,
nbFamillesProgrammees: famillesProgrammees.length,
nbFamillesRecidive: famillesRecidiveTribunal.length,
};
}
function computeIntervalGendarmerieProcureur(familles: Famille[]): number {
const intervals = familles.flatMap((f) => {
const evtGendarmerie = f.Evenements.find((e) => isGendarmerie(e));
const evtProcureur = f.Evenements.find((e) => isEvtProcureur(e));
// consider only intervals for families with both events date
if (!evtGendarmerie?.Date || !evtProcureur?.Date) {
return [];
}
const intervalInDays = differenceInDays(
evtProcureur.Date,
evtGendarmerie.Date
);
if (intervalInDays < 0) {
console.warn(
`IntervalGendarmerieProcureur < 0 for ${f.Titre} (${f.notionId})`
);
return [];
} else {
return [intervalInDays];
}
});
return average(intervals);
}
function computeIntervalProcureurTribunalCorrectionnel(
familles: Famille[]
): number {
const intervals = familles.flatMap((f) => {
const evtProcureur = f.Evenements.find((e) => isEvtProcureur(e));
const evtTribunal = f.Evenements.find(
(e) => e.Type === "Tribunal correctionnel"
);
// consider only intervals for families with both events date
if (!evtProcureur?.Date || !evtTribunal?.Date) {
return [];
}
const intervalInDays = differenceInDays(
evtTribunal.Date,
evtProcureur.Date
);
if (intervalInDays < 0) {
console.warn(
`IntervalProcureurTribunalCorrectionnel < 0 for ${f.Titre} (${f.notionId})`
);
return [];
} else {
return [intervalInDays];
}
});
return average(intervals);
}