Skip to content

feat(spp_mis_demo_v2): add demo API client and statistics data#70

Open
jeremi wants to merge 11 commits into19.0from
feat/mis-demo-api-client
Open

feat(spp_mis_demo_v2): add demo API client and statistics data#70
jeremi wants to merge 11 commits into19.0from
feat/mis-demo-api-client

Conversation

@jeremi
Copy link
Member

@jeremi jeremi commented Mar 5, 2026

Summary

  • Add demo_api_client.xml: pre-configured QGIS API client with GIS read/all scopes
  • Add demo_statistics.xml: 9 CEL variables + statistics for GIS/dashboard (total households, members, children, elderly, disabled, gender, enrollment)
  • Add test_demo_statistics.py: tests for statistics loading, variable refs, GIS publishing, and aggregation
  • Add spp_statistic, spp_aggregation, spp_studio as dependencies

Test plan

  • Run ./scripts/test_single_module.sh spp_mis_demo_v2
  • Verify demo statistics appear after module install
  • Verify QGIS demo client is created with correct scopes

@gemini-code-assist
Copy link

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 enhances the demo environment by introducing essential components for GIS integration and data visualization. It provides a ready-to-use API client for QGIS plugin testing and a set of predefined statistics that can be displayed on GIS maps and dashboards. These additions streamline the development and testing of features reliant on geographical data and aggregated metrics, ensuring a robust foundation for future enhancements.

Highlights

  • Demo API Client Added: A pre-configured QGIS API client (demo_api_client.xml) has been added, granting gis:read and gis:all scopes to facilitate testing of QGIS plugin integration.
  • Demo Statistics Introduced: Nine new CEL variables and corresponding spp.statistic records (demo_statistics.xml) have been created to provide demo data for GIS and dashboard visualizations. These statistics cover demographics like total households, members, children, elderly, disabled, gender, and program enrollment.
  • New Test Suite for Statistics: A comprehensive test file (test_demo_statistics.py) has been added to verify the proper loading, variable referencing, GIS publishing, and aggregation service accessibility of the newly introduced demo statistics.
  • Module Dependencies Updated: The module's dependencies have been expanded to include spp_statistic, spp_aggregation, and spp_studio to support the new statistics and aggregation functionalities.
Changelog
  • spp_mis_demo_v2/manifest.py
    • Added spp_statistic, spp_aggregation, and spp_studio to the list of required dependencies.
    • Included data/demo_statistics.xml and data/demo_api_client.xml in the module's data files.
  • spp_mis_demo_v2/data/demo_api_client.xml
    • Created a new XML file defining a spp.api.client record named 'QGIS Demo Client'.
    • Configured two spp.api.client.scope records for the QGIS client, granting gis:read and gis:all permissions.
  • spp_mis_demo_v2/data/demo_statistics.xml
    • Defined 9 spp.cel.variable records, specifying computation logic for various demographic and program enrollment metrics.
    • Created 9 spp.statistic records, each linked to a CEL variable, and configured them for publishing to GIS and dashboards.
  • spp_mis_demo_v2/tests/init.py
    • Imported the new test_demo_statistics module to include its tests in the test suite.
  • spp_mis_demo_v2/tests/test_demo_statistics.py
    • Added a new test file containing TestDemoStatistics class.
    • Implemented tests to verify the existence, variable references, GIS publishing, aggregation service accessibility, and category assignments of the demo statistics.
    • Included a test to confirm that the GIS discovery endpoint returns the demo statistics.
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.

@codecov
Copy link

codecov bot commented Mar 5, 2026

Codecov Report

❌ Patch coverage is 45.66929% with 69 lines in your changes missing coverage. Please review.
✅ Project coverage is 57.70%. Comparing base (69e0e5a) to head (aa4db87).

Files with missing lines Patch % Lines
spp_mis_demo_v2/models/mis_demo_generator.py 45.66% 69 Missing ⚠️

❌ Your patch check has failed because the patch coverage (45.66%) is below the target coverage (70.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0      #70      +/-   ##
==========================================
+ Coverage   55.79%   57.70%   +1.90%     
==========================================
  Files         162      178      +16     
  Lines        9291    11110    +1819     
==========================================
+ Hits         5184     6411    +1227     
- Misses       4107     4699     +592     
Flag Coverage Δ
spp_base_common 90.26% <ø> (ø)
spp_dci_demo 69.23% <ø> (?)
spp_mis_demo_v2 67.08% <45.66%> (?)
spp_programs 45.43% <ø> (ø)
spp_security 66.66% <ø> (ø)

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

Files with missing lines Coverage Δ
spp_mis_demo_v2/__manifest__.py 0.00% <ø> (ø)
spp_mis_demo_v2/models/mis_demo_generator.py 67.44% <45.66%> (ø)

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

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 a demo API client and a set of statistical indicators, complete with data definitions and tests for the OpenSPP MIS Demo V2 module. However, two critical security vulnerabilities were identified in the pre-configured API client: API secrets are stored in plaintext in the database, and the demo client is granted excessive permissions (gis:all), violating the principle of least privilege. Beyond security, the implementation also contains critical issues that will prevent the module from installing or tests from passing, including an invalid scope in the API client data causing an XML loading error, and an incorrect method signature in new statistics tests leading to a TypeError. A minor inconsistency in the noupdate flag usage for new demo data files also needs to be addressed.

Comment on lines +19 to +27
<record id="demo_api_client_qgis" model="spp.api.client">
<field name="name">QGIS Demo Client</field>
<field
name="description"
>Demo API client for testing QGIS plugin integration. Grants read access to GIS layers, reports, and statistics queries.</field>
<field name="partner_id" ref="base.main_partner" />
<field name="organization_type_id" ref="spp_consent.org_type_government" />
<field name="active" eval="True" />
</record>

Choose a reason for hiding this comment

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

security-critical critical

Creating an API client using the spp.api.client model results in the plaintext client_secret being stored in the database. This is because the client_secret field in the underlying model (defined in spp_api_v2/models/api_client.py) is a stored Char field and is not cleared after creation. This exposes sensitive API credentials to anyone with database access, significantly increasing the risk of unauthorized API access if the database is compromised.

<!-- GIS Read Scope - View layers, catalog, statistics -->
<record id="demo_api_client_qgis_scope_gis_read" model="spp.api.client.scope">
<field name="client_id" ref="demo_api_client_qgis" />
<field name="resource">gis</field>

Choose a reason for hiding this comment

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

critical

The resource value gis is not a valid option for the spp.api.client.scope model. This will cause an XML validation error when trying to install or update the module. The resource field is a Selection field that does not include gis as a choice. This issue is also present on line 42. You need to extend this selection in the appropriate module (e.g., spp_gis_report) to add gis as a valid option.

Comment on lines +94 to +96
result = self.aggregation_service.compute_aggregation(
registrant_ids=registrant_ids, statistics=[stat_name]
)

Choose a reason for hiding this comment

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

critical

The call to compute_aggregation is incorrect. The method signature is compute_aggregation(self, scope, ...), but you are passing registrant_ids as a keyword argument and omitting the required scope argument. This will cause a TypeError.

You need to construct a scope dictionary to pass the registrant IDs. Since you are querying groups, you should also specify an appropriate CEL profile.

                scope = {
                    "scope_type": "cel",
                    "cel_expression": f"r.id in {registrant_ids}",
                    "cel_profile": "registry_groups",
                }
                result = self.aggregation_service.compute_aggregation(
                    scope=scope, statistics=[stat_name]
                )

Comment on lines +40 to +47
<record id="demo_api_client_qgis_scope_gis_all" model="spp.api.client.scope">
<field name="client_id" ref="demo_api_client_qgis" />
<field name="resource">gis</field>
<field name="action">all</field>
<field
name="description"
>Full GIS access including creating and managing geofences.</field>
</record>

Choose a reason for hiding this comment

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

security-medium medium

The demo API client is granted the gis:all scope, which provides full administrative access to GIS resources, including geofence management. Following the Principle of Least Privilege, API clients should only be granted the minimum permissions necessary for their function. For a QGIS plugin integration, read-only access (gis:read) is typically sufficient. Granting excessive permissions increases the potential impact if the demo credentials are leaked or compromised.

The separation allows the same underlying variable to be published
to multiple contexts with different presentation settings.
-->
<odoo noupdate="0">

Choose a reason for hiding this comment

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

medium

This file uses noupdate="0" at the <odoo> tag level, which means these demo records will be updated every time the module is updated. This can lead to user customizations being overwritten. For demo data, it's standard practice to use noupdate="1" to prevent this. I recommend changing the structure to be consistent with other demo files like demo_api_client.xml and to follow best practices:

<odoo>
    <data noupdate="1">
        <!-- all record definitions -->
    </data>
</odoo>

jeremi added 5 commits March 5, 2026 16:25
Add demo data files for API testing and statistics indicators:
- demo_api_client.xml: pre-configured QGIS API client with GIS scopes
- demo_statistics.xml: 9 CEL variables + statistics for GIS/dashboard
- test_demo_statistics.py: tests for statistics loading and aggregation

Add spp_statistic, spp_aggregation, spp_studio as dependencies.
The demo_api_client.xml creates scopes with resource='gis', which is a
selection value registered by spp_api_v2_gis. Without this dependency,
the XML load fails with "Wrong value for spp.api.client.scope.resource: 'gis'".
…resh

Port the geographic data pipeline from the internal branch:
- Add load_geographic_data/country_code wizard fields and presets
- Add _load_geographic_data() to load area shapes via spp_demo loader
- Add _assign_registrant_areas() to assign municipalities to registrants
- Add _generate_coordinates() for GPS points within area polygons
- Add _refresh_gis_reports() to populate report data immediately
- Add geographic data summary to success notification
- Add spp_registrant_gis dependency for GPS coordinate field

Without this, GIS reports are empty and QGIS plugin queries return no data.
Wrap demo_statistics.xml records in <data noupdate="1"> to prevent
overwriting on module update, consistent with other demo files.

Replace test_statistics_accessible_via_aggregation_service with
test_statistics_have_valid_cel_accessors, which tests the correct
property: that each statistic's variable has a cel_accessor configured.
The original test used an incorrect compute_aggregation signature
(registrant_ids kwarg instead of the required scope positional arg).
@jeremi jeremi force-pushed the feat/mis-demo-api-client branch from 200ce24 to 310f648 Compare March 5, 2026 09:26
jeremi added 6 commits March 5, 2026 20:38
The _generate_coordinates method tried to read area.geo_polygon.data
and pass it through wkbloads(), but the spp_gis ORM field already
returns a Shapely geometry object which has no .data attribute. This
caused a silent AttributeError for every area, resulting in zero
coordinates generated for any registrant.

Use the Shapely geometry directly instead of trying to re-parse it.
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