diff --git a/src/data/Famille.ts b/src/data/Famille.ts index efb3874..cf84979 100644 --- a/src/data/Famille.ts +++ b/src/data/Famille.ts @@ -5,6 +5,8 @@ import { isPeriodContaining } from "../period/isPeriodContaining"; import { ContexteEntreeDC } from "./ContexteEntreeDC"; import { EvenementFamille } from "./EvenementFamille"; import { StatutFamille } from "./StatutFamille"; +import { StatutSocial } from "./StatutSocial"; +import { StatutPenal } from "./StatutPenal"; export type Famille = Readonly<{ notionId: string; @@ -13,6 +15,8 @@ export type Famille = Readonly<{ Integration: Date | null; ContexteEntree: ContexteEntreeDC; Sortie: Date | null; + Penal: StatutPenal; + Social: StatutSocial; // sorted by date asc Evenements: EvenementFamille[]; DerniereModification: Date; diff --git a/src/data/StatutPenal.ts b/src/data/StatutPenal.ts new file mode 100644 index 0000000..1372117 --- /dev/null +++ b/src/data/StatutPenal.ts @@ -0,0 +1,14 @@ +export const optionsStatutPenal = [ + "Aucune poursuite", + "Entendus FO", + "Composition pénale refusée", + "Convoquée CRPC", + "Convoqué devant tribunal collégial", + "Appel de la condamnation", + "Classement sous condition", + "Avertissement pénal probatoire", + "Composition pénale acceptée", + "Classée sans suite", + "Enquête en cours", +] as const; +export type StatutPenal = (typeof optionsStatutPenal)[number]; diff --git a/src/data/StatutSocial.ts b/src/data/StatutSocial.ts new file mode 100644 index 0000000..39e8a5d --- /dev/null +++ b/src/data/StatutSocial.ts @@ -0,0 +1,10 @@ +export const optionsStatutSocial = [ + "Pas d’enquête sociale", + "Convoquée par les services sociaux", + "Convocation Juge pour Enfants", + "MJIE", + "Classement après MIJE ou AEMO ou", + "Classement après IP", + "Enquête en cours", +] as const; +export type StatutSocial = (typeof optionsStatutSocial)[number]; diff --git a/src/data/TypeEvenementsSocial.ts b/src/data/TypeEvenementsSocial.ts index ee0084d..b5f47ef 100644 --- a/src/data/TypeEvenementsSocial.ts +++ b/src/data/TypeEvenementsSocial.ts @@ -3,7 +3,7 @@ export const typesEvenementsSocial = [ "Information préoccupante 1", "Information préoccupante 2", "Information préoccupante 3", - "Classement social sans suite", + "Classement suite IP", "Enquête sociale", "Juge pour enfants", "Audition des enfants", diff --git a/src/notion/fetch/checkDbPropertyOptionsMatchesType.ts b/src/notion/fetch/checkDbPropertyOptionsMatchesType.ts index 375a8a9..89eee83 100644 --- a/src/notion/fetch/checkDbPropertyOptionsMatchesType.ts +++ b/src/notion/fetch/checkDbPropertyOptionsMatchesType.ts @@ -1,39 +1,60 @@ -import { GetDatabaseResponse } from "@notionhq/client/build/src/api-endpoints"; +import { + DatabaseObjectResponse, + GetDatabaseResponse, +} from "@notionhq/client/build/src/api-endpoints"; +import { richTextToPlainText } from "../utils/text/richTextToPlainText"; export function checkDbPropertyOptionsMatchesType( - db: GetDatabaseResponse, + db: DatabaseObjectResponse, propName: string, typeOptions: readonly string[] ) { - function diff(list1: readonly string[], list2: readonly string[]): string[] { - const set = new Set(list1); - list2.forEach((item) => set.delete(item)); - return [...set]; + const prop = db.properties[propName]; + const dbTitle = richTextToPlainText(db.title); + const fullPropName = `"${dbTitle}">"${propName}"`; + if (!prop) { + throw new Error( + `No such prop ${fullPropName}. Existing prop names: ${Object.keys( + db.properties + )}` + ); } - const dbPropOptions = propOptions(db.properties[propName]); + const dbPropOptions = propOptions(prop); const notInType = diff(dbPropOptions, typeOptions); const notInDb = diff(typeOptions, dbPropOptions); - if (notInType.length > 0) { - console.warn( - `Options for DB Property ${propName} not in Type Options: ${notInType}` - ); - } - - if (notInDb.length > 0) { - console.warn(`Type Options not in for DB Property ${propName}: ${notInDb}`); - } - function propOptions( - prop: GetDatabaseResponse["properties"][string] - ): string[] { - if (prop.type === "select") { - return prop.select.options.map((o) => o.name); - } else if (prop.type === "status") { - return prop.status.options.map((o) => o.name); - } else if (prop.type === "multi_select") { - return prop.multi_select.options.map((o) => o.name); - } else { - return []; + if (notInType.length > 0 || notInDb.length > 0) { + let message = `DB Property ${fullPropName} schema/types mismatch:`; + if (notInType.length > 0) { + message += `\n - ${ + notInType.length + } options missing from type: [${notInType.map((i) => `"${i}"`)}]`; } + if (notInDb.length > 0) { + message += `\n - ${ + notInDb.length + } options missing from db: [${notInDb.map((i) => `"${i}"`)}]`; + } + console.warn(message); } } + +function propOptions( + prop: GetDatabaseResponse["properties"][string] +): string[] { + if (prop.type === "select") { + return prop.select.options.map((o) => o.name); + } else if (prop.type === "status") { + return prop.status.options.map((o) => o.name); + } else if (prop.type === "multi_select") { + return prop.multi_select.options.map((o) => o.name); + } else { + return []; + } +} + +function diff(list1: readonly string[], list2: readonly string[]): string[] { + const set = new Set(list1); + list2.forEach((item) => set.delete(item)); + return [...set]; +} diff --git a/src/notion/fetch/checkEventDbSchema.ts b/src/notion/fetch/checkEventDbSchema.ts new file mode 100644 index 0000000..283a866 --- /dev/null +++ b/src/notion/fetch/checkEventDbSchema.ts @@ -0,0 +1,14 @@ +import { Client } from "@notionhq/client"; +import { typesEvenements } from "../../data/TypeEvenement"; +import { checkDbPropertyOptionsMatchesType } from "./checkDbPropertyOptionsMatchesType"; +import { DatabaseObjectResponse } from "@notionhq/client/build/src/api-endpoints"; + +export async function checkEventDbSchema( + notionClient: Client, + familEventsDbId: string +) { + const eventsDb = (await notionClient.databases.retrieve({ + database_id: familEventsDbId, + })) as DatabaseObjectResponse; + checkDbPropertyOptionsMatchesType(eventsDb, "Type", typesEvenements); +} diff --git a/src/notion/fetch/checkFamilyDbSchema.ts b/src/notion/fetch/checkFamilyDbSchema.ts new file mode 100644 index 0000000..64c9b3f --- /dev/null +++ b/src/notion/fetch/checkFamilyDbSchema.ts @@ -0,0 +1,27 @@ +import { Client } from "@notionhq/client"; +import { contexteEntreeDCs } from "../../data/ContexteEntreeDC"; +import { statutsFamille } from "../../data/StatutFamille"; +import { checkDbPropertyOptionsMatchesType } from "./checkDbPropertyOptionsMatchesType"; +import { propContexteEntree, propPenal, propSocial } from "./dbfamilleDesc"; +import { optionsStatutSocial } from "../../data/StatutSocial"; +import { optionsStatutPenal } from "../../data/StatutPenal"; +import { propStatut } from "./dbfamilleDesc"; +import { DatabaseObjectResponse } from "@notionhq/client/build/src/api-endpoints"; + +export async function checkFamilyDbSchema( + notionClient: Client, + familiesDbId: string +) { + const familyDb = (await notionClient.databases.retrieve({ + database_id: familiesDbId, + })) as DatabaseObjectResponse; + checkDbPropertyOptionsMatchesType(familyDb, propStatut, statutsFamille); + checkDbPropertyOptionsMatchesType( + familyDb, + propContexteEntree, + contexteEntreeDCs + ); + + checkDbPropertyOptionsMatchesType(familyDb, propPenal, optionsStatutPenal); + checkDbPropertyOptionsMatchesType(familyDb, propSocial, optionsStatutSocial); +} diff --git a/src/notion/fetch/dbIds.ts b/src/notion/fetch/dbIds.ts new file mode 100644 index 0000000..3aa672a --- /dev/null +++ b/src/notion/fetch/dbIds.ts @@ -0,0 +1 @@ +export const familEventsDbId: string = "c4d434b4603c4481a4d445618ecdf999"; diff --git a/src/notion/fetch/dbfamilleDesc.ts b/src/notion/fetch/dbfamilleDesc.ts new file mode 100644 index 0000000..a9a5e33 --- /dev/null +++ b/src/notion/fetch/dbfamilleDesc.ts @@ -0,0 +1,7 @@ +export const familiesDbId: string = "5b69e02b296d4a578f8c8ab7fe8b05da"; +export const propDerniereModification = "Dernière modification"; +export const propSocial = "Social"; +export const propPenal = "Pénal"; + +export const propStatut = "Statut"; +export const propContexteEntree = "Contexte d’entrée DC"; diff --git a/src/notion/fetch/fetchFamiliesWithEventsFromNotion.ts b/src/notion/fetch/fetchFamiliesWithEventsFromNotion.ts index 9555955..9798bb9 100644 --- a/src/notion/fetch/fetchFamiliesWithEventsFromNotion.ts +++ b/src/notion/fetch/fetchFamiliesWithEventsFromNotion.ts @@ -1,13 +1,10 @@ import { Client, isFullPage } from "@notionhq/client"; import { PageObjectResponse } from "@notionhq/client/build/src/api-endpoints"; -import { - ContexteEntreeDC, - contexteEntreeDCs, -} from "../../data/ContexteEntreeDC"; +import { ContexteEntreeDC } from "../../data/ContexteEntreeDC"; import { EvenementFamille } from "../../data/EvenementFamille"; import { Famille } from "../../data/Famille"; -import { StatutFamille, statutsFamille } from "../../data/StatutFamille"; -import { TypeEvenement, typesEvenements } from "../../data/TypeEvenement"; +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"; @@ -15,30 +12,25 @@ import { statusPropertyToText } from "../utils/properties/statusPropertyToText"; import { titlePropertyToText } from "../utils/properties/titlePropertyToText"; import { queryAllDbResults } from "../utils/queryAllDbResults"; import { richTextPropertyToPlainText } from "../utils/text/richTextPropertyToPlainText"; -import { checkDbPropertyOptionsMatchesType } from "./checkDbPropertyOptionsMatchesType"; - -const contexteEntreeDCPropName = "Contexte d’entrée DC"; +import { familEventsDbId } from "./dbIds"; +import { + propContexteEntree, + familiesDbId, + propDerniereModification, + propPenal, + propSocial, +} from "./dbfamilleDesc"; +import { checkFamilyDbSchema } from "./checkFamilyDbSchema"; +import { checkEventDbSchema } from "./checkEventDbSchema"; +import { StatutPenal } from "../../data/StatutPenal"; +import { StatutSocial } from "../../data/StatutSocial"; export async function fetchFamiliesWithEventsFromNotion( notionClient: Client ): Promise { - const familiesDbId: string = "5b69e02b296d4a578f8c8ab7fe8b05da"; - const familEventsDbId: string = "c4d434b4603c4481a4d445618ecdf999"; + await checkFamilyDbSchema(notionClient, familiesDbId); - const familyDb = await notionClient.databases.retrieve({ - database_id: familiesDbId, - }); - checkDbPropertyOptionsMatchesType(familyDb, "Statut", statutsFamille); - checkDbPropertyOptionsMatchesType( - familyDb, - contexteEntreeDCPropName, - contexteEntreeDCs - ); - - const eventsDb = await notionClient.databases.retrieve({ - database_id: familEventsDbId, - }); - checkDbPropertyOptionsMatchesType(eventsDb, "Type", typesEvenements); + await checkEventDbSchema(notionClient, familEventsDbId); const eventPages = ( await queryAllDbResults(notionClient, { @@ -95,15 +87,17 @@ function buildFamily( Statut: statusPropertyToText(pageProperties, "Statut") as StatutFamille, ContexteEntree: selectPropertyToText( pageProperties, - contexteEntreeDCPropName + propContexteEntree ) as ContexteEntreeDC, Integration: datePropertyToDate(pageProperties, "Intégration"), Sortie: datePropertyToDate(pageProperties, "Sortie"), Evenements: familyEvents.filter((fe) => fe.notionIdFamille === page.id), DerniereModification: datePropertyToDate( pageProperties, - "Dernière modification" + propDerniereModification )!, + Penal: statusPropertyToText(pageProperties, propPenal) as StatutPenal, + Social: statusPropertyToText(pageProperties, propSocial) as StatutSocial, }; return family; } diff --git a/src/statistiques/v1/computeELStatsAtDate.ts b/src/statistiques/v1/computeELStatsAtDate.ts index 3a92ee3..ec46cf0 100644 --- a/src/statistiques/v1/computeELStatsAtDate.ts +++ b/src/statistiques/v1/computeELStatsAtDate.ts @@ -15,31 +15,6 @@ export function computeELStatsAtDate( isResistant(famille, asOfDate) || isExResistant(famille, asOfDate) ); - const famillesAvecProcedureCivile = familleResistantesOrEx.filter((famille) => - famille.Evenements.find( - (evt) => isProcedureCivile(evt) && isEvenementBefore(evt, asOfDate) - ) - ); - - const famillesAvecClasseesSocialSansSuite = familleResistantesOrEx.filter( - (famille) => - famille.Evenements.find( - (evt) => - isProcedureCivile(evt) && - isEvenementBefore(evt, asOfDate) && - evt.Type === "Classement social sans suite" - ) - ); - - const famillesAvecJugeDesEnfants = familleResistantesOrEx.filter((famille) => - famille.Evenements.find( - (evt) => - isProcedureCivile(evt) && - isEvenementBefore(evt, asOfDate) && - evt.Type === "Juge pour enfants" - ) - ); - const famillesAvecContrôleFiscal = familleResistantesOrEx.filter((f) => f.Evenements.find((e) => e.Type === "Contrôle fiscal") ); diff --git a/src/statistiques/v2/sociales/StatsSociales.ts b/src/statistiques/v2/sociales/StatsSociales.ts index 48115e6..8855cef 100644 --- a/src/statistiques/v2/sociales/StatsSociales.ts +++ b/src/statistiques/v2/sociales/StatsSociales.ts @@ -6,8 +6,8 @@ export const statsSocialesDesc = { nbFamillesProcedureCivile: { label: "Nb Familles avec une procedure sociale", }, - nbFamilleAvecClassementSansSuite: { - label: "Nb Familles avec un classement sans suite", + nbFamilleAvecClassementSuiteIP: { + label: "Nb Familles avec un classement suite IP (evenements)", }, nbFamilleAvecIP: { @@ -32,6 +32,32 @@ export const statsSocialesDesc = { }, }, }, + propFamilleSocial: { + label: "Stats Familles basé sur la Propriété Social", + stats: { + pasDenquete: { + label: "Pas d’enquête sociale", + }, + convoqueeServiceSociaux: { + label: "Convoquée par les services sociaux", + }, + convocationJugePourEnfants: { + label: "Convocation Juge pour Enfants", + }, + mije: { + label: "MIJE", + }, + classementApresMijeOuAemo: { + label: "Classement après MIJE ou AEMO ou", + }, + classementApresIP: { + label: "Classement après IP", + }, + enqueteEnCours: { + label: "Enquête en cours", + }, + }, + }, }, } as const; diff --git a/src/statistiques/v2/sociales/computeStatsSociales.ts b/src/statistiques/v2/sociales/computeStatsSociales.ts index 96f84d4..51caf5b 100644 --- a/src/statistiques/v2/sociales/computeStatsSociales.ts +++ b/src/statistiques/v2/sociales/computeStatsSociales.ts @@ -39,10 +39,29 @@ export function computeStatsSociales(familles: Famille[]): StatsSociales { (e) => e.Type === "Juge pour enfants" && !isEvenementBefore(e, now) ).length, }, - nbFamilleAvecClassementSansSuite: filterFamillesWithOneOfEvenementsOfType( + nbFamilleAvecClassementSuiteIP: filterFamillesWithOneOfEvenementsOfType( familles, - "Classement social sans suite" + "Classement suite IP" ).length, + propFamilleSocial: { + classementApresIP: familles.filter( + (f) => f.Social === "Classement après IP" + ).length, + classementApresMijeOuAemo: familles.filter( + (f) => f.Social === "Classement après MIJE ou AEMO ou" + ).length, + convocationJugePourEnfants: familles.filter( + (f) => f.Social === "Convocation Juge pour Enfants" + ).length, + convoqueeServiceSociaux: familles.filter( + (f) => f.Social === "Convoquée par les services sociaux" + ).length, + enqueteEnCours: familles.filter((f) => f.Social === "Enquête en cours") + .length, + mije: familles.filter((f) => f.Social === "MJIE").length, + pasDenquete: familles.filter((f) => f.Social === "Pas d’enquête sociale") + .length, + }, }; return statsCiviles; }