Skip to content

feat: consolidate schema, seeds, migrations, and APIs across multiple ap - #23

Open
dvalenca wants to merge 24 commits into
devfrom
traffic-violations-try
Open

feat: consolidate schema, seeds, migrations, and APIs across multiple ap#23
dvalenca wants to merge 24 commits into
devfrom
traffic-violations-try

Conversation

@dvalenca

@dvalenca dvalenca commented Jun 6, 2026

Copy link
Copy Markdown

Resumo
Este branch consolida ~227 commits de trabalho abrangendo múltiplos apps do monorepo Atlas. As principais áreas de mudança são: banco de dados compartilhado (schema + migrations + seeds), API de traffic-violations (reforma completa), cyclist-profile (cross-tabulation), emergency-calls (FK de ruas + endpoints v2), traffic-deaths (filtros), cycling-infra (city_id por point-in-polygon).
Banco de Dados Compartilhado (packages/database)
Schema:

Substitui official_streets por street_codes — tabela enxuta derivada de pcr_streets (sem transport_corridor, perimeter_road, neighborhood_code/name)
Adiciona traffic_violations_catalog — catálogo canônico de infrações com known_variants (text[]), category, differentiation
Adiciona description_corrections — mapeamento de descrições com encoding quebrado para versões corrigidas
Adiciona traffic_violations_locations — registro canônico de localizações de infração
Adiciona traffic_equipment — equipamentos de fiscalização da PCR
Renomeia violation_code → cttu_code, remove violation_type_id/coordinates/complementary_data de traffic_violations
Migrations:
0006_slim_cyclops — adiciona city_id em ciclomapa_infra
0007_clean_duplicates — limpa duplicatas e adiciona UNIQUE constraints em tabelas cycling-infra
0012_add_execution_columns — adiciona executed, liquidated, paid em recife_budget_actions
0013_drop_official_streets — migração crítica que substitui official_streets por street_codes
Seeders:
Remove seed-official-streets.ts (substituído pela migration 0013)
Adiciona seed-cyclist-counts-imported.ts e import-google-sheets.ts para integração com Google Sheets
Atualiza seed-traffic-violations.ts e seed-pcr-streets.ts para o novo schema
Traffic Violations (apps/traffic-violations)
Reforma completa do pipeline ETL (build-infraction-catalog.py, classify-catalog.py, etl-normalize.py)
Novo catálogo de infrações com ~200 entradas do CTB (Art. 162 a 254) com variantes, categorias e diferenciação
Migrations do app: 0002_add_traffic_violations_catalog.sql e 0003_rebuild_traffic_violations_catalog.sql (criação + backfill)
API: remove violation_type_id de todos os endpoints, violation_code → cttu_code, remove coordinates, match de categoria por descrição
query-helpers.ts: category resolution via description = ANY(known_variants)
Testes atualizados para refletir novo schema
Cyclist Profile (apps/cyclist-profile)
Adiciona min_interviews query param (default 30) nos endpoints points, survey-locations, filters
API de cross-tabulation completa: dictionary, categories, filters, points, OD, distribution, downloads
Emergency Calls (apps/emergency-calls)
Substitui fuzzy name matching por FK pcr_street_id
Adiciona accidents_by_category ao GeoJSON de concentração
Endpoint v2/streets/history com categorias normalizadas, faixas etárias e breakdown horário
Corrige agrupamento de concentration/geojson por pcr_address
Corrige gender matching com ILIKE (Masculino/Feminino)
Adiciona motivo_desf_cat ao v1/streets/search
Default start_year=2020 em todos os endpoints
Traffic Deaths (apps/traffic-deaths)
Adiciona filtro transportMode ao endpoint causas-secundarias
Corrige endpoints v1 com dados reais, matriz de colisão, decoder DATASUS e nomes RMR
Cycling Infra (apps/cycling-infra)
Remove ciclomapa, corrige city_ids via limites IBGE
Atribui city_id via point-in-polygon para 5 cidades RMR faltantes
Captura ways de borda
Docs & Specs
docs/ARCHITECTURE_ANALYSIS.md — análise arquitetural do monorepo
docs/traffic-violations-deploy-analise.md — análise de deploy e checklist para produção
OpenAPI specs atualizadas para todos os serviços modificados
Notas de Deploy
traffic-violations ainda não está no deploy matrix do CI/CD — precisa ser adicionado em deploy futuro
Migration 0013 é destrutiva (DROP TABLE official_streets CASCADE) — requer validação prévia em staging
Há gap na migration 0011 — renumerar 0012→0011 e 0013→0012 em limpeza futura

dvalenca added 24 commits May 20, 2026 14:52
…tegorias normalizadas

- Troca Client + ensureConnection() por Pool (conexão automática)
- Remove ~25 chamadas de ensureConnection() de todos os handlers
- Corrige RMR: 14 cidades (remove Goiana, adiciona as 5 faltantes)
- Adiciona display_name com capitalização e acentos corretos
- validos agora é objeto com breakdown (atendimento_concluido,
  removido_particulares, removido_bombeiros, obito_local)
- por_sexo: keys lowercase + nao_informado
- por_faixa_etaria: 0_17_anos, 18_29_anos, ..., nao_informado
  (split 50+ em 50-64 e 65+)
- por_categoria: 9 buckets fixos (sinistro/atropelamento x 4 + outro)
- Adiciona campo total = validos.total + invalidos
- Remove módulo unsafe-streets v1 (handlers, routes, index)
- Limpa dados mock de v2 unsafe-streets
- Remove extensao/km mock de streets endpoints
- Corrige tipos (string→number) em summary
- Atualiza specs para refletir mudanças
…fix null gender

- GET /v1/calls/profiles now returns por_sexo, por_faixa_etaria,
  por_categoria no mesmo formato normalizado do /v1/cities
- Reordena registro de rotas em app.ts (summaryRoutes antes de
  callsRoutes) para evitar conflito /calls/profiles vs /calls/{id}
- Corrige captura de gênero null tanto em profiles quanto em cities
…colisão, decoder DATASUS e nome RMR

- Corrige /v1/summary: query real com ocorrência × residência filtrado RMR
- Corrige /v1/cities-by-year: retorna 14 cidades RMR com dados reais, ordenado por total
- Corrige /v1/matrix: substitui cidades×anos por matriz de colisão vítima×contraparte
- Corrige /v1/filtros: filtros reais, decoder DATASUS idade, porFaixaEtaria no resumo
- Corrige /v1/causas-secundarias: adiciona filtros cidade/ano
- Corrige /v2/deaths/by-city: filtro RMR
- Cria lib/rmr.ts: constantes RMR compartilhadas (6 e 7 dígitos)
- Cria lib/idade.ts: decoder DATASUS SIM + classificador faixa etária
…es, capture border ways

- Remove ciclomapa_infra table (migration 0008), all GeoJSON files, and related API routes
- Add city_boundaries table (migration 0009) from pe_limites_municipais.geojson (IBGE 2025)
- Fix city_ids in pdc_ways.json via point-in-polygon (fix_pdc_city_ids.py)
- Capture border ways missed by area(a) using around.border_ways:20 (fetch_border_ways.py)
- Update seed script: load city boundaries via ST_GeomFromGeoJSON
- Update API queries: replace ciclomapa_infra with pdc_relation_ways
…des RMR faltantes

Igarassu (2606804): 37.63 km PDC
Moreno (2609402): 15.62 km PDC
Ilha de Itamaracá (2607604): 6.77 km PDC
Itapissuma (2607752): 6.27 km PDC
Araçoiaba (2601052): 3.43 km PDC

PDC ways nestas cidades foram atribuídos via fix_pdc_city_ids.py
(point-in-polygon sobre limites municipais do IBGE).

Overpass fetch das 5 cidades retornou 0 elementos (sem ciclovias em OSM).
Essas cidades têm apenas infraestrutura PDC (state-funded).
…h geometry/extension data and fix frontend compatibility

- citySummary: fix total_streets as number, add extensaoTotalKm from pcr_streets
- cityConcentration: add street_extension_km per entry via pcr_streets JOIN
- cityGeoJSON: replace empty coordinates with real PostGIS geometry (ST_AsGeoJSON/ST_Collect), add extension_km in properties
- streetSummary: add street_extension_km
- streetGeoJSON: add real geometry + extension_km
- /v1/streets/top: add 'nome' field in response items
- /v1/streets/history: accept ?via= as alias for ?nome=
- /v1/streets/search: accept ?street= as alias for ?nome=
- import pcrStreets schema in db/schema.ts
- fuzzyMatchStreet helper for matching emergency addresses against pcr_streets.nlogra_conc
…nd add period

- Add pcr_street_id FK column to emergency_calls linking to pcr_streets
- Add indexes on emergency_calls(pcr_street_id) and pcr_streets(nlogra_conc)
- Backfill exact matches via pcr_address = nlogra_conc
- Replace slow correlated EXISTS+LIKE query (>60s timeout) with fast
  IN subquery using the new index (~30ms)
- Add period { start_year, end_year } to city summary response
- Cast COUNT(*)::int to prevent bigint being returned as string
…ead of raw address

- Group by pcr_address (normalized) instead of address (raw) in
  concentration, geojson, and most_dangerous_street queries
- Filter out generic PCR addresses (NAO IDENTIFICADO, #N/A, OUTRO MUNICIPIO)
  to exclude non-specific street records from rankings
- Move genericPcrFilter to module scope for reuse across handlers
- This fixes top rankings showing 'BR', 'AV', empty strings as
  most dangerous streets
…matching and match by pcr_address only

- Replace fuzzyMatchStreet() O(N*M) loop with FK-based pcr_address→nlogra_conc
  mapping in cityConcentration and cityGeoJSON handlers
- Use pcr_street_id FK for extensaoTotalKm query in citySummary (via FK→name→all segments)
- Use pcr_street_id FK for extension/geometry queries in streetSummary and streetGeoJSON
- Change street-level endpoints to match by pcr_address ILIKE only instead of
  address ILIKE (avoids false positives and ensures canonical name matching)
- Add start_year/end_year query params to citySummary, cityConcentration, cityGeoJSON
…et_id FK with normalized categories, age groups, and hourly breakdown
…gories, filters, points, OD, distribution and downloads

- Refactor app.ts to standard createApp()/.route() pattern (10 other apps)
- Consolidate health route into single file with DB check
- Add GET /v1/dictionary — 35 field definitions with labels, types, sources
- Add GET /v1/categories — distinct values from JSONB fields
- Add GET /v1/filters — available years, areas, points, categories
- Add POST /v1/cross — dynamic cross-tabulation engine with 19 fields, 24 metrics, whitelist, min_n suppression
- Add GET /v1/points + /points.geojson — aggregated survey points with spatial query
- Add POST /v1/od/matrix + /od/flows.geojson — origin-destination matrix and desire lines
- Add POST /v1/distribution — histograms for age, distance_time, days_total
- Add POST /v1/downloads/aggregate.csv — CSV export from cross results
- Refactor summary endpoint to clean cards format with methodology metadata
- Remove inline handlers and raw Hono app, align with monorepo standards
…no), add 2020 as default startYear, ensure int casts
…start_year=2020 on all endpoints

- Create shared src/lib/categories.ts (CATEGORY_MAP + normalizeCategories)
- Add accidents_by_category (9 buckets) to city GeoJSON response
- Add default start_year=2020 to v1/summary, v1/cities, v1/streets,
  v1/filters, calls, summary endpoints
- Use EXTRACT(YEAR FROM date) >= start_year filter on v1/summary handler
- Use EXTRACT(YEAR FROM date) >= start_year filter on v1/cities handler
- Fix missing AppRouteHandler/SummaryRoute imports in summary handler
… points, survey-locations, filters endpoints

- GET /v1/points, /v1/points.geojson: ?min_interviews=30 default
- GET /v1/cyclist-profiles/survey-locations: ?min_interviews=30 default
- GET /v1/filters: ?min_interviews=30 default for count_points
- Replaces hardcoded HAVING count(*) >= 3 with configurable threshold
- Add 6 dashboard endpoints (overview, top-violations, top-streets, temporal, agent-analysis, violation-codes)
- All dashboard endpoints accept violation_codes filter for frontend-driven category classification
- Include law_code (base legal CTB) in all violation responses
- Remove mock data: by_hour (now real EXTRACT HOUR query), violations_per_km (now from pcr_streets.db2gse_sde)
- Remove /violations/hotspots endpoint (coordinates were NULL)
- Fix /violations/geojson to JOIN pcr_streets for street geometry via ST_AsGeoJSON
- Change db/index.ts from pg.Client to pg.Pool for auto-connection
- Fix agent categorization: 7,8=manual, 9=eletronico
- Add pcrStreets export to db/schema.ts
- Add location_street_matches schema, relations, zod schemas and types
- Add street matching pipeline, normalizer, matcher and validation routes
…assificacao CTB

- Cria tabela violation_categories (violation_code, law_code, description_keyword, category)
- Seed script que importa CSV com 243 artigos do CTB classificados em 8 categorias:
  Segurança viária, Pedestres, Ciclistas, Transporte coletivo, Fluidez, Estacionamento, Administrativas, Outras
- Normaliza law_code do banco para matching com CSV (acentos, cross-references, formatação)
- Keyword-based sub-classificacao para codigos 5452, 5819, 5622, 6122, 6017 (ex: mesmo artigo cobre pedestres E ciclistas)
- Adiciona parametro 'category' aos endpoints dashboard (top-violations, top-streets, temporal, agent-analysis)
- Novo endpoint GET /dashboard/categories lista categorias com totais
- 211 codigos classificados, 0 unmatched
…es, gitignore

- Extract shared query helpers (AGENT_INFO, buildConditions, resolveCategoryCodes)
- Refactor dashboard handlers to use shared helpers
- Add seed pipeline (seed-all, seed-violation-categories, seed-description-corrections)
- Add ETL normalize script (Python) for processing raw CSV data
- Add reference CSVs (CTB classification, description corrections, equipment)
- Add migration for traffic_locations and traffic_equipment tables
- Add .gitignore for large raw data files
- Refactor match pipeline, street matcher/normalizer
- Update route definitions and validation schemas
…log, and API

ETL Pipeline:
- Rewrite etl-normalize.py for 19 files (2007-2025), 2 formats (TSV/CSV), 7 columns
- Fix 2015/2024 date parsing (time from separate column, not embedded)

Location Dictionary (build-location-dict.py):
- Extract equipment (semaphores + posts -> lat/lon), street names, directions, address numbers
- Inverted-index token-based street matching (97.4% coverage)
- Street type normalization (AV/R/C/EST/PCA/REF abbreviations)
- Address number extraction (EM FRENTE, NUMERO, AONUMERO, DEFRONTE patterns)
- Landmark fallback (aeroporto -> Praca Ministro Salgado Filho)
- Street renames (NORTE -> Norte Miguel Arraes de Alencar, SUL -> Sul Cid Sampaio)

Infraction Catalog (build-infraction-catalog.py + classify-catalog.py):
- 500 canonical descriptions from 985 raw variants
- 489 classified into 8 categories via CTB table + keyword rules
- Encoding correction auto-detection (trigram similarity) + truncation fixes
- descricao_mapping.csv: raw -> canonical mapping for known_variants[]

Database:
- Drop: violation_categories, description_corrections, traffic_equipment,
  face_quadra, pcr_streets_new, location_street_matches
- Add: infraction_catalog (618 rows with known_variants TEXT[])
- street_codes: table -> VIEW over pcr_streets
- traffic_locations: merge extracted fields + matching results
- traffic_violations.street_code: populated from traffic_locations (98.8%)

API:
- top-violations: add total/percentage/violations_count, fix category+code filter
- Category resolution via infraction_catalog (description = ANY(known_variants))
- Remove matching/validation endpoints (obsolete location_street_matches)
- Fix buildConditions: apply both category AND violation_codes when present
- database: replace official_streets with street_codes (migration 0013)
- database: add slim_cyclops, clean_duplicates, execution_columns migrations
- database: add traffic_violations_catalog, description_corrections,
  traffic_violations_locations, traffic_equipment tables
- database: import-google-sheets and seed-cyclist-counts-imported for
  Google Sheets integration
- traffic-violations: rebuild ETL pipeline, infraction catalog, and API
- traffic-violations: violation_code -> cttu_code, remove violation_type_id,
  remove coordinates, description-based category matching
- traffic-violations: new app migrations for catalog rebuild and backfill
- cyclist-profile: add min_interviews param, cross-tabulation API
- emergency-calls: pcr_street_id FK, accidents_by_category, hourly breakdown
- traffic-deaths: transportMode filter for causas-secundarias
- cycling-infra: city_id via point-in-polygon for 5 RMR cities
- docs: ARCHITECTURE_ANALYSIS and deploy analysis for traffic-violations
- specs: updated OpenAPI v1/v0 specs for all changed services
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant