Skip to content

Commit f1740c6

Browse files
committed
fix(spp_statistic): fix unreachable privacy service delegation
Empty recordsets in Odoo are falsy, so checking `if privacy_service:` would always evaluate to False even when the model is installed. Changed to `if privacy_service is not None:` to correctly detect when the spp.metrics.privacy model is available.
1 parent 600f57a commit f1740c6

13 files changed

Lines changed: 213 additions & 223 deletions

File tree

spp_metrics_core/README.md

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ Shared foundation for all metrics (statistics, simulation metrics, etc.) in Open
44

55
## Overview
66

7-
`spp_metrics_core` provides the base model and categorization system that eliminates duplication of genuinely shared
8-
fields across different metric types. All domain modules (statistics, simulations, dashboards) inherit from the base
9-
model and add their own computation-specific fields.
7+
`spp_metrics_core` provides the base model and categorization system that eliminates
8+
duplication of genuinely shared fields across different metric types. All domain modules
9+
(statistics, simulations, dashboards) inherit from the base model and add their own
10+
computation-specific fields.
1011

1112
## Architecture
1213

@@ -24,8 +25,8 @@ spp.metric.base (AbstractModel)
2425

2526
Abstract model providing genuinely shared fields for all metric types.
2627

27-
Concrete models define their own computation-specific fields (metric_type, format, expressions, etc.) since these vary
28-
incompatibly between metric types.
28+
Concrete models define their own computation-specific fields (metric_type, format,
29+
expressions, etc.) since these vary incompatibly between metric types.
2930

3031
**Identity**
3132

@@ -72,8 +73,8 @@ Shared categorization for all metric types:
7273

7374
## Defining Metrics
7475

75-
Since `spp.metric.base` is an **AbstractModel**, it does not store data directly. Domain modules define concrete metrics
76-
by inheriting from the base:
76+
Since `spp.metric.base` is an **AbstractModel**, it does not store data directly. Domain
77+
modules define concrete metrics by inheriting from the base:
7778

7879
- `spp_statistic` - Defines published statistics
7980
- `spp_simulation` - Defines simulation metrics
@@ -113,9 +114,9 @@ Reference categories in your metrics:
113114

114115
```xml
115116
<record id="my_custom_metric" model="custom.metric">
116-
<field name="name">my_metric</field>
117-
<field name="label">My Custom Metric</field>
118-
<field name="category_id" ref="spp_metrics_core.category_population"/>
117+
<field name="name">my_metric</field>
118+
<field name="label">My Custom Metric</field>
119+
<field name="category_id" ref="spp_metrics_core.category_population" />
119120
</record>
120121
```
121122

@@ -125,19 +126,19 @@ Add domain-specific categories:
125126

126127
```xml
127128
<record id="category_health" model="spp.metric.category">
128-
<field name="name">Health</field>
129-
<field name="code">health</field>
130-
<field name="description">Health-related metrics</field>
131-
<field name="sequence">50</field>
129+
<field name="name">Health</field>
130+
<field name="code">health</field>
131+
<field name="description">Health-related metrics</field>
132+
<field name="sequence">50</field>
132133
</record>
133134
```
134135

135136
## Migration
136137

137138
### From spp_statistic.category
138139

139-
The migration automatically renames `spp.statistic.category` to `spp.metric.category` while preserving all data and
140-
external references.
140+
The migration automatically renames `spp.statistic.category` to `spp.metric.category`
141+
while preserving all data and external references.
141142

142143
**Before**:
143144

@@ -156,7 +157,8 @@ See [Migration Guide](../../docs/migration/statistics-refactoring.md) for detail
156157
## Benefits
157158

158159
1. **No Duplication**: Genuinely shared fields defined once, reused everywhere
159-
2. **Model-Specific Freedom**: Each concrete model defines its own computation fields without conflicts
160+
2. **Model-Specific Freedom**: Each concrete model defines its own computation fields
161+
without conflicts
160162
3. **Consistent UI**: Common fields (name, label, category) display the same way
161163
4. **Shared Categories**: One categorization system for all metrics
162164
5. **Future-Proof**: New metric types easily add their own computation approaches
@@ -174,7 +176,8 @@ See [Migration Guide](../../docs/migration/statistics-refactoring.md) for detail
174176

175177
## Architecture Documentation
176178

177-
See [Statistics System Architecture](../../docs/architecture/statistics-systems.md) for the complete system design.
179+
See [Statistics System Architecture](../../docs/architecture/statistics-systems.md) for
180+
the complete system design.
178181

179182
## License
180183

spp_metrics_core/__manifest__.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,39 +23,4 @@
2323
"application": False,
2424
"installable": True,
2525
"auto_install": False,
26-
"description": """
27-
OpenSPP Metrics Core
28-
====================
29-
30-
Unified foundation for all metrics (statistics, simulation metrics, etc.)
31-
32-
Architecture
33-
------------
34-
Provides a base abstract model that eliminates field duplication across
35-
different metric types (statistics, simulation metrics, etc.).
36-
37-
::
38-
39-
spp.metric.base (AbstractModel)
40-
41-
├── spp.statistic (extends with publication flags)
42-
└── spp.simulation.metric (extends with scenario-specific fields)
43-
44-
Models
45-
------
46-
- ``spp.metric.base``: Abstract model with shared metric fields
47-
- ``spp.metric.category``: Shared categorization for all metric types
48-
49-
Fields Provided
50-
---------------
51-
- **Identity**: name, label, description
52-
- **Presentation**: unit, decimal_places
53-
- **Categorization**: category_id
54-
- **Metadata**: sequence, active
55-
56-
Migration
57-
---------
58-
Automatically migrates ``spp.statistic.category`` to ``spp.metric.category``
59-
to maintain backward compatibility while providing a unified category model.
60-
""",
6126
}

spp_metrics_core/data/metric_categories.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
Part of OpenSPP. See LICENSE file for full copyright and licensing details.
44
-->
55
<odoo>
6-
76
<!-- Default Metric Categories -->
87

98
<record id="category_population" model="spp.metric.category">
@@ -33,5 +32,4 @@
3332
<field name="description">Distribution and entitlement metrics</field>
3433
<field name="sequence">40</field>
3534
</record>
36-
3735
</odoo>

spp_metrics_core/migrations/19.0.1.0.0/pre-migrate.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,7 @@ def migrate(cr, version):
5050
seq_exists = cr.fetchone()[0]
5151

5252
if seq_exists:
53-
_logger.info(
54-
"Found spp_statistic_category_id_seq sequence, "
55-
"renaming to spp_metric_category_id_seq"
56-
)
53+
_logger.info("Found spp_statistic_category_id_seq sequence, renaming to spp_metric_category_id_seq")
5754

5855
# Rename sequence
5956
cr.execute(

spp_metrics_core/models/metric_category.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,7 @@ class MetricCategory(models.Model):
5959
help="Parent category for hierarchical organization",
6060
)
6161

62-
_sql_constraints = [
63-
("code_unique", "UNIQUE(code)", "Category code must be unique!")
64-
]
62+
_sql_constraints = [("code_unique", "UNIQUE(code)", "Category code must be unique!")]
6563

6664
@api.constrains("parent_id")
6765
def _check_parent_recursion(self):

spp_metrics_core/tests/test_metric_category.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
22

3-
from odoo.exceptions import ValidationError
43
from odoo.tests import tagged
54
from odoo.tests.common import TransactionCase
65

spp_statistic/__manifest__.py

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -25,52 +25,4 @@
2525
"application": False,
2626
"installable": True,
2727
"auto_install": False,
28-
"description": """
29-
OpenSPP Statistics
30-
==================
31-
32-
Provides a configuration layer for publishable statistics that can be
33-
consumed by multiple contexts (GIS, dashboards, APIs, reports).
34-
35-
Architecture
36-
------------
37-
Statistics separate "what to compute" (CEL variables) from "where to publish"
38-
(contexts) and "how to present" (labels, formats, grouping).
39-
40-
::
41-
42-
spp.cel.variable (defines computation)
43-
44-
└── Referenced by ──► spp.statistic (defines publication)
45-
├── is_published_gis
46-
├── is_published_dashboard
47-
├── is_published_api
48-
49-
└── spp.statistic.context
50-
├── context (gis/dashboard/api)
51-
├── label, format, group
52-
└── context-specific config
53-
54-
Models
55-
------
56-
- ``spp.statistic``: Publishable statistic referencing a CEL variable
57-
- ``spp.metric.category``: Organization categories for statistics
58-
- ``spp.statistic.context``: Context-specific presentation configuration
59-
60-
Usage
61-
-----
62-
Define statistics via XML or UI::
63-
64-
<record id="stat_children_under_5" model="spp.statistic">
65-
<field name="name">children_under_5</field>
66-
<field name="variable_id" ref="cel_var_children_under_5"/>
67-
<field name="label">Children Under 5</field>
68-
<field name="is_published_gis" eval="True"/>
69-
<field name="is_published_dashboard" eval="True"/>
70-
</record>
71-
72-
Query statistics for a context::
73-
74-
stats = env["spp.statistic"].get_published_for_context("gis")
75-
""",
7628
}
Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8" ?>
22
<!--
33
Default Statistic Categories
44
@@ -8,53 +8,63 @@
88
Note: Uses spp.metric.category (migrated from spp.statistic.category)
99
-->
1010
<odoo noupdate="1">
11-
1211
<!-- Demographics - Population composition -->
1312
<record id="category_demographics" model="spp.metric.category">
1413
<field name="name">Demographics</field>
1514
<field name="code">demographics</field>
16-
<field name="description">Population composition statistics: age groups, gender distribution, household size</field>
15+
<field
16+
name="description"
17+
>Population composition statistics: age groups, gender distribution, household size</field>
1718
<field name="sequence">10</field>
1819
</record>
1920

2021
<!-- Vulnerability - Risk indicators -->
2122
<record id="category_vulnerability" model="spp.metric.category">
2223
<field name="name">Vulnerability</field>
2324
<field name="code">vulnerability</field>
24-
<field name="description">Vulnerability indicators: disability, female-headed households, at-risk populations</field>
25+
<field
26+
name="description"
27+
>Vulnerability indicators: disability, female-headed households, at-risk populations</field>
2528
<field name="sequence">20</field>
2629
</record>
2730

2831
<!-- Programs - Enrollment and coverage -->
2932
<record id="category_programs" model="spp.metric.category">
3033
<field name="name">Programs</field>
3134
<field name="code">programs</field>
32-
<field name="description">Program statistics: enrollment counts, coverage rates, benefit distribution</field>
35+
<field
36+
name="description"
37+
>Program statistics: enrollment counts, coverage rates, benefit distribution</field>
3338
<field name="sequence">30</field>
3439
</record>
3540

3641
<!-- Geographic - Area-based metrics -->
3742
<record id="category_geographic" model="spp.metric.category">
3843
<field name="name">Geographic</field>
3944
<field name="code">geographic</field>
40-
<field name="description">Geographic statistics: area coverage, density, distribution patterns</field>
45+
<field
46+
name="description"
47+
>Geographic statistics: area coverage, density, distribution patterns</field>
4148
<field name="sequence">40</field>
4249
</record>
4350

4451
<!-- Economic - Financial indicators -->
4552
<record id="category_economic" model="spp.metric.category">
4653
<field name="name">Economic</field>
4754
<field name="code">economic</field>
48-
<field name="description">Economic statistics: income levels, poverty indicators, benefit amounts</field>
55+
<field
56+
name="description"
57+
>Economic statistics: income levels, poverty indicators, benefit amounts</field>
4958
<field name="sequence">50</field>
5059
</record>
5160

5261
<!-- Fairness - Equity metrics -->
5362
<record id="category_fairness" model="spp.metric.category">
5463
<field name="name">Fairness</field>
5564
<field name="code">fairness</field>
56-
<field name="description">Equity and fairness metrics: disparity ratios, inclusion rates, coverage gaps</field>
65+
<field
66+
name="description"
67+
>Equity and fairness metrics: disparity ratios, inclusion rates, coverage gaps</field>
5768
<field name="sequence">60</field>
5869
</record>
59-
6070
</odoo>

spp_statistic/models/statistic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def apply_suppression(self, value, count=None, context=None):
267267

268268
# Delegate to unified privacy service
269269
privacy_service = self.env.get("spp.metrics.privacy")
270-
if privacy_service:
270+
if privacy_service is not None:
271271
stat_config = {"minimum_count": min_count, "suppression_display": display_mode}
272272
return privacy_service.suppress_value(value, count, stat_config=stat_config)
273273

spp_statistic/tests/test_statistic.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,7 @@ def test_get_published_by_category(self):
192192
"""Test grouping statistics by category."""
193193
Statistic = self.env["spp.statistic"]
194194

195-
cat2 = self.env["spp.metric.category"].create(
196-
{"name": "Second", "code": "second"}
197-
)
195+
cat2 = self.env["spp.metric.category"].create({"name": "Second", "code": "second"})
198196

199197
Statistic.create(
200198
{

0 commit comments

Comments
 (0)