Skip to content

Repository files navigation

Assemblée Nationale

TODO: petite présentation du projet.

Description des données

Nous utilisons l'API des tricoteuses pour récupérer les données. Voici le lien vers la documentation.

L'Assemblée nationale travaille à partir de dossiers législatifs. Un dossier législatif permet de suivre l'évolution d'un texte législatif : en commission, au Sénat, amendements... Deux types de textes législatifs nous intéressent : proposition de loi proposée par un député, projet de loi proposé par le gouvernement.

Voici les différents endpoint:

  • dossiers => https://parlement.tricoteuses.fr/dossiers
  • texte de loi => https://parlement.tricoteuses.fr/documents
  • amendements => https://parlement.tricoteuses.fr/amendements
  • députés => https://parlement.tricoteuses.fr/acteurs
  • votes => https://parlement.tricoteuses.fr/scrutins

Information sur le chemin d'une loi Les documents parlementaires

Documentation technique de l'API des tricoteuses

Contributing

Installation

Dépendances à installer

Setup

  1. Installer les dépendances :
uv sync
  1. Créer le fichier .env à partir de l'exemple, puis l'adapter si besoin :
cp .env.example .env
  1. Démarrer PostgreSQL (instance locale définie dans docker-compose.yml, sur le port 5432) :
docker compose up -d db

Les valeurs par défaut de .env.example correspondent à ce conteneur (postgres/postgres, base ipolitics).

Usages

Exécuter des commandes

Il y a deux façons d'exécuter des commandes:

  • uv run main.py [-h] [-d] [-r] [-e] [-a]
  • just

Just permet de facilement exécuter des commandes. Si vous ne voulez pas l'installer, vous pouvez toujours utiliser uv run main.py

Télécharger les données sur l'API des tricoteuses

La commande suivante va télécharger sur l'API des tricoteuses et créer des fichiers JSON dans le répertoire ./data/. Pour rajouter un endpoint, il suffit de modifier la variable APIS dans ./etl/download.py

uv run main.py -d
# ou
just download

Construire la base de donnée

uv run main.py -r
# ou
just db-rebuild

Exécuter l'ETL

uv run main.py -e
# ou
just etl

Recréer la DB et exécuter l'ETL

uv run main.py -a
# ou
just all

Comment marche l'ETL

Après avoir exécuté just download, les données de l'API des tricoteuses sont sauvegardées dans le dossier ./data/ sous la forme de fichiers JSON. L'ETL permet d'extraire des données de ces fichiers pour les sauvegarder dans la base de données. La base de données est représentée à l'aide de modèles créés via SqlAlchemy.

Pour charger un fichier en db, il faut rajouter un modèle qui porte le même nom que le fichier sans l'extension. Pour rajouter un champ du json dans la table, il faut rajouter une colonne dans le modèle du même type que le json et du même nom.

L'ETL est capable d'inférer à partir des modèles les fichiers à ouvrir et les champs à charger.

Exemple

Voici une partie du fichier ./data/dossiers.json

  {
    "uid": "DLR5L17N54464",
    "dataset": 17,
    "chambre": "AN",
    "numero": "4464",
    ...
    }

Je veux rajouter le champ chambre dans la DB et faire en sorte que l'ETL l'ajoute de lui-même.

  1. Rajouter le champ dans le modèle
class Dossier(Base):
    __tablename__ = "dossiers"

    uid: Mapped[str] = mapped_column(primary_key=True)
    titre: Mapped[str] = mapped_column(String(1000))
    dataset: Mapped[int]
    chambre: Mapped[str] = mapped_column(String(5)) # <-------- nouvelle colonne qui porte le même nom que le champ du fichier json

Le champ doit porter le même nom sinon l'ETL ne sera pas capable de le trouver.

  1. Exécuter just all

Objets parlementaires chargés

Table Endpoint Contenu Volume (législature 17)
acteurs /acteurs Députés / sénateurs (référentiel trans-législature) ~3 100
organes /organes Groupes politiques, commissions, assemblées… ~5 400
mandats /mandats Jointure acteur ↔ organe (appartenance + dates) ~25 600
scrutins /scrutins Scrutins publics et résultat agrégé (pour/contre/abstentions) ~8 200
groupesVotants /groupesVotants Résultat d'un scrutin ventilé par groupe politique ~121 500 *
documents /documents Textes (projets/propositions de loi, rapports…) ~4 500
auteursDocument /auteursDocument Auteur(s) d'un document ~20 000
coSignatairesDocument /coSignatairesDocument Co-signataires d'un document ~116 000

* groupesVotants n'expose pas de filtre legislature : la table couvre toutes les législatures (~98 300 lignes se rattachent à un scrutin de la L17, soit 12 groupes pour chacun des 8 192 scrutins concernés). Se scoper par jointure sur scrutins.

Les jointures se font par les colonnes …RefUid (références molles, nullable, indexées). Schéma entité-relation ci-dessous — les boîtes ne montrent que les colonnes clés (PK + FK + quelques champs parlants) ; la liste complète est dans les modèles models/.

erDiagram
  dossiers {
    string uid PK
    string titre
    string libelleProcedure
    string statut
  }
  documents {
    string uid PK
    string dossierRefUid FK
    string auteurPrincipalUid FK
    text   titrePrincipal
    string classeLibelle
    bool   texteLoi
    string dateDepot
  }
  amendements {
    string uid PK
    string acteurRefUid FK
    string groupePolitiqueRefUid FK
    string dossierRefUid FK
    string documentRefUid FK
    string scrutinRefUid FK
    string numeroLong
    string divisionArticleDesignation
    text   exposeSommaire
    string sortAmendement
    string dateDepot
  }
  acteurs {
    string uid PK
    string groupeParlementaireUid FK
    string nom
    string prenom
    string chambre
    bool   actif
  }
  organes {
    string uid PK
    string codeType
    string libelleAbrev
    string positionPolitique
  }
  mandats {
    string uid PK
    string acteurRefUid FK
    string organeRefUid FK
    string typeOrgane
    string libQualite
    string dateDebut
    string dateFin
  }
  scrutins {
    string uid PK
    string dossierRefUid FK
    string documentRefUid FK
    string amendementRefUid FK
    string dateScrutin
    text   objet
    string code
    int    pour
    int    contre
    int    abstentions
  }
  groupesVotants {
    string uid PK
    string scrutinRefUid FK
    string organeRefUid FK
    string positionMajoritaire
    int    pour
    int    contre
    int    abstentions
  }
  auteursDocument {
    string uid PK
    string documentRefUid FK
    string acteurRefUid FK
    string qualite
  }
  coSignatairesDocument {
    string uid PK
    string documentRefUid FK
    string acteurRefUid FK
    string dateCosignature
  }

  dossiers    ||--o{ documents             : "dossierRefUid"
  dossiers    ||--o{ amendements           : "dossierRefUid"
  dossiers    ||--o{ scrutins              : "dossierRefUid"
  documents   ||--o{ amendements           : "documentRefUid"
  documents   ||--o{ scrutins              : "documentRefUid"
  documents   ||--o{ auteursDocument       : "documentRefUid"
  documents   ||--o{ coSignatairesDocument : "documentRefUid"
  acteurs     ||--o{ documents             : "auteurPrincipalUid"
  acteurs     ||--o{ amendements           : "acteurRefUid"
  acteurs     ||--o{ mandats               : "acteurRefUid"
  acteurs     ||--o{ auteursDocument       : "acteurRefUid"
  acteurs     ||--o{ coSignatairesDocument : "acteurRefUid"
  organes     ||--o{ acteurs               : "groupeParlementaireUid"
  organes     ||--o{ amendements           : "groupePolitiqueRefUid"
  organes     ||--o{ mandats               : "organeRefUid"
  organes     ||--o{ groupesVotants        : "organeRefUid"
  amendements ||--o{ scrutins              : "amendementRefUid"
  scrutins    ||--o{ amendements           : "scrutinRefUid"
  scrutins    ||--o{ groupesVotants        : "scrutinRefUid"
Loading

Le lien amendement ↔ scrutin est natif, et dans les deux sens : scrutins.amendementRefUid pointe vers l'amendement tranché par le scrutin, et amendements.scrutinRefUid vers le scrutin qui a tranché l'amendement. Le second est le plus large (~11 600 amendements contre ~6 800), un même scrutin pouvant trancher plusieurs amendements identiques. La jointure reste clairsemée : la plupart des amendements sont tranchés à main levée, sans scrutin public.

Analyse : détection des mentions de collaboration externe

Objectif : repérer les amendements dont l'exposé sommaire déclare une collaboration ou une inspiration avec une entité externe (lobby, syndicat, association, entreprise, fédération professionnelle, ONG…) — formulations du type « travaillé avec… », « en concertation avec… », « inspiré de… ».

Le détecteur (analysis/detect_mentions_regex.py) fonctionne par expressions régulières : déterministe, instantané et sans coût, il matche des familles de formulations calibrées sur le corpus réel, avec des exclusions contextuelles (acteurs publics ou parlementaires, référents textuels type « proposé par le texte ») pour limiter les faux positifs.

Lancer une analyse

La base doit être alimentée au préalable (table amendements, voir les sections ETL ci-dessus).

# Tout le corpus, résultats en JSONL uniquement
just detect-mentions-regex

# + écriture des mentions dans la table amendement_mentions
just detect-mentions-regex --persist

# Sur un sous-ensemble
just detect-mentions-regex --limit 100

# Équivalent sans just :
uv run python -m analysis.detect_mentions_regex --persist

Sorties

  • JSONL brut : analysis/output/mentions_regex.jsonl (une ligne par amendement, dossier gitignoré), plus un récap console des formulations rencontrées et de leur fréquence.
  • Base (avec --persist) : table amendement_mentions, une ligne par mention détectée (amendementUid, citation, formulation, modele='regex:v1', createdAt). L'écriture est idempotente par amendement et scopée au tag modele='regex:v1' : les lignes produites par d'autres détecteurs (ex. un LLM) ne sont jamais touchées.
  • Le repérage regex ne remplit ni entite, ni typeEntite, ni externe : identifier et qualifier l'entité demande une analyse sémantique (prévue dans une itération ultérieure).

Tables d'analyse et rebuild

amendement_mentions est une table d'analyse : elle n'est pas listée dans ETL_TABLES (etl/database.py) et survit donc à un db-rebuild, contrairement à amendements/dossiers qui sont détruites puis rechargées. Sa colonne amendementUid est une référence molle vers amendements.uid (pas de ForeignKey), afin qu'aucune contrainte ne bloque le drop de la table ETL. Pour ajouter une nouvelle analyse, créer un modèle sur ce principe (hors ETL_TABLES).

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages