Skip to content

Commit 18a18bb

Browse files
committed
fix(spp_gis_report): fix aggregation logic for sum/min/max and N+1 query
- 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
1 parent 3066a78 commit 18a18bb

File tree

4 files changed

+73
-43
lines changed

4 files changed

+73
-43
lines changed

spp_gis_report/models/gis_report.py

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -472,17 +472,23 @@ def _compute_base_aggregation(self):
472472
# (e.g., barangays when base level is municipality). We include all
473473
# descendant areas in the query and aggregate results back to the
474474
# base-level parent.
475-
child_to_base = {}
476-
for base_area in base_areas:
477-
child_to_base[base_area.id] = base_area.id
478-
descendants = self.env["spp.area"].search(
479-
[
480-
("id", "child_of", base_area.id),
481-
("id", "!=", base_area.id),
482-
]
483-
)
484-
for desc in descendants:
485-
child_to_base[desc.id] = base_area.id
475+
#
476+
# Fetch all descendants of all base areas in a single query, then build
477+
# the child_to_base mapping in memory to avoid an N+1 query pattern.
478+
child_to_base = {base_area.id: base_area.id for base_area in base_areas}
479+
all_descendants = self.env["spp.area"].search(
480+
[
481+
("id", "child_of", base_areas.ids),
482+
("id", "not in", base_areas.ids),
483+
]
484+
)
485+
# For each descendant, walk up its parent chain to find the base-level ancestor
486+
for desc in all_descendants:
487+
ancestor = desc.parent_id
488+
while ancestor and ancestor.id not in child_to_base:
489+
ancestor = ancestor.parent_id
490+
if ancestor:
491+
child_to_base[desc.id] = child_to_base[ancestor.id]
486492

487493
# Add area filter to domain (base areas + all descendants)
488494
domain.append((area_field, "in", list(child_to_base.keys())))
@@ -525,12 +531,30 @@ def _compute_base_aggregation(self):
525531
base_id = child_to_base.get(area_id, area_id)
526532
value = group.get(self.aggregation_field) or 0
527533
count = group[f"{area_field}_count"]
528-
# Accumulate weighted values for proper re-aggregation
529-
results[base_id]["raw"] += value * count
534+
if agg_func == "avg":
535+
# Accumulate weighted sum so we can compute a proper
536+
# weighted average across subgroups when rolling up.
537+
results[base_id]["raw"] += value * count
538+
elif agg_func == "sum":
539+
# read_group already returns the sum for the subgroup;
540+
# multiplying by count would inflate the total.
541+
results[base_id]["raw"] += value
542+
elif agg_func == "min":
543+
# Keep the lowest value seen across subgroups.
544+
if results[base_id]["count"] == 0:
545+
results[base_id]["raw"] = value
546+
else:
547+
results[base_id]["raw"] = min(results[base_id]["raw"], value)
548+
elif agg_func == "max":
549+
# Keep the highest value seen across subgroups.
550+
if results[base_id]["count"] == 0:
551+
results[base_id]["raw"] = value
552+
else:
553+
results[base_id]["raw"] = max(results[base_id]["raw"], value)
530554
results[base_id]["count"] += count
531555
results[base_id]["weight"] += count
532556

533-
# For avg: convert accumulated (value*count) back to weighted average
557+
# For avg: convert accumulated weighted sum back to weighted average
534558
if agg_func == "avg":
535559
for data in results.values():
536560
if data["count"] > 0:

spp_registrant_gis/README.rst

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ OpenSPP Registrant GIS
2020
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
2121
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
2222
:alt: License: LGPL-3
23-
.. |badge3| image:: https://img.shields.io/badge/github-OpenSPP%2Fopenspp--modules-lightgray.png?logo=github
24-
:target: https://github.com/OpenSPP/openspp-modules/tree/19.0/spp_registrant_gis
25-
:alt: OpenSPP/openspp-modules
23+
.. |badge3| image:: https://img.shields.io/badge/github-OpenSPP%2FOpenSPP2-lightgray.png?logo=github
24+
:target: https://github.com/OpenSPP/OpenSPP2/tree/19.0/spp_registrant_gis
25+
:alt: OpenSPP/OpenSPP2
2626

2727
|badge1| |badge2| |badge3|
2828

@@ -33,11 +33,11 @@ groups, enabling proximity-based targeting and mapping.
3333
Key Capabilities
3434
~~~~~~~~~~~~~~~~
3535

36-
- Store latitude/longitude coordinates on any registrant (individual or
37-
group)
38-
- Query registrants by geographic location using PostGIS spatial
39-
operators
40-
- Visualize registrant locations on maps via GIS widgets
36+
- Store latitude/longitude coordinates on any registrant (individual or
37+
group)
38+
- Query registrants by geographic location using PostGIS spatial
39+
operators
40+
- Visualize registrant locations on maps via GIS widgets
4141

4242
Key Models
4343
~~~~~~~~~~
@@ -53,11 +53,11 @@ Model Extension
5353
UI Location
5454
~~~~~~~~~~~
5555

56-
- **Individual Form**: Located in Profile tab under "Location" section
57-
(after phone numbers)
58-
- **Group Form**: Located in Profile tab under "Location" section
59-
(after phone numbers)
60-
- Field is read-only when registrant is disabled
56+
- **Individual Form**: Located in Profile tab under "Location" section
57+
(after phone numbers)
58+
- **Group Form**: Located in Profile tab under "Location" section (after
59+
phone numbers)
60+
- Field is read-only when registrant is disabled
6161

6262
Security
6363
~~~~~~~~
@@ -68,11 +68,11 @@ permissions from ``spp_registry``.
6868
Technical Details
6969
~~~~~~~~~~~~~~~~~
7070

71-
- Field type: ``fields.GeoPointField`` (from ``spp_gis``)
72-
- Storage: PostGIS POINT geometry with SRID 4326 (WGS84)
73-
- Supports spatial operators: ``gis_intersects``, ``gis_within``,
74-
``gis_contains``, ``gis_distance``, etc.
75-
- Widget: ``geo_point`` for coordinate input/display
71+
- Field type: ``fields.GeoPointField`` (from ``spp_gis``)
72+
- Storage: PostGIS POINT geometry with SRID 4326 (WGS84)
73+
- Supports spatial operators: ``gis_intersects``, ``gis_within``,
74+
``gis_contains``, ``gis_distance``, etc.
75+
- Widget: ``geo_point`` for coordinate input/display
7676

7777
Dependencies
7878
~~~~~~~~~~~~
@@ -92,10 +92,10 @@ Dependencies
9292
Bug Tracker
9393
===========
9494

95-
Bugs are tracked on `GitHub Issues <https://github.com/OpenSPP/openspp-modules/issues>`_.
95+
Bugs are tracked on `GitHub Issues <https://github.com/OpenSPP/OpenSPP2/issues>`_.
9696
In case of trouble, please check there if your issue has already been reported.
9797
If you spotted it first, help us to smash it by providing a detailed and welcomed
98-
`feedback <https://github.com/OpenSPP/openspp-modules/issues/new?body=module:%20spp_registrant_gis%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
98+
`feedback <https://github.com/OpenSPP/OpenSPP2/issues/new?body=module:%20spp_registrant_gis%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
9999

100100
Do not contact contributors directly about support or help with technical issues.
101101

@@ -124,6 +124,6 @@ Current maintainers:
124124

125125
|maintainer-jeremi| |maintainer-gonzalesedwin1123| |maintainer-reichie020212|
126126

127-
This module is part of the `OpenSPP/openspp-modules <https://github.com/OpenSPP/openspp-modules/tree/19.0/spp_registrant_gis>`_ project on GitHub.
127+
This module is part of the `OpenSPP/OpenSPP2 <https://github.com/OpenSPP/OpenSPP2/tree/19.0/spp_registrant_gis>`_ project on GitHub.
128128

129129
You are welcome to contribute.

spp_registrant_gis/static/description/index.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ <h1>OpenSPP Registrant GIS</h1>
374374
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
375375
!! source digest: sha256:3f603a69c4731312b90dc10708243432cd74193750065d7fe86c2364732c1e9a
376376
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
377-
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/license-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OpenSPP/openspp-modules/tree/19.0/spp_registrant_gis"><img alt="OpenSPP/openspp-modules" src="https://img.shields.io/badge/github-OpenSPP%2Fopenspp--modules-lightgray.png?logo=github" /></a></p>
377+
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/license-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OpenSPP/OpenSPP2/tree/19.0/spp_registrant_gis"><img alt="OpenSPP/OpenSPP2" src="https://img.shields.io/badge/github-OpenSPP%2FOpenSPP2-lightgray.png?logo=github" /></a></p>
378378
<p>Extends registrants with GPS coordinates for spatial queries and
379379
geographic analysis. Adds a PostGIS point field to both individuals and
380380
groups, enabling proximity-based targeting and mapping.</p>
@@ -413,8 +413,8 @@ <h2>UI Location</h2>
413413
<ul class="simple">
414414
<li><strong>Individual Form</strong>: Located in Profile tab under “Location” section
415415
(after phone numbers)</li>
416-
<li><strong>Group Form</strong>: Located in Profile tab under “Location” section
417-
(after phone numbers)</li>
416+
<li><strong>Group Form</strong>: Located in Profile tab under “Location” section (after
417+
phone numbers)</li>
418418
<li>Field is read-only when registrant is disabled</li>
419419
</ul>
420420
</div>
@@ -455,10 +455,10 @@ <h2>Dependencies</h2>
455455
</div>
456456
<div class="section" id="bug-tracker">
457457
<h3><a class="toc-backref" href="#toc-entry-1">Bug Tracker</a></h3>
458-
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OpenSPP/openspp-modules/issues">GitHub Issues</a>.
458+
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OpenSPP/OpenSPP2/issues">GitHub Issues</a>.
459459
In case of trouble, please check there if your issue has already been reported.
460460
If you spotted it first, help us to smash it by providing a detailed and welcomed
461-
<a class="reference external" href="https://github.com/OpenSPP/openspp-modules/issues/new?body=module:%20spp_registrant_gis%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
461+
<a class="reference external" href="https://github.com/OpenSPP/OpenSPP2/issues/new?body=module:%20spp_registrant_gis%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
462462
<p>Do not contact contributors directly about support or help with technical issues.</p>
463463
</div>
464464
<div class="section" id="credits">
@@ -473,7 +473,7 @@ <h4><a class="toc-backref" href="#toc-entry-3">Authors</a></h4>
473473
<h4><a class="toc-backref" href="#toc-entry-4">Maintainers</a></h4>
474474
<p>Current maintainers:</p>
475475
<p><a class="reference external image-reference" href="https://github.com/jeremi"><img alt="jeremi" src="https://github.com/jeremi.png?size=40px" /></a> <a class="reference external image-reference" href="https://github.com/gonzalesedwin1123"><img alt="gonzalesedwin1123" src="https://github.com/gonzalesedwin1123.png?size=40px" /></a> <a class="reference external image-reference" href="https://github.com/reichie020212"><img alt="reichie020212" src="https://github.com/reichie020212.png?size=40px" /></a></p>
476-
<p>This module is part of the <a class="reference external" href="https://github.com/OpenSPP/openspp-modules/tree/19.0/spp_registrant_gis">OpenSPP/openspp-modules</a> project on GitHub.</p>
476+
<p>This module is part of the <a class="reference external" href="https://github.com/OpenSPP/OpenSPP2/tree/19.0/spp_registrant_gis">OpenSPP/OpenSPP2</a> project on GitHub.</p>
477477
<p>You are welcome to contribute.</p>
478478
</div>
479479
</div>

spp_registrant_gis/views/res_partner_views.xml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
<field name="inherit_id" ref="spp_registry.view_individuals_form" />
88
<field name="arch" type="xml">
99
<!-- Add GPS coordinates after phone numbers section -->
10-
<xpath expr="//page[@name='profile']//group[@name='phone_section']" position="after">
10+
<xpath
11+
expr="//page[@name='profile']//group[@name='phone_section']"
12+
position="after"
13+
>
1114
<separator string="Location" />
1215
<group name="gis_section">
1316
<field name="coordinates" readonly="disabled" widget="geo_point" />
@@ -23,7 +26,10 @@
2326
<field name="inherit_id" ref="spp_registry.view_individuals_form" />
2427
<field name="arch" type="xml">
2528
<!-- Add GPS coordinates after phone numbers section -->
26-
<xpath expr="//page[@name='group_profile']//group[@name='group_phone_section']" position="after">
29+
<xpath
30+
expr="//page[@name='group_profile']//group[@name='group_phone_section']"
31+
position="after"
32+
>
2733
<separator string="Location" />
2834
<group name="group_gis_section">
2935
<field name="coordinates" readonly="disabled" widget="geo_point" />

0 commit comments

Comments
 (0)