What is actually wrong with this dataset, rather than which checks were written. Generated from the same run that built the docs.
Share of incidents whose PDF report date lands a day later than the dashboard's exact timestamp, by the hour the incident actually happened. The later it gets, the more often the report rolls into the next day.
Actual defects only, measured on every run. Two are deliberate flags rather than regressions. Correct-but-surprising characteristics are kept out of this table on purpose; see dataset characteristics and geocoding below.
| Issue | What it means | Rows | Share | Owner |
|---|---|---|---|---|
| pdf_dashboard_date_mismatch | PDF and dashboard disagree on the incident date by a day or more | 2,301 | 10.09% | flagged in merge_notes, not repaired |
| complaint_collisions | One complaint number on materially different incidents | 69 | 0.3% | source data, flag only |
| republished_pdf_rows | Same incident ingested twice under a revised PDF filename | 68 | 0.3% | ingest bug, fix in drop_exact_source_duplicates |
| ungeocoded | No coordinates resolved | 19 | 0.08% | run a Python cache-fill |
| date_sentinel | Date failed to parse and fell back to 1900-01-01 | 5 | 0.02% | source data, flag only |
| date_future | Incident date is in the future | 2 | 0.01% | source data, poisons naive freshness checks |
Correct but worth knowing. None of these is a defect, which is why they are not in the table above.
| Measure | What it is | Rows | Share |
|---|---|---|---|
| geocoded | Incidents with coordinates resolved | 22,784 | 99.92% |
| in_village | Geocoded inside one of the 8 policing zone polygons | 20,044 | 87.9% |
| pdf_only | Present in the PDF activity reports only, so no NIBRS coding or exact timestamp | 8,994 | 39.44% |
| dashboard_only | Present in the Power BI export only, so no narrative text | 7,429 | 32.58% |
| both_sources | Matched across both ingest streams and merged | 6,380 | 27.98% |
| zone_snapped | Attached to a zone by the 55m nearest-zone fallback rather than strict containment | 1,630 | 7.15% |
| outside_village | Geocoded outside the village. Correct: OPPD reports incidents in neighbouring towns | 1,110 | 4.87% |
99.92% of incidents have coordinates (22,784 of 22,803). Only 19 do not. The pipeline is cache-first, so a run resolves almost everything without calling the geocoding API.
lower(trim(location)), while the pipeline keys on normalize_location(),
which also strips punctuation and standardises street suffixes. So this measures the dbt-side
key, not the pipeline: 15,649 of the unmatched rows are
geocoded perfectly well. It is here to quantify what publishing a real
location_key upstream would buy, nothing more.
Snapped rows fell within 55m of a polygon and were attached by the nearest-zone fallback. Outside-village rows are correct, not errors.
The docs site renders test definitions but not results. These are the results.
| Check | Applies to | Status | Failures |
|---|---|---|---|
| singular | assert_no_complaint_collisions | warn | 34 |
| singular | assert_no_republished_pdf_duplicates | warn | 68 |
| singular | assert_zone_assignment_parity | pass | |
| not_null | crime_details.record_id | pass | |
| relationships | crime_details.record_id | pass | |
| unique | crime_details.record_id | pass | |
| accepted_values | crimes.primary_crime_against | pass | |
| accepted_values | crimes.source | pass | |
| not_null | crimes.date | pass | |
| not_null | crimes.record_id | pass | |
| unique_combination_of_columns | crimes | pass | |
| not_null | data_profile_summary.measure | pass | |
| unique | data_profile_summary.measure | pass | |
| not_null | data_quality_summary.issue | pass | |
| not_null | data_quality_summary.row_count | pass | |
| unique | data_quality_summary.issue | pass | |
| not_null | dim_date.date_day | pass | |
| unique | dim_date.date_day | pass | |
| not_null | dim_offense.offense_text | pass | |
| unique | dim_offense.offense_text | pass | |
| accepted_values | dim_zone.zone_id | pass | |
| not_null | dim_zone.zone_id | pass | |
| unique | dim_zone.zone_id | pass | |
| accepted_values | fct_incidents.primary_crime_against | pass | |
| accepted_values | fct_incidents.source | pass | |
| accepted_values | fct_incidents.zone_status | pass | |
| not_null | fct_incidents.record_id | pass | |
| not_null | fct_incidents.zone_status | pass | |
| unique | fct_incidents.record_id | pass | |
| unique_combination_of_columns | fct_incidents | pass | |
| accepted_values | geocode_coverage.naive_key_status | pass | |
| not_null | geocode_coverage.record_id | pass | |
| relationships | geocode_coverage.record_id | pass | |
| unique | geocode_coverage.record_id | pass | |
| accepted_values | int_complaint_quality.quality_class | pass | |
| not_null | int_complaint_quality.complaint_num | pass | |
| not_null | int_complaint_quality.row_count | pass | |
| unique | int_complaint_quality.complaint_num | pass | |
| not_null | int_crimes_deduplicated.record_id | pass | |
| unique | int_crimes_deduplicated.record_id | pass | |
| accepted_values | int_crimes_zoned.zone_id | pass | |
| accepted_values | int_crimes_zoned.zone_status | pass | |
| not_null | int_crimes_zoned.record_id | pass | |
| not_null | int_crimes_zoned.zone_status | pass | |
| unique | int_crimes_zoned.record_id | pass | |
| not_null | stg_crime_details.record_id | pass | |
| unique | stg_crime_details.record_id | pass | |
| not_null | stg_crimes.record_id | pass | |
| unique | stg_crimes.record_id | pass | |
| not_null | stg_geocode_cache.location_key | pass | |
| unique | stg_geocode_cache.location_key | pass | |
| not_null | stg_policing_zones.zone_id | pass | |
| not_null | stg_policing_zones.zone_name | pass | |
| unique | stg_policing_zones.zone_id | pass |