O M A H A   I N C I D E N T S             [ KRZ ]   krz.sh

WHAT
police activity across the omaha metro, pulled from the agencies'
own feeds and joined against alpr camera locations from
openstreetmap.

COVERAGE
omaha pd                dcgis arcgis view, 2022-01-01 onward,
nibrs offence records, updated daily.
no stop or disposition data.
bellevue pd             sarpy county publiccrimemap, cad calls
papillion pd            for service with stop type, disposition
la vista pd             and category. rolling 12-month window:
sarpy county so         records age out of the feed, so the local
archive is the only long-term copy. gretna
and springfield appear as fire only; their
police departments do not report to it.
council bluffs pd       cbpd public cfs feed, refreshed every ten
minutes, rolling 12-month window. stop
type, disposition, priority, response time.
street addresses withheld; points exact.
ralston pd              no machine-readable feed. absent.

alpr cameras            openstreetmap via overpass, the same data
deflock renders. 168 nodes in the metro
bbox. odbl, attribution required.

SETUP
uv venv .venv
uv pip install --python .venv/bin/python -r requirements.txt

USE
.venv/bin/python ingest.py --full        # first run, backfill
.venv/bin/python ingest.py               # daily, last 30 days
.venv/bin/python ingest.py cbpd sarpy    # one source at a time
.venv/bin/python ingest.py opd_csv       # 2015-2023 csv archive
.venv/bin/python app.py

ARCHIVE
.github/workflows/daily-pull.yml runs the pull at 11:17 utc and
keeps the database as metro.db.gz on the "archive" release, so the
archive does not depend on any one machine. each run restores that
asset, pulls, refuses to publish if any source came back with fewer
rows than it started with, then uploads and fails loudly if a feed
has not moved in seven days.

first run: trigger it manually with bootstrap enabled, which pulls
every feed in full and creates the release. after that the restore
step is mandatory -- a bootstrap over a live archive throws away
whatever has already aged out of the sarpy and council bluffs
feeds.

github disables scheduled workflows after 60 days without repo
activity, and emails first. that is the most likely way this stops
quietly.

to run the pull locally instead:

0 6 * * *  cd /path/to/omaha-incidents && .venv/bin/python ingest.py

NOTES
all three arcgis services return utc epochs; their where-clause
literals do not agree (opd and council bluffs utc, sarpy central).
ingest.py stores occurred_at in local time.

each feed has its own taxonomy and none of them are comparable, so
ingest.py derives one cross-agency flag, is_stop, per source. stop
outcomes compare citation and arrest rate by substring, which is
all the two disposition vocabularies support: an agency that
records warnings less thoroughly shows a higher citation rate for
that reason alone.

colour scheme follows prefers-color-scheme. plotly writes colours
into the figure, so assets/theme.js reports the media query into a
store and app.py builds each figure from it. restyling after the
fact does not work: swapping a maplibre basemap at runtime leaves
it rebuilding with no data layers.

the camera-proximity panel compares stops against a non-stop
baseline. cameras and stops both concentrate on arterials, so a
gap between the curves is a starting point, not a finding.

raw_data/ingress.db is the old 2015-2023 sqlite build. nothing
reads it any more.

SCREENSHOTS
screenshots/*.png are from the previous 2015-2023 dashboard.

krz.sh