provinans / src /endopath /dictionary.py
reversely's picture
Upload folder using huggingface_hub
eea689d verified
Raw
History Blame Contribute Delete
3.82 kB
"""The target data dictionary an Intake submission points the tool at.
Intake points at a report source and at a target dictionary (docs/prd.md section
8.1). A dictionary names the variables to capture, each variable's permitted
values, and whether confirming a variable requires a licensed reference source.
The built-in dictionary is the CAP endometrium checklist, derived from
`endopath.fields` so it never drifts from `GET /api/fields`. An uploaded
dictionary arrives on the ingestion request in the same shape.
"""
from __future__ import annotations
import json
from typing import Optional
from pydantic import BaseModel, Field
from sqlalchemy import Connection
from endopath import fields, storage
# The CAP protocol the built-in checklist transcribes, recorded in
# data/taxonomy/sources.csv. The dictionary carries the source_id so a reviewer
# can trace every variable to the standard it came from.
BUILTIN_DICTIONARY_ID = "cap_uterus/5.1.0.0"
BUILTIN_DICTIONARY_TITLE = "CAP endometrium checklist"
class DictionaryVariable(BaseModel):
"""One variable to capture. `permitted_values` is None for an open
vocabulary (a stage code, a lymph node string) and a closed list for an
enumerated field. `licence_required` marks a variable whose confirmation
needs a licensed reference source, so a deployment without that licence can
gate it."""
name: str
label: Optional[str] = None
permitted_values: Optional[list[str]] = None
licence_required: bool = False
class DataDictionary(BaseModel):
"""A target dictionary: an id, a title, and the variables to capture. Both
the built-in CAP checklist and an uploaded dictionary use this shape."""
id: str
title: str
variables: list[DictionaryVariable] = Field(default_factory=list)
@property
def licence_required_variables(self) -> list[str]:
return [v.name for v in self.variables if v.licence_required]
def register(conn: Connection, dictionary: DataDictionary) -> DataDictionary:
"""Store a submitted dictionary so the compilation job (#91) can read it back by id after the
request that submitted it has returned. Returns the stored dictionary."""
storage.upsert_dictionary(
conn,
dictionary_id=dictionary.id,
title=dictionary.title,
variables_json=json.dumps([v.model_dump() for v in dictionary.variables]),
)
return dictionary
def get_registered(conn: Connection, dictionary_id: str) -> Optional[DataDictionary]:
"""The stored dictionary, reconstructed from its row, or None if it was never submitted."""
row = storage.get_dictionary(conn, dictionary_id)
if row is None:
return None
variables = [DictionaryVariable(**item) for item in json.loads(row["variables_json"])]
return DataDictionary(id=row["id"], title=row["title"], variables=variables)
def reset(conn: Connection) -> None:
"""Clear the stored dictionaries, the queue, and their artifacts. For tests and a fresh process."""
storage.clear_intake_state(conn)
def builtin_cap_dictionary() -> DataDictionary:
"""The CAP endometrium checklist as a dictionary, one variable per checklist
field, permitted values from each field's closed vocabulary (None for the
open ones). No variable is licence-gated: every value is extracted from the
report and stored with its citation, not drawn from a licensed source."""
variables = [
DictionaryVariable(
name=spec.name,
label=spec.label,
permitted_values=list(spec.enum_values) if spec.enum_values else None,
licence_required=False,
)
for spec in fields.FIELDS
]
return DataDictionary(
id=BUILTIN_DICTIONARY_ID,
title=BUILTIN_DICTIONARY_TITLE,
variables=variables,
)