Skip to content

feat: geofence-based geographic targeting for programs#76

Open
jeremi wants to merge 141 commits into
19.0from
feature/program-geofence
Open

feat: geofence-based geographic targeting for programs#76
jeremi wants to merge 141 commits into
19.0from
feature/program-geofence

Conversation

@jeremi

@jeremi jeremi commented Mar 6, 2026

Copy link
Copy Markdown
Member

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.
  • Stabilize GIS edit/map widgets (re-render lifecycle, draw interaction behavior).

Follow-up Fixes From Review

  • Fixed complex-geometry distance behavior in GIS domain operators:
    • For MultiPolygon / GeometryCollection, (geojson, distance) now applies ST_Buffer(...) correctly instead of ignoring the distance operand.
    • Keeps existing SRID handling parity by transforming to EPSG:3857 when SRID is 4326.
  • Added regression tests for distance-buffer behavior on both complex geometry types.
  • Aligned access-rights and menu visibility:
    • Added explicit read ACLs in spp_program_geofence for spp.gis.geofence and spp.gis.geofence.tag for Programs Viewer/Validator roles.
    • Added groups restriction on the Geofences menu to avoid exposing menu items to users lacking read access.

Notable Functional Additions

spp_program_geofence

  • 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.

spp_gis

  • Operator support for complex geometry types via ST_GeomFromGeoJSON.
  • OSM style fallback in renderer/edit widgets when MapTiler key is missing.
  • Placeholder-key handling in controller (YOUR_MAPTILER_API_KEY_HERE treated as unconfigured).

Test Plan

  • Added unit tests for:
    • MultiPolygon/GeometryCollection SQL generation.
    • Complex geometry distance-buffer SQL path.
  • Local validation in this environment:
    • python3 -m py_compile spp_gis/operators.py spp_gis/tests/test_geo_fields.py
  • Full module test execution via ./spp t ... is environment-blocked here (tomllib missing in local Python, and fallback script depends on shuf).

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a robust geofence-based geographic targeting system for programs, allowing for precise and flexible definition of eligibility zones. It significantly enhances the underlying GIS infrastructure by adding support for complex geometric types and improving the reliability and user experience of map widgets. These changes collectively empower programs with more advanced spatial management tools and ensure a more secure and resilient mapping environment.

Highlights

  • New Geofence-Based Targeting Module: Introduced a new spp_program_geofence module that enables programs to define geographic boundaries (geofences) for registrant eligibility.
  • Hybrid Two-Tier Spatial Targeting: Implemented a sophisticated eligibility manager that uses a two-tier approach: precise GPS coordinates and an administrative area fallback for registrants without GPS data.
  • Enhanced GIS Capabilities: Upgraded spp_gis to support MultiPolygon and GeometryCollection GeoJSON types, significantly expanding the flexibility of spatial queries.
  • Map Widget Improvements and Fixes: Made MapTiler API key optional with an OpenStreetMap (OSM) raster tile fallback, and resolved several map widget bugs including WebGL context leaks, draw control stacking, and layer removal issues.
  • SQL Injection Prevention: Addressed potential SQL injection vulnerabilities in GeoJSON operators by implementing parameterized queries for geometry creation.
Changelog
  • spp_gis/operators.py
    • Added support for 'MultiPolygon' and 'GeometryCollection' types in ALLOWED_LAYER_TYPE.
    • Introduced create_from_geojson method to safely construct geometry from GeoJSON using parameterized SQL queries.
    • Updated validate_geojson to accept 'MultiPolygon' and 'GeometryCollection' types.
    • Modified domain_query to utilize create_from_geojson for complex geometry types.
  • spp_gis/static/src/js/views/gis/gis_renderer/gis_renderer.esm.js
    • Made MapTiler API key configuration conditional, allowing map rendering without it.
    • Implemented OpenStreetMap (OSM) raster tile fallback when a MapTiler API key is not provided.
    • Made GeocodingControl conditional on the presence of a MapTiler API key.
    • Changed API key fetching error logging from error to warn.
  • spp_gis/static/src/js/widgets/gis_edit_map/field_gis_edit_map.esm.js
    • Made MapTiler API key configuration conditional.
    • Introduced _getMapStyle method to provide OSM raster tile fallback when a MapTiler API key is not available.
    • Added this.map.remove() before creating a new map instance to prevent WebGL context leaks.
    • Updated removeSourceAndLayer to correctly remove specific polygon, point, and linestring layers.
    • Added this.map.removeControl(this.draw) to prevent draw control stacking.
    • Removed debug console.log statements and a placeholder image popup on map click.
    • Changed API key fetching error logging from error to warn.
  • spp_gis/tests/test_geo_fields.py
    • Added TestOperatorMultiPolygon class to test MultiPolygon and GeometryCollection types.
    • Verified domain_query correctly handles MultiPolygon and GeometryCollection GeoJSON, including parameterized SQL and table-qualified column names.
    • Confirmed domain_query accepts GeoJSON strings and Shapely objects for complex types.
    • Ensured validate_geojson accepts new geometry types and rejects invalid ones.
    • Verified that Polygon queries continue to use coordinate-based construction.
  • spp_program_geofence/DESCRIPTION.md
    • Added a detailed description of the new module's features and limitations.
  • spp_program_geofence/init.py
    • Initialized the Python package for the new module.
  • spp_program_geofence/manifest.py
    • Defined the module's metadata, dependencies, and data files.
  • spp_program_geofence/models/init.py
    • Imported eligibility_manager and program models.
  • spp_program_geofence/models/eligibility_manager.py
    • Registered 'Geofence Eligibility' as a new manager type.
    • Implemented GeofenceMembershipManager with include_area_fallback and fallback_area_type_id fields.
    • Added _get_combined_geometry to union geofence geometries using shapely.ops.unary_union.
    • Implemented _find_eligible_registrants for two-tier spatial eligibility (GPS coordinates and administrative area fallback).
    • Provided methods for enroll_eligible_registrants, verify_cycle_eligibility, and import_eligible_registrants (including asynchronous import for large datasets).
    • Added action_preview_eligible for UI preview of eligible registrants.
  • spp_program_geofence/models/program.py
    • Extended spp.program model with geofence_ids (Many2many) and geofence_count fields.
    • Added _compute_geofence_count and action_open_geofences methods.
  • spp_program_geofence/security/ir.model.access.csv
    • Defined access control rules for the spp.program.membership.manager.geofence model for different user roles.
  • spp_program_geofence/static/description/index.html
    • Added a brief HTML description for the module.
  • spp_program_geofence/tests/init.py
    • Initialized the test suite for the module.
  • spp_program_geofence/tests/test_geofence_eligibility.py
    • Added TestGeofenceEligibility class with comprehensive tests for the geofence eligibility manager's logic.
    • Tested Tier 1 (coordinates) and Tier 2 (area fallback) eligibility, including various scenarios like multiple geofences, no geofences, disabled registrants, and target types.
    • Included tests for MultiPolygon geofences and the geofence_count field.
    • Added TestGeofenceEligibilityOfficer to verify access rights for program officers.
  • spp_program_geofence/views/eligibility_manager_view.xml
    • Defined the form view for the geofence eligibility manager, including settings for area fallback, program geofence display, and a preview section.
  • spp_program_geofence/views/geofence_view.xml
    • Defined list, form, and search views for spp.gis.geofence and added a menu item under 'Area'.
  • spp_program_geofence/views/program_view.xml
    • Extended the program form view to include a stat button for geofence count and a section on the 'Overview' tab for managing geofence_ids.
Activity
  • All 86 tests for spp_gis passed successfully.
  • All 23 tests for spp_program_geofence passed successfully.
  • Code quality checks (linters: ruff, ruff-format, prettier) passed.
  • Manual UI testing was performed to verify geofence creation, configuration of the geofence eligibility manager, previewing, and importing registrants.
  • Map display functionality was verified with and without the MapTiler API key.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@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 a major new feature for geofence-based geographic targeting, including a new spp_program_geofence module and extensive tests. It also enhances the spp_gis module by adding support for MultiPolygon and GeometryCollection, and crucially fixes a potential SQL injection vulnerability by using parameterized queries. The map widgets are improved by making the MapTiler API key optional with an OpenStreetMap fallback, and several pre-existing bugs related to WebGL context leaks and control stacking are fixed. The changes are well-structured and of high quality. I have a few suggestions to improve maintainability and robustness.

Comment thread spp_gis/static/src/js/widgets/gis_edit_map/field_gis_edit_map.esm.js Outdated
Comment thread spp_program_geofence/models/eligibility_manager.py Outdated
@codecov

codecov Bot commented Mar 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.84042% with 383 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.13%. Comparing base (7c329cf) to head (3c22e09).

Files with missing lines Patch % Lines
spp_gis_report/models/gis_report.py 53.43% 95 Missing ⚠️
spp_api_v2_gis/routers/ogc_features.py 40.90% 52 Missing ⚠️
spp_cel_domain/models/cel_translator.py 65.71% 48 Missing ⚠️
spp_api_v2_gis/services/ogc_service.py 85.96% 40 Missing ⚠️
spp_api_v2_gis/routers/processes.py 77.27% 25 Missing ⚠️
spp_api_v2_gis/services/layers_service.py 13.04% 20 Missing ⚠️
spp_metric_service/models/demographic_dimension.py 84.87% 18 Missing ⚠️
spp_api_v2_gis/services/spatial_query_service.py 84.15% 16 Missing ⚠️
spp_gis_report/__init__.py 40.74% 16 Missing ⚠️
spp_demo_phl_luzon/models/area_loader.py 84.04% 15 Missing ⚠️
... and 11 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0      #76      +/-   ##
==========================================
+ Coverage   71.68%   72.13%   +0.44%     
==========================================
  Files        1010     1010              
  Lines       60072    61250    +1178     
==========================================
+ Hits        43062    44181    +1119     
- Misses      17010    17069      +59     
Flag Coverage Δ
spp_analytics 93.13% <ø> (ø)
spp_api_v2 79.92% <91.17%> (+0.13%) ⬆️
spp_api_v2_change_request 66.85% <ø> (ø)
spp_api_v2_gis 84.27% <84.40%> (+12.74%) ⬆️
spp_api_v2_programs ?
spp_audit_programs ?
spp_base_common 90.26% <ø> (ø)
spp_cel_domain 61.80% <67.78%> (+0.65%) ⬆️
spp_demo 73.63% <ø> (+2.24%) ⬆️
spp_demo_phl_luzon 87.59% <87.59%> (?)
spp_drims 81.01% <100.00%> (+1.46%) ⬆️
spp_drims_sl_demo 68.65% <85.71%> (-0.26%) ⬇️
spp_gis 75.30% <92.00%> (+1.20%) ⬆️
spp_gis_report 78.20% <53.16%> (-4.41%) ⬇️
spp_hazard 97.60% <94.02%> (-1.99%) ⬇️
spp_hazard_programs 97.14% <ø> (ø)
spp_metric_service 88.91% <86.86%> (-0.67%) ⬇️
spp_mis_demo_v2 68.08% <82.14%> (-5.53%) ⬇️
spp_program_geofence 80.23% <ø> (?)
spp_programs 65.12% <ø> (ø)
spp_registry 86.83% <ø> (?)
spp_security 66.66% <ø> (ø)
spp_source_tracking ?
spp_source_tracking_programs ?
spp_studio_change_requests_programs ?
spp_studio_events ?

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

Files with missing lines Coverage Δ
spp_api_v2/middleware/auth.py 76.11% <100.00%> (+0.36%) ⬆️
spp_api_v2/models/fastapi_endpoint_registry.py 94.28% <100.00%> (+0.34%) ⬆️
spp_api_v2/schemas/__init__.py 100.00% <100.00%> (ø)
spp_api_v2/schemas/bundle.py 100.00% <100.00%> (ø)
spp_api_v2_gis/__manifest__.py 0.00% <ø> (ø)
spp_api_v2_gis/models/__init__.py 100.00% <100.00%> (ø)
spp_api_v2_gis/models/api_client_scope.py 100.00% <100.00%> (ø)
spp_api_v2_gis/models/fastapi_endpoint.py 100.00% <100.00%> (ø)
spp_api_v2_gis/routers/__init__.py 100.00% <100.00%> (ø)
spp_api_v2_gis/schemas/__init__.py 100.00% <100.00%> (ø)
... and 55 more

... and 77 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.

@jeremi

jeremi commented Mar 12, 2026

Copy link
Copy Markdown
Member Author

Addressed Gemini review feedback in commit 46c90b6:

  • Fixed draw handler robustness in GIS edit widget by using event features first and guarding geometry access (with fallback to draw state).
  • Replaced import magic numbers with named constants in geofence eligibility manager (ASYNC_IMPORT_THRESHOLD, IMPORT_CHUNK_SIZE).

Kept one medium suggestion as follow-up (optional): deduplicate identical OSM fallback style object between gis_renderer and field_gis_edit_map via shared utility. No functional risk from current duplication.

jeremi added 19 commits March 12, 2026 15:07
…operators

The Operator class only supported Point, LineString, and Polygon types
in domain queries. When shapely's unary_union creates a MultiPolygon
from non-overlapping polygons, the operator validation silently rejected
it, returning SQL("FALSE") and matching zero registrants.

Add ST_GeomFromGeoJSON path for complex geometry types that cannot be
easily constructed from coordinates.
New module that adds geofence-based geographic targeting to programs:
- Program-level geofence_ids field on Overview tab
- Geofence eligibility manager with hybrid two-tier spatial queries
  (GPS coordinates + administrative area fallback)
- Preview button showing matched registrant count
- Composable with other eligibility managers via AND logic
…ibility

- Use gis_intersects instead of gis_within for Tier 1 spatial query;
  gis_within generates ST_Within(value, field) which is backwards for
  point-in-polygon checks, while gis_intersects is symmetric
- Use disabled=None instead of disabled=False in domain (Datetime field)
- Use fields.Datetime.now() for disabled test data (not Boolean True)
- Use group_ids with Command.link() for Odoo 19 compatibility in tests
- Escape single quotes in create_from_geojson to prevent SQL injection
- Make preview_count/preview_error regular fields instead of computed;
  spatial queries now only run when the Preview button is clicked
- Use elif instead of two independent if statements for target_type
- Simplify _import_registrants loop to list comprehension
…ent UI

- Add fallback_area_type_id field to restrict Tier 2 area fallback to a
  specific administrative level (e.g. District), preventing overly broad
  matches from large provinces or regions
- Add geofence list/form/search views with menu under Area top-level,
  so users can browse and manage geofences independently
- Allow inline geofence creation from the program form
- Add 3 tests for area type filter behavior
When no MapTiler API key is configured, the map widget now falls back
to OpenStreetMap raster tiles instead of failing silently. This makes
the GIS features work out of the box without requiring a third-party
API key. Users who want vector tiles can still configure a MapTiler key.
…back

- Fix WebGL context leak: destroy previous map before creating new one
  in renderMap() to prevent accumulating WebGL contexts on onPatched
- Fix draw control stacking: remove previous MapboxDraw control before
  adding a new one in addDrawInteraction()
- Fix removeSourceAndLayer: remove all three layer IDs (polygon, point,
  linestring) instead of the source ID which doesn't match any layer
- Remove console.log debug statements from updateArea and onTrash
- Remove hardcoded laos_farm.png placeholder popup on polygon click
- Fix SQL injection in create_from_geojson: use SQL() with bound
  parameters instead of manual string escaping
- Apply OSM raster tile fallback to gis_renderer (matching edit widget)
- Guard GeocodingControl behind API key check in renderer
- Fix early-return-in-loop in eligibility manager methods with
  ensure_one()
- Log exceptions in preview instead of silently swallowing them
- Use efficient set lookup for beneficiary exclusion
- Use Command.set()/Command.clear() instead of tuple syntax in tests
The default system parameter value "YOUR_MAPTILER_API_KEY_HERE" was
being returned as a valid key, causing 403 errors from MapTiler instead
of falling back to OSM tiles.
renderMap() was overriding the OSM fallback style with a MapTiler
style reference when defaultRaster was set, even without an API key.
Guard the raster style override behind mapTilerKey check.
…nu order

The GeoPolygonField edit widget requires a GIS view (ir.ui.view with
type=gis) with data and raster layers to render the map. Without it,
opening a geofence form raised "No GIS view defined".

Also moved the Geofences menu item to sequence 200 so it appears last
in the Area menu.
When renderMap() destroys the old map, the MapboxDraw control's
internal map reference becomes null. Later, addDrawInteraction()
tried to removeControl(this.draw) from the new map, but the draw
control called this.map.off() on its now-null internal reference.

Fix: set this.draw = null before map.remove() so addDrawInteraction
skips the removeControl call for stale controls.
Move the Geographic Scope card from Overview to Configuration tab,
matching the card-based UI pattern. Add geofence_ids field to the
program creation wizard so geofences can be set during initial setup.
Existing geometry was added as a static map source/layer, making it
non-interactive: shapes couldn't be clicked, selected, or edited.
Now geometry is loaded into the MapboxDraw control via draw.add(),
enabling click-to-select, vertex editing, and trash deletion.
Also handles draw.delete event to clear the field value.
The tag_ids field on spp.gis.geofence was pointing to spp.vocabulary,
which is a generic vocabulary model containing all tag categories
(Country, Currency, etc.). Replace with a dedicated spp.gis.geofence.tag
model so the tags dropdown only shows geofence-specific tags.
- Remove dead methods from field_gis_edit_map (onLoadMap, addSourceAndLayer,
  addSource, addLayer, removeSourceAndLayer) no longer called after draw
  refactor
- Fix event listener stacking in addDrawInteraction: store handler refs
  and remove previous listeners before adding new ones, preventing
  duplicate record.update() calls when onUIChange() is called
- Fix disabled registrant filter: use ("disabled", "=", False) for
  consistency with DefaultEligibilityManager
@jeremi jeremi force-pushed the feature/program-geofence branch from 46c90b6 to 8e77302 Compare March 12, 2026 08:09
jeremi added 6 commits March 15, 2026 00:21
Add foundational components for OGC API - Processes (Part 1: Core):
- spp.gis.process.job model for async job tracking with job_worker
- Pydantic schemas for process list, description, execution, status
- ProcessRegistry service with dynamic indicator enum from spp.indicator
- Cron job for cleanup of stale/expired process jobs
- ACL entries for the new model
- processes.py: GET /gis/ogc/processes, GET /gis/ogc/processes/{id},
  POST /gis/ogc/processes/{id}/execution with sync/async support
- jobs.py: GET /gis/ogc/jobs, GET /gis/ogc/jobs/{id},
  GET /gis/ogc/jobs/{id}/results, DELETE /gis/ogc/jobs/{id}
- Wire routers into FastAPI endpoint registry
- Update OGC conformance to declare Processes classes
- Add processes link to OGC landing page
- Refactor statistics endpoint to delegate to ProcessRegistry
Tests cover:
- ProcessRegistry: list, describe, unknown ID, indicator enum, x-openspp-statistics
- Pydantic schemas: ProcessSummary, ExecuteRequest, StatusInfo, JobList
- spp.gis.process.job model: create, dismiss, stale cleanup, cron
- Input validation: single/batch geometry, bare arrays, limits, proximity
- HTTP integration: process list/describe, execution, async flow,
  job scoping, dismiss, conformance, landing page links
- Remove 'icon' field from test category creation (spp.metric.category
  does not have an icon field; Odoo 19 raises ValueError on unknown fields)
- Fix Odoo False-vs-None for empty Text fields: use `message or None`
  in _build_status_info to prevent Pydantic ValidationError when
  job.message is False instead of None
- Apply fix in both processes.py and jobs.py routers
- Restrict ACL: base.group_user gets read-only, base.group_system gets
  full CRUD (routers use sudo() for all mutations)
- Add UNIQUE constraint on job_id field
- Sanitize exception messages in job records to prevent leaking internals
- Extract shared helpers (_helpers.py): build_status_info, check_gis_scope,
  get_base_url used by both processes and jobs routers
- Extract process execution logic (process_execution.py): run_spatial_statistics,
  run_proximity_statistics used by both sync (router) and async (model) paths
- Add safe int parsing for ir.config_parameter values with fallback defaults
- Use SPATIAL_STATISTICS/PROXIMITY_STATISTICS constants instead of string literals
…OGC Processes

Address consumer feedback from the QGIS plugin team:

- Always include geometries_failed in batch summary responses (#1)
- Always populate computed_at with current UTC timestamp, even for empty results (#2)
- Add Retry-After: 5 header to async 201 responses and in-progress job status (#3/#8)
- Track batch progress per-geometry via on_progress callback in job execution (#4)
- Add x-openspp-batch-limit: 100 extension to spatial-statistics process description (#5)
jeremi and others added 6 commits June 12, 2026 11:38
Sync README.rst and index.html with the current oca-gen-addon-readme
output for spp_change_request_v2, spp_drims_sl, spp_gis_indicators,
spp_hazard, and spp_security (formatting-only churn).
…t area

The spp_hazard severity migration (Selection -> spp.vocabulary.code M2O,
severity/severity_override -> severity_id/severity_override_id) missed the
spp_drims extension of spp.hazard.incident.area, whose compute still referenced
the removed severity_override field. This broke registry load on any full stack
(e.g. spp_drims_sl), though spp_hazard's own tests stayed green.

- Replace the Selection effective_severity with a computed effective_severity_id
  Many2one and map CAP codes to a 1-5 severity_numeric for the choropleth.
- Update the DRIMS demo generator to write severity_id/severity_override_id from
  CAP vocabulary codes instead of legacy numeric strings.
- Add a focused test for the effective-severity compute.
- SQL injection (pylint E8103 + Semgrep): build dynamic identifiers with
  psycopg2.sql.Identifier in ogc_service and spp_gis_report instead of f-strings.
- XXE (Semgrep): parse bundled Luzon area XML with a hardened lxml parser
  (resolve_entities=False, no_network=True) instead of stdlib xml.etree.
- sudo on sensitive models (Semgrep CRITICAL): document why sudo is required
  (aggregate metrics, deletion-integrity check, API enum) and suppress with the
  correct rule id; these are read-only and authorized at the service boundary.
…ca-checks

areas_luzon.xml and population_weights.csv are loaded at runtime (area loader /
population weights), not at install. Declare them under oca_data_manual so
oca-checks-odoo-module no longer reports file-not-used.
- Drop unused params (no-unused-vars errors).
- Extract _matchesCondition and choropleth legend helpers to bring
  _matchesDomain and renderChoroplethLegends under the complexity limit.
- Move inline comments to their own lines (no-inline-comments).
Mechanical formatting (ruff-format, prettier, end-of-file, mixed-line-ending) on
files already modified by this PR, to satisfy the pre-commit hooks. No logic
changes.
Comment thread spp_api_v2_gis/services/ogc_service.py Fixed
Comment thread spp
Comment thread spp_api_v2_gis/models/process_job.py Fixed
Comment thread spp_api_v2_gis/routers/jobs.py Fixed
Comment thread spp_api_v2_gis/routers/processes.py Fixed
Comment thread spp_hazard/models/hazard_incident.py Fixed
Comment thread spp_hazard/models/hazard_incident.py Fixed
Comment thread spp_mis_demo_v2/models/mis_demo_generator.py
Comment thread spp_mis_demo_v2/models/mis_demo_generator.py
Comment thread spp_mis_demo_v2/models/mis_demo_generator.py
…rvice

ruff-format wrapped the inline-suppressed assignment across lines, leaving the
nosemgrep after the match's start line so Semgrep no longer honoured it. Move the
suppression to the line directly above the match, matching the other sudo sites.
The deprecated 'description' key tripped pylint-odoo's mandatory
manifest-deprecated-key (C8103) check. The summary covers the module purpose;
data attribution is kept as a comment.
Formatting-only (line wrapping/reflow) on files this PR modified, to satisfy the
pre-commit ruff-format and prettier hooks. No logic changes.
… calls

The semgrep pre-commit hook (--error) was failing on unsuppressed
odoo-sudo-without-context warnings for API/system-context sudo calls (config
params, job records, geofence/incident lookups). These run after router-level
auth; add justified nosemgrep suppressions matching the existing pattern. No
logic change.
CI's cached pre-commit env held older docutils/markdown-it-py, which render RST
table columns at different widths than a fresh install, producing spurious
README drift (spp_change_request_v2, spp_gis_indicators, spp_drims_sl). Pin
docutils==0.23 and markdown-it-py==4.2.0 on the oca-gen-addon-readme hook so the
output is reproducible across environments; this also refreshes the CI cache.
The sudo suppression comments exceeded the 120-char line length (ruff E501).
Trim the inline justification; the nosemgrep directive is unchanged.
Catch up with 19.0 (45 commits behind): brings in regenerated addon READMEs
(resolving pre-commit README drift seen on the PR merge ref), DRIMS UI work,
spp_programs config UX, and Odoo 19 migrations. No conflicts; no overlap with
the geofence CI fixes on this branch.
CI generates RST tables with different column widths than a local fresh install
(an unidentified env factor; the committed text is otherwise identical). These 5
modules' fragments are unchanged vs 19.0, so regenerate the READMEs to exactly
match CI's oca-gen-addon-readme output, clearing the pre-commit README drift.
Cosmetic table-width only; no content change. docutils/markdown-it-py remain
pinned so the output stays reproducible.
…d, bbox helpers

Add tests for get_feature_count (report + error paths), get_feature_by_id /
_get_report_feature_by_id (report path + not-found errors), the bbox-to-geojson
and coordinate-extraction helpers across all geometry types. Layer-type cases
skip when spp.area has no geo field (matching existing setup). All 353 module
tests pass.
…ew actions

Add tests for _calculate_quantiles, _calculate_jenks_breaks, the member-expansion
constraint (valid + invalid source), next-refresh compute, action_view_data,
action_view_map, and action_refresh_now. All 188 module tests pass.
Add HTTP integration tests for the geofences write router: OPTIONS discovery,
gis:geofence scope enforcement (403 on POST/DELETE without scope), and a
successful geofence creation (201). All 357 module tests pass.
…vice

The setup probed for a field named 'polygon' (the actual field is 'geo_polygon')
so spp.gis.data.layer was never created and 8 layer-type tests always skipped.
Fix the field name and supply the required view_id (a gis-type ir.ui.view), so
11 data-layer tests now run, covering the data-layer code paths in LayersService.
All 357 module tests pass.
Existing tests always passed load_shapes=False, leaving _load_shapes uncovered.
Add tests calling it directly and via load_shapes=True; the method handles a
missing geo_polygon field gracefully (spp_gis not in this module's deps).
…callback

Add tests for SpatialQueryService._get_empty_statistics, _build_filter_clauses
(all branches), _resolve_individuals_to_groups (empty + membership resolution),
and process.job._make_batch_progress_callback (non-batch + throttled writes).
Add direct tests for the module-level cache helpers: _make_cache_key (stable +
config-sensitive + unsortable-config fallback), get/cache roundtrip, invalidate,
and FIFO eviction past the size limit.
@gonzalesedwin1123

Copy link
Copy Markdown
Member

CI green-up + coverage pass (ACN)

Worked through the failing checks, brought the branch current with 19.0, reviewed the security findings, and added tests for the lowest-covered new code. Branch is now MERGEABLE / all required checks green. Summary for reviewers:

CI fixes

  • spp_drims severity migration — the severityseverity_id / severity_override_id (CAP-vocabulary Many2one) migration missed the spp_drims extension of spp.hazard.incident.area, crashing registry load on the full spp_drims_sl stack (its own-module tests stayed green). Reworked effective_severityeffective_severity_id + the severity_numeric choropleth mapping, and updated the DRIMS demo wizard to write vocab codes.
  • Semgrep (8 errors → 0) — SQL-injection (psycopg2.sql.Identifier), XXE (hardened lxml parser), and sudo-on-sensitive findings (justified # nosemgrep with the correct rule id; these are read-only/system-context calls authorized at the router layer).
  • eslint — fixed unused-vars + reduced _matchesDomain / renderChoroplethLegends complexity via extract-method; moved inline comments.
  • oca-checks — declared the programmatically-loaded spp_demo_phl_luzon data files under an oca_data_manual manifest key.
  • pylint (mandatory) — SQL-injection + dropped the deprecated manifest description key.
  • formatting — ruff-format / prettier / eol across PR-touched files.

Caught up with 19.0

Merged 19.0 in (clean, no conflicts, no overlap with the changes above). This resolved the README pre-commit drift at its source — CI tests the PR merge ref, and 19.0's README-regen commit had left the branch's generated READMEs stale. Also pinned the README-gen deps (docutils/markdown-it-py) for reproducible output.

GitHub Advanced Security

Reviewed all 91 open Semgrep alerts: none introduced by this work, all warning/note severity, concentrated in demo/vendored/pre-existing code that the repo's own semgrep config already excludes. Nothing actionable in this PR. One area worth a separate, repo-level look: the credential-logging alerts in spp_api_v2/routers/oauth.py + middleware/auth.py (pre-existing on 19.0).

Test coverage

Added tests for the lowest-covered new code (all module suites green):

  • spp_api_v2_gis/services/layers_service.py (was 0% on the data-layer paths) — also fixed a setup bug (polygongeo_polygon, plus the required view_id) that had silently skipped 8 data-layer tests.
  • spp_gis_report/models/gis_report.py, spp_api_v2_gis/routers/ogc_features.py (OGC Part 4 write endpoints over HTTP), spp_demo_phl_luzon/models/area_loader.py, spp_api_v2_gis spatial-query helpers + process-job callback, and the spp_cel_domain translation cache.

Happy to adjust any of the suppressions or split anything out if you'd prefer it handled differently.

Catch up with 19.0 (35 commits) before merge so CI validates the true final
merge state. No logic changes on this branch.
The 19.0 catch-up reintroduced README.rst/index.html generated with a different
docutils column width. Regenerate with the pinned doc-gen deps so they match
CI's oca-gen-addon-readme output. Cosmetic table-width only; no content change.
The previous local regen used a different Python's docutils than CI, drifting 5
modules' README tables. These modules' readme fragments are unchanged vs 19.0, so
restore 19.0's CI-generated README.rst/index.html — exactly what CI's hook
produces, clearing the README drift.
The OSM raster fallback style object was duplicated verbatim in the GIS renderer
and the geo-edit-map field widget. Extract it into osm_fallback_style.esm.js and
import it in both (addresses a Gemini review note). No behavior change.
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.

3 participants