omop-constructs 0.3.4__tar.gz → 0.3.6__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 (55) hide show
  1. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/PKG-INFO +1 -1
  2. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/pyproject.toml +1 -1
  3. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/concepts/concept_standardisation.py +1 -2
  4. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/episodes/surgical_joins.py +0 -1
  5. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/events/__init__.py +10 -1
  6. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/events/dx_linked_event_mv.py +0 -3
  7. omop_constructs-0.3.6/src/omop_constructs/alchemy/events/dx_linked_obs_mv.py +51 -0
  8. omop_constructs-0.3.6/src/omop_constructs/alchemy/events/dx_linked_procedure_mv.py +44 -0
  9. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/events/event_factories.py +117 -2
  10. omop_constructs-0.3.6/src/omop_constructs/alchemy/events/observation_queries.py +16 -0
  11. omop_constructs-0.3.6/src/omop_constructs/alchemy/events/procedure_queries.py +12 -0
  12. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/README.md +0 -0
  13. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/__init__.py +0 -0
  14. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/__init__.py +0 -0
  15. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/adapters.py +0 -0
  16. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/concepts/__init__.py +0 -0
  17. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/concepts/concept_mappers.py +0 -0
  18. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/conditions/__init__.py +0 -0
  19. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/conditions/condition_joins.py +0 -0
  20. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/conditions/condition_mappers.py +0 -0
  21. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/demography/__init__.py +0 -0
  22. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/demography/demography_matview.py +0 -0
  23. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/demography/demography_queries.py +0 -0
  24. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/episodes/__init__.py +0 -0
  25. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/episodes/condition_episode_mv.py +0 -0
  26. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/episodes/condition_episode_objects.py +0 -0
  27. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/episodes/episode_factories.py +0 -0
  28. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/episodes/episode_joins.py +0 -0
  29. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/episodes/surgical_procedure_mv.py +0 -0
  30. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/episodes/treatment_summary_joins.py +0 -0
  31. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/events/event_queries.py +0 -0
  32. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/modifiers/__init__.py +0 -0
  33. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/modifiers/condition_modifier_join.py +0 -0
  34. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/modifiers/condition_modifier_mv.py +0 -0
  35. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/modifiers/modifier_factories.py +0 -0
  36. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/modifiers/modifier_joins.py +0 -0
  37. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/modifiers/modifier_mappers.py +0 -0
  38. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/alchemy/source_factories.py +0 -0
  39. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/core/__init__.py +0 -0
  40. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/core/base.py +0 -0
  41. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/core/constructs.py +0 -0
  42. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/core/ddl.py +0 -0
  43. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/core/errors.py +0 -0
  44. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/core/materialized.py +0 -0
  45. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/core/plan.py +0 -0
  46. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/core/registry.py +0 -0
  47. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/py.typed +0 -0
  48. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/semantics/__init__.py +0 -0
  49. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/semantics/adapters.py +0 -0
  50. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/semantics/lookups.py +0 -0
  51. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/semantics/runtime_lookups.py +0 -0
  52. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/services/__init__.py +0 -0
  53. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/services/concept.py +0 -0
  54. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/services/person.py +0 -0
  55. {omop_constructs-0.3.4 → omop_constructs-0.3.6}/src/omop_constructs/typing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: omop-constructs
3
- Version: 0.3.4
3
+ Version: 0.3.6
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>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "omop-constructs"
3
- version = "0.3.4"
3
+ version = "0.3.6"
4
4
  description = "For building complex constructs on top of the omop-alchemy library."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,5 +1,4 @@
1
- from orm_loader.helpers import Base
2
- from collections import defaultdict
1
+
3
2
  import sqlalchemy.orm as so
4
3
  import sqlalchemy as sa
5
4
  from omop_alchemy.cdm.model.vocabulary import Concept, Concept_Relationship
@@ -78,7 +78,6 @@ all_cancer_relevant_surg = (
78
78
  .subquery(name="all_cancer_relevant_surg")
79
79
  )
80
80
 
81
-
82
81
  cancer_relevant_surg_select = (
83
82
  sa.select(
84
83
  sa.func.row_number().over().label("mv_id"),
@@ -12,6 +12,13 @@ from .dx_linked_event_mv import (
12
12
  DxMeasurementMV
13
13
  )
14
14
 
15
+ from .dx_linked_procedure_mv import (
16
+ DxProcedureMV,
17
+ )
18
+ from .dx_linked_obs_mv import (
19
+ DxObservationMV,
20
+ )
21
+
15
22
  __all__ = [
16
23
  "WeightDxMV",
17
24
  "WeightChangeDxMV",
@@ -23,5 +30,7 @@ __all__ = [
23
30
  "DistressThermometerDxMV",
24
31
  "ECOGDxMV",
25
32
  "SmokingPYHDxMV",
26
- "DxMeasurementMV"
33
+ "DxMeasurementMV",
34
+ "DxProcedureMV",
35
+ "DxObservationMV",
27
36
  ]
@@ -22,9 +22,6 @@ from .event_queries import (
22
22
  dx_all_measurements
23
23
  )
24
24
 
25
-
26
-
27
-
28
25
  class ConditionEpisodeMeasurementCols:
29
26
  __table_args__ = {"extend_existing": True}
30
27
 
@@ -0,0 +1,51 @@
1
+ import sqlalchemy as sa
2
+ import sqlalchemy.orm as so
3
+ from datetime import date
4
+ from typing import Optional
5
+ from orm_loader.helpers import Base
6
+
7
+ from omop_constructs.alchemy.episodes.condition_episode_mv import ConditionEpisodeMV
8
+ from ...core.materialized import MaterializedViewMixin
9
+ from ...core.constructs import register_construct
10
+ from ..episodes import ConditionEpisodeMV
11
+ from .observation_queries import (
12
+ dx_all_observations
13
+ )
14
+
15
+ class ConditionEpisodeObservationCols:
16
+ __table_args__ = {"extend_existing": True}
17
+
18
+ mv_id: so.Mapped[int] = so.mapped_column(primary_key=True)
19
+
20
+ person_id: so.Mapped[int] = so.mapped_column(sa.Integer)
21
+ event_id: so.Mapped[int] = so.mapped_column(sa.Integer)
22
+ event_date: so.Mapped[date] = so.mapped_column(sa.Date)
23
+ event_concept_id: so.Mapped[int] = so.mapped_column(sa.Integer)
24
+ event_label: so.Mapped[Optional[str]] = so.mapped_column(sa.String, nullable=True)
25
+
26
+ value_as_number: so.Mapped[Optional[float]] = so.mapped_column(sa.Float, nullable=True)
27
+ qualifier_concept_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
28
+ value_concept_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
29
+
30
+ episode_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
31
+ episode_concept_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
32
+ episode_label: so.Mapped[Optional[str]] = so.mapped_column(sa.String, nullable=True)
33
+
34
+ episode_start_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date, nullable=True)
35
+ episode_end_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date, nullable=True)
36
+
37
+ episode_delta_days: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
38
+
39
+
40
+ @register_construct
41
+ class DxObservationMV(ConditionEpisodeObservationCols, MaterializedViewMixin, Base):
42
+ __mv_name__ = "dx_observation_mv"
43
+ __mv_select__ = dx_all_observations.select()
44
+ __mv_index__ = "person_id"
45
+ __deps__ = (ConditionEpisodeMV.__mv_name__,)
46
+ __tablename__ = __mv_name__
47
+
48
+ observation_concept_id: so.Mapped[int] = so.mapped_column(sa.Integer)
49
+ observation_date: so.Mapped[Optional[date]] = so.mapped_column(sa.DateTime)
50
+ value_concept_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
51
+ qualifier_concept_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
@@ -0,0 +1,44 @@
1
+ import sqlalchemy as sa
2
+ import sqlalchemy.orm as so
3
+ from datetime import date
4
+ from typing import Optional
5
+ from orm_loader.helpers import Base
6
+
7
+ from omop_constructs.alchemy.episodes.condition_episode_mv import ConditionEpisodeMV
8
+ from ...core.materialized import MaterializedViewMixin
9
+ from ...core.constructs import register_construct
10
+ from ..episodes import ConditionEpisodeMV
11
+ from .procedure_queries import (
12
+ dx_all_procedures
13
+ )
14
+
15
+ class ConditionEpisodeProcedureCols:
16
+ __table_args__ = {"extend_existing": True}
17
+
18
+ mv_id: so.Mapped[int] = so.mapped_column(primary_key=True)
19
+
20
+ person_id: so.Mapped[int] = so.mapped_column(sa.Integer)
21
+ event_id: so.Mapped[int] = so.mapped_column(sa.Integer)
22
+ event_date: so.Mapped[date] = so.mapped_column(sa.Date)
23
+ event_concept_id: so.Mapped[int] = so.mapped_column(sa.Integer)
24
+ event_label: so.Mapped[Optional[str]] = so.mapped_column(sa.String, nullable=True)
25
+
26
+ episode_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
27
+ episode_concept_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
28
+ episode_label: so.Mapped[Optional[str]] = so.mapped_column(sa.String, nullable=True)
29
+
30
+ episode_start_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date, nullable=True)
31
+ episode_end_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date, nullable=True)
32
+
33
+ episode_delta_days: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, nullable=True)
34
+
35
+ @register_construct
36
+ class DxProcedureMV(ConditionEpisodeProcedureCols, MaterializedViewMixin, Base):
37
+ __mv_name__ = "dx_procedure_mv"
38
+ __mv_select__ = dx_all_procedures.select()
39
+ __mv_index__ = "person_id"
40
+ __deps__ = (ConditionEpisodeMV.__mv_name__,)
41
+ __tablename__ = __mv_name__
42
+
43
+ procedure_concept_id: so.Mapped[int] = so.mapped_column(sa.Integer)
44
+ procedure_datetime: so.Mapped[Optional[date]] = so.mapped_column(sa.DateTime)
@@ -5,12 +5,14 @@ from typing import Iterable, Any, Sequence, Union
5
5
  from omop_constructs.alchemy.episodes import ConditionEpisodeMV
6
6
  from omop_alchemy.cdm.model.structural import Episode_Event
7
7
  from omop_semantics.runtime.default_valuesets import runtime # type: ignore
8
- from omop_alchemy.cdm.model import Measurement, Concept
9
-
8
+ from omop_alchemy.cdm.model import Measurement, Concept, Procedure_Occurrence, Observation
10
9
  from sqlalchemy.orm.attributes import InstrumentedAttribute
10
+
11
11
  SQLExpr = Union[ColumnElement[Any], InstrumentedAttribute[Any]]
12
12
 
13
13
  modifier_concept = so.aliased(Concept, name="modifier_concept")
14
+ procedure_concept = so.aliased(Concept, name="procedure_concept")
15
+ observation_concept = so.aliased(Concept, name="observation_concept")
14
16
 
15
17
  def attach_to_condition_episode_via_episode_event(
16
18
  base_event_subq: sa.Subquery,
@@ -120,6 +122,59 @@ def attach_to_condition_episode(
120
122
  .subquery(name=name)
121
123
  )
122
124
 
125
+ def procedure_event_core(
126
+ *,
127
+ concept_ids: Iterable[int] | None = None,
128
+ name: str = "procedure_core",
129
+ include_cols: Sequence[SQLExpr] = (),
130
+ ) -> sa.Subquery:
131
+ """
132
+ Minimal procedure event surface (person/id/date/concept/label + extras).
133
+ """
134
+ q = (
135
+ sa.select(
136
+ Procedure_Occurrence.person_id.label("person_id"),
137
+ Procedure_Occurrence.procedure_occurrence_id.label("event_id"),
138
+ Procedure_Occurrence.procedure_date.label("event_date"),
139
+ Procedure_Occurrence.procedure_concept_id.label("event_concept_id"),
140
+ procedure_concept.concept_name.label("event_label"),
141
+ *include_cols,
142
+ )
143
+ .join(
144
+ procedure_concept,
145
+ procedure_concept.concept_id == Procedure_Occurrence.procedure_concept_id,
146
+ isouter=True,
147
+ )
148
+ )
149
+
150
+ if concept_ids is not None:
151
+ q = q.where(Procedure_Occurrence.procedure_concept_id.in_(list(concept_ids)))
152
+
153
+ return q.subquery(name=name)
154
+
155
+ def procedure_attached_to_condition_episode(
156
+ *,
157
+ concept_ids: Iterable[int] | None = None,
158
+ include_cols: Sequence[SQLExpr] = (),
159
+ name: str,
160
+ prefer_explicit_link: bool = True,
161
+ ) -> sa.Subquery:
162
+ core = procedure_event_core(
163
+ concept_ids=concept_ids,
164
+ include_cols=include_cols,
165
+ name=f"{name}_core",
166
+ )
167
+
168
+ return attach_to_condition_episode(
169
+ core,
170
+ event_id_col=core.c.event_id,
171
+ date_col=core.c.event_date,
172
+ person_col=core.c.person_id,
173
+ name=name,
174
+ prefer_explicit_link=prefer_explicit_link,
175
+ )
176
+
177
+
123
178
  def measurement_event_core(
124
179
  *,
125
180
  concept_ids: Iterable[int] | None = None,
@@ -176,6 +231,66 @@ def measurement_attached_to_condition_episode(
176
231
  name=name,
177
232
  )
178
233
 
234
+ def observation_event_core(
235
+ *,
236
+ concept_ids: Iterable[int] | None = None,
237
+ name: str = "observation_core",
238
+ include_cols: Sequence[SQLExpr] = (),
239
+ unlinked_only: bool = True,
240
+ ) -> sa.Subquery:
241
+ """
242
+ Minimal observation event surface (person/id/date/concept/label + extras).
243
+ """
244
+ q = (
245
+ sa.select(
246
+ Observation.person_id.label("person_id"),
247
+ Observation.observation_id.label("event_id"),
248
+ Observation.observation_date.label("event_date"),
249
+ Observation.observation_concept_id.label("event_concept_id"),
250
+ observation_concept.concept_name.label("event_label"),
251
+ Observation.value_as_concept_id.label("value_concept_id"),
252
+ Observation.qualifier_concept_id.label("qualifier_concept_id"),
253
+ *include_cols,
254
+ )
255
+ .join(
256
+ observation_concept,
257
+ observation_concept.concept_id == Observation.observation_concept_id,
258
+ isouter=True,
259
+ )
260
+ )
261
+
262
+ if unlinked_only:
263
+ q = q.where(Observation.observation_event_id == None)
264
+
265
+ if concept_ids is not None:
266
+ q = q.where(Observation.observation_concept_id.in_(list(concept_ids)))
267
+
268
+ return q.subquery(name=name)
269
+
270
+ def observation_attached_to_condition_episode(
271
+ *,
272
+ concept_ids: Iterable[int] | None = None,
273
+ include_cols: Sequence[SQLExpr] = (),
274
+ name: str,
275
+ unlinked_only: bool = True,
276
+ prefer_explicit_link: bool = True,
277
+ ) -> sa.Subquery:
278
+ core = observation_event_core(
279
+ concept_ids=concept_ids,
280
+ include_cols=include_cols,
281
+ name=f"{name}_core",
282
+ unlinked_only=unlinked_only,
283
+ )
284
+
285
+ return attach_to_condition_episode(
286
+ core,
287
+ event_id_col=core.c.event_id,
288
+ date_col=core.c.event_date,
289
+ person_col=core.c.person_id,
290
+ name=name,
291
+ prefer_explicit_link=prefer_explicit_link,
292
+ )
293
+
179
294
  def episode_relevant_window(
180
295
  starting_query: sa.Subquery,
181
296
  *,
@@ -0,0 +1,16 @@
1
+ from .event_factories import observation_attached_to_condition_episode, episode_relevant_window
2
+ from omop_alchemy.cdm.model import Observation
3
+ dx_all_observations = episode_relevant_window(
4
+ observation_attached_to_condition_episode(
5
+ concept_ids=None, # all obs
6
+ include_cols=[
7
+ Observation.value_as_number,
8
+ Observation.qualifier_concept_id,
9
+ ],
10
+ name="dx_all_observations",
11
+ unlinked_only=False,
12
+ ),
13
+ max_days_post=365,
14
+ max_days_prior=30,
15
+ name="dx_all_observations_windowed",
16
+ )
@@ -0,0 +1,12 @@
1
+ from .event_factories import procedure_attached_to_condition_episode, episode_relevant_window
2
+
3
+ dx_all_procedures = episode_relevant_window(
4
+ procedure_attached_to_condition_episode(
5
+ concept_ids=None,
6
+ include_cols=[],
7
+ name="dx_all_procedures",
8
+ ),
9
+ max_days_post=90,
10
+ max_days_prior=30,
11
+ name="dx_all_procedures_windowed",
12
+ )