omop-semantics 0.2.1__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. omop_semantics-0.2.2/PKG-INFO +84 -0
  2. omop_semantics-0.2.2/README.md +50 -0
  3. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/pyproject.toml +5 -2
  4. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/runtime/instance_loader.py +12 -0
  5. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/runtime/renderers.py +2 -2
  6. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/runtime/resolver.py +17 -20
  7. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/runtime/value_sets.py +21 -19
  8. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/configuration/core/omop_named_sets.yaml +2 -2
  9. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/generated_models/omop_named_sets.py +2 -2
  10. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/instances/valuesets.yaml +12 -12
  11. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/unknowns.py +4 -2
  12. omop_semantics-0.2.1/PKG-INFO +0 -139
  13. omop_semantics-0.2.1/README.md +0 -105
  14. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/__init__.py +0 -0
  15. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/base.py +0 -0
  16. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/runtime/__init__.py +0 -0
  17. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/runtime/default_valuesets.py +0 -0
  18. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/runtime/unknown_handlers.py +0 -0
  19. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/__init__.py +0 -0
  20. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/codegen.py +0 -0
  21. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/configuration/core/omop_base.yaml +0 -0
  22. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/configuration/core/omop_profiles.yaml +0 -0
  23. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/configuration/core/omop_templates.yaml +0 -0
  24. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/configuration/core/omop_unknowns.yaml +0 -0
  25. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/configuration/profiles/omop_episodes.yaml +0 -0
  26. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/configuration/profiles/omop_modifiers.yaml +0 -0
  27. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/configuration/profiles/omop_staging.yaml +0 -0
  28. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/configuration/registry/omop_semantic_registry.yaml +0 -0
  29. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/dump.py +0 -0
  30. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/generated_models/__init__.py +0 -0
  31. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/generated_models/omop_semantic_registry.py +0 -0
  32. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/generated_models/template_set.py +0 -0
  33. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/instances/demographic.yaml +0 -0
  34. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/instances/enumerators.yaml +0 -0
  35. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/instances/genomic.yaml +0 -0
  36. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/instances/profile_groups.yaml +0 -0
  37. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/instances/profiles.yaml +0 -0
  38. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/schema/instances/provider_specialty.yaml +0 -0
  39. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/utils/__init__.py +0 -0
  40. {omop_semantics-0.2.1 → omop_semantics-0.2.2}/src/omop_semantics/utils/paths.py +0 -0
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.3
2
+ Name: omop-semantics
3
+ Version: 0.2.2
4
+ Summary: Define, validate, and use schema-backed semantic conventions for OMOP CDM
5
+ Author: gkennos
6
+ Author-email: gkennos <georgina.kennedy@unsw.edu.au>
7
+ Requires-Dist: ipykernel>=7.2.0
8
+ Requires-Dist: linkml>=1.11.1
9
+ Requires-Dist: linkml-runtime>=1.11.1
10
+ Requires-Dist: python-dotenv>=1.2.2
11
+ Requires-Dist: ruamel-yaml>=0.18.17
12
+ Requires-Dist: typing-extensions>=4.15.0
13
+ Requires-Dist: urllib3>=2.7.0
14
+ Requires-Dist: ipython>=8.0 ; extra == 'dev'
15
+ Requires-Dist: pytest>=9.0.3 ; extra == 'dev'
16
+ Requires-Dist: pytest-cov>=4.0 ; extra == 'dev'
17
+ Requires-Dist: mypy>=1.8 ; extra == 'dev'
18
+ Requires-Dist: ruff>=0.4 ; extra == 'dev'
19
+ Requires-Dist: rich>=13.0 ; extra == 'dev'
20
+ Requires-Dist: tornado>=6.5.5 ; extra == 'dev'
21
+ Requires-Dist: pygments>=2.20.0 ; extra == 'dev'
22
+ Requires-Dist: mkdocs-material>=9.7.6 ; extra == 'docs'
23
+ Requires-Dist: mkdocstrings-python>=2.0.1 ; extra == 'docs'
24
+ Requires-Dist: mkdocs>=1.6.1 ; extra == 'docs'
25
+ Requires-Dist: mkdocs-mermaid2-plugin>=1.2.3 ; extra == 'docs'
26
+ Requires-Dist: pygments>=2.20.0 ; extra == 'docs'
27
+ Requires-Python: >=3.12
28
+ Project-URL: Homepage, https://australiancancerdatanetwork.github.io/omop-semantics/
29
+ Project-URL: Repository, https://github.com/AustralianCancerDataNetwork/omop-semantics
30
+ Project-URL: Issues, https://github.com/AustralianCancerDataNetwork/omop-semantics/issues
31
+ Provides-Extra: dev
32
+ Provides-Extra: docs
33
+ Description-Content-Type: text/markdown
34
+
35
+ # omop-semantics
36
+
37
+ **omop-semantics** is a Python library for defining and managing **semantic conventions on top of OMOP CDM**.
38
+
39
+ It gives you a structured, versioned, portable layer for describing which OMOP concepts are valid in a given context, how they map to CDM row shapes, and what fallback concepts to use when a mapping cannot be completed. Conventions are authored in YAML, validated against schemas, and exposed as typed Python objects — not scattered across ETL code, SQL, or documentation.
40
+
41
+ ---
42
+
43
+ ## Three runtime surfaces
44
+
45
+ - **Value-set runtime**
46
+ Stable named concept ids for application logic:
47
+ `from omop_semantics.runtime.default_valuesets import runtime`
48
+
49
+ - **Template/profile runtime**
50
+ Compiled semantic templates and CDM profiles via `OmopSemanticEngine`.
51
+
52
+ - **Fallback concepts**
53
+ Canonical unknown and default concepts with reason codes:
54
+ `from omop_semantics.unknowns import UNKNOWN`
55
+
56
+ ---
57
+
58
+ ## Key properties
59
+
60
+ - **Portable** — no database or vocabulary graph required
61
+ - **Versionable** — conventions are tracked in git alongside code
62
+ - **Extensible** — add project-specific definitions on top of the shipped ones
63
+ - **Integrates with pipelines** — drives ETL logic, validation, and documentation from a single source
64
+
65
+ ---
66
+
67
+ ## When to use this
68
+
69
+ Use **omop-semantics** if you:
70
+
71
+ - have project-specific rules about which OMOP concepts are valid,
72
+ - need consistent concept groupings across ETL and analytics,
73
+ - want semantic conventions to be explicit, testable, and versioned,
74
+ - are working in domains like oncology where OMOP alone is too permissive.
75
+
76
+ ---
77
+
78
+ ## Docs
79
+
80
+ - [Usage](docs/usage.md) — loading paths and code patterns
81
+ - [Data Model](docs/data-model.md) — profiles, templates, and semantic objects
82
+ - [Schema & Instances](docs/schema-and-instances.md) — authoring assets and file organisation
83
+ - [Fallback Concepts](docs/unknowns.md) — the shipped unknown and default concepts
84
+ - [Internals](docs/internals.md) — package structure and load-time behaviour
@@ -0,0 +1,50 @@
1
+ # omop-semantics
2
+
3
+ **omop-semantics** is a Python library for defining and managing **semantic conventions on top of OMOP CDM**.
4
+
5
+ It gives you a structured, versioned, portable layer for describing which OMOP concepts are valid in a given context, how they map to CDM row shapes, and what fallback concepts to use when a mapping cannot be completed. Conventions are authored in YAML, validated against schemas, and exposed as typed Python objects — not scattered across ETL code, SQL, or documentation.
6
+
7
+ ---
8
+
9
+ ## Three runtime surfaces
10
+
11
+ - **Value-set runtime**
12
+ Stable named concept ids for application logic:
13
+ `from omop_semantics.runtime.default_valuesets import runtime`
14
+
15
+ - **Template/profile runtime**
16
+ Compiled semantic templates and CDM profiles via `OmopSemanticEngine`.
17
+
18
+ - **Fallback concepts**
19
+ Canonical unknown and default concepts with reason codes:
20
+ `from omop_semantics.unknowns import UNKNOWN`
21
+
22
+ ---
23
+
24
+ ## Key properties
25
+
26
+ - **Portable** — no database or vocabulary graph required
27
+ - **Versionable** — conventions are tracked in git alongside code
28
+ - **Extensible** — add project-specific definitions on top of the shipped ones
29
+ - **Integrates with pipelines** — drives ETL logic, validation, and documentation from a single source
30
+
31
+ ---
32
+
33
+ ## When to use this
34
+
35
+ Use **omop-semantics** if you:
36
+
37
+ - have project-specific rules about which OMOP concepts are valid,
38
+ - need consistent concept groupings across ETL and analytics,
39
+ - want semantic conventions to be explicit, testable, and versioned,
40
+ - are working in domains like oncology where OMOP alone is too permissive.
41
+
42
+ ---
43
+
44
+ ## Docs
45
+
46
+ - [Usage](docs/usage.md) — loading paths and code patterns
47
+ - [Data Model](docs/data-model.md) — profiles, templates, and semantic objects
48
+ - [Schema & Instances](docs/schema-and-instances.md) — authoring assets and file organisation
49
+ - [Fallback Concepts](docs/unknowns.md) — the shipped unknown and default concepts
50
+ - [Internals](docs/internals.md) — package structure and load-time behaviour
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "omop-semantics"
3
- version = "0.2.1"
3
+ version = "0.2.2"
4
4
  description = "Define, validate, and use schema-backed semantic conventions for OMOP CDM"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -23,7 +23,7 @@ omop-semantics = "omop_semantics:main"
23
23
  [project.optional-dependencies]
24
24
  dev = [
25
25
  "ipython>=8.0",
26
- "pytest>=7.0",
26
+ "pytest>=9.0.3",
27
27
  "pytest-cov>=4.0",
28
28
  "mypy>=1.8",
29
29
  "ruff>=0.4",
@@ -45,6 +45,9 @@ build-backend = "uv_build"
45
45
  [tool.pytest.ini_options]
46
46
  testpaths = ["tests"]
47
47
 
48
+ [tool.pyright]
49
+ ignore = ["src/omop_semantics/schema/generated_models"]
50
+
48
51
  [project.urls]
49
52
  Homepage = "https://australiancancerdatanetwork.github.io/omop-semantics/"
50
53
  Repository = "https://github.com/AustralianCancerDataNetwork/omop-semantics"
@@ -80,6 +80,18 @@ def merge_instance_files(
80
80
  "groups": merged_groups
81
81
  }
82
82
 
83
+ def needs_profile_interpolation(path: Path) -> bool:
84
+ """Return True if any registry member in the file uses a string cdm_profile reference."""
85
+ data = yaml_loader.load_as_dict(str(path))
86
+ if not isinstance(data, dict):
87
+ return False
88
+ for group in data.get("groups", []) or []:
89
+ for member in (group.get("registry_members") or []):
90
+ if isinstance(member.get("cdm_profile"), str):
91
+ return True
92
+ return False
93
+
94
+
83
95
  def merge_registry_fragments(fragments: Sequence[RegistryFragment]) -> RegistryFragment:
84
96
  groups: list[RegistryGroup] = []
85
97
 
@@ -243,11 +243,11 @@ def render_registry_group(group: RegistryGroup) -> Html:
243
243
  ],
244
244
  )
245
245
 
246
+ notes = f"<p><em>{h(group.notes)}</em></p>" if group.notes else ""
246
247
  return Html(
247
248
  f"<h3>{h(group.name)} ({h(group.role)})</h3>"
248
249
  f"{html}"
249
- f"<p><em>{h(group.notes)}</em></p>" if group.notes else
250
- f"<h3>{h(group.name)} ({h(group.role)})</h3>{html}"
250
+ f"{notes}"
251
251
  )
252
252
 
253
253
  def render_registry_fragment(fragment: RegistryFragment) -> Html:
@@ -12,7 +12,6 @@ from omop_semantics.schema.generated_models.omop_semantic_registry import (
12
12
  from pathlib import Path
13
13
  from dataclasses import dataclass
14
14
  from typing import Iterable, TypedDict, Optional, Set
15
- from pydantic import ValidationError
16
15
  from .renderers import render_semantic_object, render_profile_object, Html, tr, h, table, as_list, render_compiled_templates
17
16
  from .instance_loader import (
18
17
  load_profiles,
@@ -20,6 +19,7 @@ from .instance_loader import (
20
19
  merge_instance_files,
21
20
  merge_registry_fragments,
22
21
  load_symbol_module,
22
+ needs_profile_interpolation,
23
23
  )
24
24
  from omop_semantics.utils.paths import INSTANCE_DIR
25
25
 
@@ -368,8 +368,6 @@ class OmopRegistryRuntime:
368
368
  """
369
369
  if self._compiled_by_name is None:
370
370
  self.compile_index()
371
- if self._compiled_by_name is None:
372
- raise RuntimeError("No compiled templates available")
373
371
  return self._compiled_by_name[name]
374
372
 
375
373
  def get_runtime(self, name: str) -> RuntimeTemplate:
@@ -413,8 +411,6 @@ class OmopRegistryRuntime:
413
411
  """
414
412
  if self._compiled_by_role is None:
415
413
  self.compile_index()
416
- if self._compiled_by_role is None:
417
- raise RuntimeError("No compiled templates available")
418
414
  return self._compiled_by_role.get(role, [])
419
415
 
420
416
  def compile_all(self, role: str | None = None) -> list[CompiledTemplate]:
@@ -433,9 +429,7 @@ class OmopRegistryRuntime:
433
429
  """
434
430
  if self._compiled_by_name is None:
435
431
  self.compile_index()
436
- if self._compiled_by_name is None:
437
- raise RuntimeError("Template compilation failed")
438
- if role is None or self._compiled_by_role is None:
432
+ if role is None:
439
433
  return list(self._compiled_by_name.values())
440
434
  return self._compiled_by_role.get(role, [])
441
435
 
@@ -1053,6 +1047,8 @@ class OmopSemanticEngine:
1053
1047
  cls,
1054
1048
  registry_paths: Iterable[Path],
1055
1049
  profile_paths: Iterable[Path] = (),
1050
+ *,
1051
+ profiles_path: Path | None = None,
1056
1052
  ) -> "OmopSemanticEngine":
1057
1053
  """
1058
1054
  Construct a semantic engine from YAML registry and profile files.
@@ -1061,10 +1057,9 @@ class OmopSemanticEngine:
1061
1057
  Profile YAML files are loaded into a symbolic profile namespace for
1062
1058
  documentation and inspection.
1063
1059
 
1064
- The shipped registry instance files often store `cdm_profile` as a
1065
- string such as `observation_simple`. This method resolves those names
1066
- against the built-in `profiles.yaml` catalogue before validating and
1067
- compiling the registry fragment.
1060
+ Registry files that store ``cdm_profile`` as a string name (e.g.
1061
+ ``observation_simple``) are automatically expanded against the CDM
1062
+ profile catalogue before validation.
1068
1063
 
1069
1064
  Parameters
1070
1065
  ----------
@@ -1072,7 +1067,10 @@ class OmopSemanticEngine:
1072
1067
  Paths to registry fragment YAML files.
1073
1068
  profile_paths
1074
1069
  Optional paths to symbolic profile/group YAML files that should be
1075
- exposed through `profile_runtime`.
1070
+ exposed through ``profile_runtime``.
1071
+ profiles_path
1072
+ Path to the CDM profile catalogue YAML. Defaults to the shipped
1073
+ ``profiles.yaml`` when not provided.
1076
1074
 
1077
1075
  Returns
1078
1076
  -------
@@ -1081,17 +1079,16 @@ class OmopSemanticEngine:
1081
1079
  """
1082
1080
  fragments: list[RegistryFragment] = []
1083
1081
  profile_objects: dict[str, dict] = {}
1084
- cdm_profiles = load_profiles(INSTANCE_DIR / "profiles.yaml")
1082
+ cdm_profiles = load_profiles(profiles_path or INSTANCE_DIR / "profiles.yaml")
1085
1083
 
1086
1084
  for p in registry_paths:
1087
- try:
1088
- fragments.append(load_registry_fragment(p))
1089
- except ValidationError:
1090
- # Shipped registry instance files often refer to cdm_profile by
1091
- # name and need interpolation against the profile catalogue
1092
- # before they conform to RegistryFragment.
1085
+ if needs_profile_interpolation(p):
1086
+ # File uses string-named cdm_profile references; expand them
1087
+ # against the catalogue before validating as RegistryFragment.
1093
1088
  interpolated = merge_instance_files([p], cdm_profiles)
1094
1089
  fragments.append(RegistryFragment.model_validate(interpolated))
1090
+ else:
1091
+ fragments.append(load_registry_fragment(p))
1095
1092
 
1096
1093
  for p in profile_paths:
1097
1094
  profile_objects.update(load_symbol_module(p))
@@ -1,16 +1,3 @@
1
- from dataclasses import dataclass
2
- from omop_semantics.schema.generated_models.omop_named_sets import (
3
- OmopConcept,
4
- OmopGroup,
5
- OmopEnum,
6
- OmopSemanticObject,
7
- CDMSemanticUnits,
8
- CDMValueSet,
9
- CDMValueSets
10
- )
11
- from abc import ABC
12
- from .renderers import tr, table, h, Html
13
-
14
1
  """
15
2
  Runtime accessors for OMOP semantic value sets.
16
3
 
@@ -40,8 +27,20 @@ as a pure read-only semantic access layer.
40
27
  """
41
28
 
42
29
  from abc import ABC
30
+ from dataclasses import dataclass
43
31
  from typing import Mapping
44
32
 
33
+ from omop_semantics.schema.generated_models.omop_named_sets import (
34
+ OmopConcept,
35
+ OmopGroup,
36
+ OmopEnum,
37
+ OmopSemanticObject,
38
+ CDMSemanticUnits,
39
+ CDMValueSet,
40
+ CDMValueSets,
41
+ )
42
+ from .renderers import tr, table, h, Html
43
+
45
44
  class _RuntimeLabelledConcepts(ABC):
46
45
  """
47
46
  Thin shared base for runtime objects that expose a label -> concept_id mapping.
@@ -67,7 +66,10 @@ class _RuntimeLabelledConcepts(ABC):
67
66
  def __getattr__(self, label: str) -> int:
68
67
  if label.startswith("_"):
69
68
  raise AttributeError(label)
70
- return self._by_label[label]
69
+ try:
70
+ return self._by_label[label]
71
+ except KeyError:
72
+ raise AttributeError(label) from None
71
73
 
72
74
  def __repr__(self) -> str:
73
75
  labels = ", ".join(self.labels)
@@ -234,10 +236,10 @@ class RuntimeSemanticUnit:
234
236
  for value in labelled_item.values():
235
237
  try:
236
238
  return getattr(value, name)
237
- except AttributeError:
239
+ except KeyError:
238
240
  pass
239
241
 
240
- raise AttributeError(name)
242
+ raise KeyError(name)
241
243
 
242
244
  def __repr__(self) -> str:
243
245
  parts = []
@@ -404,7 +406,7 @@ def compile_valuesets(defs: CDMValueSets) -> RuntimeValueSets:
404
406
  for vs in defs.valuesets:
405
407
  members = {
406
408
  (unit.name or "[unlabelled]"): RuntimeSemanticUnit(unit)
407
- for unit in vs.members
409
+ for unit in vs.semantic_units
408
410
  }
409
411
 
410
412
  compiled[vs.valueset_name] = RuntimeValueSet(
@@ -485,7 +487,7 @@ def interpolate_valuesets(
485
487
  for vs in raw["valuesets"]:
486
488
  resolved_members: list[CDMSemanticUnits] = []
487
489
 
488
- for name in vs["members"]:
490
+ for name in vs["semantic_units"]:
489
491
  if name not in semantic_index:
490
492
  raise KeyError(f"Unknown semantic unit referenced in valuesets.yaml: {name}")
491
493
 
@@ -516,7 +518,7 @@ def interpolate_valuesets(
516
518
  valuesets.append(
517
519
  CDMValueSet(
518
520
  valueset_name=vs["name"],
519
- members=resolved_members,
521
+ semantic_units=resolved_members,
520
522
  )
521
523
  )
522
524
 
@@ -12,7 +12,7 @@ classes:
12
12
  CDMValueSet:
13
13
  slots:
14
14
  - valueset_name
15
- - members
15
+ - semantic_units
16
16
  CDMSemanticUnits:
17
17
  slots:
18
18
  - name
@@ -41,7 +41,7 @@ slots:
41
41
  required: true
42
42
  name:
43
43
  range: string
44
- members:
44
+ semantic_units:
45
45
  range: CDMSemanticUnits
46
46
  multivalued: true
47
47
  required: true
@@ -135,7 +135,7 @@ class OmopValueSet(OmopSemanticObject):
135
135
  'slot_usage': {'class_uri': {'equals_string': 'OmopValueSet',
136
136
  'name': 'class_uri'}}})
137
137
 
138
- members: list[CDMSemanticUnits] = Field(default=..., json_schema_extra = { "linkml_meta": {'domain_of': ['OmopValueSet', 'CDMValueSet']} })
138
+ members: Optional[list[OmopSemanticObject]] = Field(default=None, json_schema_extra = { "linkml_meta": {'domain_of': ['OmopValueSet']} })
139
139
  class_uri: Literal["OmopValueSet"] = Field(default=..., json_schema_extra = { "linkml_meta": {'domain_of': ['OmopSemanticObject'], 'equals_string': 'OmopValueSet'} })
140
140
  name: Optional[str] = Field(default=None, json_schema_extra = { "linkml_meta": {'domain_of': ['OmopSemanticObject', 'CDMSemanticUnits']} })
141
141
  notes: Optional[str] = Field(default=None, json_schema_extra = { "linkml_meta": {'domain_of': ['OmopSemanticObject']} })
@@ -163,7 +163,7 @@ class CDMValueSet(ConfiguredBaseModel):
163
163
  linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({'from_schema': 'https://example.org/omop_semantics/cdm_value_sets'})
164
164
 
165
165
  valueset_name: str = Field(default=..., json_schema_extra = { "linkml_meta": {'domain_of': ['CDMValueSet']} })
166
- members: list[CDMSemanticUnits] = Field(default=..., json_schema_extra = { "linkml_meta": {'domain_of': ['OmopValueSet', 'CDMValueSet']} })
166
+ semantic_units: list[CDMSemanticUnits] = Field(default=..., json_schema_extra = { "linkml_meta": {'domain_of': ['CDMValueSet']} })
167
167
 
168
168
 
169
169
  class CDMSemanticUnits(ConfiguredBaseModel):
@@ -1,41 +1,41 @@
1
1
  valuesets:
2
2
  - name: genomic
3
- members:
3
+ semantic_units:
4
4
  - genomic_value_group
5
5
  - genomic_mapped_types
6
6
  - name: modifiers
7
- members:
7
+ semantic_units:
8
8
  - modifier_fields
9
9
  - modifier_tables
10
10
  - name: types
11
- members:
11
+ semantic_units:
12
12
  - treatment_episode_types
13
13
  - disease_episode_types
14
14
  - source_types
15
15
  - name: treatment_modifiers
16
- members:
16
+ semantic_units:
17
17
  - treatment_intent
18
18
  - treatment_modality
19
19
  - treatment_modifier_values
20
20
  - name: condition_modifiers
21
- members:
21
+ semantic_units:
22
22
  - condition_modifier_values
23
23
  - tumor_grade
24
24
  - numeric_condition_modifiers
25
25
  - condition_status
26
26
  - name: nlp
27
- members:
27
+ semantic_units:
28
28
  - document_type
29
29
  - document_encoding
30
30
  - document_language
31
31
  - name: cancer_procedures
32
- members:
32
+ semantic_units:
33
33
  - cancer_consult_types
34
34
  - encounter_provider_specialty
35
35
  - cancer_procedure_types
36
36
  - procedures_by_location
37
37
  - name: measurements_numeric
38
- members:
38
+ semantic_units:
39
39
  - body_size_units
40
40
  - body_size_measurements
41
41
  - lab_measurements
@@ -43,19 +43,19 @@ valuesets:
43
43
  - proms_numeric
44
44
  - performance_status_measurements
45
45
  - name: staging
46
- members:
46
+ semantic_units:
47
47
  - t_stage_concepts
48
48
  - n_stage_concepts
49
49
  - m_stage_concepts
50
50
  - group_stage_concepts
51
51
  - stage_edition
52
52
  - name: visits
53
- members:
53
+ semantic_units:
54
54
  - visit_modalities
55
55
  - name: observations
56
- members:
56
+ semantic_units:
57
57
  - demography_concepts
58
58
  - sact_concepts
59
59
  - name: unknowns
60
- members:
60
+ semantic_units:
61
61
  - unknowns
@@ -20,7 +20,9 @@ This is a stable public surface. External consumers should import from
20
20
  'mapping_failed'
21
21
  """
22
22
 
23
+ from collections.abc import Mapping
23
24
  from dataclasses import dataclass
25
+ from types import MappingProxyType
24
26
  from typing import Literal
25
27
 
26
28
 
@@ -55,7 +57,7 @@ class UnknownValue:
55
57
  reason: UnknownReason | None = None
56
58
 
57
59
 
58
- UNKNOWN: dict[str, UnknownValue] = {
60
+ UNKNOWN: Mapping[str, UnknownValue] = MappingProxyType({
59
61
  "generic": UnknownValue(4129922, "Unknown", "missing"),
60
62
  "gender": UnknownValue(4214687, "Gender Unknown", "missing"),
61
63
  "condition": UnknownValue(44790729, "Unknown problem", "mapping_failed"),
@@ -66,6 +68,6 @@ UNKNOWN: dict[str, UnknownValue] = {
66
68
  "stage_edition": UnknownValue(1634449, "8th", "default_value"),
67
69
  "therapeutic_regimen": UnknownValue(4207655, "prescription of therapeutic regimen", "mapping_failed"),
68
70
  "drug_trial": UnknownValue(4207655, "clinical drug trial", "ambiguous"),
69
- }
71
+ })
70
72
 
71
73
  __all__ = ["UnknownReason", "UnknownValue", "UNKNOWN"]
@@ -1,139 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: omop-semantics
3
- Version: 0.2.1
4
- Summary: Define, validate, and use schema-backed semantic conventions for OMOP CDM
5
- Author: gkennos
6
- Author-email: gkennos <georgina.kennedy@unsw.edu.au>
7
- Requires-Dist: ipykernel>=7.2.0
8
- Requires-Dist: linkml>=1.11.1
9
- Requires-Dist: linkml-runtime>=1.11.1
10
- Requires-Dist: python-dotenv>=1.2.2
11
- Requires-Dist: ruamel-yaml>=0.18.17
12
- Requires-Dist: typing-extensions>=4.15.0
13
- Requires-Dist: urllib3>=2.7.0
14
- Requires-Dist: ipython>=8.0 ; extra == 'dev'
15
- Requires-Dist: pytest>=7.0 ; extra == 'dev'
16
- Requires-Dist: pytest-cov>=4.0 ; extra == 'dev'
17
- Requires-Dist: mypy>=1.8 ; extra == 'dev'
18
- Requires-Dist: ruff>=0.4 ; extra == 'dev'
19
- Requires-Dist: rich>=13.0 ; extra == 'dev'
20
- Requires-Dist: tornado>=6.5.5 ; extra == 'dev'
21
- Requires-Dist: pygments>=2.20.0 ; extra == 'dev'
22
- Requires-Dist: mkdocs-material>=9.7.6 ; extra == 'docs'
23
- Requires-Dist: mkdocstrings-python>=2.0.1 ; extra == 'docs'
24
- Requires-Dist: mkdocs>=1.6.1 ; extra == 'docs'
25
- Requires-Dist: mkdocs-mermaid2-plugin>=1.2.3 ; extra == 'docs'
26
- Requires-Dist: pygments>=2.20.0 ; extra == 'docs'
27
- Requires-Python: >=3.12
28
- Project-URL: Homepage, https://australiancancerdatanetwork.github.io/omop-semantics/
29
- Project-URL: Repository, https://github.com/AustralianCancerDataNetwork/omop-semantics
30
- Project-URL: Issues, https://github.com/AustralianCancerDataNetwork/omop-semantics/issues
31
- Provides-Extra: dev
32
- Provides-Extra: docs
33
- Description-Content-Type: text/markdown
34
-
35
- # omop_semantics
36
-
37
- **omop_semantics** is a Python library for defining and managing **semantic conventions on top of OMOP CDM**.
38
-
39
- It lets you describe conventions in code
40
-
41
- - which OMOP concepts you want to have on hand as named key concepts to improve ergonomics in analytic code,
42
- - how they are grouped,
43
- - what roles they play
44
- - and provide profiles to render these targets uniformly into CDM tables.
45
-
46
- The goal is to make these conventions **explicit, versioned, and reusable**, instead of being buried in code, SQL, or documentation. They are also extensible so that you can add opinionated layers on top of default specifications that may be relevant in a domain-specific context only.
47
-
48
- ---
49
-
50
- ## Current structure
51
-
52
- The library has three user-facing surfaces:
53
-
54
- - **Value-set runtime**
55
- For stable named ids and ergonomic downstream access such as
56
- `from omop_semantics.runtime.default_valuesets import runtime`.
57
-
58
- - **Template/profile runtime**
59
- For working with semantic templates, compiled template views, and CDM row
60
- shapes via `OmopSemanticEngine`.
61
-
62
- - **Fallback concepts**
63
- For canonical "unknown", "not recorded", and default concepts via
64
- `from omop_semantics.unknowns import UNKNOWN`.
65
-
66
- If you are starting new downstream code today:
67
-
68
- 1. use `runtime.default_valuesets` when you need stable named concept ids,
69
- 2. use `OmopSemanticEngine` when you need templates, profiles, or profile
70
- groups,
71
- 3. use `omop_semantics.unknowns` when you need standard fallback concepts and
72
- their reason codes.
73
-
74
- ---
75
-
76
- ## Key ideas
77
-
78
- - **Human-authored**
79
- Semantic rules and concept groups are written in YAML and validated with schemas.
80
-
81
- - **Portable**
82
- No database or graph store required.
83
-
84
- - **Versionable**
85
- Conventions can evolve over time and be tracked in git.
86
-
87
- - **Integrates with pipelines**
88
- Can drive ETL logic, validation, and documentation so they stay in sync.
89
-
90
- ---
91
-
92
- ## Typical workflow
93
-
94
- 1. **Define a schema**
95
- Describes what kinds of semantic objects and roles exist (e.g. staging,
96
- modifiers).
97
-
98
- 2. **Write YAML instances**
99
- Lists actual OMOP concepts, profiles, and templates used in your project.
100
-
101
- 3. **Load the runtime surface you need**
102
- Use value sets for named ids, or the semantic engine for template/profile
103
- work.
104
-
105
- 4. **Use it in code**
106
- For validation, cohort logic, ETL constraints, or documentation.
107
-
108
- ---
109
-
110
- ## When should you use this?
111
-
112
- Use **omop_semantics** if you:
113
-
114
- - have project-specific rules about which OMOP concepts are valid,
115
- - need consistent concept groupings across ETL and analytics,
116
- - want semantic conventions to be explicit, testable, and versioned,
117
- - are working in domains like oncology where OMOP alone is too permissive.
118
-
119
- ---
120
-
121
- ## Docs map
122
-
123
- - `docs/usage.md`
124
- Recommended loading paths for value sets, templates/profiles, fallback
125
- concepts, and lower-level helpers.
126
-
127
- - `docs/data-model.md`
128
- The conceptual distinction between profiles, profile groups, templates, and
129
- semantic objects.
130
-
131
- - `docs/schema-and-instances.md`
132
- Canonical authoring assets and how the shipped schema/instance files are
133
- organized.
134
-
135
- - `docs/internals.md`
136
- Repo structure, public runtime surfaces, and load-time behavior.
137
-
138
- - `docs/unknowns.md`
139
- Canonical fallback concepts and how to use their reason codes.
@@ -1,105 +0,0 @@
1
- # omop_semantics
2
-
3
- **omop_semantics** is a Python library for defining and managing **semantic conventions on top of OMOP CDM**.
4
-
5
- It lets you describe conventions in code
6
-
7
- - which OMOP concepts you want to have on hand as named key concepts to improve ergonomics in analytic code,
8
- - how they are grouped,
9
- - what roles they play
10
- - and provide profiles to render these targets uniformly into CDM tables.
11
-
12
- The goal is to make these conventions **explicit, versioned, and reusable**, instead of being buried in code, SQL, or documentation. They are also extensible so that you can add opinionated layers on top of default specifications that may be relevant in a domain-specific context only.
13
-
14
- ---
15
-
16
- ## Current structure
17
-
18
- The library has three user-facing surfaces:
19
-
20
- - **Value-set runtime**
21
- For stable named ids and ergonomic downstream access such as
22
- `from omop_semantics.runtime.default_valuesets import runtime`.
23
-
24
- - **Template/profile runtime**
25
- For working with semantic templates, compiled template views, and CDM row
26
- shapes via `OmopSemanticEngine`.
27
-
28
- - **Fallback concepts**
29
- For canonical "unknown", "not recorded", and default concepts via
30
- `from omop_semantics.unknowns import UNKNOWN`.
31
-
32
- If you are starting new downstream code today:
33
-
34
- 1. use `runtime.default_valuesets` when you need stable named concept ids,
35
- 2. use `OmopSemanticEngine` when you need templates, profiles, or profile
36
- groups,
37
- 3. use `omop_semantics.unknowns` when you need standard fallback concepts and
38
- their reason codes.
39
-
40
- ---
41
-
42
- ## Key ideas
43
-
44
- - **Human-authored**
45
- Semantic rules and concept groups are written in YAML and validated with schemas.
46
-
47
- - **Portable**
48
- No database or graph store required.
49
-
50
- - **Versionable**
51
- Conventions can evolve over time and be tracked in git.
52
-
53
- - **Integrates with pipelines**
54
- Can drive ETL logic, validation, and documentation so they stay in sync.
55
-
56
- ---
57
-
58
- ## Typical workflow
59
-
60
- 1. **Define a schema**
61
- Describes what kinds of semantic objects and roles exist (e.g. staging,
62
- modifiers).
63
-
64
- 2. **Write YAML instances**
65
- Lists actual OMOP concepts, profiles, and templates used in your project.
66
-
67
- 3. **Load the runtime surface you need**
68
- Use value sets for named ids, or the semantic engine for template/profile
69
- work.
70
-
71
- 4. **Use it in code**
72
- For validation, cohort logic, ETL constraints, or documentation.
73
-
74
- ---
75
-
76
- ## When should you use this?
77
-
78
- Use **omop_semantics** if you:
79
-
80
- - have project-specific rules about which OMOP concepts are valid,
81
- - need consistent concept groupings across ETL and analytics,
82
- - want semantic conventions to be explicit, testable, and versioned,
83
- - are working in domains like oncology where OMOP alone is too permissive.
84
-
85
- ---
86
-
87
- ## Docs map
88
-
89
- - `docs/usage.md`
90
- Recommended loading paths for value sets, templates/profiles, fallback
91
- concepts, and lower-level helpers.
92
-
93
- - `docs/data-model.md`
94
- The conceptual distinction between profiles, profile groups, templates, and
95
- semantic objects.
96
-
97
- - `docs/schema-and-instances.md`
98
- Canonical authoring assets and how the shipped schema/instance files are
99
- organized.
100
-
101
- - `docs/internals.md`
102
- Repo structure, public runtime surfaces, and load-time behavior.
103
-
104
- - `docs/unknowns.md`
105
- Canonical fallback concepts and how to use their reason codes.