diff --git a/src/data/ContexteEntreeDC.ts b/src/data/ContexteEntreeDC.ts index ff03280..6e0b055 100644 --- a/src/data/ContexteEntreeDC.ts +++ b/src/data/ContexteEntreeDC.ts @@ -1,6 +1,9 @@ -export type ContexteEntreeDC = - | "Pas de demande (Plein droit)" - | "Pas de demande" - | "Après refus" - | "Après mise en demeure" - | "Après poursuite procureur"; +export const contexteEntreeDCs = [ + "Pas de demande (Plein droit)", + "Pas de demande", + "Après refus", + "Après mise en demeure", + "Après poursuite procureur", +] as const; + +export type ContexteEntreeDC = (typeof contexteEntreeDCs)[number]; diff --git a/src/data/StatutFamille.ts b/src/data/StatutFamille.ts index 292824e..d876dd2 100644 --- a/src/data/StatutFamille.ts +++ b/src/data/StatutFamille.ts @@ -1,10 +1,13 @@ -export type StatutFamille = - | "Résistant.e" - | "Ex résistant·e·s" - | "À préciser" - | "Se questionne" - | "Désobéissence décidée" - | "Rédaction Déclaration" - | "Déclaration Validée - Attente éléments" - | "Abdandon" - | "Incompatible"; +export const statutsFamille = [ + "Résistant.e", + "Ex résistant·e·s", + "À préciser", + "Se questionne", + "Désobéissance décidée", + "Rédaction Déclaration", + "Déclaration validée - Attente éléments", + "Abandon", + "Incompatible", +] as const; + +export type StatutFamille = (typeof statutsFamille)[number]; diff --git a/src/data/TypeEvenement.ts b/src/data/TypeEvenement.ts index eefbe18..958d83e 100644 --- a/src/data/TypeEvenement.ts +++ b/src/data/TypeEvenement.ts @@ -1,34 +1,37 @@ -export type TypeEvenement = - | "Récidive gendarmerie" - | "Appel du jugement" - | "Tribunal de police judiciaire" - | "Signalement au procureur" - | "Mise en demeure de scolarisation" - | "Signalement" - | "Audition gendarmerie / police" - | "Convocation procureur" - | "Audition procureur" - | "Composition pénale refusée" - | "Composition pénale acceptée" - | "Classement social sans suite" - | "Classement pénal sans suite" - | "Enquête sociale" - | "Information préoccupante" - | "Juge pour enfants" - | "Tribunal correctionnel" - | "Convocation CRPC" - | "Plaidoirie" - | "Audience CRPC" - | "Refus CRPC" - | "Acceptation CRPC" // PLaceholder see does not exist in Notion yet // See https://discord.com/channels/990921361121746984/1245360366322585691/1248260713634336839 - | "Audition des enfants" - | "Assistance éducative" - | "Contrôle forcé" - | "Refus de contrôle" - | "Rappel à la loi" - | "Passage police municipale" - | "Administrateur AD'HOC" - | "Validation désobéissance" - | "Contrôle URSSAF" - | "Contrôle fiscal" - | "Gendarmerie/Forces de l'ordre"; +export const typesEvenements = [ + "Récidive gendarmerie", + "Appel du jugement", + "Tribunal de police judiciaire", + "Signalement au procureur", + "Mise en demeure de scolarisation", + "Signalement", + "Audition gendarmerie / police", + "Convocation procureur", + "Audition procureur", + "Composition pénale refusée", + "Composition pénale acceptée", + "Classement social sans suite", + "Classement pénal sans suite", + "Enquête sociale", + "Information préoccupante", + "Juge pour enfants", + "Tribunal correctionnel", + "Convocation CRPC", + "Plaidoirie", + "Audience CRPC", + "Refus CRPC", + "Acceptation CRPC", // PLaceholder see does not exist in Notion yet // See https://discord.com/channels/990921361121746984/1245360366322585691/1248260713634336839 + "Audition des enfants", + "Assistance éducative", + "Contrôle forcé", + "Refus de contrôle", + "Rappel à la loi", + "Passage police municipale", + "Administrateur AD'HOC", + "Validation désobéissance", + "Contrôle URSSAF", + "Contrôle fiscal", + "Gendarmerie/Forces de l'ordre", +] as const; + +export type TypeEvenement = (typeof typesEvenements)[number]; diff --git a/src/data/checkDataConsistency.ts b/src/data/checkDataConsistency.ts index 0476378..4bc0cec 100644 --- a/src/data/checkDataConsistency.ts +++ b/src/data/checkDataConsistency.ts @@ -49,7 +49,11 @@ function checkFamilyDataConsistency(family: Famille): ConsistencyReport { issueType: "Ex résistant.e.s sans date Sortie", }); } - if (family.Integration && family.Sortie && family.Integration > family.Sortie) { + if ( + family.Integration && + family.Sortie && + family.Integration > family.Sortie + ) { consistencyErrors.push({ familyId: family.Titre, issueType: "Date Intégration > date Sortie ", @@ -68,8 +72,8 @@ function checkFamilyDataConsistency(family: Famille): ConsistencyReport { ) !== undefined; if ( miseEnDemeureBeforeInteg && - family.ContexteEntree !== "Après mise en demeure" && - family.ContexteEntree !== "Après poursuite procureur" + family.ContexteEntree !== "Après mise en demeure" && + family.ContexteEntree !== "Après poursuite procureur" ) { consistencyWarnings.push({ familyId: family.Titre, @@ -77,14 +81,7 @@ function checkFamilyDataConsistency(family: Famille): ConsistencyReport { }); } } - consistencyWarnings.push( - ...family.Evenements.filter((e) => !isValidEvenementFamille(e.Type)).map( - (e) => ({ - familyId: family.Titre, - issueType: `Evenement ${e.notionId} a un Type non géré: "${e.Type}"`, - }) - ) - ); + consistencyWarnings.push( ...family.Evenements.filter((e) => e.Type !== null && e.Date === null).map( (e) => ({ diff --git a/src/notion/fetch/checkDbPropertyOptionsMatchesType.ts b/src/notion/fetch/checkDbPropertyOptionsMatchesType.ts new file mode 100644 index 0000000..375a8a9 --- /dev/null +++ b/src/notion/fetch/checkDbPropertyOptionsMatchesType.ts @@ -0,0 +1,39 @@ +import { GetDatabaseResponse } from "@notionhq/client/build/src/api-endpoints"; + +export function checkDbPropertyOptionsMatchesType( + db: GetDatabaseResponse, + 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 dbPropOptions = propOptions(db.properties[propName]); + 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 []; + } + } +} diff --git a/src/notion/fetch/fetchFamiliesWithEventsFromNotion.ts b/src/notion/fetch/fetchFamiliesWithEventsFromNotion.ts index 2b0167f..2c51acf 100644 --- a/src/notion/fetch/fetchFamiliesWithEventsFromNotion.ts +++ b/src/notion/fetch/fetchFamiliesWithEventsFromNotion.ts @@ -1,10 +1,13 @@ import { Client, isFullPage } from "@notionhq/client"; import { PageObjectResponse } from "@notionhq/client/build/src/api-endpoints"; -import { ContexteEntreeDC } from "../../data/ContexteEntreeDC"; +import { + ContexteEntreeDC, + contexteEntreeDCs, +} from "../../data/ContexteEntreeDC"; import { EvenementFamille } from "../../data/EvenementFamille"; import { Famille } from "../../data/Famille"; -import { StatutFamille } from "../../data/StatutFamille"; -import { TypeEvenement } from "../../data/TypeEvenement"; +import { StatutFamille, statutsFamille } from "../../data/StatutFamille"; +import { TypeEvenement, typesEvenements } from "../../data/TypeEvenement"; import { datePropertyToDate } from "../utils/properties/datePropertyToDate"; import { relationPropertyToPageId } from "../utils/properties/relationPropertyToPageId"; import { selectPropertyToText } from "../utils/properties/selectPropertyToText"; @@ -12,6 +15,9 @@ 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"; export async function fetchFamiliesWithEventsFromNotion( notionClient: Client @@ -19,14 +25,25 @@ export async function fetchFamiliesWithEventsFromNotion( const familiesDbId: string = "5b69e02b296d4a578f8c8ab7fe8b05da"; const familEventsDbId: string = "c4d434b4603c4481a4d445618ecdf999"; + 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); + const eventPages = ( await queryAllDbResults(notionClient, { database_id: familEventsDbId, - sorts: [ - {property: "Date", - direction: "ascending" - } - ] + sorts: [{ property: "Date", direction: "ascending" }], }) ).filter(isFullPage); const familyPages = ( @@ -45,7 +62,7 @@ export async function fetchFamiliesWithEventsFromNotion( }) ); // Fix to remove the empty "Famille de résistant" page that is often created by mistake - const filtered = familles.filter(f => f.Titre !== "Famille de résistant"); + const filtered = familles.filter((f) => f.Titre !== "Famille de résistant"); return filtered; } @@ -78,7 +95,7 @@ function buildFamily( Statut: statusPropertyToText(pageProperties, "Statut") as StatutFamille, ContexteEntree: selectPropertyToText( pageProperties, - "Contexte d’entrée DC" + contexteEntreeDCPropName ) as ContexteEntreeDC, Integration: datePropertyToDate(pageProperties, "Intégration"), Sortie: datePropertyToDate(pageProperties, "Sortie"),