omop-constructs 0.2.3__tar.gz → 0.2.5__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 (42) hide show
  1. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/PKG-INFO +2 -2
  2. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/pyproject.toml +2 -2
  3. omop_constructs-0.2.5/src/omop_constructs/alchemy/conditions/__init__.py +7 -0
  4. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/episodes/__init__.py +3 -3
  5. omop_constructs-0.2.5/src/omop_constructs/alchemy/episodes/condition_episode_mv.py +123 -0
  6. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/episodes/episode_factories.py +12 -33
  7. omop_constructs-0.2.5/src/omop_constructs/alchemy/episodes/episode_joins.py +34 -0
  8. omop_constructs-0.2.3/src/omop_constructs/alchemy/episodes/condition_episode_mv.py +0 -65
  9. omop_constructs-0.2.3/src/omop_constructs/alchemy/episodes/episode_joins.py +0 -22
  10. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/README.md +0 -0
  11. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/__init__.py +0 -0
  12. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/__init__.py +0 -0
  13. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/adapters.py +0 -0
  14. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/concepts/__init__.py +0 -0
  15. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/concepts/concept_mappers.py +0 -0
  16. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/concepts/concept_standardisation.py +0 -0
  17. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/conditions/condition_joins.py +0 -0
  18. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/conditions/condition_mappers.py +0 -0
  19. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/modifiers/__init__.py +0 -0
  20. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/modifiers/condition_modifier_join.py +0 -0
  21. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/modifiers/condition_modifier_mv.py +0 -0
  22. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/modifiers/modifier_factories.py +0 -0
  23. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/modifiers/modifier_joins.py +0 -0
  24. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/modifiers/modifier_mappers.py +0 -0
  25. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/alchemy/source_factories.py +0 -0
  26. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/core/__init__.py +0 -0
  27. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/core/base.py +0 -0
  28. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/core/constructs.py +0 -0
  29. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/core/ddl.py +0 -0
  30. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/core/errors.py +0 -0
  31. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/core/materialized.py +0 -0
  32. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/core/plan.py +0 -0
  33. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/core/registry.py +0 -0
  34. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/py.typed +0 -0
  35. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/semantics/__init__.py +0 -0
  36. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/semantics/adapters.py +0 -0
  37. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/semantics/lookups.py +0 -0
  38. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/semantics/runtime_lookups.py +0 -0
  39. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/services/__init__.py +0 -0
  40. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/services/concept.py +0 -0
  41. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/services/person.py +0 -0
  42. {omop_constructs-0.2.3 → omop_constructs-0.2.5}/src/omop_constructs/typing.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: omop-constructs
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: For building complex constructs on top of the omop-alchemy library.
5
5
  Author: Georgie Kennedy
6
6
  Author-email: Georgie Kennedy <georgina.kennedy@unsw.edu.au>
7
7
  Requires-Dist: omop-alchemy>=0.5.12
8
- Requires-Dist: omop-semantics>=0.1.8
8
+ Requires-Dist: omop-semantics>=0.1.10
9
9
  Requires-Dist: psycopg2-binary>=2.9.11
10
10
  Requires-Dist: sqlalchemy>=2.0.45
11
11
  Requires-Dist: urllib3>=2.6.3
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "omop-constructs"
3
- version = "0.2.3"
3
+ version = "0.2.5"
4
4
  description = "For building complex constructs on top of the omop-alchemy library."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -9,7 +9,7 @@ authors = [
9
9
  requires-python = ">=3.12"
10
10
  dependencies = [
11
11
  "omop-alchemy>=0.5.12",
12
- "omop-semantics>=0.1.8",
12
+ "omop-semantics>=0.1.10",
13
13
  "psycopg2-binary>=2.9.11",
14
14
  "sqlalchemy>=2.0.45",
15
15
  "urllib3>=2.6.3",
@@ -0,0 +1,7 @@
1
+ from .condition_joins import condition_window
2
+ from .condition_mappers import Condition_Window
3
+
4
+ __all__ = [
5
+ "condition_window",
6
+ "Condition_Window"
7
+ ]
@@ -1,13 +1,13 @@
1
- from .condition_episode_mv import OverarchingDiseaseEpisodeMV
1
+ from .condition_episode_mv import OverarchingDiseaseEpisodeMV, TreatmentRegimenCycleMV
2
2
  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
4
4
 
5
5
  __all__ = [
6
6
  "OverarchingDiseaseEpisodeMV",
7
+ "TreatmentRegimenCycleMV",
7
8
  "episode_of_care_select",
8
9
  "disease_extent_select",
9
10
  "overarching_disease_episode",
10
11
  "get_episode_query",
11
- "require_condition_anchor",
12
12
  "get_episode_hierarchy_query",
13
13
  ]
@@ -0,0 +1,123 @@
1
+ import sqlalchemy as sa
2
+ import sqlalchemy.orm as so
3
+ from orm_loader.helpers import Base
4
+ from datetime import date
5
+ from typing import Optional
6
+ from .episode_joins import overarching_disease_episode, treatment_regimen_with_cycles
7
+ from ...core.materialized import MaterializedViewMixin
8
+ from ...core.constructs import register_construct
9
+
10
+ @register_construct
11
+ class OverarchingDiseaseEpisodeMV(
12
+ MaterializedViewMixin,
13
+ Base,
14
+ ):
15
+ """
16
+ Materialized view representing overarching disease episodes
17
+ with optional child disease-extent episodes.
18
+
19
+ One row per (episode_of_care, disease_extent_episode?) pair.
20
+ """
21
+
22
+ __mv_name__ = "overarching_disease_episode_mv"
23
+ __mv_select__ = overarching_disease_episode.select()
24
+ __mv_index__ = "disease_episode_id"
25
+ __deps__ = ()
26
+ __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)
44
+
45
+ def __repr__(self) -> str:
46
+ if self.has_extent:
47
+ return (
48
+ f"<OverarchingDiseaseEpisode "
49
+ f"{self.disease_episode_id} -> extent {self.extent_episode_id}>"
50
+ )
51
+ return f"<OverarchingDiseaseEpisode {self.disease_episode_id}>"
52
+
53
+ @property
54
+ def has_extent(self) -> bool:
55
+ return self.extent_episode_id is not None
56
+
57
+ @property
58
+ def disease_interval(self):
59
+ return (self.disease_episode_start_date, self.disease_episode_end_date)
60
+
61
+ @property
62
+ def extent_interval(self):
63
+ if self.extent_episode_id is None:
64
+ return None
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)
@@ -1,6 +1,7 @@
1
1
  import sqlalchemy as sa
2
2
  import sqlalchemy.orm as so
3
- from typing import Iterable
3
+ from sqlalchemy.sql import ColumnElement
4
+ from typing import Iterable, Any
4
5
  from omop_alchemy.cdm.model import (
5
6
  Condition_Occurrence,
6
7
  Episode,
@@ -42,28 +43,6 @@ def get_episode_query(
42
43
  .subquery(name=name)
43
44
  )
44
45
 
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
-
67
46
 
68
47
  def get_episode_hierarchy_query(
69
48
  parent_episode_subq: sa.Subquery,
@@ -76,18 +55,18 @@ def get_episode_hierarchy_query(
76
55
 
77
56
  return (
78
57
  sa.select(
79
- parent_episode_subq.c.episode_id.label("parent_episode_id"),
58
+ parent_episode_subq.c.episode_id.label("disease_episode_id"),
80
59
  parent_episode_subq.c.person_id.label("person_id"),
81
- parent_episode_subq.c.episode_concept_id.label("parent_episode_concept_id"),
82
- parent_episode_subq.c.episode_label.label("parent_episode_label"),
83
- parent_episode_subq.c.episode_start_date.label("parent_start_date"),
84
- parent_episode_subq.c.episode_end_date.label("parent_end_date"),
60
+ parent_episode_subq.c.episode_concept_id.label("disease_episode_concept_id"),
61
+ parent_episode_subq.c.episode_label.label("disease_episode_label"),
62
+ parent_episode_subq.c.episode_start_date.label("disease_episode_start_date"),
63
+ parent_episode_subq.c.episode_end_date.label("disease_episode_end_date"),
85
64
 
86
- child_episode_subq.c.episode_id.label("child_episode_id"),
87
- child_episode_subq.c.episode_concept_id.label("child_episode_concept_id"),
88
- child_episode_subq.c.episode_label.label("child_episode_label"),
89
- child_episode_subq.c.episode_start_date.label("child_start_date"),
90
- child_episode_subq.c.episode_end_date.label("child_end_date"),
65
+ child_episode_subq.c.episode_id.label("extent_episode_id"),
66
+ child_episode_subq.c.episode_concept_id.label("extent_episode_concept_id"),
67
+ child_episode_subq.c.episode_label.label("extent_episode_label"),
68
+ child_episode_subq.c.episode_start_date.label("extent_start_date"),
69
+ child_episode_subq.c.episode_end_date.label("extent_end_date"),
91
70
  )
92
71
  .join(
93
72
  child_episode_subq,
@@ -0,0 +1,34 @@
1
+ from .episode_factories import get_episode_query, get_episode_hierarchy_query
2
+ from omop_semantics.runtime.default_valuesets import runtime
3
+
4
+ episode_of_care_select = get_episode_query(
5
+ [runtime.types.disease_episode_types.episode_of_care], # type: ignore
6
+ name="episode_of_care"
7
+ )
8
+
9
+ disease_extent_select = get_episode_query(
10
+ [runtime.types.disease_episode_types.disease_progression, runtime.types.disease_episode_types.metastatic], # type: ignore
11
+ name="disease_extent"
12
+ )
13
+
14
+ overarching_disease_episode = get_episode_hierarchy_query(
15
+ episode_of_care_select,
16
+ disease_extent_select,
17
+ 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",
34
+ )
@@ -1,65 +0,0 @@
1
- import sqlalchemy as sa
2
- from orm_loader.helpers import Base
3
- from .episode_joins import overarching_disease_episode
4
- from ...core.materialized import MaterializedViewMixin
5
- from ...core.constructs import register_construct
6
-
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
-
26
- @register_construct
27
- class OverarchingDiseaseEpisodeMV(
28
- OverarchingDiseaseEpisodeCols,
29
- MaterializedViewMixin,
30
- Base,
31
- ):
32
- """
33
- Materialized view representing overarching disease episodes
34
- with optional child disease-extent episodes.
35
-
36
- One row per (episode_of_care, disease_extent_episode?) pair.
37
- """
38
-
39
- __mv_name__ = "overarching_disease_episode_mv"
40
- __mv_select__ = overarching_disease_episode.select()
41
- __mv_index__ = "disease_episode_id"
42
- __deps__ = ()
43
- __tablename__ = __mv_name__
44
-
45
- def __repr__(self) -> str:
46
- if self.has_extent:
47
- return (
48
- f"<OverarchingDiseaseEpisode "
49
- f"{self.disease_episode_id} -> extent {self.extent_episode_id}>"
50
- )
51
- return f"<OverarchingDiseaseEpisode {self.disease_episode_id}>"
52
-
53
- @property
54
- def has_extent(self) -> bool:
55
- return self.extent_episode_id is not None
56
-
57
- @property
58
- def disease_interval(self):
59
- return (self.disease_episode_start_date, self.disease_episode_end_date)
60
-
61
- @property
62
- def extent_interval(self):
63
- if self.extent_episode_id is None:
64
- return None
65
- return (self.extent_start_date, self.extent_end_date)
@@ -1,22 +0,0 @@
1
- from .episode_factories import require_condition_anchor, get_episode_query, get_episode_hierarchy_query
2
- from omop_semantics.runtime.default_valuesets import runtime
3
-
4
- episode_of_care_select = require_condition_anchor(
5
- get_episode_query(
6
- [runtime.episodes.disease_episode_types.episode_of_care], # type: ignore
7
- name="episode_of_care"
8
- )
9
- )
10
-
11
- disease_extent_select = require_condition_anchor(
12
- get_episode_query(
13
- [runtime.episodes.disease_episode_types.metastatic, runtime.episodes.disease_episode_types.disease_progression], # type: ignore
14
- name="disease_extent"
15
- )
16
- )
17
-
18
- overarching_disease_episode = get_episode_hierarchy_query(
19
- episode_of_care_select,
20
- disease_extent_select,
21
- name="overarching_disease_episode",
22
- )