2626from duckpipe .fetch_climat import CLIMAT_BRONZE_PATH , build_stations_csv
2727from duckpipe .fetch_dpe import DPE_BRONZE_PATH , build_dpe_sample
2828from duckpipe .pipeline_registry import register_pipelines
29+ from duckpipe .pipelines .iris import make_iris_prix_pipeline
2930from duckpipe .pipelines .prix_millesime import make_prix_millesime_pipeline
3031
3132logger = logging .getLogger (__name__ )
@@ -61,18 +62,39 @@ def cmd_ingest(args: argparse.Namespace) -> None:
6162 logger .info ("[ok] ingest %s" , name )
6263
6364
65+ def _uri_exists (uri : str ) -> bool :
66+ return fetch .gcs_exists (uri ) if fetch .is_gcs_uri (uri ) else Path (uri ).exists ()
67+
68+
69+ def _annees_points_disponibles (env : catalogs .Environment , year : int ) -> list [int ]:
70+ """Millésimes annexes dont les dvf_points existent en silver : un millésime
71+ manquant réduit la fenêtre poolée d'iris_prix au lieu d'échouer le run
72+ (même tolérance que l'évolution des fiches dans publish_web)."""
73+ annees : list [int ] = []
74+ for annee in catalogs .WEB_MILLESIMES :
75+ if annee == year :
76+ continue
77+ if _uri_exists (catalogs .dvf_points_path (env , annee )):
78+ annees .append (annee )
79+ else :
80+ logger .warning ("[warn] dvf_points_%s absent, fenêtre poolée réduite" , annee )
81+ return annees
82+
83+
6484def cmd_run (args : argparse .Namespace ) -> None :
6585 env = catalogs .get_environment (args .env , local_root = args .local_root )
6686 catalog = catalogs .build_catalog (env , year = args .year , run_date = args .run_date )
6787
6888 if args .pipeline == "prix_millesime" :
6989 pipeline = make_prix_millesime_pipeline (args .year )
90+ elif args .pipeline == "iris_prix" :
91+ pipeline = make_iris_prix_pipeline (args .year , _annees_points_disponibles (env , args .year ))
7092 else :
7193 pipelines = register_pipelines ()
7294 if args .pipeline not in pipelines :
7395 raise SystemExit (
7496 f"pipeline inconnu : { args .pipeline !r} "
75- f"(disponibles : { ', ' .join (sorted (pipelines ))} , prix_millesime)"
97+ f"(disponibles : { ', ' .join (sorted (pipelines ))} , prix_millesime, iris_prix )"
7698 )
7799 pipeline = pipelines [args .pipeline ]
78100
@@ -120,10 +142,7 @@ def cmd_validate_gold(args: argparse.Namespace) -> None:
120142 )
121143 previous_top : list [str ] | None = None
122144 latest = catalogs .gold_latest_path (env )
123- latest_exists = (
124- fetch .gcs_exists (latest ) if fetch .is_gcs_uri (latest ) else Path (latest ).exists ()
125- )
126- if latest_exists :
145+ if _uri_exists (latest ):
127146 with fetch .local_read_path (latest ) as latest_path :
128147 previous_top = [
129148 row [0 ]
@@ -138,12 +157,26 @@ def cmd_validate_gold(args: argparse.Namespace) -> None:
138157 report_dest = catalogs .dq_report_path (env , "gold" , args .run_date ),
139158 )
140159
160+ # Contrôles gold du quartier, si l'étape score_quartier a produit la
161+ # table (facultatif : le run doit rester rejouable sans la maille IRIS).
162+ quartier_uri = catalogs .gold_quartier_path (env , args .run_date )
163+ if _uri_exists (quartier_uri ):
164+ with fetch .local_read_path (quartier_uri ) as quartier_path :
165+ con .execute (
166+ "CREATE TABLE score_quartier AS SELECT * FROM "
167+ f"read_parquet('{ quartier_path } ')"
168+ )
169+ validation .validate_gold_quartier (
170+ con ,
171+ report_dest = catalogs .dq_report_path (env , "gold_quartier" , args .run_date ),
172+ )
173+ else :
174+ logger .warning ("[warn] score_quartier absent, contrôle gold quartier ignoré" )
175+
141176 # Contrôles gold des avis, si l'étape NLP a produit la table (facultatif :
142177 # la couverture avis est partielle et le pipeline peut tourner sans).
143178 avis_uri = catalogs .gold_avis_path (env , args .run_date )
144- avis_exists = (
145- fetch .gcs_exists (avis_uri ) if fetch .is_gcs_uri (avis_uri ) else Path (avis_uri ).exists ()
146- )
179+ avis_exists = _uri_exists (avis_uri )
147180 if avis_exists :
148181 with fetch .local_read_path (avis_uri ) as avis_path :
149182 con .execute (
@@ -165,6 +198,13 @@ def cmd_publish(args: argparse.Namespace) -> None:
165198 validation .publish (
166199 catalogs .gold_score_path (env , args .run_date ), catalogs .gold_latest_path (env )
167200 )
201+ # Score quartier : publié s'il existe (toléré absent, comme les avis — les
202+ # runs antérieurs à la maille IRIS restent rejouables).
203+ quartier_uri = catalogs .gold_quartier_path (env , args .run_date )
204+ if _uri_exists (quartier_uri ):
205+ validation .publish (quartier_uri , catalogs .gold_quartier_latest_path (env ))
206+ else :
207+ logger .warning ("[warn] score_quartier absent, publication quartier ignorée" )
168208
169209
170210def cmd_publish_web (args : argparse .Namespace ) -> None :
0 commit comments