feat: check db prop options outside data consistency

This commit is contained in:
Sébastien Arod 2024-09-06 14:34:14 +02:00
parent f54a860359
commit a6c42aef80
6 changed files with 133 additions and 71 deletions

View file

@ -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];

View file

@ -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];

View file

@ -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];

View file

@ -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) => ({

View file

@ -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 [];
}
}
}

View file

@ -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 dentré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 dentrée DC"
contexteEntreeDCPropName
) as ContexteEntreeDC,
Integration: datePropertyToDate(pageProperties, "Intégration"),
Sortie: datePropertyToDate(pageProperties, "Sortie"),