Pāriet uz saturu

Refactoring Plan — Code Audit Sprints

Avots: 2026-05-12 pamatīga kodu audita rezultāts (sk. Lielais audits sadaļā zemāk) Branch: refactor/code-audit-sprints (gan geo-backend, gan geo-mobile) Total estimate: ~143h, 8 sprinti + Sprint 0 quick wins, ~3 mēneši

Šis dokuments ir vienīgais "kur mēs esam" patiesības avots refaktora ceļā. Atjaunina katrs sprints — ar atzīmētiem checkbox uzdevumiem, datumu un piezīmēm par to, kas atklājās.

Identiska kopija dzīvo geo-mobile/docs/REFACTORING_PLAN.md (sync kā ar CLAUDE.md).


Galvenais konteksts (saīsināts audit)

Failu izmēra TOP problēmas

Mobile: - screens/map_screen.dart9 423 LOC (god-screen, kritisks) - screens/monitored_properties_screen.dart3 440 LOC - screens/settings_screen.dart — 1 621 LOC (aug strauji) - services/cadastre_service.dart — 1 097 LOC (mixed responsibilities) - 4 skice_*_service.dart faili dublē formatētāju paterns

Backend: - services/vzd_local_service.py3 043 LOC (34 endpoint'i vienā Flask app)

Galvenie smell signāli

Signāls Skaits Risks
Color(0x...) ārpus theme 249 vietās 32 failos Nav design tokens
fontSize: hardcoded 806 izsaukumi Nav typography tokens
catch (_) { ... } (klusi norij) 147 vietās 47 failos Slēptas problēmas
Mobile widget tests 1 fails (4%) Nav UI safety net
Mobile screen tests 0 (0%) Brings deploys uz baidem
Backend tests allow_failure: true True Green CI bez signāla

Top arhitektūras parādi

  1. map_screen.dart god-screen — jauns feature = +300-500 LOC
  2. vzd_local_service.py monolīts — 34 @app.route vienā failā
  3. 4 skice_*_service failu duplicēšanās
  4. Gmail SMTP laika bumba (Google var noņemt App Password access)
  5. AUTH_BYPASS=1 defaults to ON — silent prod risks

Sprintu kopskats

Sprint Tēma Effort Risks Atkarība
0 3 Quick wins (1 day total) 11h Low
A Theme tokenization 12h Low
B map_screen.dart demolish #1 25h Med A
C map_screen.dart demolish #2 + monitored 25h Med B
D Backend Blueprint refaktors 18h Med — (paralēli B/C)
E Test coverage uzplūdes 20h Low D
F Skice + Capture konsolidācija 18h Low E
G External hardening (Mailgun, VMD cache, Billing) 15h Med
H CI cleanup + lint regulas 10h Low

Atkarību diagramma

0 (quick wins) ──► A (theme) ──► B (map split) ──► C (map split 2)
                                                    │
                                                    ▼
D (backend BP) ─── paralēli ─── E (testi) ──► F (skice)
                                              │
G (external) ─── paralēli ─── H (CI cleanup) ◄┘

Sprint 0 — Quick Wins ✅ PABEIGTS (2026-05-12)

Statuss: Done. Commits: 0977c54 (backend), fc76222 (mobile).

  • 0.1 vmd_api.py retry + 5-min response cache
  • urllib3 Retry 3× ar exp. backoff (1s, 2s, 4s) uz 408/429/500/502/503/504
  • In-memory cache ar TTL 5min, thread-safe lock, _MISS sentinel
  • Negative 404 cache (lai neslogo VMD)
  • clear_cache() helper testiem
  • 3 jauni testi par cache uzvedību
  • 0.2 Backend CI: noņemts allow_failure: true
  • Integration testi izlaisti ar -m "not integration"
  • 108 backend testi tagad reāli bloķē deploy (105 + 3 jauni)
  • 0.3 Lint regulas + dart fix --apply
  • prefer_const_constructors + prefer_const_literals_to_create_immutables + avoid_redundant_argument_values
  • 327 fixes 63 failos automātiski
  • 1 regressija labota manuāli (polygon_clip_100_cases_test typed closures)
  • 435 mobile testi pass

Sprint A — Theme Tokenization (1-2 nedēļas, ~12h)

Mērķis: likvidēt 249× Color(0x...), 806× fontSize:, 200+ hardcoded EdgeInsets.

ATKLĀJUMS sākot: lib/theme/app_spacing.dart un lib/theme/app_typography.dart JAU EKSISTĒ ar pilnu skalu — tikai netiek lietoti. Plāns: migrācija, ne izveide.

  • A.1 lib/theme/app_spacing.dart — JAU EKSISTĒ (xs/sm/md/lg/xl/xxl + helper EdgeInsets/SizedBox)
  • A.2 lib/theme/app_typography.dart — JAU EKSISTĒ (display/headline/title/body/label skala)
  • A.3 widgets/forest_charts.dart — migrēts 2026-05-12 → theme/forest_palette.dart. 11 sugu krāsas + 4 vecuma klases ekstrahētas uz domēna paletes (ne AppColors, jo tās ir mežzinātnes-specifiskas, ne UI tokens). Vienota maiņa karteslejām + PDF eksportam tagad iespējama 1 vietā.
  • A.x1 widgets/warning_banner.dart — migrēts 2026-05-12. 5 hardcoded → AppColors.statusGood, geofenceOutside, geofenceWarningFlash, statusWarn.
  • A.x2 widgets/search_panel.dart — migrēts 2026-05-12. 9 hardcoded → AppColors.statusGood, searchResultCadastre, searchResultPlace, searchHighlight.
  • A.x3 widgets/drawing_toolbar.dart + widgets/map_overlays.dart — migrēti 2026-05-12. 5 hardcoded → jauns toolbarBgDark + jauns snapTargetBlue + esošie audioAccent, trackRecordingAccent.
  • A.4 widgets/status_bar.dart — migrēts 2026-05-12 (4 krāsas → AppColors tokens: statusGood, statusWarn, cautionAmber, criticalRed)
  • A.5 widgets/weather_banner.dart — migrēts 2026-05-12 (6 krāsas → AppColors weatherSafe/Caution/Danger + Bg varianti; 9 fontSize → AppTypography bodySm/labelMd/labelSm; EdgeInsets → AppSpacing)
  • [~] A.6 screens/map_screen.dart — daļēja EdgeInsets migrācija 2026-05-12: 14/97 (14%) ievietoti AppSpacing tokens (allSm/allMd/allLg/horizontalLg). Atlikušie 85 ir non-grid vērtības (20, 50, 60, 6, 10, fromLTRB mix) un risk-prone tikt pārveidotas bez vizuālas regresijas — gaida Sprint B ekstraktus, kur mazākajos widget'os tos var izvērtēt individuāli. 132 fontSize neaiztikti — tie ir nested TextStyle(...) ar krāsām/svariem, prasa strukturālu maiņu, ko labāk veikt pēc Sprint B widget ekstraktiem.
  • [~] A.7 screens/monitored_properties_screen.dart — daļēji migrēts 2026-05-12:
  • 5 hardcoded Color(0x...) → AppColors tokens (4× deadlineUrgent jauns + 1× snapTargetBlue esošs); 0 atlikušas.
  • 6 EdgeInsets literāli (5× all(12) + 1× all(16)) → AppSpacing.allMd/allLg.
  • Atlikušie 60+ EdgeInsets ir non-grid (only/fromLTRB ar mixed 4/6/8/10) un 76 inline fontSize neaiztikti — gaida Sprint C ekstraktus (properties_tab.dart, notifications_tab.dart), kur šaurākajos widget'os tos var izvērtēt individuāli.
  • A.8 Lint regulas jau spēkā (Sprint 0.3) — pievienot avoid_const_constructors un (vēlāk) custom lint, kas aizliedz Color(0x...) ārpus theme/

Atkarība: Sprint 0 ✅


Sprint B — map_screen.dart demolish #1 (2 nedēļas, ~25h)

Mērķis: -2500 LOC no map_screen.dart. Galvenokārt ekstraktējams ar zemu state coupling.

  • B.0 Dead code sweep 2026-05-12 — 7 unused private metodes + 6 unused lauki + 2 orphan local var + 1 kaskāde-orphan kartēšana noņemts. 262 LOC dzēstas (map_screen.dart 9404 → 9142). flutter analyze: 18 → 3 issues (3 atlikušie ir info-level pre-existing BuildContext across async gaps). 521 unit tests pass; 2 integration tests fail external API atkarību dēļ (LAD), nesaistīts.
  • Methods: _buildActiveMapSummary (84 LOC), _showMarkerColorPicker (44 LOC), _drawingHintText (21), _drawingHintIcon (13), _currentMeasurement (11), _overlayCategoryIcon (14), _translateForestField (4) — visi unused_element.
  • Fields: _lastAutosaveAt, _lastTappedFeature, _lastTappedCaNumurs, _baseMapExpanded, _overlaysExpanded, _isDragging — visi write-only, nav reader.
  • Cascade: _forestFieldNames static const map (~50 LOC) palikas dead pēc _translateForestField dzēšanas; arī dzēsts.
  • B.1 widgets/basemaps_overlays_sheet.dart — KORIĢĒT: nav atsevišķa _BasemapsAndOverlaysSheet klases. Faktiskā struktūra: izklīdis funkcijās _buildOverlaySections + nestēts ExpansionTile UI. Atklāj plāns nepareizs — jārestrukturizē.
  • [~] B.2 PIVOT 2026-05-12: _buildTopChipRow izrādījās tikai 15 LOC (plāns kļūdījās ar ~300 LOC estimate — tas ir vienkāršs Row(ScaleBar, search-chip)). Tā vietā ekstrahēts _showMoreMenuSheet + _moreMenuTile + _MoreMenuItem uz widgets/more_menu_sheet.dart:
  • Eksportē MoreMenuItem data class + showMoreMenuSheet(context, items: ...) funkciju
  • _showMoreMenuSheet map_screen-ā tikai sastāda items sarakstu un izsauc ekstrakto funkciju
  • -94 LOC map_screen.dart (9142 → 9048)
  • flutter analyze: 3 → 3 (nemainās, joprojām tikai pre-existing async-gap infos)
  • B.2-orig _buildTopChipRow ekstraktēšana atcelta — par mazu, lai izmaksātos atsevišķs widget fails.
  • B.2b 2026-05-12: _showSharePicker (109 LOC) → widgets/share_picker_sheet.dart (showSharePickerSheet(context, ...) top-level funkcija). Eksports izmanto PackageService + KmlService. _showSharePicker map_screen-ā tagad 7-rindu wrapper. map_screen: 9048 → 8948 LOC (-100).
  • B.2c 2026-05-12: _showVisibilityPanel + _visibilityTile (kopā ~250 LOC) → widgets/visibility_panel_sheet.dart. Vissarežģītākais ekstrakts šajā sesijā: 4 state lauki + 2 setState mutācijas + 2 dažādas zoom darbības. Atrisinājums:
  • Sheet uztur lokālas hidden Set kopijas (StatefulBuilder lokālā state)
  • onChanged callback paziņo parent setState ar jaunajām Set instancēm
  • onZoomToMainCa un onZoomToBounds(LatLngBounds) abstrahē _mapController.fitCamera no widget'a
  • map_screen: 8948 → 8716 LOC (-232). Wrapper tagad 18 rindas.
  • B.2e 2026-05-12: Apgrūtinājumu UI saraksts (4 metodes, ~117 LOC) → widgets/encumbrance_list.dart (static EncumbranceList.header/tile/severityOf/colorOf). Tīrs UI ekstrakts — EncumbranceInfo ir no cadastre_service.dart, krāsu klasifikācija pēc Latvian/English atslēgvārdiem (mikroliegumi/biotopi/aizsargjoslas/utml). map_screen: 8598 → 8483 LOC (-115).
  • B.2d 2026-05-12: Divi mazi sheet ekstrakti vienā commit:
  • _showHeritageDetails (60 LOC) → widgets/heritage_detail_sheet.dart (showHeritageDetailSheet(context, m)). Tīrs ekstrakts — tikai HeritageMonument un launchUrl async.
  • _showGpsMenu (70 LOC) → widgets/gps_menu_sheet.dart (showGpsMenuSheet(context, isActive, currentSettings, onToggleGps, onUpdateSettings)). Geofence iestatījumu navigācija saglabāta.
  • map_screen: 8716 → 8598 LOC (-118).
  • B.3 ATCELTS — _buildActiveMapSummary jau bija dead, dzēsts B.0.
  • B.4 services/ozols_layer_controller.dart — Timer + reload + polygon clip (~400 LOC)
  • B.5 services/audio_recording_controller.dart — koplietots ar Sprint 11 atrastiem ekstraktiem (~300 LOC)
  • B.6 Funkcionāls regresijas tests — visu plūsmas manuāli pārbaudīt
  • B.7 map_screen.dart LOC pēc Sprint B: cieri uz ~6 900 LOC (no 9 142 pēc B.0)

Risks: Med — daudzu state lauku saites jāmaina. Pieprasa rūpīgu testēšanu pēc katra extract.

Sprint B sesija 2026-05-12 — kopsavilkums

Rezultāts: map_screen.dart 9404 → 8483 LOC (-921, -9.8%), 37% no Sprint B mērķa (mērķis ~6900).

Veikti 7 commit'i: 1. B.0 — dead code sweep (262 LOC) 2. B.2 — MoreMenuSheet (94 LOC) 3. B.2b — SharePickerSheet (100 LOC) 4. B.2c — VisibilityPanelSheet (232 LOC) ← vissarežģītākais 5. B.2d — HeritageDetailSheet + GpsMenuSheet (118 LOC) 6. B.2e — EncumbranceList (115 LOC)

Arhitektūras vērtība: 6 jauni widget faili ar konsekventu pattern — top-level show*Sheet(context, ...) funkcijas + callback-only saskarne. flutter analyze nav pievienojusi jaunas warnings (3 pre-existing async-gap infos paliek).

Sprint B atlikušais (nākamai sesijai)

Augstākās prioritātes (ranked pēc LOC ietekmes): 1. _showLayerPicker (~470 LOC, 5157) — biggest single extract, complex state 2. _buildOverlaySections (~180 LOC) — saistīts ar layer picker 3. _showDrawingColorPicker + _showFeatureColorPicker (~250 LOC kopā) — konsolidēt vienā ColorPickerDialog widget 4. _showCaSettingsSheet (~360 LOC), _showCaEditDialog, _showDrawingEditDialog — medium dialogi 5. B.4 Ozols controller — joprojām prasa arhitektūras lēmumu (ChangeNotifier vai pass-through) 6. B.5 audio controller — jau ekstrahēts iepriekš uz widgets/audio_record_dialog.dart

Ieteikumi nākamajai sesijai

  • Sākt ar B.2f (Color picker konsolidācija) — divi līdzīgi metodes vienā commit'ā, ~250 LOC, medium risk
  • Pēc tam _showLayerPicker kā atsevišķs commit (lielākais atlikušais ekstrakts, ~470 LOC)
  • Pirms B.4 (Ozols) — pieņemt arhitektūras lēmumu: ChangeNotifier-based controller vai pass-through callback pattern (kā B.2c VisibilityPanel)

Sprint C — map_screen.dart demolish #2 + monitored_properties split (2 nedēļas, ~25h)

  • C.1 services/drawing_controller.dart — zīmēšanas state + undo/redo (~600 LOC)
  • C.2 widgets/geofence_map_binding.dart — geofence rendering (~200 LOC)
  • C.3 services/deeplink_resolver.dart — post-frame deeplinks (~150 LOC)
  • C.4 services/map_state_auto_saver.dart — Timer + prefs flush (~200 LOC)
  • C.5 widgets/encumbrance_mini_sheet.dart — encumbrance sheet (~80 LOC)
  • C.6 monitored_properties_screen.dart sadalīt:
  • properties_tab.dart — saraksts
  • notifications_tab.dart — push notifications
  • property_search_panel.dart — search UI komponente
  • C.7 map_screen.dart cieri: < 3 500 LOC
  • C.8 monitored_properties_screen.dart cieri: < 1 500 LOC

Sprint D — Backend Blueprint refaktors (1-2 nedēļas, ~18h)

Mērķis: vzd_local_service.py 3 043 LOC → 6 Blueprint moduļi, katrs < 500 LOC.

  • D.1 Struktūras izveide:
    services/vzd_local_service/
      __init__.py            # create_app(), reģistrē blueprintus
      blueprints/
        property_bp.py       # /api/property/*
        geocode_bp.py        # /api/geocode/*, /api/reverse-geocode
        forest_bp.py         # /api/forest/*
        subscription_bp.py   # /api/subscription/*, /api/checkout/*, /webhook
        event_bp.py          # /api/event/*, /api/services-config
        metrics_bp.py        # /metrics, /health
      helpers/
        db_pool.py           # init_pool, query_one, query_all
        payload.py           # _build_property_payload + sub-funkcijas
    
  • D.2 Property Blueprint extract (8 endpointu)
  • D.3 Geocode Blueprint extract (4 endpointu)
  • D.4 Forest Blueprint extract (3 endpointu)
  • D.5 Subscription Blueprint extract (Stripe + Customer)
  • D.6 Event Blueprint extract
  • D.7 Metrics Blueprint extract
  • D.8 Helper'i: db_pool + payload
  • D.9 Esošie testi paliek funkcionāli; pievienot Blueprint-level tests
  • D.10 Deploy verifikācija (gunicorn vzd_local_service:app joprojām strādā vai gunicorn entrypoint vajag atjaunot)

Risks: Med — entrypoint maiņa Docker konteinerim.


Sprint E — Test Coverage Uzplūdes (1-2 nedēļas, ~20h)

5 kritiskās plūsmas, kurām DRŪKĀKI testus:

  • E.1 Stripe webhook tests (backend) — kritisks, klusu silent fail = klients samaksā bez subscription
  • E.2 Property aggregator integration test (backend, in-memory PG fixture vai SQLite)
  • E.3 Skice → KML/PDF eksports roundtrip (mobile)
  • E.4 Marker capture (foto/audio → save → pārlādes) (mobile)
  • E.5 Deeplink → MapScreen integration (mobile)
  • E.6 CI: pievienot test/widgets un atsevišķi test/screens direktorijas (tagad ne CI iekšā)
  • E.7 Mērķis: 8-10 jauni widget testi
  • E.8 Mērķis: 3 jauni screen smoke tests (Map / Settings / Skice)

Sprint F — Skice + Capture Konsolidācija (1-2 nedēļas, ~18h)

  • F.1 services/skice_*_service (4 faili) → vienots services/skice/:
  • skice_service.dart ar exporter strategy
  • exporters/kml_exporter.dart
  • exporters/pdf_exporter.dart
  • exporters/shp_exporter.dart
  • exporters/geojson_exporter.dart
  • F.2 media_capture + media_service apvienot → media_service.dart ar capture submodule
  • F.3 Vienots CapturePicker UI (foto + audio + piezīme + skice no viena FAB)
  • F.4 cadastre_service.dart sadalīt 3 daļās:
  • cadastre_api_client.dart — HTTP klients
  • cadastre_geometry.dart — ģeometrijas parsing
  • cadastre_cache.dart — caching layer
  • F.5 Mobile services count: 66 → ~52

Sprint G — External Services Hardening (1 nedēļa, ~15h)

  • G.1 Gmail SMTP → Mailgun (vai SES) migrācija
  • email_service.py swap SMTP backend
  • Update .env.example
  • DEPLOYMENT.md doc update
  • G.2 VMD epak retry + 5-min cache (jau Sprint 0 daļēji)
  • G.3 BillingService fasāde priekš Stripe (mazina lock-in blast radius)
  • G.4 DB index audit — pg_stat_user_indexes Grafana panel
  • G.5 Drop legacy deployment/refresh_cron.sh

Sprint H — CI + Cleanup (1 nedēļa, ~10h)

  • H.1 .gitlab-ci.yml (699 LOC) — izvilkt biežos blokus uz deploy/lib/*.sh
  • H.2 catch (_) audit pa servisiem — pievienot Sentry hooks
  • H.3 Pārvaldīt AUTH_BYPASS=1 default (sk. CLAUDE.md) — uz prod tas jāuzliek 0
  • H.4 Atjaunot CI cron entries dokumentāciju
  • H.5 Final code style review + lint pass

Progress Tracking

Pārliecinies, ka aizpildi šos pēc katra sprinta:

Sprint Statuss Pabeigts Reālais effort Piezīmes
0 ✅ pabeigts 2026-05-12 ~2h VMD cache + CI blokus + lint regulas (327 auto-fixes)
A 9/9 daļēji: A.3+A.4+A.5+A.x1+A.x2+A.x3 done; A.6+A.7 daļēji 2026-05-12 ~4h 8 widget'i migrēti (50+ hardcoded krāsas); A.6/A.7 daļēji — non-grid spacings un fontSize gaida Sprint B/C dekompozīciju
B 🔄 37% (6/16 commit'i): B.0+B.2+B.2b+B.2c+B.2d+B.2e done 2026-05-12 (sesija 1) ~5h map_screen 9404 → 8483 LOC (-921, -9.8%). 6 jauni widget faili. Mērķis ~6900 LOC; atlikušais — _showLayerPicker ~470, _showCaSettingsSheet ~360, _buildOverlaySections ~180, color picker konsolidācija, B.4 Ozols
C not started Atkarīgs no B
D not started Paralēli ar B/C
E not started Atkarīgs no D
F not started Atkarīgs no E
G not started
H not started

Konvencijas

  1. Branch: Viss darbs refactor/code-audit-sprints branchā. Katra sprinta beigās — squash merge uz main (geo-backend) vai feature branch (geo-mobile).
  2. Commit prefix: refactor(sprint-X.Y): ...
  3. Pēc katra extract: flutter analyze + pytest + manuāls smoke test telefonā.
  4. Sprint progresa update: šo REFACTORING_PLAN.md failu atjauno pa ceļam.

Atsauces

  • Iepriekšējais ātrais audit (post-Sprint 11): commit 6d18255 un 05f5582
  • Audit dokuments: šo failu pamatā ir 2026-05-12 pamatīga kodu audita pilna atskaite (sk. session chat history)
  • Sprintu sekošana: GitLab milestones (sagatavojami nākamajā darba sesijā)