Skip to content

Commit 19b9704

Browse files
committed
treatment eps
1 parent d5bb2de commit 19b9704

4 files changed

Lines changed: 104 additions & 55 deletions

File tree

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
from .condition_episode_mv import OverarchingDiseaseEpisodeMV
1+
from .condition_episode_mv import OverarchingDiseaseEpisodeMV, TreatmentRegimenCycleMV
22
from .episode_joins import episode_of_care_select, disease_extent_select, overarching_disease_episode
3-
from .episode_factories import get_episode_query, require_condition_anchor, get_episode_hierarchy_query
3+
from .episode_factories import get_episode_query, get_episode_hierarchy_query
44

55
__all__ = [
66
"OverarchingDiseaseEpisodeMV",
7+
"TreatmentRegimenCycleMV",
78
"episode_of_care_select",
89
"disease_extent_select",
910
"overarching_disease_episode",
1011
"get_episode_query",
11-
"require_condition_anchor",
1212
"get_episode_hierarchy_query",
1313
]
Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,14 @@
11
import sqlalchemy as sa
2+
import sqlalchemy.orm as so
23
from orm_loader.helpers import Base
3-
from .episode_joins import overarching_disease_episode
4+
from datetime import date
5+
from typing import Optional
6+
from .episode_joins import overarching_disease_episode, treatment_regimen_with_cycles
47
from ...core.materialized import MaterializedViewMixin
58
from ...core.constructs import register_construct
69

7-
class OverarchingDiseaseEpisodeCols:
8-
__table_args__ = {"extend_existing": True}
9-
10-
disease_episode_id = sa.Column(sa.Integer, primary_key=True)
11-
person_id = sa.Column(sa.Integer)
12-
13-
disease_episode_concept_id = sa.Column(sa.Integer)
14-
disease_episode_label = sa.Column(sa.String)
15-
16-
disease_episode_start_date = sa.Column(sa.Date)
17-
disease_episode_end_date = sa.Column(sa.Date)
18-
19-
extent_episode_id = sa.Column(sa.Integer)
20-
extent_episode_concept_id = sa.Column(sa.Integer)
21-
extent_episode_label = sa.Column(sa.String)
22-
23-
extent_start_date = sa.Column(sa.Date)
24-
extent_end_date = sa.Column(sa.Date)
25-
2610
@register_construct
2711
class OverarchingDiseaseEpisodeMV(
28-
OverarchingDiseaseEpisodeCols,
2912
MaterializedViewMixin,
3013
Base,
3114
):
@@ -41,6 +24,23 @@ class OverarchingDiseaseEpisodeMV(
4124
__mv_index__ = "disease_episode_id"
4225
__deps__ = ()
4326
__tablename__ = __mv_name__
27+
__table_args__ = {"extend_existing": True}
28+
29+
disease_episode_id: so.Mapped[int] = so.mapped_column(sa.Integer, primary_key=True)
30+
person_id: so.Mapped[int] = so.mapped_column(sa.Integer)
31+
32+
disease_episode_concept_id: so.Mapped[int] = so.mapped_column(sa.Integer)
33+
disease_episode_label: so.Mapped[str] = so.mapped_column(sa.String)
34+
35+
disease_episode_start_date: so.Mapped[date] = so.mapped_column(sa.Date)
36+
disease_episode_end_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date, nullable=True)
37+
38+
extent_episode_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
39+
extent_episode_concept_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
40+
extent_episode_label: so.Mapped[Optional[str]] = so.mapped_column(sa.String, nullable=True)
41+
42+
extent_start_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date, nullable=True)
43+
extent_end_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date, nullable=True)
4444

4545
def __repr__(self) -> str:
4646
if self.has_extent:
@@ -62,4 +62,62 @@ def disease_interval(self):
6262
def extent_interval(self):
6363
if self.extent_episode_id is None:
6464
return None
65-
return (self.extent_start_date, self.extent_end_date)
65+
return (self.extent_start_date, self.extent_end_date)
66+
67+
@register_construct
68+
class TreatmentRegimenCycleMV(
69+
MaterializedViewMixin,
70+
Base,
71+
):
72+
"""
73+
Materialized view representing treatment regimens
74+
with optional child treatment cycles.
75+
76+
One row per (treatment_regimen, treatment_cycle?) pair.
77+
"""
78+
79+
__mv_name__ = "treatment_regimen_cycle_mv"
80+
__mv_select__ = treatment_regimen_with_cycles.select()
81+
__mv_index__ = "regimen_episode_id"
82+
__deps__ = ()
83+
__tablename__ = __mv_name__
84+
__table_args__ = {"extend_existing": True}
85+
86+
regimen_episode_id: so.Mapped[int] = so.mapped_column(sa.Integer, primary_key=True)
87+
person_id: so.Mapped[int] = so.mapped_column(sa.Integer)
88+
89+
regimen_episode_concept_id: so.Mapped[int] = so.mapped_column(sa.Integer)
90+
regimen_episode_label: so.Mapped[str] = so.mapped_column(sa.String)
91+
92+
regimen_start_date: so.Mapped[date] = so.mapped_column(sa.Date)
93+
regimen_end_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date, nullable=True)
94+
95+
cycle_episode_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
96+
cycle_episode_concept_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
97+
cycle_episode_label: so.Mapped[Optional[str]] = so.mapped_column(sa.String, nullable=True)
98+
99+
cycle_start_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date, nullable=True)
100+
cycle_end_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date, nullable=True)
101+
102+
103+
def __repr__(self) -> str:
104+
if self.has_cycle:
105+
return (
106+
f"<TreatmentRegimenCycle "
107+
f"{self.regimen_episode_id} -> cycle {self.cycle_episode_id}>"
108+
)
109+
return f"<TreatmentRegimenCycle {self.regimen_episode_id}>"
110+
111+
@property
112+
def has_cycle(self) -> bool:
113+
return self.cycle_episode_id is not None
114+
115+
@property
116+
def regimen_interval(self):
117+
return (self.regimen_start_date, self.regimen_end_date)
118+
119+
@property
120+
def cycle_interval(self):
121+
if self.cycle_episode_id is None:
122+
return None
123+
return (self.cycle_start_date, self.cycle_end_date)

src/omop_constructs/alchemy/episodes/episode_factories.py

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import sqlalchemy as sa
22
import sqlalchemy.orm as so
3-
from typing import Iterable
3+
from sqlalchemy.sql import ColumnElement
4+
from typing import Iterable, Any
45
from omop_alchemy.cdm.model import (
56
Condition_Occurrence,
67
Episode,
@@ -42,28 +43,6 @@ def get_episode_query(
4243
.subquery(name=name)
4344
)
4445

45-
def require_condition_anchor(
46-
episode_subq: sa.Subquery,
47-
name: str | None = None,
48-
) -> sa.Subquery:
49-
"""
50-
Filters an Episode subquery to only episodes with at least one
51-
linked Condition_Occurrence via Episode_Event.
52-
"""
53-
exists_condition = sa.exists().where(
54-
sa.and_(
55-
Episode_Event.episode_id == episode_subq.c.episode_id,
56-
Episode_Event.episode_event_field_concept_id
57-
== runtime.modifiers.modifier_fields.condition_occurrence_id,
58-
Condition_Occurrence.condition_occurrence_id == Episode_Event.event_id,
59-
)
60-
)
61-
return (
62-
sa.select(*episode_subq.c)
63-
.where(exists_condition)
64-
.subquery(name=name or episode_subq.name)
65-
)
66-
6746

6847
def get_episode_hierarchy_query(
6948
parent_episode_subq: sa.Subquery,
Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
1-
from .episode_factories import require_condition_anchor, get_episode_query, get_episode_hierarchy_query
1+
from .episode_factories import get_episode_query, get_episode_hierarchy_query
22
from omop_semantics.runtime.default_valuesets import runtime
33

4-
episode_of_care_select = require_condition_anchor(
5-
get_episode_query(
4+
episode_of_care_select = get_episode_query(
65
[runtime.types.disease_episode_types.episode_of_care], # type: ignore
76
name="episode_of_care"
87
)
9-
)
108

11-
disease_extent_select = require_condition_anchor(
12-
get_episode_query(
9+
disease_extent_select = get_episode_query(
1310
[runtime.types.disease_episode_types.disease_progression, runtime.types.disease_episode_types.metastatic], # type: ignore
1411
name="disease_extent"
1512
)
16-
)
1713

1814
overarching_disease_episode = get_episode_hierarchy_query(
1915
episode_of_care_select,
2016
disease_extent_select,
2117
name="overarching_disease_episode",
18+
)
19+
20+
treatment_regimen_select = get_episode_query(
21+
[runtime.types.treatment_episode_types.treatment_regimen], # type: ignore
22+
name="treatment_regimen",
23+
)
24+
25+
treatment_cycle_select = get_episode_query(
26+
[runtime.types.treatment_episode_types.treatment_cycle], # type: ignore
27+
name="treatment_cycle",
28+
)
29+
30+
treatment_regimen_with_cycles = get_episode_hierarchy_query(
31+
parent_episode_subq=treatment_regimen_select,
32+
child_episode_subq=treatment_cycle_select,
33+
name="treatment_regimen_with_cycles",
2234
)

0 commit comments

Comments
 (0)