Skip to content

feat: GIS foundations and registrant GPS coordinates#42

Open
jeremi wants to merge 2 commits into19.0from
feat/gis-foundations
Open

feat: GIS foundations and registrant GPS coordinates#42
jeremi wants to merge 2 commits into19.0from
feat/gis-foundations

Conversation

@jeremi
Copy link
Member

@jeremi jeremi commented Feb 17, 2026

Summary

  • spp_gis: Add OGC API support, area boundary handling improvements
  • spp_gis_report: Refactor report model for improved data layer management
  • spp_registrant_gis (new): Adds GPS coordinate fields to registrants for spatial queries

Origin

Cherry-picked from openspp-modules-v2 branch claude/global-alliance-policy-basket.

Test plan

  • Existing GIS field tests pass
  • New spp_registrant_gis installs and tests pass
  • GIS report generation works with refactored model

Note

Medium Risk
Touches spatial SQL generation and report aggregation logic, so regressions could impact query correctness and reported metrics across inherited models/area hierarchies; the changes are scoped and backed by new tests.

Overview
Fixes GIS domain SQL generation in joined queries by passing Odoo’s alias into Operator and emitting a table-qualified geometry column ("alias"."field") in PostGIS expressions; adds regression tests to ensure backward-compatible behavior when no alias is provided.

Improves GIS report aggregation correctness across area hierarchies by expanding the base-level area filter to include all descendant areas and rolling their read_group results up to the configured base ancestor (with special handling for avg/sum/min/max rollups), and makes GeoJSON export more robust by accepting either Shapely geometries or WKT/WKB strings for area polygons.

Adds a new addon, spp_registrant_gis, that extends res.partner with a coordinates GeoPointField, surfaces it on individual/group profile forms via the geo_point widget, and includes basic installation/docs and transaction tests.

Written by Cursor Bugbot for commit b6d283b. This will update automatically on new commits. Configure here.

@gemini-code-assist
Copy link

Summary of Changes

Hello @jeremi, 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 significant enhancements to the Geographic Information System (GIS) capabilities within the OpenSPP platform. It provides foundational improvements to the spp_gis module by enabling more robust SQL query generation for spatial operations, particularly when dealing with complex table joins. The spp_gis_report module has been refined to provide more accurate aggregation of data across hierarchical geographic areas. Crucially, a new module, spp_registrant_gis, has been added to integrate GPS coordinates directly into registrant profiles, paving the way for advanced spatial analysis and proximity-based services for individuals and groups.

Highlights

  • Enhanced GIS Query Generation: The spp_gis module now supports table-qualified column names in spatial SQL queries, significantly improving robustness when dealing with complex joins and preventing ambiguous column references.
  • Improved GIS Report Aggregation: The spp_gis_report module's aggregation logic has been refactored to correctly include and roll up data from descendant geographic areas to their base-level ancestors, ensuring more accurate reporting.
  • New Registrant GPS Coordinates Module: A new module, spp_registrant_gis, has been introduced to add GPS coordinate fields to registrant records, enabling spatial querying, mapping, and geographic analysis for individuals and groups.
Changelog
  • spp_gis/fields.py
    • Modified the condition_to_sql method to pass table aliases to GIS operator objects, ensuring proper SQL qualification.
  • spp_gis/operators.py
    • Updated the Operator class constructor to accept an optional table_alias parameter.
    • Added a qualified_field_name property to generate SQL-safe, table-qualified field names.
    • Modified get_postgis_query to utilize the new qualified_field_name for all spatial operations.
  • spp_gis/tests/test_geo_fields.py
    • Added a new test class TestOperatorTableAlias to verify the correct generation of table-qualified column names by the Operator class, both with and without aliases.
  • spp_gis_report/models/gis_report.py
    • Refactored the _compute_base_aggregation method to build a mapping of descendant areas to their base-level ancestors, allowing for accurate aggregation of data from more granular areas.
    • Adjusted the aggregation logic to correctly accumulate raw values, counts, and weights for descendant areas into their respective base areas.
    • Updated the _to_geojson method to handle GeoPolygonField values that might be Shapely geometry objects or WKT/WKB strings.
  • spp_registrant_gis/
    • Introduced a new Odoo module to extend res.partner with a GeoPointField for GPS coordinates.
    • Provided README.rst and DESCRIPTION.md detailing the module's capabilities, UI location, security, and technical details.
    • Integrated the new coordinates field into the individual and group profile forms via res_partner_views.xml.
    • Included comprehensive unit tests (test_registrant_gis.py) to ensure the field's existence, writeability, and default empty state for both individual and group registrants.
Activity
  • Initial development was cherry-picked from the openspp-modules-v2 branch claude/global-alliance-policy-basket.
  • Existing GIS field tests were confirmed to pass.
  • The new spp_registrant_gis module was successfully installed and its tests passed.
  • GIS report generation was verified to function correctly with the refactored model.
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.

Copy link

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

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 significant GIS functionality. It adds support for table aliases in GIS operators to prevent ambiguous column names in complex queries, which is a solid improvement. A new module spp_registrant_gis is introduced to store GPS coordinates for registrants, complete with UI additions and tests. The GIS reporting has been refactored to handle hierarchical data by aggregating from descendant administrative areas up to a base level.

While the changes are largely positive, I've identified a critical bug in the report aggregation logic that incorrectly calculates sum, min, and max aggregations. I've also found a significant performance issue (N+1 query) in the same reporting feature. Addressing these issues is crucial for the correctness and scalability of the GIS reports. I've also left a minor comment on a newly added empty security file.

Comment on lines 529 to 531
results[base_id]["raw"] += value * count
results[base_id]["count"] += count
results[base_id]["weight"] += count

Choose a reason for hiding this comment

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

critical

There is a critical bug in the aggregation logic for sum, min, and max methods. The current implementation results[base_id]['raw'] += value * count is only correct when recalculating a weighted average (for the avg method).

  • For sum: value is already the sum for the subgroup. Multiplying it by count will inflate the total sum incorrectly. It should just be added to the total.
  • For min and max: Multiplying by count is meaningless. You should be taking the min of the minimums or max of the maximums across the subgroups.

This logic needs to be branched based on the agg_func.

Suggested change
results[base_id]["raw"] += value * count
results[base_id]["count"] += count
results[base_id]["weight"] += count
if agg_func == "avg":
# For avg, accumulate weighted sum to recalculate later
results[base_id]["raw"] += value * count
elif agg_func == "sum":
results[base_id]["raw"] += value
elif agg_func == "min":
# Initialize if it's the first value for this base_id
if results[base_id]["count"] == 0:
results[base_id]["raw"] = value
else:
results[base_id]["raw"] = min(results[base_id]["raw"], value)
elif agg_func == "max":
# Initialize if it's the first value for this base_id
if results[base_id]["count"] == 0:
results[base_id]["raw"] = value
else:
results[base_id]["raw"] = max(results[base_id]["raw"], value)
results[base_id]["count"] += count
results[base_id]["weight"] += count

Comment on lines 476 to 485
for base_area in base_areas:
child_to_base[base_area.id] = base_area.id
descendants = self.env["spp.area"].search(
[
("id", "child_of", base_area.id),
("id", "!=", base_area.id),
]
)
for desc in descendants:
child_to_base[desc.id] = base_area.id

Choose a reason for hiding this comment

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

high

There is a performance issue here due to an N+1 query problem. The self.env['spp.area'].search(...) call is inside a loop that iterates over base_areas. If there are many base areas, this will result in a large number of database queries, significantly slowing down the report generation.

To improve performance, you should refactor this to avoid making database queries inside a loop. One approach is to fetch all descendant areas at once and then process them in memory to build the child_to_base mapping. This will reduce the number of queries from N (where N is the number of base areas) to a small constant number.

@@ -0,0 +1 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink

Choose a reason for hiding this comment

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

medium

This ir.model.access.csv file contains only a header row. If no specific access rights are needed for this module, this file should be removed to avoid confusion. If access rights are intended, they should be added below the header. An empty or header-only access file can be misleading.

@codecov
Copy link

codecov bot commented Feb 18, 2026

Codecov Report

❌ Patch coverage is 78.29457% with 28 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.74%. Comparing base (5ac7496) to head (18a18bb).

Files with missing lines Patch % Lines
spp_gis_report/models/gis_report.py 31.57% 26 Missing ⚠️
spp_gis/fields.py 0.00% 1 Missing ⚠️
spp_registrant_gis/__manifest__.py 0.00% 1 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (5ac7496) and HEAD (18a18bb). Click for more details.

HEAD has 7 uploads less than BASE
Flag BASE (5ac7496) HEAD (18a18bb)
fastapi 1 0
endpoint_route_handler 1 0
spp_alerts 1 0
spp_api_v2_cycles 1 0
spp_api_v2_change_request 1 0
spp_api_v2_data 1 0
spp_api_v2 1 0
Additional details and impacted files
@@             Coverage Diff             @@
##             19.0      #42       +/-   ##
===========================================
- Coverage   71.31%   58.74%   -12.57%     
===========================================
  Files         299      249       -50     
  Lines       23618    21282     -2336     
===========================================
- Hits        16844    12503     -4341     
- Misses       6774     8779     +2005     
Flag Coverage Δ
endpoint_route_handler ?
fastapi ?
spp_alerts ?
spp_api_v2 ?
spp_api_v2_change_request ?
spp_api_v2_cycles ?
spp_api_v2_data ?
spp_area_hdx 88.33% <ø> (?)
spp_base_common 92.81% <ø> (ø)
spp_drims 87.33% <ø> (?)
spp_drims_sl_demo 70.75% <ø> (?)
spp_gis 79.11% <98.24%> (?)
spp_gis_report 90.10% <31.57%> (?)
spp_mis_demo_v2 0.00% <ø> (?)
spp_programs 49.56% <ø> (ø)
spp_registrant_gis 97.05% <97.05%> (?)
spp_security 51.08% <ø> (ø)

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

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

- Add OGC API support and area boundary handling to spp_gis
- Refactor GIS report model for improved data layer management
- Add new spp_registrant_gis module with GPS coordinates for registrants
- For sum: accumulate the subgroup sum directly instead of multiplying
  by count (which inflated results)
- For min/max: track the true minimum/maximum across subgroups rather
  than multiplying by count (which was meaningless)
- For avg: behaviour unchanged (weighted accumulation then divide)
- Replace per-base-area descendant search loop with a single batched
  query followed by in-memory parent-chain traversal, eliminating the
  N+1 query pattern
@jeremi jeremi force-pushed the feat/gis-foundations branch from b6d283b to 18a18bb Compare February 18, 2026 14:21
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

Comments