feat: stats social basé sur propriété
parent
80172ebb11
commit
8dda887349
|
@ -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;
|
||||
|
|
|
@ -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];
|
|
@ -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];
|
|
@ -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",
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export const familEventsDbId: string = "c4d434b4603c4481a4d445618ecdf999";
|
|
@ -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";
|
|
@ -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<Famille[]> {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue