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. 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. 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. 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. CalculMéthode I2 version 1 (définition), par législature, année parlementaire et session.
- 5. Résultats figés199 valeurs, calculées le 6 novembre 2026 (lot 400c1859), servies par l'API (/api/indicateurs/I2).
- 6. AffichageGraphiques et tableaux de la page Motions de censure, avec la source de chaque élément.
Fichiers sources
| Source | Fichier | Législature | Publié le | Collecté le | MD5 (publié / calculé) |
|---|---|---|---|---|---|
| AN_DOSSIERS | Dossiers_Legislatifs_XV.json.zip | 15 | 09/06/2022 | 06/11/2026 | 08f79aa683f73dbfe43b96c3eff0a64d08f79aa683f73dbfe43b96c3eff0a64d |
| AN_DOSSIERS | Dossiers_Legislatifs.json.zip | 16 | 28/06/2024 | 06/11/2026 | e7eb837f9ff3128e3dcbcdeddf6a12d4e7eb837f9ff3128e3dcbcdeddf6a12d4 |
| AN_DOSSIERS | Dossiers_Legislatifs.json.zip | 17 | 06/11/2026 | 06/11/2026 | c3f6a8e8a40d8fe716a8ceee5f5129e1c3f6a8e8a40d8fe716a8ceee5f5129e1 |
| AN_SCRUTINS | Scrutins_XV.json.zip | 15 | 09/06/2022 | 06/11/2026 | df161de32586f3de9684bd0c0ebf6a40df161de32586f3de9684bd0c0ebf6a40 |
| AN_SCRUTINS | Scrutins.json.zip | 16 | 28/06/2024 | 06/11/2026 | fd88a684d8eeca8725c469aa332a7b3efd88a684d8eeca8725c469aa332a7b3e |
| AN_SCRUTINS | Scrutins.json.zip | 17 | 06/11/2026 | 06/11/2026 | 8d7f028c156e9b673a55720ef15bd5088d7f028c156e9b673a55720ef15bd508 |
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.VOTEEperiodes — 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 comptescalculer() — 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)
)