canvas 0.8.1__py3-none-any.whl → 0.9.0__py3-none-any.whl

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.

Potentially problematic release.


This version of canvas might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: canvas
3
- Version: 0.8.1
3
+ Version: 0.9.0
4
4
  Summary: SDK to customize event-driven actions in your Canvas instance
5
5
  License: MIT
6
6
  Author: Canvas Team
@@ -65,7 +65,7 @@ canvas_cli/apps/emit/event_fixtures/VITAL_SIGN_UPDATED.ndjson,sha256=Er0aUWIYkqb
65
65
  canvas_cli/apps/logs/__init__.py,sha256=ehY9SRb6nBw81xZF50yyBlUZJtNR2VeVSNI5sFuWJ7o,64
66
66
  canvas_cli/apps/logs/logs.py,sha256=BFpZ-2OF2Rs1EMLePo5UjqC9fKQeqm8qZobNTFNCL_M,1972
67
67
  canvas_cli/apps/plugin/__init__.py,sha256=G_nLsu6cdko5OjatnbqUyEboGcNlGGLwpZmCBxMKdfo,236
68
- canvas_cli/apps/plugin/plugin.py,sha256=i1c4aKNlUrqlvQSz8agIi8aW2wWb8E5t6oh8sOpVJxw,14826
68
+ canvas_cli/apps/plugin/plugin.py,sha256=pAyZQ5mNUkTvvwGLVjr8gW-4BXRh12GgTCSqinTFIb0,14832
69
69
  canvas_cli/apps/plugin/tests.py,sha256=SsYeYY25ly9TMn-nkJEZjLaPCyFbT4vs1sN_FnQbJ5U,2746
70
70
  canvas_cli/apps/run_plugins/__init__.py,sha256=iAMgX_6D3CdjQodGx_azwhSjouaxquOm8Z8QVXnlTFE,117
71
71
  canvas_cli/apps/run_plugins/run_plugins.py,sha256=qsf6-UhFAZpIL-1C50fzSoIwXMsZISxg2fxzM46UHTA,384
@@ -156,7 +156,7 @@ canvas_sdk/handlers/base.py,sha256=7kw95-nYOsphP2_dVoJxBvi2RsuUpwBo5RcKvo924Yw,5
156
156
  canvas_sdk/handlers/cron_task.py,sha256=Q4_D3bDKE5hyEpCf0JnGSgZlnLh_g9RSP3oEfIuLJ9Y,945
157
157
  canvas_sdk/protocols/__init__.py,sha256=3u9zet5D4DX4V953tLCoN1xhaOhAUCwGwscMv-7IIxo,186
158
158
  canvas_sdk/protocols/base.py,sha256=GhFZejVrbMdVQ2LkLlRQh0m0wHA7XSUGD0GA3NyeT8U,153
159
- canvas_sdk/protocols/clinical_quality_measure.py,sha256=8Z3onmmIA8hW0k8-nierCqahlpGuofT52grAP4Dv2fU,3548
159
+ canvas_sdk/protocols/clinical_quality_measure.py,sha256=39ddtSIBCFB4DF7Kpuh2uDwSxH-mV7sjrsVrQY7tEuo,4835
160
160
  canvas_sdk/protocols/timeframe.py,sha256=SlTDhTy0TqPXKS9JZFeTVApQJDf8C-NIRLqFJltB17g,1148
161
161
  canvas_sdk/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
162
162
  canvas_sdk/utils/__init__.py,sha256=sFuhqcWvXb2-33FOuXZgWuUeC5jZL2MDoqjGoQZGwd8,60
@@ -171,22 +171,22 @@ canvas_sdk/v1/data/base.py,sha256=ZMZEpwkeg3ewl3QsbPAF7-z1MqqWu5hCR-IibWQCiHI,61
171
171
  canvas_sdk/v1/data/billing.py,sha256=w5vK7aWqOGEkgjrzJWTiDsywUdxFdK7naEImZkvKxHk,2040
172
172
  canvas_sdk/v1/data/command.py,sha256=z9LHw95Vq8efZE5y9r-Ei-Yzt8qj-o-BE3x9BBb4yPQ,945
173
173
  canvas_sdk/v1/data/common.py,sha256=OvF4Oxo3BqE1Cv2lfyA1MXp_q_VHwoihyiJ0ETfa5lY,3459
174
- canvas_sdk/v1/data/condition.py,sha256=EHDb9o1ixXFLZKODCpp-2Wvhc6OmiA0ENPSzRId6pAU,1858
174
+ canvas_sdk/v1/data/condition.py,sha256=qUiwTf4Le5GIhWRgvXZFsXTS4nH4trNBc2mtJsO12FQ,1858
175
175
  canvas_sdk/v1/data/detected_issue.py,sha256=c1rKZuPV_tTp_1yDJJa88gIsixpPV_F-a3eooje8BW8,1710
176
176
  canvas_sdk/v1/data/device.py,sha256=uxeQRT8ljzBmDwsARFDB9MZKAVAKOA-pu4nAa6fcHeU,1655
177
177
  canvas_sdk/v1/data/imaging.py,sha256=z6CtVcBYv8EL7fxzT0pWPUuh1mt5XFPlRDNNbOjvLng,4303
178
- canvas_sdk/v1/data/lab.py,sha256=nGV2FGDtnj7NcO8WS5LRXv5JUiLjuWMtm0i-PUNsKLk,10313
178
+ canvas_sdk/v1/data/lab.py,sha256=9_yZAX_JQRWRkdjegivpAd3NAlZ_pV-t6eehPKXhJKU,10909
179
179
  canvas_sdk/v1/data/medication.py,sha256=PGOOTnhrPSSExSuQJyIdsDLSg09riMb2usvpRmcUo7s,1998
180
180
  canvas_sdk/v1/data/note.py,sha256=cC0fifgVby04YcjuOAVycpfsDGIGaJ8_INFRXQID2wU,6607
181
181
  canvas_sdk/v1/data/observation.py,sha256=BftaK7tOZaTlfUKoTH_A-X23J4DUFRVWpvZhkBJ1DJg,3710
182
- canvas_sdk/v1/data/patient.py,sha256=0OB07_w2DoAYwCFU0mIZ2KDqcM5uwTG9wyhEOepd2pQ,2729
182
+ canvas_sdk/v1/data/patient.py,sha256=iUSTAsEB_cVEUP12mkvJ7J5DkYhUZWmHTDw4JPdB6pc,3090
183
183
  canvas_sdk/v1/data/protocol_override.py,sha256=Dra2qf5-VX7qsK5mWy6U5XU7Y9v4NGkobRU4BAeAxkU,1835
184
184
  canvas_sdk/v1/data/questionnaire.py,sha256=8RIX_YtxCZd2NK9hK41ehywHA5vHks1wqigZf5LHPvI,6908
185
185
  canvas_sdk/v1/data/staff.py,sha256=J-U5NeKH-P41ph6pWiTd5v-QYQHV0GmvxxGVVUUf1nc,2786
186
186
  canvas_sdk/v1/data/task.py,sha256=9N66XL4B51rla4Q-Tn7STIGvR5Qr6-DNqfMYnT6t8yE,3585
187
187
  canvas_sdk/v1/data/user.py,sha256=DmA8fNNGwiNOmuR-RYxQw1NpxN5yYhgwtRtRBRJ_22g,309
188
188
  canvas_sdk/value_set/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
189
- canvas_sdk/value_set/custom.py,sha256=33ceZ416kJ4sftqMGTQVUpmfMyydSl51gpxmmilM6F0,17842
189
+ canvas_sdk/value_set/custom.py,sha256=0xzGP86ioCGcsx8wwf46SXfe9IefLSGVq7Z4yrHbEUU,19709
190
190
  canvas_sdk/value_set/tests/test_value_sets.py,sha256=ZoDdR6npR9L8h7qGdQj3B7lRghHI3PBV3klRDYV7wak,3116
191
191
  canvas_sdk/value_set/v2022/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
192
192
  canvas_sdk/value_set/v2022/adverse_event.py,sha256=8zsNFBsLDRY0mrPomzogD-W15xyo2CFFkiEcT2ruGCg,1032
@@ -250,7 +250,7 @@ plugin_runner/tests/test_sandbox.py,sha256=I44rz0sbxqtWm6mAG8fGhneE1yu9M-K3PMkE4
250
250
  pubsub/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
251
251
  pubsub/pubsub.py,sha256=pyTW0JU8mtaqiAV6g6xjZwel1CVy2EonPMU-_vkmhUM,1044
252
252
  settings.py,sha256=6YTybI0eNeuDamnXdRrEUOBICf9bM1uW1lCwj4IR9sU,2081
253
- canvas-0.8.1.dist-info/METADATA,sha256=oRCTBDikhw0IZcizZpGWXye9dYRtt0cKV_gk2snL3BE,4663
254
- canvas-0.8.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
255
- canvas-0.8.1.dist-info/entry_points.txt,sha256=VSmSo1IZ3aEfL7enmLmlWSraS_IIkoXNVeyXzgRxFiY,46
256
- canvas-0.8.1.dist-info/RECORD,,
253
+ canvas-0.9.0.dist-info/METADATA,sha256=of4-iMy4Axlmd6WC0hOlBCdQVVoNmTxYnTmxi1rHkHc,4663
254
+ canvas-0.9.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
255
+ canvas-0.9.0.dist-info/entry_points.txt,sha256=VSmSo1IZ3aEfL7enmLmlWSraS_IIkoXNVeyXzgRxFiY,46
256
+ canvas-0.9.0.dist-info/RECORD,,
@@ -54,7 +54,7 @@ def _build_package(package: Path) -> Path:
54
54
  def _get_name_from_metadata(host: str, token: str, package: Path) -> str | None:
55
55
  """Extract metadata from a provided package and return the package name if it exists in the metadata."""
56
56
  try:
57
- with open(package) as package_file:
57
+ with open(package, "rb") as package_file:
58
58
  metadata_response = requests.post(
59
59
  plugin_url(host, "extract-metadata"),
60
60
  headers={"Authorization": f"Bearer {token}"},
@@ -1,13 +1,18 @@
1
- from typing import Any, cast
1
+ from typing import TYPE_CHECKING, Any, cast
2
2
 
3
3
  import arrow
4
- from django.db.models import Model
5
4
 
6
5
  from canvas_sdk.events import EventType
7
6
  from canvas_sdk.protocols.base import BaseProtocol
8
7
  from canvas_sdk.protocols.timeframe import Timeframe
8
+ from canvas_sdk.v1.data import Patient
9
9
  from canvas_sdk.v1.data.condition import Condition
10
+ from canvas_sdk.v1.data.lab import LabOrder, LabReport
10
11
  from canvas_sdk.v1.data.medication import Medication
12
+ from canvas_sdk.v1.data.protocol_override import ProtocolOverride
13
+
14
+ if TYPE_CHECKING:
15
+ from django.db.models import Model
11
16
 
12
17
 
13
18
  class ClinicalQualityMeasure(BaseProtocol):
@@ -54,6 +59,19 @@ class ClinicalQualityMeasure(BaseProtocol):
54
59
  """
55
60
  return cls.__name__
56
61
 
62
+ @staticmethod
63
+ def relative_float(value: str) -> float:
64
+ """Relative float method (better explanation needed here)."""
65
+ try:
66
+ sigma = 0.0
67
+ if value[0] == "<" and value[1] != "=":
68
+ sigma = -1e-6
69
+ elif value[0] == ">" and value[1] != "=":
70
+ sigma = +1e-6
71
+ return float(value.strip("<≤=≥>")) + sigma
72
+ except (ValueError, IndexError):
73
+ return 0
74
+
57
75
  @property
58
76
  def timeframe(self) -> Timeframe:
59
77
  """The default Timeframe (self.timeframe) for all protocols.
@@ -74,27 +92,41 @@ class ClinicalQualityMeasure(BaseProtocol):
74
92
  incurring more SQL queries.
75
93
  """
76
94
 
77
- def patient_id(model: type[Model]) -> str:
78
- return cast(
79
- str,
80
- model._default_manager.select_related("patient")
81
- .values_list("patient__id")
82
- .get(id=self.event.target)[0],
83
- )
95
+ def patient_id(model: "type[Model]") -> str:
96
+ if model == Patient:
97
+ return self.target
98
+ else:
99
+ return cast(
100
+ str,
101
+ model._default_manager.select_related("patient")
102
+ .values_list("patient__id")
103
+ .get(id=self.event.target)[0],
104
+ )
105
+
106
+ models = {
107
+ EventType.CONDITION_ASSESSED: Condition,
108
+ EventType.CONDITION_CREATED: Condition,
109
+ EventType.CONDITION_RESOLVED: Condition,
110
+ EventType.CONDITION_UPDATED: Condition,
111
+ EventType.LAB_ORDER_CREATED: LabOrder,
112
+ EventType.LAB_ORDER_UPDATED: LabOrder,
113
+ EventType.LAB_REPORT_CREATED: LabReport,
114
+ EventType.LAB_REPORT_UPDATED: LabReport,
115
+ EventType.MEDICATION_LIST_ITEM_CREATED: Medication,
116
+ EventType.MEDICATION_LIST_ITEM_UPDATED: Medication,
117
+ EventType.PATIENT_CREATED: Patient,
118
+ EventType.PATIENT_UPDATED: Patient,
119
+ EventType.PROTOCOL_OVERRIDE_CREATED: ProtocolOverride,
120
+ EventType.PROTOCOL_OVERRIDE_DELETED: ProtocolOverride,
121
+ EventType.PROTOCOL_OVERRIDE_UPDATED: ProtocolOverride,
122
+ }
84
123
 
85
124
  if not self._patient_id:
86
- # TODO: Add cases for ProtocolOverride
87
- match self.event.type:
88
- case EventType.CONDITION_CREATED | EventType.CONDITION_UPDATED:
89
- self._patient_id = patient_id(Condition)
90
- case (
91
- EventType.MEDICATION_LIST_ITEM_CREATED
92
- | EventType.MEDICATION_LIST_ITEM_UPDATED
93
- ):
94
- self._patient_id = patient_id(Medication)
95
- case _:
96
- raise AssertionError(
97
- f"Event type {self.event.type} not supported by 'patient_id_from_event'"
98
- )
125
+ try:
126
+ self._patient_id = patient_id(models[self.event.type])
127
+ except KeyError as error:
128
+ raise ValueError(
129
+ f"Event type {self.event.type} not supported by 'patient_id_from_event'"
130
+ ) from error
99
131
 
100
132
  return self._patient_id
@@ -34,13 +34,13 @@ class Condition(models.Model):
34
34
 
35
35
  id = models.UUIDField()
36
36
  dbid = models.BigIntegerField(primary_key=True)
37
- onset_date = models.DateField()
38
- resolution_date = models.DateField()
39
- clinical_status = models.CharField(choices=ClinicalStatus.choices)
40
37
  deleted = models.BooleanField()
41
38
  entered_in_error = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
42
39
  committer = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
43
40
  patient = models.ForeignKey(Patient, on_delete=models.DO_NOTHING, related_name="conditions")
41
+ onset_date = models.DateField()
42
+ resolution_date = models.DateField()
43
+ clinical_status = models.CharField(choices=ClinicalStatus.choices)
44
44
 
45
45
 
46
46
  class ConditionCoding(models.Model):
canvas_sdk/v1/data/lab.py CHANGED
@@ -1,6 +1,10 @@
1
1
  from django.db import models
2
2
 
3
- from canvas_sdk.v1.data.base import CommittableModelManager
3
+ from canvas_sdk.v1.data.base import (
4
+ CommittableModelManager,
5
+ TimeframeLookupQuerySetMixin,
6
+ ValueSetLookupQuerySet,
7
+ )
4
8
  from canvas_sdk.v1.data.condition import Condition
5
9
  from canvas_sdk.v1.data.patient import Patient
6
10
 
@@ -79,6 +83,21 @@ class LabReview(models.Model):
79
83
  patient_communication_method = models.CharField()
80
84
 
81
85
 
86
+ class LabValueTimeframeLookupQuerySetMixin(TimeframeLookupQuerySetMixin):
87
+ """A class that adds queryset functionality to filter using timeframes."""
88
+
89
+ @property
90
+ def timeframe_filter_field(self) -> str:
91
+ """Returns the field that should be filtered on. Can be overridden for different models."""
92
+ return "report__original_date"
93
+
94
+
95
+ class LabValueQuerySet(ValueSetLookupQuerySet, LabValueTimeframeLookupQuerySetMixin):
96
+ """LabValueQuerySet."""
97
+
98
+ pass
99
+
100
+
82
101
  class LabValue(models.Model):
83
102
  """A class representing a lab value."""
84
103
 
@@ -87,6 +106,8 @@ class LabValue(models.Model):
87
106
  app_label = "canvas_sdk"
88
107
  db_table = "canvas_sdk_data_api_labvalue_001"
89
108
 
109
+ objects = LabValueQuerySet.as_manager()
110
+
90
111
  id = models.UUIDField()
91
112
  dbid = models.BigIntegerField(primary_key=True)
92
113
  created = models.DateTimeField()
@@ -1,7 +1,19 @@
1
1
  from typing import Self
2
2
 
3
3
  import arrow
4
+ from django.contrib.postgres.fields import ArrayField
4
5
  from django.db import models
6
+ from django.db.models import TextChoices
7
+
8
+
9
+ class SexAtBirth(TextChoices):
10
+ """SexAtBirth."""
11
+
12
+ FEMALE = "F", "female"
13
+ MALE = "M", "male"
14
+ OTHER = "O", "other"
15
+ UNKNOWN = "UNK", "unknown"
16
+ BLANK = "", ""
5
17
 
6
18
 
7
19
  class Patient(models.Model):
@@ -17,7 +29,7 @@ class Patient(models.Model):
17
29
  first_name = models.CharField()
18
30
  last_name = models.CharField()
19
31
  birth_date = models.DateField()
20
- sex_at_birth = models.CharField()
32
+ sex_at_birth = models.CharField(choices=SexAtBirth.choices)
21
33
  created = models.DateTimeField()
22
34
  modified = models.DateTimeField()
23
35
  prefix = models.CharField()
@@ -30,6 +42,7 @@ class Patient(models.Model):
30
42
  gender_identity_term = models.CharField()
31
43
  gender_identity_code = models.CharField()
32
44
  preferred_pronouns = models.CharField()
45
+ biological_race_codes = ArrayField(models.CharField())
33
46
  last_known_timezone = models.CharField()
34
47
  mrn = models.CharField()
35
48
  active = models.BooleanField()
@@ -181,6 +181,89 @@ class Antiarrhythmics(ValueSet):
181
181
  }
182
182
 
183
183
 
184
+ class LabReportCreatinine(ValueSet):
185
+ """LabReportCreatinine."""
186
+
187
+ VALUE_SET_NAME = "Lab Report Creatinine"
188
+ EXPANSION_VERSION = "CanvasHCC Update 2018-10-04"
189
+
190
+ LOINC = {
191
+ "2160-0",
192
+ }
193
+
194
+
195
+ class HypertensiveChronicKidneyDisease(ValueSet):
196
+ """
197
+ **Clinical Focus:** This value set contains concepts that represent hypertensive kidney disease.
198
+
199
+ **Data Element Scope:** This value set may use the Quality Data Model (QDM) category related to Diagnosis.
200
+
201
+ **Inclusion Criteria:** Includes only relevant concepts associated with hypertensive kidney disease.
202
+
203
+ **Exclusion Criteria:** No exclusions.
204
+ """
205
+
206
+ OID = "2.16.840.1.113883.3.464.1003.109.12.1017"
207
+ VALUE_SET_NAME = "Hypertensive Chronic Kidney Disease"
208
+ EXPANSION_VERSION = "eCQM Update 2020-05-07"
209
+
210
+ ICD10CM = {"I120", "I129", "I130", "I1310", "I1311", "I132", "I150", "I151"}
211
+
212
+ ICD9CM = {
213
+ "40301",
214
+ "40310",
215
+ "40311",
216
+ "40390",
217
+ "40391",
218
+ "40400",
219
+ "40401",
220
+ "40402",
221
+ "40403",
222
+ "40410",
223
+ "40411",
224
+ "40412",
225
+ "40413",
226
+ "40490",
227
+ "40491",
228
+ "40492",
229
+ "40493",
230
+ }
231
+
232
+ SNOMEDCT = {
233
+ "111438007",
234
+ "123799005",
235
+ "123800009",
236
+ "14973001",
237
+ "193003",
238
+ "194774006",
239
+ "194783001",
240
+ "19769006",
241
+ "23130000",
242
+ "28119000",
243
+ "285831000119108",
244
+ "285841000119104",
245
+ "286371000119107",
246
+ "32916005",
247
+ "38481006",
248
+ "39018007",
249
+ "397748008",
250
+ "427889009",
251
+ "428575007",
252
+ "473392002",
253
+ "49220004",
254
+ "57684003",
255
+ "62240004",
256
+ "65443008",
257
+ "66052004",
258
+ "66610008",
259
+ "73410007",
260
+ "78544004",
261
+ "81363003",
262
+ "86234004",
263
+ "90493000",
264
+ }
265
+
266
+
184
267
  class DiabetesWithoutComplication(ValueSet):
185
268
  """Diabetes Without Complication."""
186
269
 
File without changes