mirror of
https://framagit.org/enfance-libre/statistiques
synced 2025-12-07 13:13:44 +00:00
wip stats departementales
This commit is contained in:
parent
456cb3279d
commit
da5e450564
11 changed files with 440 additions and 284 deletions
4
src/data/Departement.ts
Normal file
4
src/data/Departement.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export type Departement = {
|
||||||
|
notionId: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
14
src/index.ts
14
src/index.ts
|
|
@ -19,6 +19,7 @@ import { nettoyerDonneesFamilles } from "./data/nettoyage/familles/preparerDonne
|
||||||
import { statsAutresDesc } from "./statistiques/v2/autres/StatsAutres";
|
import { statsAutresDesc } from "./statistiques/v2/autres/StatsAutres";
|
||||||
import { computeStatsAutres } from "./statistiques/v2/autres/computeStatsAutres";
|
import { computeStatsAutres } from "./statistiques/v2/autres/computeStatsAutres";
|
||||||
import { updateUpdateDate as updateRootPageUpdateDate } from "./notion/publish/updateUpdateDate";
|
import { updateUpdateDate as updateRootPageUpdateDate } from "./notion/publish/updateUpdateDate";
|
||||||
|
import { computeStatsDepartementales } from "./statistiques/v2/departemental/computeStatDepartement";
|
||||||
|
|
||||||
type ProcessOptions = {
|
type ProcessOptions = {
|
||||||
dryRun: boolean;
|
dryRun: boolean;
|
||||||
|
|
@ -51,12 +52,12 @@ function buildProcessOptions(): ProcessOptions {
|
||||||
await checkDbSchemas(notionClient);
|
await checkDbSchemas(notionClient);
|
||||||
|
|
||||||
console.log("Téléchargement des données...");
|
console.log("Téléchargement des données...");
|
||||||
const donneesFamillesBrutes = await fetchFamiliesWithEventsFromNotion(
|
const donneesNotion = await fetchFamiliesWithEventsFromNotion(notionClient);
|
||||||
notionClient
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("Nettoyage des données brutes...");
|
console.log("Nettoyage des données brutes...");
|
||||||
const { familles, messages } = nettoyerDonneesFamilles(donneesFamillesBrutes);
|
const { familles, messages } = nettoyerDonneesFamilles(
|
||||||
|
donneesNotion.familles
|
||||||
|
);
|
||||||
|
|
||||||
const donneesBloquantes = messages.filter(
|
const donneesBloquantes = messages.filter(
|
||||||
(m) => m.severite === "Donnée incohérente bloquante"
|
(m) => m.severite === "Donnée incohérente bloquante"
|
||||||
|
|
@ -107,6 +108,10 @@ function buildProcessOptions(): ProcessOptions {
|
||||||
const statsSociales = computeStatsSociales(familles);
|
const statsSociales = computeStatsSociales(familles);
|
||||||
const statsAutres = computeStatsAutres(familles);
|
const statsAutres = computeStatsAutres(familles);
|
||||||
const statsGeneralesMensuelles = computeStatsGeneralesMensuelles(familles);
|
const statsGeneralesMensuelles = computeStatsGeneralesMensuelles(familles);
|
||||||
|
const statsDepartementales = computeStatsDepartementales(
|
||||||
|
familles,
|
||||||
|
donneesNotion.departements
|
||||||
|
);
|
||||||
const mermaidDiagramStatsGeneralesMensuellesCode =
|
const mermaidDiagramStatsGeneralesMensuellesCode =
|
||||||
mermaidDiagramStatsGeneralesMensuelles(statsGeneralesMensuelles);
|
mermaidDiagramStatsGeneralesMensuelles(statsGeneralesMensuelles);
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
|
|
@ -131,6 +136,7 @@ function buildProcessOptions(): ProcessOptions {
|
||||||
sociales: statsSociales,
|
sociales: statsSociales,
|
||||||
statsAutres: statsAutres,
|
statsAutres: statsAutres,
|
||||||
StatsGeneralesMensuelles: statsGeneralesMensuelles,
|
StatsGeneralesMensuelles: statsGeneralesMensuelles,
|
||||||
|
statsDepartementales: statsDepartementales,
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
" "
|
" "
|
||||||
|
|
|
||||||
40
src/notion/fetch/fetchContacts.ts
Normal file
40
src/notion/fetch/fetchContacts.ts
Normal 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;
|
||||||
|
}
|
||||||
26
src/notion/fetch/fetchDepartements.ts
Normal file
26
src/notion/fetch/fetchDepartements.ts
Normal 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;
|
||||||
|
}
|
||||||
53
src/notion/fetch/fetchEvenements.ts
Normal file
53
src/notion/fetch/fetchEvenements.ts
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -1,47 +1,26 @@
|
||||||
import { Client, isFullPage } from "@notionhq/client";
|
import { Client } from "@notionhq/client";
|
||||||
import { PageObjectResponse } from "@notionhq/client/build/src/api-endpoints";
|
|
||||||
import { ContexteEntreeDC } from "../../data/ContexteEntreeDC";
|
|
||||||
import { EvenementFamille } from "../../data/EvenementFamille";
|
|
||||||
import { Famille } from "../../data/Famille";
|
import { Famille } from "../../data/Famille";
|
||||||
import { StatutFamille } from "../../data/StatutFamille";
|
import { CacheConfig } from "../utils/queryAllDbResults";
|
||||||
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 { Contact } from "../../data/Contact";
|
import { Contact } from "../../data/Contact";
|
||||||
import { Mission } from "../../data/Mission";
|
import { Mission } from "../../data/Mission";
|
||||||
import { relationPropertyToPageIds } from "../utils/properties/relationPropertyToPageIds";
|
|
||||||
import { checkboxPropertyToBoolean } from "../utils/properties/checkboxPropertyToBoolean";
|
|
||||||
import { Amende } from "../../data/Amende";
|
import { Amende } from "../../data/Amende";
|
||||||
import { fetchAmendes } from "./fetchAmendes";
|
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 = {
|
export type FetchedData = {
|
||||||
notionId: string;
|
familles: Famille[];
|
||||||
name: string;
|
departements: Departement[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function fetchFamiliesWithEventsFromNotion(
|
export async function fetchFamiliesWithEventsFromNotion(
|
||||||
notionClient: Client,
|
notionClient: Client,
|
||||||
cacheConfig: CacheConfig = { ttl: 3600 * 1000 }
|
cacheConfig: CacheConfig = { ttl: 3600 * 1000 }
|
||||||
): Promise<Famille[]> {
|
): Promise<FetchedData> {
|
||||||
const departements: Departement[] = await fetchDepartements(
|
const departements: Departement[] = await fetchDepartements(
|
||||||
notionClient,
|
notionClient,
|
||||||
cacheConfig
|
cacheConfig
|
||||||
|
|
@ -61,213 +40,5 @@ export async function fetchFamiliesWithEventsFromNotion(
|
||||||
departements,
|
departements,
|
||||||
contacts
|
contacts
|
||||||
);
|
);
|
||||||
return familles;
|
return { familles, departements };
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
112
src/notion/fetch/fetchFamilles.ts
Normal file
112
src/notion/fetch/fetchFamilles.ts
Normal 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;
|
||||||
|
}
|
||||||
32
src/notion/fetch/fetchMissions.ts
Normal file
32
src/notion/fetch/fetchMissions.ts
Normal 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;
|
||||||
|
}
|
||||||
106
src/statistiques/v2/departemental/computeStatDepartement.ts
Normal file
106
src/statistiques/v2/departemental/computeStatDepartement.ts
Normal 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
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
42
src/statistiques/v2/penales/computeStatDepartement.ts
Normal file
42
src/statistiques/v2/penales/computeStatDepartement.ts
Normal 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
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -23,9 +23,8 @@ import {
|
||||||
} from "./computeFamilleAvecInfosProceduresPenales";
|
} from "./computeFamilleAvecInfosProceduresPenales";
|
||||||
import { computeIntervalMedGendarmerieOuProcureur } from "./intervals/computeIntervalMedGendarmerieOuProcureur";
|
import { computeIntervalMedGendarmerieOuProcureur } from "./intervals/computeIntervalMedGendarmerieOuProcureur";
|
||||||
import { groupBy } from "lodash";
|
import { groupBy } from "lodash";
|
||||||
import { percent } from "../math/percent";
|
|
||||||
import { isEvtTypeProcedurePenaleHorsGendarmerie } from "../../../data/TypeEvenementsPenal";
|
|
||||||
import { computeStatsAmendes } from "./computeStatsAmendes";
|
import { computeStatsAmendes } from "./computeStatsAmendes";
|
||||||
|
import { computeStatDepartement } from "./computeStatDepartement";
|
||||||
|
|
||||||
export type FamilleAvecInfoTribunaux = Famille & {
|
export type FamilleAvecInfoTribunaux = Famille & {
|
||||||
infoTribunaux: InfoTribunalCorrectionnel[];
|
infoTribunaux: InfoTribunalCorrectionnel[];
|
||||||
|
|
@ -175,41 +174,6 @@ export function computeStatsPenales(familles: Famille[]): StatsPenales {
|
||||||
return 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(
|
function computeCrpc(
|
||||||
famillesResistantesOuEx: Famille[]
|
famillesResistantesOuEx: Famille[]
|
||||||
): StatsPenales["procureur"]["crpc"] {
|
): StatsPenales["procureur"]["crpc"] {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue