Skip to content

feat: add metrics core and statistics system#43

Open
jeremi wants to merge 1 commit into19.0from
feat/statistics-system
Open

feat: add metrics core and statistics system#43
jeremi wants to merge 1 commit into19.0from
feat/statistics-system

Conversation

@jeremi
Copy link
Member

@jeremi jeremi commented Feb 17, 2026

Summary

  • spp_metrics_core (new): Unified foundation for all metrics — categories and base model
  • spp_statistic (new): Publishable statistics with k-anonymity privacy protection, built on CEL variables
  • spp_statistic_studio (new): Studio UI for statistics configuration (auto-installs with spp_statistic + spp_studio)

Dependencies

Origin

From openspp-modules-v2 branch claude/global-alliance-policy-basket.

Test plan

  • spp_metrics_core installs and tests pass
  • spp_statistic installs and tests pass
  • spp_statistic_studio auto-installs when spp_studio is present

New modules:
- spp_metrics_core: unified foundation for all metrics (categories, base model)
- spp_statistic: publishable statistics with k-anonymity privacy protection
- spp_statistic_studio: Studio UI for statistics configuration (auto-installs
  when both spp_statistic and spp_studio are present)
@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 a comprehensive system for managing metrics and statistics within OpenSPP. It establishes a robust core module that standardizes metric definitions and categorization, enabling consistent data representation and reducing redundancy. Building on this foundation, a new statistics module allows for the creation of publishable statistics, incorporating essential features like k-anonymity for privacy and flexible context-specific configurations. A dedicated studio module provides an intuitive user interface for configuring and overseeing these statistics, streamlining the process of making data available across various platforms and reports.

Highlights

  • New Metrics Core Module (spp_metrics_core): Introduced a foundational module for all metric types, providing a base abstract model (spp.metric.base) with genuinely shared fields (identity, presentation, categorization, metadata) and a unified categorization system (spp.metric.category). This module aims to eliminate field duplication and ensure consistent UI across different metric implementations.
  • New Statistics Module (spp_statistic): Added a module for defining publishable statistics, building upon the new metrics core. Statistics are linked to CEL variables for computation and include features for k-anonymity privacy protection (small cell suppression), context-specific configurations, and publication flags for various outputs (GIS, dashboards, API, reports).
  • New Statistics Studio UI Module (spp_statistic_studio): Implemented a dedicated UI module for managing statistics and metric categories. This module provides user-friendly list, form, search, and kanban views, and is designed for auto-installation when both spp_statistic and spp_studio are present.
  • Category Model Migration: The existing spp.statistic.category model has been migrated and renamed to spp.metric.category within spp_metrics_core. A pre-migration script ensures data and external references are preserved, unifying category management across all metric types.
Changelog
  • spp_metrics_core/README.md
    • Added a new README file detailing the purpose, architecture, models, usage, migration, and benefits of the spp_metrics_core module.
  • spp_metrics_core/init.py
    • Added the initialization file for the spp_metrics_core module.
  • spp_metrics_core/manifest.py
    • Added the manifest file for the spp_metrics_core module, including metadata, dependencies, and data files.
  • spp_metrics_core/data/metric_categories.xml
    • Added default metric categories (Population, Coverage, Targeting, Distribution) as XML records.
  • spp_metrics_core/migrations/19.0.1.0.0/pre-migrate.py
    • Added a pre-migration script to rename the spp_statistic_category table and its sequence to spp_metric_category.
  • spp_metrics_core/models/init.py
    • Added the initialization file for the spp_metrics_core models, importing metric_base and metric_category.
  • spp_metrics_core/models/metric_base.py
    • Added the spp.metric.base abstract model, defining common fields for all metric types.
  • spp_metrics_core/models/metric_category.py
    • Added the spp.metric.category model for shared metric categorization, including fields and constraints.
  • spp_metrics_core/pyproject.toml
    • Added a pyproject.toml file for build system configuration.
  • spp_metrics_core/security/ir.model.access.csv
    • Added access control rules for the spp.metric.category model.
  • spp_metrics_core/tests/init.py
    • Added the initialization file for the spp_metrics_core tests.
  • spp_metrics_core/tests/test_metric_base.py
    • Added tests for the spp.metric.base model, verifying field existence, inheritance, default values, and category assignment.
  • spp_metrics_core/tests/test_metric_category.py
    • Added tests for the spp.metric.category model, covering creation, unique code constraint, parent-child relationships, and sequence ordering.
  • spp_metrics_core/tests/test_migration.py
    • Added tests for the migration script, verifying table and sequence existence and default category loading.
  • spp_statistic/init.py
    • Added the initialization file for the spp_statistic module.
  • spp_statistic/manifest.py
    • Added the manifest file for the spp_statistic module, including metadata, dependencies, and data files.
  • spp_statistic/data/statistic_categories.xml
    • Added default statistic categories as XML records, referencing spp.metric.category.
  • spp_statistic/models/init.py
    • Added the initialization file for the spp_statistic models, importing statistic and statistic_context.
  • spp_statistic/models/statistic.py
    • Added the spp.statistic model for defining publishable statistics, inheriting from spp.metric.base.
  • spp_statistic/models/statistic_context.py
    • Added the spp.statistic.context model for context-specific overrides of statistic presentation and privacy settings.
  • spp_statistic/pyproject.toml
    • Added a pyproject.toml file for build system configuration.
  • spp_statistic/security/ir.model.access.csv
    • Added access control rules for spp.statistic and spp.statistic.context models.
  • spp_statistic/tests/init.py
    • Added the initialization file for the spp_statistic tests.
  • spp_statistic/tests/test_statistic.py
    • Added tests for the spp.statistic model, covering category integration, statistic creation, name format validation, querying, grouping, dictionary conversion, and context override functionality.
  • spp_statistic_studio/init.py
    • Added an empty initialization file for the spp_statistic_studio module.
  • spp_statistic_studio/manifest.py
    • Added the manifest file for the spp_statistic_studio module, including metadata, dependencies, and auto-installation logic.
  • spp_statistic_studio/pyproject.toml
    • Added a pyproject.toml file for build system configuration.
  • spp_statistic_studio/security/ir.model.access.csv
    • Added access control rules for spp.statistic, spp.metric.category, and spp.statistic.context for studio managers.
  • spp_statistic_studio/views/menus.xml
    • Added menu items for Statistics and Categories under the Studio Settings.
  • spp_statistic_studio/views/statistic_category_views.xml
    • Added list, form, search views, and an action for spp.metric.category.
  • spp_statistic_studio/views/statistic_views.xml
    • Added list, form, search, and kanban views, and an action for spp.statistic.
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 a new spp_metrics_core module to establish a unified foundation for all metric types, including statistics and simulations. The core module defines an abstract base model (spp.metric.base) for genuinely shared fields like identity, presentation, and categorization, and a shared category model (spp.metric.category). A pre-migration script is included to rename the old spp.statistic.category table and sequence to spp.metric.category, ensuring data preservation. The spp_statistic module is updated to depend on spp_metrics_core, with its spp.statistic model now inheriting from spp.metric.base and using spp.metric.category for categorization. The spp_statistic_studio module is also updated to reflect these changes in its views and menus. Review comments highlight several areas for improvement: ensuring correct SQL constraint definitions using _sql_constraints instead of models.Constraint in spp_statistic/models/statistic.py and spp_statistic/models/statistic_context.py, verifying the validity of relative documentation links in spp_metrics_core/README.md, correcting inconsistencies in the spp_metrics_core/__manifest__.py description regarding fields provided by the base model and the category model name, improving the domain string readability in spp_statistic/models/statistic.py, and refactoring spp_metrics_core tests to be self-contained by not depending on spp_statistic.

# active inherited from spp.metric.base

# ─── Constraints ────────────────────────────────────────────────────
_name_unique = models.Constraint("UNIQUE(name)", "Statistic name must be unique.")

Choose a reason for hiding this comment

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

high

Using models.Constraint is not the standard Odoo way to define a SQL constraint and will not create a database-level unique constraint. To ensure data integrity at the database level, you should use the _sql_constraints attribute.

    _sql_constraints = [("name_unique", "UNIQUE(name)", "Statistic name must be unique!")]

Comment on lines +103 to +106
_statistic_context_unique = models.Constraint(
"UNIQUE(statistic_id, context)",
"Each statistic can only have one configuration per context.",
)

Choose a reason for hiding this comment

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

high

Using models.Constraint is not the standard Odoo way to define a SQL constraint and will not create a database-level unique constraint. To ensure a unique configuration per statistic and context, you should use the _sql_constraints attribute.

    _sql_constraints = [
        ("statistic_context_unique", "UNIQUE(statistic_id, context)", "Each statistic can only have one configuration per context.")
    ]

category = env['spp.metric.category'].search([...])
```

See [Migration Guide](../../docs/migration/statistics-refactoring.md) for details.

Choose a reason for hiding this comment

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

medium

The relative links to documentation in this file (here and on line 177) might be broken depending on the repository structure or how the documentation is viewed. It would be beneficial to verify that these links resolve correctly to avoid confusion for developers.

Comment on lines +52 to +53
- **Computation**: metric_type, cel_expression, aggregation
- **Presentation**: format, unit, decimal_places

Choose a reason for hiding this comment

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

medium

The Fields Provided list in the manifest's description is inconsistent with the implementation in metric_base.py and the README.md. The base model intentionally does not include metric_type, cel_expression, aggregation, or format fields; these are meant for concrete models to define. This section should be updated to accurately reflect the fields provided by spp.metric.base.

Suggested change
- **Computation**: metric_type, cel_expression, aggregation
- **Presentation**: format, unit, decimal_places
- **Presentation**: unit, decimal_places



@tagged("post_install", "-at_install")
class TestMetricBase(TransactionCase):

Choose a reason for hiding this comment

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

medium

The tests in spp_metrics_core depend on the spp_statistic module to test the functionality of the spp.metric.base abstract model. Core module tests should be self-contained to avoid circular dependencies and simplify testing.

Consider refactoring these tests to create a temporary, dummy model within the test case that inherits from spp.metric.base. This would allow you to test the abstract model's functionality in isolation, making the tests for this core module more robust and independent.

Models
------
- ``spp.statistic``: Publishable statistic referencing a CEL variable
- ``spp.statistic.category``: Organization categories for statistics

Choose a reason for hiding this comment

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

medium

The manifest description incorrectly refers to spp.statistic.category. This model has been migrated to spp.metric.category and now resides in the spp_metrics_core module. The documentation should be updated to reflect this change.

Suggested change
- ``spp.statistic.category``: Organization categories for statistics
- ``spp.metric.category``: Organization categories for statistics

string="CEL Variable",
required=True,
ondelete="restrict",
domain="[('source_type', 'in', ['aggregate', 'computed', 'field']), " "('state', '=', 'active')]",

Choose a reason for hiding this comment

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

medium

The domain string is constructed by concatenating two separate string literals. While this is valid Python, it's unconventional and slightly harder to read. It's better to define the domain as a single string for clarity and maintainability.

        domain="[('source_type', 'in', ['aggregate', 'computed', 'field']), ('state', '=', 'active')]"

],
"installable": True,
# Bridge module: auto-install when both spp_statistic and spp_studio are present
"auto_install": ["spp_statistic", "spp_studio"],

Choose a reason for hiding this comment

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

medium

The auto_install key in an Odoo manifest expects a boolean value. Setting it to True will ensure the module is installed automatically when all its dependencies are present. The current value is a list of strings, which is incorrect.

Suggested change
"auto_install": ["spp_statistic", "spp_studio"],
"auto_install": True,

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