Skip to content

Commit 7fb2231

Browse files
sstruzikclaude
andcommitted
docs(OED): Sphinx reference site with spec-generated fields & coded values
Standalone Furo/MyST site for the Open Exposure Data standard. Field reference (by input file: Loc/Acc/ReinsInfo/ReinsScope) and coded-value lists (perils, occupancy, construction, country, coverage) are generated at build time from oed.json (_ext/gen_oed_reference.py, which regenerates oed.json from the CSVs if absent); plus the migrated spec chapters and an overview/hierarchy explanation. Cross-component links via intersphinx (orchestrator-driven). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ee7d913 commit 7fb2231

17 files changed

Lines changed: 2112 additions & 0 deletions

docs/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
build/
2+
source/reference/_generated/
3+
**/__pycache__/
4+
.jupyter_cache/
5+
jupyter_execute/
6+
.DS_Store

docs/Makefile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Minimal Sphinx makefile for the ORD reference site
2+
SPHINXOPTS ?=
3+
SPHINXBUILD ?= sphinx-build
4+
SOURCEDIR = source
5+
BUILDDIR = build
6+
7+
help:
8+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
9+
10+
.PHONY: help Makefile
11+
12+
%: Makefile
13+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"""Generate OED reference pages from ``oed.json`` (single source of truth).
2+
3+
The OED standard is defined by ``oed.json`` at the repo root — the same machine-readable
4+
spec that ods-tools consumes. Rather than hand-maintain reference tables, this extension
5+
reads ``oed.json`` at build time and writes MyST Markdown into ``reference/_generated/``
6+
which the reference pages include. Edit ``oed.json`` (and its source CSVs), not the
7+
generated Markdown.
8+
9+
Runs on the Sphinx ``config-inited`` event; also runnable standalone for quick checks.
10+
"""
11+
import json
12+
import os
13+
14+
HERE = os.path.dirname(os.path.abspath(__file__))
15+
REPO_ROOT = os.path.abspath(os.path.join(HERE, os.pardir, os.pardir, os.pardir))
16+
OED_JSON = os.path.join(REPO_ROOT, "oed.json")
17+
OUT_DIR = os.path.join(HERE, os.pardir, "reference", "_generated")
18+
19+
# input-file groups, in exposure-modelling order
20+
FILE_ORDER = [("Loc", "Location"), ("Acc", "Account"),
21+
("ReinsInfo", "Reinsurance Info"), ("ReinsScope", "Reinsurance Scope")]
22+
23+
24+
def _cell(text):
25+
return str(text).replace("|", "\\|").replace("\n", " ").replace("\r", " ").strip()
26+
27+
28+
def _pipe_table(columns, rows):
29+
out = ["| " + " | ".join(columns) + " |",
30+
"| " + " | ".join(["---"] * len(columns)) + " |"]
31+
for row in rows:
32+
out.append("| " + " | ".join(_cell(c) for c in row) + " |")
33+
return "\n".join(out)
34+
35+
36+
def _write(name, text):
37+
os.makedirs(OUT_DIR, exist_ok=True)
38+
with open(os.path.join(OUT_DIR, name), "w", encoding="utf-8") as fh:
39+
fh.write(text + "\n")
40+
41+
42+
def generate_fields(oed):
43+
"""input_fields -> field reference, grouped by input file."""
44+
fields = oed["input_fields"]
45+
parts = ["<!-- generated by _ext/gen_oed_reference.py from oed.json (input_fields) -->"]
46+
total = 0
47+
for key, title in FILE_ORDER:
48+
recs = fields.get(key)
49+
if not recs:
50+
continue
51+
parts.append(f"\n## {title} (`{key}`)\n")
52+
rows = []
53+
for rec in recs.values():
54+
rows.append([
55+
rec.get("Input Field Name", ""),
56+
rec.get("Type & Description", ""),
57+
rec.get("Data Type", ""),
58+
rec.get("Property field status", ""),
59+
rec.get("Default", ""),
60+
])
61+
total += 1
62+
parts.append(_pipe_table(
63+
["Field", "Description", "Data type", "Status", "Default"], rows))
64+
_write("oed_fields.md", "\n".join(parts))
65+
return total
66+
67+
68+
def generate_values(oed):
69+
"""Coded value lists -> code reference tables."""
70+
parts = ["<!-- generated by _ext/gen_oed_reference.py from oed.json (value lists) -->"]
71+
total = 0
72+
73+
# perils: nested under 'info'
74+
perils = oed.get("perils", {}).get("info", {})
75+
if perils:
76+
parts.append("\n## Perils\n")
77+
rows = [[code, r.get("DB table PerilCode", ""), r.get("Peril Description", ""),
78+
r.get("Grouped PerilCode", "")] for code, r in perils.items()]
79+
parts.append(_pipe_table(["Code", "PerilCode", "Description", "Grouped"], rows))
80+
total += len(rows)
81+
82+
def simple(key, title, cols):
83+
nonlocal total
84+
v = oed.get(key)
85+
if not isinstance(v, dict):
86+
return
87+
parts.append(f"\n## {title}\n")
88+
rows = [[code] + [rec.get(c, "") for _, c in cols] for code, rec in v.items()]
89+
parts.append(_pipe_table(["Code"] + [h for h, _ in cols], rows))
90+
total += len(rows)
91+
92+
simple("occupancy", "Occupancy codes",
93+
[("Name", "Name"), ("Description", "Description"), ("Broad Category", "Broad Category")])
94+
simple("construction", "Construction codes",
95+
[("Name", "Name"), ("Description", "Description"), ("Broad Category", "Broad Category")])
96+
simple("country", "Country codes", [("Name", "Name")])
97+
simple("CoverageValues", "Coverage types",
98+
[("CoverageID", "CoverageID"), ("Description", "Description"), ("Type", "Type")])
99+
_write("oed_values.md", "\n".join(parts))
100+
return total
101+
102+
103+
def _ensure_oed_json():
104+
"""oed.json is a generated artifact (utils/gen-json.py builds it from the CSVs) and is not
105+
committed. Generate it if missing so the docs build is self-sufficient in CI."""
106+
if os.path.exists(OED_JSON):
107+
return
108+
import subprocess
109+
import sys
110+
gen = os.path.join(REPO_ROOT, "utils", "gen-json.py")
111+
subprocess.run([sys.executable, gen, "--output-path", OED_JSON], cwd=REPO_ROOT, check=True)
112+
113+
114+
def run(app=None, config=None):
115+
_ensure_oed_json()
116+
with open(OED_JSON, encoding="utf-8") as fh:
117+
oed = json.load(fh)
118+
n_fields = generate_fields(oed)
119+
n_values = generate_values(oed)
120+
msg = f"[gen_oed_reference] wrote {n_fields} fields, {n_values} coded values -> reference/_generated/"
121+
if app is not None:
122+
from sphinx.util import logging
123+
logging.getLogger(__name__).info(msg)
124+
else:
125+
print(msg)
126+
127+
128+
def setup(app):
129+
app.connect("config-inited", run)
130+
return {"parallel_read_safe": True, "parallel_write_safe": True}
131+
132+
133+
if __name__ == "__main__":
134+
run()

docs/source/conf.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Sphinx configuration for the Open Exposure Data (OED) standard reference site.
2+
3+
The field and coded-value reference is generated at build time from ``oed.json`` by the
4+
local ``gen_oed_reference`` extension (single source of truth). Theme and MyST setup mirror
5+
the other OasisLMF documentation sites for a consistent aggregated site.
6+
"""
7+
import datetime
8+
import os
9+
import sys
10+
11+
sys.path.insert(0, os.path.abspath("_ext"))
12+
13+
project = "Open Exposure Data (OED)"
14+
author = "Oasis LMF"
15+
copyright = f"{datetime.date.today().year} Oasis LMF"
16+
17+
extensions = [
18+
"myst_parser",
19+
"sphinx_design",
20+
"sphinx_copybutton",
21+
"gen_oed_reference", # build-time reference generation from oed.json
22+
]
23+
24+
source_suffix = {".rst": "restructuredtext", ".md": "markdown"}
25+
master_doc = "index"
26+
language = "en"
27+
# _generated/*.md are include-only fragments, not standalone documents
28+
exclude_patterns = ["reference/_generated/**"]
29+
30+
myst_enable_extensions = ["colon_fence", "deflist", "substitution", "tasklist"]
31+
myst_heading_anchors = 6
32+
33+
html_theme = "furo"
34+
html_title = "Open Exposure Data (OED)"
35+
html_static_path = ["_static"] if os.path.isdir(os.path.join(os.path.dirname(__file__), "_static")) else []
36+
37+
38+
# -- Cross-component links (intersphinx, aggregated site) --------------------
39+
# The GenerateDocs orchestrator sets OASIS_INTERSPHINX_MAP (JSON) to point cross-references at
40+
# the other components' built inventories; standalone builds add nothing. Use explicit roles,
41+
# e.g. {external+ord:doc}`reference/tables` or :external+oed:ref:`some-label`.
42+
import json as _ix_json, os as _ix_os
43+
if "sphinx.ext.intersphinx" not in extensions:
44+
extensions = list(extensions) + ["sphinx.ext.intersphinx"]
45+
try:
46+
intersphinx_mapping
47+
except NameError:
48+
intersphinx_mapping = {}
49+
intersphinx_mapping.update({
50+
_k: (_v[0], _v[1])
51+
for _k, _v in _ix_json.loads(_ix_os.environ.get("OASIS_INTERSPHINX_MAP", "{}")).items()
52+
})
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
Asset Related Details
2+
======================
3+
4+
The following sections describe the OED specification for the asset value, usage, construction, and other modifiers that can influence the susceptibility of an asset to damage from a peril.
5+
Coverage total insurable value (TIV)
6+
7+
Total insurable value (TIV) for each property is captured in four location level fields:
8+
9+
• **BuildingTIV**: The total insurable value of the buildings.
10+
11+
• **ContentsTIV**: The total insurance value of contents and stock.
12+
13+
• **BITIV**: The total business interruption, or other time related, total insurable value.
14+
15+
• **OtherTIV**: The total insurable value for elements other than the main building / contents / time elements. Typically used to represent the TIV for outbuildings / ap
16+
17+
Total insurable value is not peril dependent. The currency of the TIV is specified in the **LocCurrency** field.
18+
19+
|
20+
21+
Occupancy Type
22+
##############
23+
24+
Occupancy codes are stored in the **OccupancyCode** field. The occupancy type list is predominantly a one to one mapping from the AIR CEDE occupancy codes, although some extra codes have been added. The broad categories of code and the number ranges are shown in the table below.
25+
26+
27+
.. csv-table::
28+
:widths: 10,10
29+
:header: "OED Occupancy Code Range", "Broad Category of Occupancy"
30+
31+
"1000", "Unknown"
32+
"1050 – 1099", "Residential"
33+
"1100 – 1149", "Commercial"
34+
"1150 – 1199", "Industrial"
35+
"1200 – 1249", "Religion / Government / Education"
36+
"1250 – 1299", "Transportation"
37+
"1300 – 1349", "Utilities"
38+
"1350 – 1399", "Miscellaneous"
39+
"2000 – 2799", "Industrial Facility"
40+
"3000 – 3999", "Offshore"
41+
42+
Although the code ranges above infer an extremely long list of codes there are less than 200 distinct occupancy codes in total. Yachts and automobiles are included under construction type codes rather than occupancy codes.
43+
Some users may have translated from a different (original) occupancy code to the OED occupancy code but would like to store the original occupancy code information. This can be done using the **OrgOccupancyScheme** and **OrgOccupancyCode** fields.
44+
45+
|
46+
47+
Construction Type
48+
##################
49+
50+
Construction codes are stored in the **ConstructionCode** field. The construction type list is predominantly a one to one mapping from the AIR CEDE construction codes, although some extra codes have been added. The broad categories of code and the number ranges are shown in table below.
51+
52+
53+
.. csv-table::
54+
:widths: 10,10
55+
:header: "OED Construction Code Range", "Broad Category of Construction"
56+
57+
"5000", "Unknown"
58+
"5050 – 5099", "Wood"
59+
"5100 – 5149", "Masonry"
60+
"5150 – 5199", "Concrete"
61+
"5200 – 5249", "Steel"
62+
"5250 – 5299", "Composite"
63+
"5300 – 5349", "Special"
64+
"5350 – 5399", "Mobile Homes"
65+
"5400 – 5449", "Bridges"
66+
"5450 – 5499", "Roads, Railroads, Runways"
67+
"5500 – 5549", "Dams"
68+
"5550 – 5599", "Tunnels"
69+
"5600 – 5649", "Storage Tanks"
70+
"5650 – 5699", "Pipelines"
71+
"5700 – 5749", "Chimneys"
72+
"5750 – 5799", "Towers"
73+
"5800 – 5849", "Equipment"
74+
"5850 – 5899", "Automobiles"
75+
"5900 – 5949", "Yachts"
76+
"5950 – 5999", "Miscellaneous"
77+
"6000 – 6099", "Marine Cargo General"
78+
"6100 – 6149", "Marine Cargo Combustible"
79+
"6150 – 6199", "Marine Cargo Non-Combustible"
80+
"7000 - 7999", "Offshore"
81+
82+
|
83+
84+
Although the code ranges above infer a very long list of codes there are less than 200 construction codes in total.
85+
Some users may have translated from a different (original) construction code scheme to the OED construction code scheme but would like to store the original construction code information. This can be done using the **OrgConstructionScheme** and **OrgConstructionCode** fields.
86+
87+
|
88+
89+
Other Common Modifiers
90+
######################
91+
92+
While different catastrophe models will use different modifiers to adjust the vulnerability of an asset, the following are the most commonly used modifiers:
93+
94+
• **YearBuilt**: the year the building was built. **YearUpgraded**, **RoofYearBuil** are also modifiers that allow the user to add additional information.
95+
96+
• **NumberOfStoreys**: The total number of storeys in a building. **BuildingHeight** is also available for the user to add in the precise height of the building if this is known. **FloorsOccupied** allows the specific floors in the building that are occupied to be specified.
97+
98+
• **NumberOfBuildings**: The number of buildings represented by this location. This is commonly used to indicate the presence of aggregated data. If, instead, a user has specific details about different locations, but wants to denote a linkage of some kind between each location then the **LocGroup** field can be used to link individual locations (either for reporting purposes or to define a reinsurance ‘risk’ level). **CorrelationGroup** can be used to denote a correlation in secondary uncertainty between groups of locations.
99+
100+
• **FloorArea** & **FloorAreaUnit**: The total floor area occupied, summing the area of multiple floors.
101+
102+
Other modifiers, either peril specific or less commonly used by models, are available and are listed in the specification spreadsheet. They can be identified by filtering on the SecMod column in the ‘OED Input Fields’ sheet in the specification spreadsheet.
103+
104+
|
105+
106+
Flexi-tables
107+
############
108+
109+
Despite the wide range of fields available in OED, there is always the possibility that a user needs to enter or store information without a corresponding OED field. This can be achieved through the flexi-table functionality within OED, which essentially provides a key-value pair back end table at the main hierarchical levels.
110+
To enter additional field / values, a user can enter additional columns: **FlexiLocZZZ**, **FlexPolZZZ**, **FlexiAccZZZ**, where ‘ZZZ’ contains the name of the new field.
111+
For example, if a user wants to store information on house colour, they could add an additional column to the location input file with the fieldname *FlexiLocHouseColour*.
112+
113+

0 commit comments

Comments
 (0)