From b86c0b2d13c9f107f4da9b310235273ee83ac4d1 Mon Sep 17 00:00:00 2001 From: david kiania Date: Mon, 15 Jun 2026 23:51:28 +0300 Subject: [PATCH] feat: mttr -> minutes; drop constant alarm/auto flags (migration 06) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mttr generated column is now integer minutes (source raw.mttr is decimal hours), analytics-friendly. Drop is_alarm/is_auto_created/is_auto_closed generated columns — all constant `false` in tickets.inc since alarms are filtered at ingest (still present in raw for audit; loader still filters on raw->>'is_alarm'). is_actionable kept. Co-Authored-By: Claude Opus 4.8 --- README.md | 1 + migrations/06_inc_mttr_minutes.sql | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 migrations/06_inc_mttr_minutes.sql diff --git a/README.md b/README.md index dd234ca..e036c8b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Field-ops **INC ticket** ingestion, geocoding, and read-schema that powers the | `migrations/03_inc_columns.sql` | Unpacks `tickets.inc.raw` into **typed STORED generated columns** (status, cluster, region, team, owner, sla_status, mttr, lat/lng, is_* booleans, and EAT→`timestamptz` timestamps via `tickets.eat_ts()`). Computed for all rows + auto-populated on every ingest; `raw` stays the source of truth | | `migrations/04_inc_latlng.sql` | Redefines `latitude`/`longitude` to `COALESCE(feed, ST_Y/ST_X(geom))` so they're **populated from the geocoded position** (feed is always empty); precision per `geo_source` (`location` vs `cluster` centroid) | | `migrations/05_inc_geography.sql` | Adds `geog geography(Point,4326)` (= `geom::geography`) + GiST index for **routing** — `ST_Distance`/`ST_DWithin`/KNN in real metres (nearest-vehicle, radius search) | +| `migrations/06_inc_mttr_minutes.sql` | `mttr` generated column → integer **minutes** (source is decimal hours); drops the constant `is_alarm`/`is_auto_created`/`is_auto_closed` columns (kept in `raw`). `is_actionable` retained | | `import_tickets.py` | Ingests the **newest INC CSV** from the rustfs `tickets` bucket (`automations/inc/.csv`) and upserts on `ticket_id`; geocodes clusters + INC locations | | `run_migrations.py` | Applies `migrations/*.sql` in order (ledger: `tickets.schema_migrations`) | | `shared.py` | Minimal DB/logging helpers (self-contained — no tracksolid dependency) | diff --git a/migrations/06_inc_mttr_minutes.sql b/migrations/06_inc_mttr_minutes.sql new file mode 100644 index 0000000..3dc4b58 --- /dev/null +++ b/migrations/06_inc_mttr_minutes.sql @@ -0,0 +1,25 @@ +-- 06_inc_mttr_minutes.sql — fleettickets · mttr -> minutes; drop constant flags +-- ───────────────────────────────────────────────────────────────────────────── +-- 1. Redefine the mttr generated column as integer MINUTES (source is decimal +-- hours) — numeric is the analytics-friendly type for avg/percentile/buckets. +-- 2. Drop the is_alarm / is_auto_created / is_auto_closed generated columns: we +-- filter is_alarm=true at ingest, so all three are constant `false` in +-- tickets.inc (zero information). They remain in `raw` (audit + the loader's +-- pre-insert filter still reads raw->>'is_alarm'); is_actionable is kept. +-- raw is untouched; only derived columns change. Idempotent. +-- ───────────────────────────────────────────────────────────────────────────── + +SET search_path = tickets, public; + +-- mttr: hours -> integer minutes +ALTER TABLE tickets.inc DROP COLUMN IF EXISTS mttr; +ALTER TABLE tickets.inc + ADD COLUMN IF NOT EXISTS mttr numeric GENERATED ALWAYS AS + (round(NULLIF(raw->>'mttr','')::numeric * 60)) STORED; + +COMMENT ON COLUMN tickets.inc.mttr IS 'Mean time to resolve, in MINUTES (source raw.mttr is decimal hours).'; + +-- drop the constant boolean flags (still available in raw) +ALTER TABLE tickets.inc DROP COLUMN IF EXISTS is_alarm; +ALTER TABLE tickets.inc DROP COLUMN IF EXISTS is_auto_created; +ALTER TABLE tickets.inc DROP COLUMN IF EXISTS is_auto_closed;