@@ -106,6 +106,64 @@ def _identity_key(row: dict[str, Any]) -> str | None:
106106 return candidates [0 ] if candidates else None
107107
108108
109+ def _source_metrics (
110+ directory_rows : list [dict [str , Any ]],
111+ demographic_rows : list [dict [str , Any ]],
112+ parsed_records : Iterable [dict [str , Any ]],
113+ duplicate_directory_aliases : set [str ],
114+ duplicate_demographic_aliases : set [str ],
115+ ) -> dict [str , Any ]:
116+ """Return row-free reconciliation facts for the private handoff.
117+
118+ Counts intentionally keep source rows, source-native identities, and
119+ activity categories separate. A source disappearance is not represented
120+ as closure here; comparison code owns that explicit not-observed state.
121+ """
122+ parsed = list (parsed_records )
123+
124+ def identity_set (rows : Iterable [dict [str , Any ]]) -> set [str ]:
125+ return {key for row in rows if (key := _identity_key (row ))}
126+
127+ def duplicate_row_count (rows : Iterable [dict [str , Any ]], aliases : set [str ]) -> int :
128+ return sum (1 for row in rows if set (_key_candidates (row )) & aliases )
129+
130+ categories = Counter ()
131+ activity_fields = Counter ()
132+ for item in parsed :
133+ normalized = item .get ("normalized" , {})
134+ slaughter = bool (normalized .get ("species_slaughtered" ))
135+ processing = bool (normalized .get ("processing_activities" ))
136+ if slaughter :
137+ categories ["slaughter" ] += 1
138+ if processing :
139+ categories ["processing" ] += 1
140+ if slaughter and processing :
141+ categories ["slaughter_and_processing" ] += 1
142+ if not slaughter and not processing :
143+ categories ["no_activity_category" ] += 1
144+ for group in (normalized .get ("species_slaughtered" , {}), normalized .get ("processing_activities" , {})):
145+ for field in group :
146+ activity_fields [field ] += 1
147+
148+ return {
149+ "directory_source_rows" : len (directory_rows ),
150+ "demographic_source_rows" : len (demographic_rows ),
151+ "source_native_establishments" : len (identity_set (directory_rows )),
152+ "missing_directory_identity_rows" : sum (1 for row in directory_rows if not _identity_key (row )),
153+ "duplicate_directory_aliases" : len (duplicate_directory_aliases ),
154+ "duplicate_directory_rows" : duplicate_row_count (directory_rows , duplicate_directory_aliases ),
155+ "duplicate_demographic_aliases" : len (duplicate_demographic_aliases ),
156+ "duplicate_demographic_rows" : duplicate_row_count (demographic_rows , duplicate_demographic_aliases ),
157+ "category_coverage" : {
158+ "slaughter_rows" : categories ["slaughter" ],
159+ "processing_rows" : categories ["processing" ],
160+ "slaughter_and_processing_rows" : categories ["slaughter_and_processing" ],
161+ "no_activity_category_rows" : categories ["no_activity_category" ],
162+ },
163+ "activity_field_row_counts" : dict (sorted (activity_fields .items ())),
164+ }
165+
166+
109167def _coordinate (row : dict [str , Any ]) -> tuple [dict [str , Any ] | None , str , str | None ]:
110168 latitude = _field (row , "latitude" , "lat" , "y" )
111169 longitude = _field (row , "longitude" , "lon" , "lng" , "long" , "x" )
@@ -370,6 +428,13 @@ def parse_sources(self, directory: bytes, demographics: bytes | None = None) ->
370428 "matched_demographic_rows" : len (matched_demographics ),
371429 "orphan_demographic_rows" : orphan_demographics ,
372430 "identity_conflicts" : identity_conflicts ,
431+ "source_metrics" : _source_metrics (
432+ directory_rows ,
433+ demographic_rows ,
434+ accepted + [item ["record" ] for item in quarantined ],
435+ duplicate_directory_aliases ,
436+ duplicate_demographic_aliases ,
437+ ),
373438 }
374439
375440 def run (self , raw_path : str | Path , run_dir : str | Path , artifact : SourceArtifact ) -> dict [str , Any ]:
@@ -437,6 +502,7 @@ def run_sources(self, raw_paths: dict[str, bytes | str | Path], run_dir: str | P
437502 "orphan_demographic_rows" : result ["orphan_demographic_rows" ],
438503 "identity_conflicts" : result ["identity_conflicts" ], "unmatched_demographic_is_not_closure" : True ,
439504 },
505+ "source_metrics" : result ["source_metrics" ],
440506 "geocoding" : "disabled" ,
441507 "coverage" : "FSIS-regulated meat, poultry, and egg establishments in the captured edition; state-inspection programs and non-FSIS populations excluded" ,
442508 "publication_state" : "private-candidate" ,
0 commit comments