|
| 1 | +# OpenSPP Metrics Core |
| 2 | + |
| 3 | +Shared foundation for all metrics (statistics, simulation metrics, etc.) in OpenSPP. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 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. |
| 10 | + |
| 11 | +## Architecture |
| 12 | + |
| 13 | +``` |
| 14 | +spp.metric.base (AbstractModel) |
| 15 | + │ |
| 16 | + ├── spp.statistic (extends with publication flags) |
| 17 | + ├── spp.simulation.metric (extends with scenario-specific fields) |
| 18 | + └── [Your custom metric models] |
| 19 | +``` |
| 20 | + |
| 21 | +## Models |
| 22 | + |
| 23 | +### spp.metric.base |
| 24 | + |
| 25 | +Abstract model providing genuinely shared fields for all metric types. |
| 26 | + |
| 27 | +Concrete models define their own computation-specific fields (metric_type, format, expressions, etc.) since these vary |
| 28 | +incompatibly between metric types. |
| 29 | + |
| 30 | +**Identity** |
| 31 | + |
| 32 | +- `name` - Technical identifier (e.g., 'children_under_5') |
| 33 | +- `label` - Human-readable display label (translated) |
| 34 | +- `description` - Detailed description (translated) |
| 35 | + |
| 36 | +**Presentation** |
| 37 | + |
| 38 | +- `unit` - Unit of measurement (e.g., 'people', 'USD', '%') |
| 39 | +- `decimal_places` - Decimal precision for display |
| 40 | + |
| 41 | +**Categorization** |
| 42 | + |
| 43 | +- `category_id` - Many2one to `spp.metric.category` |
| 44 | + |
| 45 | +**Metadata** |
| 46 | + |
| 47 | +- `sequence` - Display order within category |
| 48 | +- `active` - Inactive metrics are hidden |
| 49 | + |
| 50 | +**What's NOT in the base** (defined by concrete models): |
| 51 | + |
| 52 | +- `metric_type` / `format` - Each concrete model defines its own selections |
| 53 | +- `cel_expression` / `variable_id` - Computation approaches vary by type |
| 54 | +- `aggregation` - Only relevant for certain metric types |
| 55 | + |
| 56 | +### spp.metric.category |
| 57 | + |
| 58 | +Shared categorization for all metric types: |
| 59 | + |
| 60 | +- `name` - Category name (e.g., "Population") |
| 61 | +- `code` - Technical code (e.g., "population") |
| 62 | +- `description` - Category description |
| 63 | +- `sequence` - Display order |
| 64 | +- `parent_id` - Optional parent category for hierarchical organization |
| 65 | + |
| 66 | +## Default Categories |
| 67 | + |
| 68 | +- **Population** - Population counts and demographics |
| 69 | +- **Coverage** - Program coverage and reach metrics |
| 70 | +- **Targeting** - Targeting accuracy and fairness metrics |
| 71 | +- **Distribution** - Distribution and entitlement metrics |
| 72 | + |
| 73 | +## Defining Metrics |
| 74 | + |
| 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: |
| 77 | + |
| 78 | +- `spp_statistic` - Defines published statistics |
| 79 | +- `spp_simulation` - Defines simulation metrics |
| 80 | +- Custom modules - Define domain-specific metrics |
| 81 | + |
| 82 | +See the [Usage](#usage) section below for examples. |
| 83 | + |
| 84 | +## Usage |
| 85 | + |
| 86 | +### Creating Custom Metrics |
| 87 | + |
| 88 | +Inherit from `spp.metric.base` to create domain-specific metrics: |
| 89 | + |
| 90 | +```python |
| 91 | +class CustomMetric(models.Model): |
| 92 | + _name = "custom.metric" |
| 93 | + _inherit = ["spp.metric.base"] |
| 94 | + _description = "Custom Metric Type" |
| 95 | + |
| 96 | + # Shared fields inherited from base: |
| 97 | + # - name, label, description |
| 98 | + # - unit, decimal_places |
| 99 | + # - category_id, sequence, active |
| 100 | + |
| 101 | + # Define your computation-specific fields |
| 102 | + metric_type = fields.Selection([...]) # Your type selections |
| 103 | + computation_field = fields.Text() # Your computation approach |
| 104 | + |
| 105 | + # Add domain-specific fields |
| 106 | + custom_field = fields.Boolean() |
| 107 | + custom_config = fields.Text() |
| 108 | +``` |
| 109 | + |
| 110 | +### Using Categories |
| 111 | + |
| 112 | +Reference categories in your metrics: |
| 113 | + |
| 114 | +```xml |
| 115 | +<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"/> |
| 119 | +</record> |
| 120 | +``` |
| 121 | + |
| 122 | +### Creating Custom Categories |
| 123 | + |
| 124 | +Add domain-specific categories: |
| 125 | + |
| 126 | +```xml |
| 127 | +<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> |
| 132 | +</record> |
| 133 | +``` |
| 134 | + |
| 135 | +## Migration |
| 136 | + |
| 137 | +### From spp_statistic.category |
| 138 | + |
| 139 | +The migration automatically renames `spp.statistic.category` to `spp.metric.category` while preserving all data and |
| 140 | +external references. |
| 141 | + |
| 142 | +**Before**: |
| 143 | + |
| 144 | +```python |
| 145 | +category = env['spp.statistic.category'].search([...]) |
| 146 | +``` |
| 147 | + |
| 148 | +**After**: |
| 149 | + |
| 150 | +```python |
| 151 | +category = env['spp.metric.category'].search([...]) |
| 152 | +``` |
| 153 | + |
| 154 | +See [Migration Guide](../../docs/migration/statistics-refactoring.md) for details. |
| 155 | + |
| 156 | +## Benefits |
| 157 | + |
| 158 | +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 | +3. **Consistent UI**: Common fields (name, label, category) display the same way |
| 161 | +4. **Shared Categories**: One categorization system for all metrics |
| 162 | +5. **Future-Proof**: New metric types easily add their own computation approaches |
| 163 | + |
| 164 | +## Dependencies |
| 165 | + |
| 166 | +- `base` - Odoo core |
| 167 | + |
| 168 | +## Used By |
| 169 | + |
| 170 | +- `spp_metrics_services` - Aggregation and computation services |
| 171 | +- `spp_statistic` - Published statistics |
| 172 | +- `spp_simulation` - Simulation metrics |
| 173 | +- Domain modules with custom metrics |
| 174 | + |
| 175 | +## Architecture Documentation |
| 176 | + |
| 177 | +See [Statistics System Architecture](../../docs/architecture/statistics-systems.md) for the complete system design. |
| 178 | + |
| 179 | +## License |
| 180 | + |
| 181 | +LGPL-3 |
0 commit comments