Skip to content

Commit fa7bd72

Browse files
committed
feat(spp_hazard): add severity_numeric bridge and document breaking changes
Softens the downstream impact of the severity -> CAP vocabulary migration (re-land from #76): - add a stored, computed severity_numeric (5=extreme .. 1=unknown, 0=unset) on spp.hazard.incident so downstream ordering/threshold logic can consume a numeric scale without resolving CAP vocabulary codes (mirrors the spp_drims incident-area mapping). - document the breaking changes in the changelog: the severity / severity_override removal+rename, the new spp_vocabulary dependency, and the model-level category_id / start_date required relaxation.
1 parent 4bf883d commit fa7bd72

5 files changed

Lines changed: 80 additions & 0 deletions

File tree

spp_hazard/README.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,11 +1189,29 @@ Changelog
11891189
19.0.2.1.0
11901190
~~~~~~~~~~
11911191

1192+
- **BREAKING**: the 1-5 ``severity`` Selection on
1193+
``spp.hazard.incident`` and ``severity_override`` on
1194+
``spp.hazard.incident.area`` are removed and replaced by
1195+
``severity_id`` / ``severity_override_id`` (Many2one to
1196+
``spp.vocabulary.code`` on the CAP severity namespace). Modules that
1197+
extend these models must migrate: views that reference
1198+
``<field name="severity">`` / ``severity_override``, code that reads
1199+
``record.severity``, records created with ``severity="…"``, and any
1200+
``fields_to_log`` entries or domains naming the old fields. Read
1201+
``severity_numeric`` (below) where a numeric scale is needed.
1202+
- **BREAKING**: ``spp_hazard`` now depends on ``spp_vocabulary``.
1203+
- **BREAKING**: ``category_id`` and ``start_date`` are no longer
1204+
``required`` at the model level (they remain required in the incident
1205+
form view) so alert ingestion can create incidents that lack them.
11921206
- feat: severity is now a CAP v1.2 vocabulary code (``severity_id``,
11931207
``severity_override_id`` on incident areas) instead of a hardcoded 1-5
11941208
Selection; adds CAP urgency/certainty/message-type/event fields, alert
11951209
ingestion (``create_incident_from_alert``), and incident ``uuid``
11961210
(re-land from #76).
1211+
- feat: ``spp.hazard.incident`` exposes a stored ``severity_numeric``
1212+
(5=extreme, 4=severe, 3=moderate, 2=minor, 1=unknown, 0=unset) so
1213+
downstream ordering and threshold logic can consume a numeric scale
1214+
without resolving CAP vocabulary codes.
11971215
- feat: migration backfills legacy 1-5 severity values onto the
11981216
vocabulary fields when upgrading from 19.0.2.0.x (1→minor, 2→moderate,
11991217
3→severe, 4→severe, 5→extreme); existing values are never overwritten

spp_hazard/models/hazard_incident.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@
1818
CAP_CERTAINTY_NS = "urn:oasis:names:tc:cap:certainty"
1919
CAP_MSG_TYPE_NS = "urn:oasis:names:tc:cap:msg-type"
2020

21+
# CAP severity codes mapped to a 1-5 numeric scale (higher = more severe) for
22+
# ordering and threshold logic; 0 means no severity is set. Downstream modules
23+
# should read the stored ``severity_numeric`` field instead of resolving codes.
24+
CAP_SEVERITY_NUMERIC = {
25+
"extreme": 5,
26+
"severe": 4,
27+
"moderate": 3,
28+
"minor": 2,
29+
"unknown": 1,
30+
}
31+
2132

2233
def _parse_datetime_string(value):
2334
"""Parse a datetime string in either ISO 8601 or Odoo format.
@@ -109,6 +120,14 @@ class HazardIncident(models.Model):
109120
domain=f"[('namespace_uri', '=', '{CAP_SEVERITY_NS}')]",
110121
help="Overall magnitude/severity of the incident (CAP vocabulary)",
111122
)
123+
severity_numeric = fields.Integer(
124+
string="Severity (numeric)",
125+
compute="_compute_severity_numeric",
126+
store=True,
127+
help="CAP severity mapped to a 1-5 scale (5=extreme, 4=severe, "
128+
"3=moderate, 2=minor, 1=unknown; 0=unset) so downstream ordering and "
129+
"threshold logic need not resolve vocabulary codes.",
130+
)
112131

113132
# CAP (Common Alerting Protocol) fields
114133
cap_urgency_id = fields.Many2one(
@@ -230,6 +249,12 @@ def _compute_is_ongoing(self):
230249
"recovery",
231250
)
232251

252+
@api.depends("severity_id")
253+
def _compute_severity_numeric(self):
254+
"""Map the CAP severity code to a 1-5 numeric scale for ordering."""
255+
for rec in self:
256+
rec.severity_numeric = CAP_SEVERITY_NUMERIC.get(rec.severity_id.code, 0)
257+
233258
@api.depends("area_ids", "incident_area_ids.area_id")
234259
def _compute_area_count(self):
235260
"""Compute the number of affected areas from both M2M and detail records."""

spp_hazard/readme/HISTORY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
### 19.0.2.1.0
22

3+
- **BREAKING**: the 1-5 `severity` Selection on `spp.hazard.incident` and `severity_override` on `spp.hazard.incident.area` are removed and replaced by `severity_id` / `severity_override_id` (Many2one to `spp.vocabulary.code` on the CAP severity namespace). Modules that extend these models must migrate: views that reference `<field name="severity">` / `severity_override`, code that reads `record.severity`, records created with `severity="…"`, and any `fields_to_log` entries or domains naming the old fields. Read `severity_numeric` (below) where a numeric scale is needed.
4+
- **BREAKING**: `spp_hazard` now depends on `spp_vocabulary`.
5+
- **BREAKING**: `category_id` and `start_date` are no longer `required` at the model level (they remain required in the incident form view) so alert ingestion can create incidents that lack them.
36
- feat: severity is now a CAP v1.2 vocabulary code (`severity_id`, `severity_override_id` on incident areas) instead of a hardcoded 1-5 Selection; adds CAP urgency/certainty/message-type/event fields, alert ingestion (`create_incident_from_alert`), and incident `uuid` (re-land from #76).
7+
- feat: `spp.hazard.incident` exposes a stored `severity_numeric` (5=extreme, 4=severe, 3=moderate, 2=minor, 1=unknown, 0=unset) so downstream ordering and threshold logic can consume a numeric scale without resolving CAP vocabulary codes.
48
- feat: migration backfills legacy 1-5 severity values onto the vocabulary fields when upgrading from 19.0.2.0.x (1→minor, 2→moderate, 3→severe, 4→severe, 5→extreme); existing values are never overwritten and legacy columns are kept.
59
- fix: demo incident-area records now set `severity_override_id` vocabulary refs (the removed `severity_override` field broke demo installs).
610

spp_hazard/static/description/index.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2456,11 +2456,29 @@ <h2><a class="toc-backref" href="#toc-entry-2">Changelog</a></h2>
24562456
<div class="section" id="section-1">
24572457
<h1>19.0.2.1.0</h1>
24582458
<ul class="simple">
2459+
<li><strong>BREAKING</strong>: the 1-5 <tt class="docutils literal">severity</tt> Selection on
2460+
<tt class="docutils literal">spp.hazard.incident</tt> and <tt class="docutils literal">severity_override</tt> on
2461+
<tt class="docutils literal">spp.hazard.incident.area</tt> are removed and replaced by
2462+
<tt class="docutils literal">severity_id</tt> / <tt class="docutils literal">severity_override_id</tt> (Many2one to
2463+
<tt class="docutils literal">spp.vocabulary.code</tt> on the CAP severity namespace). Modules that
2464+
extend these models must migrate: views that reference
2465+
<tt class="docutils literal">&lt;field <span class="pre">name=&quot;severity&quot;&gt;</span></tt> / <tt class="docutils literal">severity_override</tt>, code that reads
2466+
<tt class="docutils literal">record.severity</tt>, records created with <tt class="docutils literal"><span class="pre">severity=&quot;…&quot;</span></tt>, and any
2467+
<tt class="docutils literal">fields_to_log</tt> entries or domains naming the old fields. Read
2468+
<tt class="docutils literal">severity_numeric</tt> (below) where a numeric scale is needed.</li>
2469+
<li><strong>BREAKING</strong>: <tt class="docutils literal">spp_hazard</tt> now depends on <tt class="docutils literal">spp_vocabulary</tt>.</li>
2470+
<li><strong>BREAKING</strong>: <tt class="docutils literal">category_id</tt> and <tt class="docutils literal">start_date</tt> are no longer
2471+
<tt class="docutils literal">required</tt> at the model level (they remain required in the incident
2472+
form view) so alert ingestion can create incidents that lack them.</li>
24592473
<li>feat: severity is now a CAP v1.2 vocabulary code (<tt class="docutils literal">severity_id</tt>,
24602474
<tt class="docutils literal">severity_override_id</tt> on incident areas) instead of a hardcoded 1-5
24612475
Selection; adds CAP urgency/certainty/message-type/event fields, alert
24622476
ingestion (<tt class="docutils literal">create_incident_from_alert</tt>), and incident <tt class="docutils literal">uuid</tt>
24632477
(re-land from #76).</li>
2478+
<li>feat: <tt class="docutils literal">spp.hazard.incident</tt> exposes a stored <tt class="docutils literal">severity_numeric</tt>
2479+
(5=extreme, 4=severe, 3=moderate, 2=minor, 1=unknown, 0=unset) so
2480+
downstream ordering and threshold logic can consume a numeric scale
2481+
without resolving CAP vocabulary codes.</li>
24642482
<li>feat: migration backfills legacy 1-5 severity values onto the
24652483
vocabulary fields when upgrading from 19.0.2.0.x (1→minor, 2→moderate,
24662484
3→severe, 4→severe, 5→extreme); existing values are never overwritten

spp_hazard/tests/test_hazard_incident.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,18 @@ def test_21_multi_record_close(self):
340340
# inc1 gets auto end_date, inc2 preserves its own
341341
self.assertTrue(inc1.end_date)
342342
self.assertEqual(str(inc2.end_date), "2024-04-01")
343+
344+
def test_22_severity_numeric(self):
345+
"""Test severity_numeric maps the CAP severity code to a 1-5 scale."""
346+
# setUpClass created the incident with the 'severe' code -> 4
347+
self.assertEqual(self.incident.severity_numeric, 4)
348+
349+
# Recomputes when the code changes
350+
self.incident.severity_id = self.severity_extreme
351+
self.assertEqual(self.incident.severity_numeric, 5)
352+
self.incident.severity_id = self.severity_moderate
353+
self.assertEqual(self.incident.severity_numeric, 3)
354+
355+
# No severity set -> 0 (sorts last in "most severe" ordering)
356+
self.incident.severity_id = False
357+
self.assertEqual(self.incident.severity_numeric, 0)

0 commit comments

Comments
 (0)