Site citoyen non officiel — sans lien avec l'Assemblée nationale ni le Conseil constitutionnel.

Comment ce chiffre est produit : Motions de censure

Chaque étape, de la source primaire jusqu'au chiffre affiché sur la page Motions de censure. Les volumes sont lus dans la base au moment de l'affichage ; le code est celui qui tourne sur le serveur (version 4aabd6c).

Parcours des données

  1. 1. Fichiers sources6 fichiers publiés par l'Assemblée nationale et la DILA, téléchargés avec leur date de publication et contrôlés par leur empreinte MD5.
  2. 2. Données brutes versionnéesAN_DOSSIERS : 35 600 enregistrements courants (35 725 versions conservées).AN_SCRUTINS : 18 282 enregistrements courants (18 289 versions conservées).
  3. 3. Lecture et transformationmotions de censure lues dans les dossiers : 63retenues (15e à 17e législature) : 63dont votées (scrutin rattaché) : 62scrutin rattaché par : sequentiel : 39scrutin rattaché par : vote_ref : 23Corrections justifiées : 0 (détail).
  4. 4. CalculMéthode I2 version 1 (définition), par législature, année parlementaire et session.
  5. 5. Résultats figés199 valeurs, calculées le 6 novembre 2026 (lot 400c1859), servies par l'API (/api/indicateurs/I2).
  6. 6. AffichageGraphiques et tableaux de la page Motions de censure, avec la source de chaque élément.

Fichiers sources

Dernière version intégrée de chaque fichier
SourceFichierLégislaturePublié leCollecté leMD5 (publié / calculé)
AN_DOSSIERSDossiers_Legislatifs_XV.json.zip1509/06/202206/11/202608f79aa683f73dbfe43b96c3eff0a64d
08f79aa683f73dbfe43b96c3eff0a64d
AN_DOSSIERSDossiers_Legislatifs.json.zip1628/06/202406/11/2026e7eb837f9ff3128e3dcbcdeddf6a12d4
e7eb837f9ff3128e3dcbcdeddf6a12d4
AN_DOSSIERSDossiers_Legislatifs.json.zip1706/11/202606/11/2026c3f6a8e8a40d8fe716a8ceee5f5129e1
c3f6a8e8a40d8fe716a8ceee5f5129e1
AN_SCRUTINSScrutins_XV.json.zip1509/06/202206/11/2026df161de32586f3de9684bd0c0ebf6a40
df161de32586f3de9684bd0c0ebf6a40
AN_SCRUTINSScrutins.json.zip1628/06/202406/11/2026fd88a684d8eeca8725c469aa332a7b3e
fd88a684d8eeca8725c469aa332a7b3e
AN_SCRUTINSScrutins.json.zip1706/11/202606/11/20268d7f028c156e9b673a55720ef15bd508
8d7f028c156e9b673a55720ef15bd508

Code du calcul

Fonctions exécutées par le serveur pour produire ce chiffre, dans l'ordre : lecture des sources, puis calcul. Le texte est extrait du programme en cours d'exécution (version 4aabd6c).

Transformation.engagements_et_motions — donnees.transformation, lignes 432-513
def engagements_et_motions(self):
    # Dossier du texte : dossier (hors procédure 13) qui contient un acte « …-DGVT » de même n° de texte.
    hote = {}
    for uid, (_, dp) in self.dossiers_json.items():
        if (dp.get("procedureParlementaire") or {}).get("code") == an.PROCEDURE_ENGAGEMENT:
            continue
        for a in an.actes(dp.get("actesLegislatifs")):
            if (a.get("codeActe") or "").endswith("-DGVT") and a.get("texteAssocie"):
                hote[an.numero_texte(a["texteAssocie"])] = (uid, a["codeActe"].split("-")[0])

    motions_brutes = []  # (motion json, dossier uid, engagement ou None, structurel ?)
    engagements = []
    for uid, (brut, dp) in self.dossiers_json.items():
        if (dp.get("procedureParlementaire") or {}).get("code") != an.PROCEDURE_ENGAGEMENT:
            continue
        tous = an.actes(dp.get("actesLegislatifs"))
        eng_actes = [a for a in tous if an.est_engagement_493(a)]
        imbriquees = {}
        for a in eng_actes:
            jour = _jour(a["dateActe"])
            texte_uid, lecture = hote.get(an.numero_texte(a.get("texteAssocie")), (None, ""))
            dossier_texte = self.dos.get(texte_uid) if texte_uid else None
            if dossier_texte is None:
                self.rapport.alerte(f"Engagement {a['uid']} : dossier du texte introuvable")
            e = Engagement493(
                uid=a["uid"],
                date=jour,
                declaration=a.get("texteAssocie") or "",
                dossier_engagement=self.dos[uid],
                dossier_texte=dossier_texte,
                lecture=lecture,
                budgetaire=bool(
                    dossier_texte and dossier_texte.procedure_code in an.PROCEDURES_BUDGETAIRES
                ),
                session=self.session(jour),
                # Source : page du dossier d'engagement sur le site de l'Assemblée
                **_trace(brut, a["uid"], an.url_dossier(dp["legislature"], uid)),
            )
            engagements.append(e)
            for m in an.actes(a.get("actesLegislatifs")):
                if m.get("codeActe") == an.ACTE_MOTION:
                    imbriquees[m["uid"]] = e
        for m in tous:
            if m.get("codeActe") != an.ACTE_MOTION:
                continue
            if m["uid"] in imbriquees:
                motions_brutes.append((brut, m, uid, imbriquees[m["uid"]], ModeRattachement.STRUCTUREL))
            elif eng_actes:  # 15e : motion non imbriquée dans un dossier d'engagement
                e = next(x for x in engagements if x.uid == eng_actes[0]["uid"])
                motions_brutes.append((brut, m, uid, e, ModeRattachement.DOSSIER_DATE))
            else:
                motions_brutes.append((brut, m, uid, None, ModeRattachement.SANS_OBJET))
    Engagement493.objects.bulk_create(engagements)
    self.rapport.comptes["engagements_49_3"] = len(engagements)

    motions = []
    for brut, m, dossier_uid, eng, mode in motions_brutes:
        vote = an.vote_de_motion(m)
        ref = (vote.get("voteRefs") or {}).get("voteRef")
        scrutin = self.scr.get(ref) if ref else None
        motions.append(
            MotionCensure(
                identifiant=m.get("texteAssocie") or "",
                dossier=self.dos[dossier_uid],
                date_depot=_jour(m.get("dateActe")),
                alinea=3 if eng else 2,
                statut=MotionCensure.Statut.RETIREE
                if m.get("dateRetrait")
                else MotionCensure.Statut.NON_VOTEE,
                engagement=eng,
                mode_rattachement_engagement=mode,
                scrutin=scrutin,
                mode_rattachement_scrutin=ModeRattachement.VOTE_REF
                if scrutin
                else ModeRattachement.SANS_OBJET,
                **_trace(brut, m["uid"], self.dos[dossier_uid].url_source),
            )
        )
    self._rattacher_scrutins(motions)
    MotionCensure.objects.bulk_create(motions)
    self.rapport.comptes["motions"] = len(motions)
    self.rapport.comptes["motions_votees"] = sum(m.statut == "votee" for m in motions)
Transformation._rattacher_scrutins — donnees.transformation, lignes 515-544
def _rattacher_scrutins(self, motions: list[MotionCensure]):
    """Scrutins MOC sans référence dans le dossier : rattachement séquentiel (méthode publiée).

    Scrutins parcourus dans l'ordre de leur numéro ; chacun est rattaché à la motion non encore
    rattachée, de même alinéa, déposée le plus tôt dans la fenêtre FENETRE_VOTE_JOURS avant le vote
    (pour l'alinéa 3, la date de référence est celle de l'engagement).
    """
    pris = {m.scrutin.uid for m in motions if m.scrutin}
    libres = sorted(
        (s for s in self.scr.values() if s.type_vote == "MOC" and s.uid not in pris),
        key=lambda s: (s.legislature_id, s.numero),
    )
    en_attente = sorted(
        (m for m in motions if m.scrutin is None and m.statut != "retiree"),
        key=lambda m: (m.engagement.date if m.engagement else m.date_depot, m.identifiant),
    )
    for s in libres:
        alinea = 3 if "alinéa 3" in s.titre else 2 if "alinéa 2" in s.titre else None
        for m in en_attente:
            reference = m.engagement.date if m.engagement else m.date_depot
            ecart = (s.date - reference).days
            if m.alinea == alinea and FENETRE_VOTE_JOURS[0] <= ecart <= FENETRE_VOTE_JOURS[1]:
                m.scrutin, m.mode_rattachement_scrutin = s, ModeRattachement.SEQUENTIEL
                en_attente.remove(m)
                break
        else:
            self.rapport.alerte(f"Scrutin de motion {s.uid} ({s.date}) non rattaché à une motion")
    for m in motions:
        if m.scrutin:
            m.statut = MotionCensure.Statut.VOTEE
periodes — indicateurs.calcul, lignes 73-81
def periodes(jour: dt.date, legislature: int) -> list[tuple[str, str]]:
    annee = annee_parlementaire(jour)
    return [
        (ResultatIndicateur.PeriodeType.LEGISLATURE, str(legislature)),
        (ResultatIndicateur.PeriodeType.ANNEE_PARLEMENTAIRE, annee),
        (ResultatIndicateur.PeriodeType.SESSION, f"{annee}/{type_session(jour)}"),
        (ResultatIndicateur.PeriodeType.LEGISLATURE_ANNEE, f"{legislature}/{annee}"),
        (ResultatIndicateur.PeriodeType.LEGISLATURE_SESSION, f"{legislature}/{annee}/{type_session(jour)}"),
    ]
_compter — indicateurs.calcul, lignes 93-103
def _compter(elements, cle_legislature, cle_date, ventilations) -> dict:
    """{(periode_type, periode): Counter(ventilation)}"""
    comptes = defaultdict(Counter)
    for e in elements:
        leg = cle_legislature(e)
        if leg not in LEGISLATURES_JALON:
            continue
        for periode in periodes(cle_date(e), leg):
            for v in ventilations(e):
                comptes[periode][v] += 1
    return comptes
calculer() — bloc I2 — indicateurs.calcul
# I2 — motions de censure (rattachées à la période de leur dépôt)
motions = list(MotionCensure.objects.select_related("scrutin"))

def vent_motion(m):
    v = ["deposees", f"deposees_alinea_{m.alinea}"]
    if m.statut == MotionCensure.Statut.VOTEE:
        v += ["votees", f"votees_alinea_{m.alinea}"]
        if m.scrutin and m.scrutin.sort == "adopté":
            v.append("adoptees")
    return v

ajouter(
    "I2", _compter(motions, lambda m: legs.a_la_date(m.date_depot), lambda m: m.date_depot, vent_motion)
)