Skip to content

feat: geofence-based geographic targeting for programs (re-land from #76)#273

Open
gonzalesedwin1123 wants to merge 10 commits into
19.0from
reland/gis-geofence
Open

feat: geofence-based geographic targeting for programs (re-land from #76)#273
gonzalesedwin1123 wants to merge 10 commits into
19.0from
reland/gis-geofence

Conversation

@gonzalesedwin1123

@gonzalesedwin1123 gonzalesedwin1123 commented Jul 2, 2026

Copy link
Copy Markdown
Member

Re-lands the scope described in reverted PR #76 (revert: #271), as part of splitting that PR into description-consistent pieces. This description incorporates the relevant content of #76 so reviewers do not need to open it; everything listed below is contained in THIS PR's diff.

Summary

  • Add spp_program_geofence module for geofence-based program targeting and eligibility management.
  • Extend spp_gis spatial operators to support MultiPolygon and GeometryCollection.
  • Make MapTiler API key optional in GIS UI, with OSM fallback when key is not configured (placeholder key YOUR_MAPTILER_API_KEY_HERE treated as unconfigured).
  • Stabilize GIS edit/map widgets (re-render lifecycle, draw interaction behavior).

Functional detail (from #76, verified against this diff)

spp_program_geofence (new module)

  • Program-level geofence_ids and geofence_count.
  • Geofence eligibility manager (spp.program.membership.manager.geofence) with:
    • Tier 1: coordinate intersection.
    • Tier 2: area intersection fallback (optional, with area type filter).
  • Program configuration UI integration and program creation wizard support.
  • Geofence management views/action/menu, gated by explicit read ACLs for Programs Viewer/Validator roles (menu groups restriction avoids exposing items to users lacking read access).

spp_gis

  • Operator support for complex geometry types via ST_GeomFromGeoJSON; for MultiPolygon/GeometryCollection, (geojson, distance) applies ST_Buffer(...) correctly (EPSG:3857 transform parity when SRID is 4326), with regression tests for the distance-buffer path on both complex types.
  • OSM style fallback in renderer/edit widgets when MapTiler key is missing; placeholder-key handling in the controller.
  • Geofence GeoJSON output includes the record uuid as feature id; new spp.gis.geofence.tag model replaces vocabulary-based geofence tags (+ACLs).

Added on top of #76 (not in the original)

  • Upgrade migration (spp_gis 19.0.2.1.0): geofence tag_ids changes co-model from spp.vocabulary to spp.gis.geofence.tag over the same rel table. Release v19.0.2.0.0 (Biliran) ships the old schema, so a pre-migration parks legacy links (the FK swap would otherwise fail) and a post-migration recreates them as tag records. Fully literal, value-parameterized SQL (clears Semgrep/pylint-odoo SQL findings; GHAS alerts #1837–#1842 fixed). Regression tests: remap, dedup across geofences, valid-link survival, fresh-DB no-op.
  • gemini-code-assist review findings, all applied: queued import guards against archived/deleted programs (+regression test) and builds membership values from ids; tier-2 domain drops the NOT-IN anti-pattern (the tier1 | tier2 union already dedups); enrolled-beneficiary exclusion via recordset subtraction; user-facing strings wrapped in _(); the map edit widget re-creates the MapLibre map only when the geometry value actually changed (prevents zoom/pan loss and WebGL context churn — a quick manual map-edit smoke test during review is recommended).
  • Coverage tests (codecov patch 96.9%): eligibility tier-2/preview/queued-import/cycle-verification paths, action_open_geofences, and the MapTiler key controller (configured/placeholder/absent key).
  • Docs: spp_gis DESCRIPTION now describes geofences, complex-geometry queries, and the OSM fallback; spp_program_geofence gains an initial HISTORY fragment; version bump + HISTORY entry for spp_gis; READMEs rendered by CI's pinned generator.

Not in this PR (other pieces of #76, re-landed separately)

spp_hazard CAP severity (#274), spp_cel_domain (#275), spp_api_v2 (#276), PHL demo data (#277), infra/tooling (#278, merged), spp_metric_service (#279), spp_gis_report (#280), spp_api_v2_gis (#281), spp_mis_demo_v2 (#282).

Verification

  • ./spp t spp_gis: 96 passed, 0 failed (includes 4 migration tests + 4 MapTiler controller tests)
  • ./spp t spp_program_geofence: 41 passed, 0 failed (includes archived-program regression test)
  • codecov: patch 96.9% (pass), project pass; remaining uncovered lines are module-level ImportError fallbacks
  • All 20 gemini-code-assist threads resolved with fixing-commit references

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces geofence-based geographic targeting for OpenSPP programs by adding the new spp_program_geofence module and extending spp_gis to support complex geometry types like MultiPolygon and GeometryCollection. It also replaces vocabulary-based geofence tags with a dedicated spp.gis.geofence.tag model, complete with migration scripts, and implements an OpenStreetMap fallback when no MapTiler API key is configured. The review feedback focuses on improving performance and code quality in the new Odoo models and JS widgets. Key recommendations include avoiding map re-creation on every patch in the GIS edit widget, optimizing recordset operations (using subtraction instead of lambda filtering and avoiding heavy NOT IN clauses in search domains), checking for active records in asynchronous tasks, and wrapping user-facing strings in _() for internationalization.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread spp_program_geofence/models/eligibility_manager.py
Comment thread spp_program_geofence/models/eligibility_manager.py
Comment thread spp_program_geofence/models/eligibility_manager.py Outdated
Comment thread spp_program_geofence/models/eligibility_manager.py Outdated
Comment thread spp_program_geofence/models/eligibility_manager.py
Comment thread spp_gis/migrations/19.0.2.1.0/post-migration.py Fixed
Comment thread spp_gis/migrations/19.0.2.1.0/post-migration.py Fixed
Comment thread spp_gis/migrations/19.0.2.1.0/post-migration.py Fixed
Comment thread spp_gis/migrations/19.0.2.1.0/pre-migration.py Fixed
Comment thread spp_gis/migrations/19.0.2.1.0/pre-migration.py Fixed
Comment thread spp_gis/migrations/19.0.2.1.0/pre-migration.py Fixed
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.92308% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.79%. Comparing base (bf61488) to head (5cb9df5).
⚠️ Report is 1 commits behind head on 19.0.

Files with missing lines Patch % Lines
spp_program_geofence/models/eligibility_manager.py 96.29% 5 Missing ⚠️
spp_program_geofence/__manifest__.py 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0     #273      +/-   ##
==========================================
- Coverage   74.86%   71.79%   -3.08%     
==========================================
  Files        1093      282     -811     
  Lines       63718    16590   -47128     
==========================================
- Hits        47701    11910   -35791     
+ Misses      16017     4680   -11337     
Flag Coverage Δ
endpoint_route_handler ?
fastapi ?
spp_aggregation ?
spp_alerts ?
spp_analytics ?
spp_api_v2 ?
spp_api_v2_change_request 66.41% <ø> (ø)
spp_api_v2_cycles 70.65% <ø> (ø)
spp_api_v2_data ?
spp_api_v2_entitlements 69.96% <ø> (ø)
spp_api_v2_gis 71.52% <ø> (ø)
spp_api_v2_products ?
spp_api_v2_programs 92.03% <ø> (ø)
spp_api_v2_service_points ?
spp_api_v2_simulation ?
spp_api_v2_vocabulary ?
spp_approval ?
spp_area ?
spp_area_hdx 81.43% <ø> (ø)
spp_attachment_av_scan ?
spp_audit ?
spp_audit_programs 0.00% <ø> (ø)
spp_banking ?
spp_base_common 90.26% <ø> (ø)
spp_base_setting ?
spp_case_base ?
spp_case_cel ?
spp_case_demo ?
spp_case_entitlements 97.61% <ø> (ø)
spp_case_graduation ?
spp_case_programs 97.14% <ø> (ø)
spp_case_registry ?
spp_case_session ?
spp_cel_domain ?
spp_cel_event ?
spp_cel_registry_search ?
spp_cel_vocabulary ?
spp_change_request_v2 75.46% <ø> (ø)
spp_claim_169 ?
spp_cr_type_assign_program 91.17% <ø> (ø)
spp_cr_types_advanced 0.00% <ø> (ø)
spp_cr_types_base 0.00% <ø> (ø)
spp_dci ?
spp_dci_client ?
spp_dci_client_dr ?
spp_dci_client_ibr ?
spp_dci_client_sr ?
spp_dci_compliance ?
spp_dci_demo 69.23% <ø> (ø)
spp_dci_indicators ?
spp_dci_server ?
spp_dci_server_social ?
spp_demo ?
spp_demo_phl_luzon ?
spp_disability_registry ?
spp_drims ?
spp_drims_sl ?
spp_drims_sl_demo ?
spp_encryption ?
spp_farmer_registry ?
spp_farmer_registry_cr ?
spp_farmer_registry_demo ?
spp_farmer_registry_vocabularies ?
spp_gis 75.85% <100.00%> (+1.75%) ⬆️
spp_gis_indicators ?
spp_gis_report ?
spp_graduation ?
spp_grm ?
spp_grm_case_link ?
spp_grm_demo ?
spp_hazard ?
spp_hazard_programs ?
spp_hxl_area ?
spp_import_match ?
spp_indicator ?
spp_irrigation ?
spp_land_record ?
spp_metric ?
spp_metric_service ?
spp_metrics_core ?
spp_metrics_services ?
spp_mis_demo_v2 ?
spp_oauth ?
spp_program_geofence 96.47% <96.47%> (+16.23%) ⬆️
spp_programs 65.27% <ø> (ø)
spp_registrant_gis ?
spp_registry 86.83% <ø> (ø)
spp_registry_group_hierarchy ?
spp_scoring ?
spp_scoring_programs ?
spp_security 66.66% <ø> (ø)
spp_service_points ?
spp_simulation ?
spp_starter_disability_registry ?
spp_starter_farmer_registry ?
spp_starter_social_registry ?
spp_starter_sp_mis ?
spp_statistic ?
spp_storage_backend ?
spp_studio ?
spp_studio_change_requests ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
spp_gis/__manifest__.py 0.00% <ø> (ø)
spp_gis/controllers/main.py 100.00% <100.00%> (+37.50%) ⬆️
spp_gis/models/geofence.py 88.54% <100.00%> (+0.90%) ⬆️
spp_gis/operators.py 81.25% <100.00%> (+7.40%) ⬆️
spp_program_geofence/__init__.py 100.00% <100.00%> (ø)
spp_program_geofence/models/__init__.py 100.00% <100.00%> (ø)
spp_program_geofence/models/program.py 100.00% <100.00%> (+16.66%) ⬆️
spp_program_geofence/wizard/__init__.py 100.00% <100.00%> (ø)
...p_program_geofence/wizard/create_program_wizard.py 100.00% <100.00%> (ø)
spp_program_geofence/__manifest__.py 0.00% <0.00%> (ø)
... and 1 more

... and 811 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread spp_gis/migrations/19.0.2.1.0/post-migration.py Fixed
Comment thread spp_gis/migrations/19.0.2.1.0/post-migration.py Fixed
Comment thread spp_gis/migrations/19.0.2.1.0/post-migration.py Fixed
Comment thread spp_gis/migrations/19.0.2.1.0/pre-migration.py Fixed
Comment thread spp_gis/migrations/19.0.2.1.0/pre-migration.py Fixed
Comment thread spp_gis/migrations/19.0.2.1.0/pre-migration.py Fixed
…tion (from #76)

Re-lands the PR #76 description scope: spp_gis complex-geometry operators
(MultiPolygon/GeometryCollection + distance buffering), OSM fallback when no
MapTiler key is configured, renderer/edit-widget lifecycle fixes, geofence
uuid in GeoJSON output, the spp.gis.geofence.tag model, and the new
spp_program_geofence module (geofence eligibility manager, program UI,
creation wizard).

Adds what the original PR lacked: a migration pair remapping existing
vocabulary-based geofence tag links (release v19.0.2.0.0 schema) onto
spp.gis.geofence.tag records — the rel table is reused, so legacy rows are
parked pre-upgrade and restored post-upgrade. Includes regression tests.
…generate spp_gis README

Semgrep flagged f-string SQL in the tag migration scripts; identifiers were
constants but composed SQL via psycopg2.sql removes the pattern entirely.
README.rst regenerated from the updated HISTORY fragment (in-scope module
only).
Replaces psycopg2.sql identifier composition with prebuilt literal queries
(the aux table name and the valid-link filter are compile-time constants),
clearing the odoo-sql-injection-string-format code-scanning alerts. Values
remain parameterized; behavior unchanged (spp_gis suite 92/0).
Every module carries a readme/HISTORY.md; the module was created in #76
without one. README.rst History section will be aligned to CI's renderer
output in a follow-up commit.
…r-facing strings in _()

- README.rst History section applied verbatim from CI's pre-commit diff.
- Address gemini-code-assist review: translatable message_post/lock-reason and
  preview notification strings (i18n).
…back

DESCRIPTION.md predates the geofence move (#74) and did not mention the
capabilities this PR adds. README will be aligned to CI's renderer output in
a follow-up commit.
@gonzalesedwin1123

Copy link
Copy Markdown
Member Author

Disposition of gemini-code-assist findings:

Applied (commit 9d9afdb): i18n _() wraps for message_post/lock-reason and preview notification strings.

Deferred as follow-ups (pre-existing #76 code; this PR re-lands reviewed behavior and we're keeping the diff scoped):

  • eligibility_manager.py:228 active-record check in the queued import — behavior change needing product decision on deactivated-program semantics.
  • eligibility_manager.py:157 NOT-IN removal and :189 recordset subtraction — valid perf/idiom improvements; tier1/tier2 counts are surfaced separately so the dedup semantics deserve their own reviewed change.
  • field_gis_edit_map.esm.js:62 map re-creation on patch — real concern; needs manual UI verification of zoom/pan/draw state across edits, best done as a dedicated GIS-widget PR.

… ladder

The module's fragments use #/## headings; ### produced an RST title-level
skip that crashed oca-gen-addon-readme.
…fence

READMEs regenerated by CI's pinned generator from the updated DESCRIPTION and
new HISTORY fragments; applied verbatim from its pre-commit diff.
@gonzalesedwin1123

Copy link
Copy Markdown
Member Author

Review & merge order for the PR #76 re-land stack

This PR is part of a 10-PR stack re-landing reverted #76 (revert: #271) in description-consistent pieces. Reviews within a batch can run in parallel; order only matters at merge time. All PR descriptions are self-contained.

Batch 1 — review now, merge in any order (independent, CI green)

PR Scope Review attention points
#273 (this) spp_gis operators/OSM fallback/widgets + new spp_program_geofence Geofence-tag migration (pre/post pair over the reused rel table)
#274 spp_hazard severity→CAP vocabulary + drims/sl_demo/hazard_programs Severity migration mapping: 1→minor, 2→moderate, 3→severe, 4→severe, 5→extreme (deliberate product decision); docs-only commit aligning five base READMEs with CI's renderer
#275 spp_cel_domain: SQL CASE compiler, smart-op lookup, cache tests 27 added edge-path tests (patch coverage 99.3%)
#276 spp_api_v2: OpenAPI polymorphic bodies, OAuth2 auth scheme, bundles Auth middleware change is security-relevant
#277 PHL geodata + spp_demo_phl_luzon + prep script Mostly demo data; batched loader refactor

Batch 2 — merge strictly after prerequisites

PR Merge only after Mechanics
#279 metric-service #275 Auto-retargets to 19.0 on parent merge; wait for its CI to re-run green before merging
#280 gis-report #279 Same retarget-then-CI pattern
#281 api-v2-gis (draft) #273 + #274 + #276 + #279 + #280 (all) Will be rebased onto 19.0 and marked ready-for-review once all five land
#282 mis-demo-v2 (draft) #281 Marked ready once #281 lands

Cautions

Verification summary per PR is in each description; the deferred gemini-code-assist suggestions are documented as comments on the affected PRs.

…and MapTiler-key paths (codecov patch target)
- eligibility_manager: guard the queued import against archived/deleted
  programs (with regression test); drop the NOT-IN anti-pattern from the
  tier-2 domain (tier1 | tier2 already dedups); recordset subtraction for
  already-enrolled exclusion; iterate ids when building membership values.
- gis_edit_map widget: only re-create the MapLibre map when the geometry
  value actually changed (prevents zoom/pan loss and WebGL context churn on
  unrelated OWL patches).
@gonzalesedwin1123

Copy link
Copy Markdown
Member Author

Update: the four previously-deferred gemini-code-assist findings are now applied as well (per maintainer request):

  • Queued import guards against archived/deleted programs (+regression test).
  • Tier-2 domain drops the NOT-IN anti-pattern (tier1 | tier2 already dedups).
  • Enrolled-beneficiary exclusion via recordset subtraction; membership values built from ids.
  • gis_edit_map re-creates the MapLibre map only when the geometry value actually changed (no more zoom/pan loss or WebGL churn on unrelated OWL patches). Recommend a quick manual map-edit smoke test during review.

Suites: spp_program_geofence 41/0, spp_gis 96/0. All gemini findings on this PR are now addressed.

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.

2 participants