Comment ce chiffre est produit : Contrôle des lois par le Conseil constitutionnel
Chaque étape, de la source primaire jusqu'au chiffre affiché sur la page Contrôle des lois par le Conseil constitutionnel. 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 sources5 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éesCONSTIT : 7 423 enregistrements courants (7 518 versions conservées).DOLE : 3 600 enregistrements courants (5 088 versions conservées).AN_DOSSIERS : 35 600 enregistrements courants (35 725 versions conservées).
- 3. Lecture et transformationdécisions DC lues dans le fonds CONSTIT : 457écartées : traités (DC01) : 6écartées : règlements des assemblées (DC02) : 30écartées : décisions annexes sur une loi : 5écartées : hors période (15e à 17e législature) : 266retenues : lois ordinaires et organiques : 150lois promulguées, dénominateur (15e à 17e législature, hors traités et lois constitutionnelles) : 463Corrections justifiées : 8 (détail).
- 4. CalculMéthode I3 version 1 (définition), par législature, année parlementaire et session.
- 5. Résultats figés657 valeurs, calculées le 6 novembre 2026 (lot 400c1859), servies par l'API (/api/indicateurs/I3).
- 6. AffichageGraphiques et tableaux de la page Contrôle des lois par le Conseil constitutionnel, 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 |
| CONSTIT | CONSTIT_20261019-211152.tar.gz | — | 19/10/2026 | 03/11/2026 | —9da7ffecffff62d6136458490e3e97a9 |
| DOLE | DOLE_20261104-214059.tar.gz | — | 04/11/2026 | 04/11/2026 | —6cb0262f0dfabeefd40a296b3f1b7fcb |
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.classer_solution — donnees.transformation, lignes 548-563
@staticmethod
def classer_solution(solution: str) -> dict:
parties = [p.strip().lower() for p in solution.split(" - ")]
principale = parties[0]
issue = {
"conformité": DecisionCC.Issue.CONFORMITE,
"non conformité partielle": DecisionCC.Issue.NC_PARTIELLE,
"non conformité totale": DecisionCC.Issue.NC_TOTALE,
}.get(principale, DecisionCC.Issue.AUTRE)
return {
"issue_principale": issue,
"reserves": "réserve" in parties[1:],
"declassement_organique": "déclassement organique" in parties[1:],
"effet_differe": "effet différé" in parties[1:],
"annexe": principale if issue == DecisionCC.Issue.AUTRE else "",
}Transformation.decisions — donnees.transformation, lignes 565-599
def decisions(self):
objets = []
for brut in _bruts(Source.CONSTIT, "xml"):
r = ET.fromstring(brut.contenu["xml"])
if (r.findtext(".//NATURE") or "").strip() != "DC":
continue
jour = _jour((r.findtext(".//DATE_DEC") or "").strip())
leg = self.legislature_a_la_date(jour) if jour else None
if leg is None:
self.rapport.comptes["decisions_hors_legislatures_connues"] += 1
continue
numero = (r.findtext(".//NUMERO") or "").strip()
classement = self.classer_solution((r.findtext(".//SOLUTION") or "").strip())
if re.search(r"-\d+-\d+$", numero):
classement["annexe"] = classement["annexe"] or "récusation"
loi = self._loi_deferee(r.find(".//LOI_DEF"), numero)
objets.append(
DecisionCC(
ecli=(r.findtext(".//ECLI") or "").strip(),
numero=numero,
nature_qualifiee=(r.findtext(".//NATURE_QUALIFIEE") or "").strip(),
date=jour,
titre=(r.findtext(".//TITRE") or "").strip(),
solution_brute=(r.findtext(".//SOLUTION") or "").strip(),
loi=loi,
url_cc=(r.findtext(".//URL_CC") or "").strip(),
legislature=leg,
**classement,
**_trace(
brut, (r.findtext(".//ID") or "").strip(), (r.findtext(".//URL_CC") or "").strip()
),
)
)
DecisionCC.objects.bulk_create(objets)
self.rapport.comptes["decisions_dc"] = len(objets)Transformation._loi_deferee — donnees.transformation, lignes 601-639
def _loi_deferee(self, loi_def, numero_decision: str) -> Loi | None:
if loi_def is None or not (loi_def.get("num") or "").strip():
return None
brut_num = loi_def.get("num")
num = brut_num.strip()
if num != brut_num:
self._correction(
"DecisionCC",
numero_decision,
"loi",
brut_num,
num,
"Espaces superflus dans le numéro de loi (CONSTIT)",
"",
)
if num in self.lois_par_numero:
return self.lois_par_numero[num]
# Numéro introuvable : loi promulguée le même jour, de titre proche (contrôle publié)
jour = _jour(loi_def.get("date"))
titre = _normaliser_titre(loi_def.text or "")[:40]
candidates = [
lo
for lo in self.lois_par_numero.values()
if lo.date_promulgation == jour and titre and titre in _normaliser_titre(lo.titre)
]
if len(candidates) == 1:
loi = candidates[0]
self._correction(
"DecisionCC",
numero_decision,
"loi",
num,
loi.numero,
"Numéro de loi introuvable ; loi unique promulguée le même jour, de même titre",
f"Loi {loi.numero} du {jour} : « {loi.titre[:100]} »",
)
return loi
self.rapport.alerte(f"Décision {numero_decision} : loi déférée {num} introuvable")
return Noneperiodes — 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 I3 — indicateurs.calcul
# I3 — décisions DC sur les lois ; dénominateur : lois promulguées hors traités et constitutionnelles
decisions = DecisionCC.objects.filter(nature_qualifiee__in=["DC03", "DC04"], annexe="")
def vent_decision(d):
v = ["deferees", d.issue_principale, f"deferees_{d.nature_qualifiee}"]
if d.issue_principale in (DecisionCC.Issue.NC_PARTIELLE, DecisionCC.Issue.NC_TOTALE):
v.append("censures")
if d.reserves:
v.append("avec_reserves")
return v
comptes_dc = _compter(decisions, lambda d: d.legislature_id, lambda d: d.date, vent_decision)
lois = Loi.objects.exclude(nature__in=[Loi.Nature.TRAITE, Loi.Nature.CONSTITUTIONNELLE])
comptes_lois = _compter(
lois,
lambda lo: legs.a_la_date(lo.date_promulgation),
lambda lo: lo.date_promulgation,
lambda lo: ["promulguees"],
)
for periode, c in comptes_lois.items():
comptes_dc[periode]["lois_promulguees"] += c["promulguees"]
traites = _compter(
Loi.objects.filter(nature=Loi.Nature.TRAITE),
lambda lo: legs.a_la_date(lo.date_promulgation),
lambda lo: lo.date_promulgation,
lambda lo: ["lois_traites_promulguees"],
)
for periode, c in traites.items():
comptes_dc[periode].update(c)
ajouter(
"I3",
comptes_dc,
ratios=[
("part_censures_deferees", "censures", "deferees", None),
("part_censures_promulguees", "censures", "lois_promulguees", None),
],
)