UNPKG

@tricoteuses/senat

Version:

Handle French Sénat's open data

327 lines (326 loc) 14.8 kB
import { sql } from "kysely"; import { jsonArrayFrom } from "kysely/helpers/postgres"; import { dbSenat } from "../databases"; import { concat, rtrim, toDateString } from "./util"; import { textes, rapports } from "./documents"; function datesSeances(lectureAssembleeId) { return jsonArrayFrom(dbSenat .withSchema("dosleg") .selectFrom("dosleg.date_seance") .where("dosleg.date_seance.lecidt", "=", lectureAssembleeId) .select(({ ref }) => [toDateString(ref("dosleg.date_seance.date_s")).as("date")])); } function lecturesAssemblee(lectureId) { return jsonArrayFrom(dbSenat .withSchema("dosleg") .selectFrom("lecass") .where("lecass.lecidt", "=", lectureId) .leftJoin("ass", "ass.codass", "lecass.codass") .leftJoin("org", "org.orgcod", "lecass.orgcod") .leftJoin("orippr", "orippr.oripprcod", "lecass.orippr") .select(({ ref }) => [ rtrim(ref("ass.libass")).as("assemblee"), "org.orgnom as libelle_organisme", "org.senorgcod as code_organisme", "lecass.ordreass as ordre_lecture_assemblee", "lecass.sesann as session", "orippr.oripprlib as origine_proposition", "lecass.ptlnum as numero_petite_loi", "lecass.ptlurl as url_petite_loi", "lecass.loiintmod as loi_intitule_modifie", "lecass.debatsurl as url_cr_debats", toDateString(ref("lecass.lecassamecomdat")).as("date_publication_amendements_commission"), toDateString(ref("lecass.lecassamedat")).as("date_publication_amendements_seance"), textes(ref("lecass.lecassidt")).as("textes"), rapports(ref("lecass.lecassidt")).as("rapports"), datesSeances(ref("lecass.lecassidt")).as("dates_seances"), ]) .orderBy("lecass.ordreass", "asc")); } function lectures(loiId) { return jsonArrayFrom(dbSenat .withSchema("dosleg") .selectFrom("lecture") .leftJoin("typlec", "typlec.typleccod", "lecture.typleccod") .where("lecture.loicod", "=", loiId) .select(({ ref }) => [ rtrim(ref("typlec.typleclib")).as("type_lecture"), rtrim(ref("lecture.leccom")).as("libelle"), "typlec.typlecord as ordre_lecture", lecturesAssemblee(ref("lecture.lecidt")).as("lectures_assemblee"), ]) .orderBy("typlec.typlecord", "asc")); } function themes(loiId) { return jsonArrayFrom(dbSenat .withSchema("dosleg") .selectFrom("the") .leftJoin("loithe", "loithe.thecle", "the.thecle") .where("loithe.loicod", "=", loiId) .select(["the.thelib as libelle"])); } const findAllDossiersQuery = dbSenat .withSchema("dosleg") .selectFrom("loi") .leftJoin("typloi", "typloi.typloicod", "loi.typloicod") .leftJoin("etaloi", "etaloi.etaloicod", "loi.etaloicod") .leftJoin("deccoc", "deccoc.deccoccod", "loi.deccoccod") .select(({ eb, ref, val }) => [ rtrim(ref("loi.loicod")).as("code"), "loi.numero as numero", "loi.signet as signet", sql `NULLIF(regexp_replace(loi.url_an, '^.*\\/(DL[^\\/]+)\\.asp$', '\\1'), '')`.as("code_dossier_an"), "loi.signetalt as signet_alternatif", rtrim(ref("loi.motclef")).as("mot_cle"), rtrim(ref("loi.loient")).as("titre_court"), concat(rtrim(ref("typloi.typloiden")), val(" "), rtrim(ref("loi.loitit"))).as("titre"), concat(rtrim(ref("typloi.typloiden")), val(" "), rtrim(ref("loi.loiint"))).as("titre_long"), concat(rtrim(ref("typloi.typloiden")), val(" "), rtrim(ref("loi.loiintori"))).as("titre_long_original"), concat(val("https://www.senat.fr/dossier-legislatif/"), ref("loi.signet"), val(".html")).as("url"), "loi.urgence as urgence", rtrim(ref("typloi.groupe")).as("code_nature_dossier"), rtrim(ref("typloi.typloilib")).as("libelle_type_dossier"), rtrim(ref("etaloi.etaloilib")).as("etat_dossier"), "loi.url_an as url_dossier_assemblee_nationale", "loi.url_presart as url_presentation_articles", "loi.url_ordonnance as url_ordonnance", "loi.orgcod as code_organisme_resolution", "deccoc.deccoclib as libelle_decision_CoC", toDateString(ref("loi.date_decision")).as("date_decision_CoC"), "loi.num_decision as num_decision_CoC", "loi.deccocurl as url_decision_CoC", "loi.doscocurl as url_dossier_CoC", toDateString(ref("loi.saisine_date")).as("date_saisine_CoC"), "loi.saisine_par as condition_saisine_CoC", toDateString(ref("loi.proaccdat")).as("date_procedure_acceleree"), toDateString(ref("loi.proaccoppdat")).as("date_opposition_procedure_acceleree"), toDateString(ref("loi.retproaccdat")).as("date_retrait_procedure_acceleree"), toDateString(ref("loi.date_loi")).as("date_promulgation"), eb .case() .when("loi.loititjo", "is not", null) .then(concat(rtrim(ref("typloi.typloiden")), val(" "), rtrim(ref("loi.loititjo")))) .else("") .end() .as("titre_JO"), toDateString(ref("loi.loidatjo")).as("date_publication_JO"), "loi.loinumjo as numero_JO", "loi.url_jo as url_JO", toDateString(ref("loi.loidatjo2")).as("date_publication_JO_correctif_1"), "loi.loinumjo2 as numero_JO_correctif_1", "loi.url_jo2 as url_JO_correctif_1", toDateString(ref("loi.loidatjo3")).as("date_publication_JO_correctif_2"), "loi.loinumjo3 as numero_JO_correctif_2", "loi.url_jo3 as url_JO_correctif_2", lectures(ref("loi.loicod")).as("lectures"), themes(ref("loi.loicod")).as("themes"), ]) .$narrowType(); export function findAllDossiers() { return findAllDossiersQuery.stream(); } export function getCodeActeLecture(codeNatureDossier, typeLecture, assemblee) { const codeAssemblee = assemblee === "Sénat" ? "SN" : assemblee === "Assemblée nationale" ? "AN" : null; if (typeLecture === "Commission mixte paritaire") { return "CMP"; } if (!codeAssemblee) { return null; } if (["ppl", "pjl", "cvn"].includes(codeNatureDossier) && typeLecture === "Première lecture") { return `${codeAssemblee}1`; } if (["ppl", "pjl", "cvn"].includes(codeNatureDossier) && typeLecture === "Deuxième lecture") { return `${codeAssemblee}2`; } if (["ppl", "pjl", "cvn"].includes(codeNatureDossier) && typeLecture === "Troisième lecture") { return `${codeAssemblee}3`; } if (["ppl", "pjl", "cvn"].includes(codeNatureDossier) && typeLecture === "Quatrième lecture") { return `${codeAssemblee}4`; } if (["ppl", "pjl", "cvn"].includes(codeNatureDossier) && typeLecture === "Nouvelle lecture") { return `${codeAssemblee}NLEC`; } if (["ppl", "pjl", "cvn"].includes(codeNatureDossier) && typeLecture === "Lecture définitive") { return `${codeAssemblee}LDEF`; } if (["ppr"].includes(codeNatureDossier) && typeLecture === "Première lecture") { return `${codeAssemblee}LUNI`; } return null; } // Helper pour déterminer le code de phase (SN1, SN2, CMP...) function getPhasePrefix(lecture, assemblee) { if (assemblee !== "Sénat") return null; const typeLibelle = (lecture.type_lecture || "").toLowerCase(); if (typeLibelle.includes("cmp") || typeLibelle.includes("mixte")) return "CMP"; if (typeLibelle.includes("nouvelle lecture")) return "SNNLEC"; if (typeLibelle.includes("d\u00e9finitive")) return "SNLDEF"; if (typeLibelle.includes("unique")) return "SNLUNI"; if (lecture.ordre_lecture) { return `SN${lecture.ordre_lecture}`; } if (typeLibelle.includes("premi\u00e8re")) return "SN1"; if (typeLibelle.includes("deuxi\u00e8me") || typeLibelle.includes("seconde")) return "SN2"; return "SN1"; } export function buildActesLegislatifs(dossier) { const actes = []; const loiSignet = dossier.signet; const lectures = dossier.lectures || []; for (const lecture of lectures) { const lecturesAssemblee = lecture.lectures_assemblee || []; for (const lecAss of lecturesAssemblee) { // On ne traite que la partie SÉNAT if (lecAss.assemblee !== "Sénat") continue; const phasePrefix = getPhasePrefix(lecture, lecAss.assemblee); if (!phasePrefix) continue; // Préparation des textes (tri chronologique) const textes = lecAss.textes || []; const textesTries = [...textes].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); // ================================================================= // A. DÉPÔT // ================================================================= const depotTexte = textesTries.find((t) => (t.origine || "").toLowerCase().includes("déposé") || (t.origine || "").toLowerCase().includes("transmis") || t.ordre_origine === "0"); if (depotTexte && depotTexte.date) { actes.push({ code_acte: `${phasePrefix}-DEPOT`, date: depotTexte.date, libelle: `Dépôt du texte n°${depotTexte.numero}`, id: depotTexte.id, numero: depotTexte.numero, uid: `${loiSignet}-${phasePrefix}-DEPOT`, session: lecAss.session, chambre: "SN", signet_dossier: loiSignet, texte_url: depotTexte.url, code_organisme: null, }); } // ================================================================= // B. COMMISSION (Rapports) // ================================================================= const rapports = lecAss.rapports || []; for (const rap of rapports) { if (rap.date) { actes.push({ code_acte: `${phasePrefix}-COM-FOND`, date: rap.date, libelle: `Rapport n°${rap.numero} de la commission`, id: rap.id, numero: rap.numero, code_organisme: rap.code_organisme, adoption: rap.adoption, uid: `${loiSignet}-${phasePrefix}-COM`, session: lecAss.session, chambre: "SN", signet_dossier: loiSignet, texte_url: rap.url, }); } } // ================================================================= // C. SÉANCE PUBLIQUE // ================================================================= const datesSeances = lecAss.dates_seances || []; if (datesSeances.length > 0) { // Tri des objets dates datesSeances.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); const premiereSeance = datesSeances[0]; if (premiereSeance && premiereSeance.date) { actes.push({ // Champs pour buildParlementActeLegislatif code_acte: `${phasePrefix}-DEBATS-SEANCE`, date: premiereSeance.date, libelle: `Discussion en séance publique`, uid: `${loiSignet}-${phasePrefix}-DEBATS-SEANCE`, session: lecAss.session, chambre: "SN", signet_dossier: loiSignet, code_organisme: null, }); } } // ================================================================= // D. DÉCISION / VOTE // ================================================================= const texteFinal = [...textesTries].reverse().find((t) => { const origine = (t.origine || "").toLowerCase(); return (origine.includes("adopté") || origine.includes("rejeté") || origine.includes("devenu résolution") || t.code_adoption === "O"); }); if (texteFinal && texteFinal.date) { const origine = (texteFinal.origine || "").toLowerCase(); let libelleStatut = "Adopté"; if (origine.includes("rejeté")) { libelleStatut = "Rejeté"; } else if (origine.includes("devenue résolution")) { libelleStatut = "Adopté"; } actes.push({ code_acte: `${phasePrefix}-DEBATS-DEC`, date: texteFinal.date, libelle: `${libelleStatut === "Adopté" ? "Adoption" : "Rejet"} (Texte n°${texteFinal.numero})`, id: texteFinal.id, numero: texteFinal.numero, adoption: libelleStatut, uid: `${loiSignet}-DEC-${texteFinal.numero}`, session: lecAss.session, chambre: "SN", signet_dossier: loiSignet, texte_url: texteFinal.url, code_organisme: null, }); } } } // ================================================================= // E. HORS LECTURE (CC & PROMULGATION) // ================================================================= if (dossier.date_decision_CoC) { actes.push({ code_acte: "CC", date: dossier.date_decision_CoC, libelle: `Décision du Conseil constitutionnel`, id: dossier.url_decision_CoC, uid: `${loiSignet}-CC`, chambre: "AN", signet_dossier: loiSignet, texte_url: dossier.url_decision_CoC || dossier.url_dossier_CoC, }); } if (dossier.date_promulgation) { actes.push({ code_acte: "PROM", date: dossier.date_promulgation, libelle: `Promulgation de la loi`, date_publication_JO: dossier.date_publication_JO, numero_JO: dossier.numero_JO, url_legifrance: dossier.url_JO, id: dossier.url_JO, uid: `${loiSignet}-PROM`, chambre: "AN", signet_dossier: loiSignet, }); } return actes.sort((a, b) => { const dateA = new Date(a.date).getTime(); const dateB = new Date(b.date).getTime(); return dateA - dateB; }); }