rmti / openapi.py
drmarolla's picture
Upload 31 files
f650248 verified
Raw History Blame Contribute Delete
6.72 kB
"""OpenAPI 3.0 description and a /docs page (Swagger UI loaded from a CDN
in the visitor's browser; the server itself needs no extra packages)."""
from .routes import API_VERSION
_unit = {"type": "number", "minimum": 0, "maximum": 1}
_C = {"type": "integer", "enum": [1, 2, 3, 4, 5],
"description": "Consequence index (Table 2): 1=0.10 Insignificant, 2=0.30 Minor, "
"3=0.50 Moderate, 4=0.75 Major, 5=1.00 Severe. Default 5."}
_N = {"type": "number", "minimum": 1, "maximum": 10_000_000_000,
"description": "Population / cohort size (drives the advisory E_log and the per-capita rate)."}
_disaster_input = {
"type": "object", "required": ["N", "L", "E_sel", "V", "rho_now"], "additionalProperties": False,
"properties": {
"N": _N, "L": {**_unit, "description": "Hazard likelihood"},
"E_sel": {**_unit, "description": "Selected exposure ('E' is accepted as an alias)"},
"V": {**_unit, "description": "Vulnerability"},
"rho_now": {**_unit, "description": "Current resilience (must be < 1 if rho_after is given)"},
"rho_after": {**_unit, "description": "Post-investment resilience (optional)"},
"C": _C},
"example": {"N": 1100000, "L": 0.875, "E_sel": 0.8, "V": 0.8,
"rho_now": 0.25, "rho_after": 0.48, "C": 4}}
_onc_input = {
"type": "object", "required": ["N", "L", "rho_now"], "additionalProperties": False,
"description": "Provide exactly one of V, albi_grade, albi_grade_counts. E is optional "
"(defaults to the log-normalised helper from N). Provide C or bclc_stage, not both.",
"properties": {
"N": _N, "L": {**_unit, "description": "Annual HCC incidence-derived likelihood"},
"E": _unit, "V": _unit,
"albi_grade": {"type": "integer", "enum": [1, 2, 3]},
"albi_grade_counts": {"type": "object", "example": {"1": 10, "2": 18, "3": 1}},
"rho_now": _unit, "rho_after": _unit, "C": _C,
"bclc_stage": {"type": "string", "enum": ["BCLC0", "BCLC_A", "EARLY_INT", "BCLC_B", "BCLC_CD"]}},
"example": {"N": 100000, "L": 0.30, "E": 0.65, "albi_grade": 2,
"rho_now": 0.20, "rho_after": 0.50, "bclc_stage": "BCLC_CD"}}
def _rank_body(item_schema, desc):
item = {"allOf": [item_schema, {"type": "object", "required": ["label"],
"properties": {"label": {"type": "string", "maxLength": 60}}}]}
return {"type": "object", "required": ["items"], "additionalProperties": False,
"properties": {"items": {"type": "array", "minItems": 1, "maxItems": 100,
"items": item, "description": desc}}}
_ok = {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}},
"400": {"description": "Validation error: {error:{code,message}}"},
"429": {"description": "Rate limited; see Retry-After header"}}
def _post(summary, schema, tag):
return {"post": {"summary": summary, "tags": [tag], "responses": _ok,
"requestBody": {"required": True, "content": {"application/json": {"schema": schema}}}}}
def _get(summary, tag, params=None):
g = {"summary": summary, "tags": [tag], "responses": _ok}
if params:
g["parameters"] = params
return {"get": g}
def spec():
return {
"openapi": "3.0.3",
"info": {
"title": "RMTI API", "version": API_VERSION,
"description": (
"Risk Mechanism Theory Index scoring for **disaster / urban risk** and **oncology (HCC) "
"surveillance triage**.\n\nR = L·E·V·(1−ρ); EAL = L·C·(1−ρ); five fixed tiers.\n\n"
"Stateless: request bodies are never stored or logged.\n\n"
"**Educational and research use only.** Not a forecast, engineering assessment or "
"actuarial figure. The oncology endpoints are not a medical device, are not clinically "
"validated, and must not be sent patient-identifiable information."),
"license": {"name": "Apache-2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0"}},
"tags": [{"name": "framework"}, {"name": "disaster"}, {"name": "oncology"}],
"paths": {
"/health": _get("Liveness check", "framework"),
"/v1/framework": _get("Fixed tier table, consequence scale and anchors", "framework"),
"/v1/exposure": _get("Advisory log-normalised exposure E_log", "framework", [
{"name": "N", "in": "query", "required": True, "schema": {"type": "number"}},
{"name": "domain", "in": "query", "schema": {"type": "string",
"enum": ["disaster", "oncology"]}}]),
"/v1/disaster/assess": _post("Score one asset or city", _disaster_input, "disaster"),
"/v1/disaster/rank": _post("Score and rank a portfolio (max 100)",
_rank_body(_disaster_input, "Assets to rank"), "disaster"),
"/v1/disaster/cohort": _get("Published eight-asset validation cohort, ranked", "disaster"),
"/v1/multi": _post("Multi-hazard, multi-category form (Eq. 3/5) — not field-validated", {
"type": "object", "required": ["hazards", "weights"], "additionalProperties": False,
"properties": {
"N": _N,
"weights": {"type": "object", "description": "social, economic, environmental; must sum to 1",
"example": {"social": 0.4, "economic": 0.3, "environmental": 0.3}},
"hazards": {"type": "array", "maxItems": 10, "items": {"type": "object"}}}}, "disaster"),
"/v1/oncology/assess": _post("Score one HCC cohort/stratum", _onc_input, "oncology"),
"/v1/oncology/rank": _post("Score and rank strata (max 100)",
_rank_body(_onc_input, "Strata to rank; use non-identifying labels"),
"oncology"),
"/v1/oncology/cohorts": _get("Published HCC cohorts and Pearl-protocol aetiologies", "oncology"),
},
}
DOCS_HTML = """<!doctype html>
<html lang="en"><head><meta charset="utf-8"><title>RMTI API docs</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css"></head>
<body><div id="ui"></div>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
<script>window.ui = SwaggerUIBundle({url: "/openapi.json", dom_id: "#ui"});</script>
</body></html>"""