canvas 0.5.0__py3-none-any.whl → 0.6.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.5.0
3
+ Version: 0.6.0
4
4
  Summary: SDK to customize event-driven actions in your Canvas instance
5
5
  License: MIT
6
6
  Author: Canvas Team
@@ -25,6 +25,7 @@ Requires-Dist: psycopg[binary] (>=3.2.2,<4.0.0)
25
25
  Requires-Dist: pydantic (>=2.6.1,<3.0.0)
26
26
  Requires-Dist: pyjwt (==2.10.0)
27
27
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
28
+ Requires-Dist: rapidfuzz (>=3.10.1,<4.0.0)
28
29
  Requires-Dist: redis (>=5.0.4,<6.0.0)
29
30
  Requires-Dist: requests
30
31
  Requires-Dist: restrictedpython (>=7.1,<8.0)
@@ -104,9 +104,9 @@ canvas_generated/services/plugin_runner_pb2.py,sha256=RfAo_imYoSuoexq-1IHhMhXZgQ
104
104
  canvas_generated/services/plugin_runner_pb2.pyi,sha256=1w-Pa4k7HtlmQAr7B6sgV64zdZplBKQKHN-S8bjwO3w,265
105
105
  canvas_generated/services/plugin_runner_pb2_grpc.py,sha256=EzJJVkP_AZ3dwBA7OxUito0NSalRmjjg8q9TZ_P18ww,4549
106
106
  canvas_sdk/__init__.py,sha256=d7V1Qsp4hSqp8opmvqp-0J33uibArUjwENMfzSDAdZg,102
107
- canvas_sdk/base.py,sha256=tcmP3zxp24kAoplT-jqrIlSpiYsJ6qRMNY-HiuJYFGc,1834
107
+ canvas_sdk/base.py,sha256=Qp_bcUFaoGBRtDXuYyTZyQC1Q6-El3w-J5Gkg8U3ps8,2040
108
108
  canvas_sdk/commands/__init__.py,sha256=ApJy2i8IRCL2YFUumk1q-YihLL2YcmLYKKiWFK04evY,2365
109
- canvas_sdk/commands/base.py,sha256=lc6OizuXTB5mDzGVpYGx9nz_OrJUKBaffmmCPxs2B0s,5155
109
+ canvas_sdk/commands/base.py,sha256=WX6nnP1OlH4LT28N_xmE55gS_FbFcZuLk_dIvSK49VY,5226
110
110
  canvas_sdk/commands/commands/allergy.py,sha256=_S0tY43sErRlTeUGCC6Thb8gMSD_JiOmRSFdLaDlr48,1296
111
111
  canvas_sdk/commands/commands/assess.py,sha256=IjsvkhzKinRaykMRzbnk6G4JAkzDhyqGG6ZvjDhNAuE,1723
112
112
  canvas_sdk/commands/commands/close_goal.py,sha256=RTmi8fGPEEag8YSrlsPn7sy9Y-WymVuU8IWpXRKgNjg,792
@@ -147,14 +147,14 @@ canvas_sdk/data/staff.py,sha256=Po-NH2UdGTTP4LnUzfItfzriJKdul-2Cd31oq9uFHQE,210
147
147
  canvas_sdk/data/task.py,sha256=UpjWjB1QqClR83zeqFcM7b7gg2w0NjfzNKsWrgShN_s,1995
148
148
  canvas_sdk/effects/__init__.py,sha256=oChQi5y_8mK2zDiNMwWYp6CaQX1_zbmwXILPF7iSt-4,69
149
149
  canvas_sdk/effects/banner_alert/__init__.py,sha256=8z5l9rwonKEcRkvD5q4DWvHPYhKJzwyGuUYoUNKYwX4,158
150
- canvas_sdk/effects/banner_alert/add_banner_alert.py,sha256=RhMEIc62T5rLO_FltGvOq0XVU_wXIdCcBpxfaQeqCJY,1497
151
- canvas_sdk/effects/banner_alert/remove_banner_alert.py,sha256=9oLetlX_22B3iRwVZqB4IpA9e9YqZ_1cmWV4vJRNDmE,559
152
- canvas_sdk/effects/banner_alert/tests.py,sha256=idaXkAAUm_pnquXn1ZbWAPmoTf16MsbXrnM6cpaJZDQ,7712
153
- canvas_sdk/effects/base.py,sha256=cyTEQ4p3IxZ3mPu6Ancr4Y7RcrVirWBbNUs9V6g7Odc,674
150
+ canvas_sdk/effects/banner_alert/add_banner_alert.py,sha256=W4Fv9IHGCWvVtFiH3s68JBRpUb8b3xsncGt2WfgMFc0,1681
151
+ canvas_sdk/effects/banner_alert/remove_banner_alert.py,sha256=PK_LzXBQ-rOH3fX182ISyutu4W69xyGgMX-1G4g4XhY,613
152
+ canvas_sdk/effects/banner_alert/tests.py,sha256=6vid5_pDeBSqgqDoeUeaVsflE0bnuSFgLpfSFV7pXLY,8531
153
+ canvas_sdk/effects/base.py,sha256=znIaDNkCYbzJxf4Q80hLRYUG5H73c8IMq1UCzeBcXjw,714
154
154
  canvas_sdk/effects/patient_chart_summary_configuration.py,sha256=_Gx7UIp4NLaSYYLLcBSOjtOD4Skqot_kt_x8zI3Ccwo,1184
155
155
  canvas_sdk/effects/protocol_card/__init__.py,sha256=ESzUYajBBmcTAad2PIwHuV3SgGj4B9JHtf9uop_V4YA,88
156
- canvas_sdk/effects/protocol_card/protocol_card.py,sha256=gn-ArEiYWoSG4IDE-at2oaPCbSIrNxz_wpucYaMNC5M,2427
157
- canvas_sdk/effects/protocol_card/tests.py,sha256=fdFPkORW7e_0FpSlxBQWUPQwF6-JQeBG1g3R7MKj9Jc,6419
156
+ canvas_sdk/effects/protocol_card/protocol_card.py,sha256=R7sHF74nup1nSOikf3o7wWukMvimrlS9AlRL0iDSlAM,2540
157
+ canvas_sdk/effects/protocol_card/tests.py,sha256=6EVFyAPA4YYIQO6UF-Xwe266LLzJRYlmmDiJC7lFoCc,6463
158
158
  canvas_sdk/events/__init__.py,sha256=JGZ-3uf0Luzr4kg5UqduupD4w6B2WVEgQ9WlcoMaPwE,81
159
159
  canvas_sdk/handlers/__init__.py,sha256=VVz2_Xyb9720R2MZmSbGPG0T8u2a5u6zSDkWlwiJZv4,49
160
160
  canvas_sdk/handlers/base.py,sha256=7kw95-nYOsphP2_dVoJxBvi2RsuUpwBo5RcKvo924Yw,599
@@ -171,18 +171,18 @@ canvas_sdk/utils/tests.py,sha256=t3MScbIfzXkQttMIvj0dRzJlFVS8LFU8WgWRrChM-H0,193
171
171
  canvas_sdk/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
172
172
  canvas_sdk/v1/data/__init__.py,sha256=LWAcICOx4fWea_lp_yNt0UIuhgjZMoNOaWx-7ZuM-dg,132
173
173
  canvas_sdk/v1/data/allergy_intolerance.py,sha256=KJg7UR8BJMuASWL5zXZ57JTmC0TQT7FAnODPvdq2mFg,2093
174
- canvas_sdk/v1/data/base.py,sha256=6JgLZsl9NRRS6s9-jjJM8Opxk_SI_sGkyokKIGPCoWI,3504
174
+ canvas_sdk/v1/data/base.py,sha256=FyKcqayXEH4KV1DGMFU63jlyoNT57-eD3TkXk_yICM0,3530
175
175
  canvas_sdk/v1/data/command.py,sha256=z9LHw95Vq8efZE5y9r-Ei-Yzt8qj-o-BE3x9BBb4yPQ,945
176
176
  canvas_sdk/v1/data/common.py,sha256=9PiJ6oCPsvCvKBHtShIg0cW5Sr8CrnM9te-m2DYJB-g,1590
177
- canvas_sdk/v1/data/condition.py,sha256=hJ3APJvRh-dKYwujujdo5kugnc6YXBES7YpUIKUTjhI,1525
177
+ canvas_sdk/v1/data/condition.py,sha256=izWzeNBUlFJOzjlp81cN2c6u78bzkFWQgSQgI4WFiC4,1535
178
178
  canvas_sdk/v1/data/detected_issue.py,sha256=c1rKZuPV_tTp_1yDJJa88gIsixpPV_F-a3eooje8BW8,1710
179
179
  canvas_sdk/v1/data/device.py,sha256=uxeQRT8ljzBmDwsARFDB9MZKAVAKOA-pu4nAa6fcHeU,1655
180
180
  canvas_sdk/v1/data/imaging.py,sha256=z6CtVcBYv8EL7fxzT0pWPUuh1mt5XFPlRDNNbOjvLng,4303
181
181
  canvas_sdk/v1/data/lab.py,sha256=nGV2FGDtnj7NcO8WS5LRXv5JUiLjuWMtm0i-PUNsKLk,10313
182
182
  canvas_sdk/v1/data/medication.py,sha256=XbV_iYgt40n8Q1IcVPtsinW2v-VJEms4T13cFNquBRE,1798
183
183
  canvas_sdk/v1/data/observation.py,sha256=BftaK7tOZaTlfUKoTH_A-X23J4DUFRVWpvZhkBJ1DJg,3710
184
- canvas_sdk/v1/data/patient.py,sha256=YGlp13cSNQHDtUJYTU37zisF94ZEKNYAlc2TJAmhdB8,1930
185
- canvas_sdk/v1/data/protocol_override.py,sha256=mUEcA7_9VkGsDmxrOxJ1eT8XVG0j8PHkd8JmQN4P3zM,1837
184
+ canvas_sdk/v1/data/patient.py,sha256=QvGJxiqX7be-p1RNw-fSXlomb7gPwCyOZOKZMlJfT2s,1945
185
+ canvas_sdk/v1/data/protocol_override.py,sha256=Dra2qf5-VX7qsK5mWy6U5XU7Y9v4NGkobRU4BAeAxkU,1835
186
186
  canvas_sdk/v1/data/questionnaire.py,sha256=wZNj8wg5aEThrHvA6-Oq5USnViyoeYGlcD5cjNmiULM,6899
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
@@ -212,7 +212,7 @@ plugin_runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
212
212
  plugin_runner/authentication.py,sha256=SDPso2AogtLAV_H0LuMDp99IMZuF3oTq-Q_AXAvJ8uc,1116
213
213
  plugin_runner/plugin_runner.py,sha256=Ae7ApJLeV_wo431wfqXHdE1xEkOcGrnOQ8TvHKQ3mkw,13447
214
214
  plugin_runner/plugin_synchronizer.py,sha256=t3zzfDw-bDK_hvUDQ434qaQuKLGgBGndzaCRAnSpuTU,2554
215
- plugin_runner/sandbox.py,sha256=jEFIzpYHYW5pTSLXTUHjyK3vz6ZrSsAw6HqJOreRWT4,9511
215
+ plugin_runner/sandbox.py,sha256=WYqCVfEL2wOj7wxvCVJb8hRaHiPJ6z1iy5zunI-D6p4,9532
216
216
  plugin_runner/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
217
217
  plugin_runner/tests/data/plugins/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
218
218
  plugin_runner/tests/fixtures/plugins/example_plugin/CANVAS_MANIFEST.json,sha256=J9T_E5vqUX4HITHbFsd6JQpw3YvMS4wR_lhI5JL2KMk,749
@@ -249,7 +249,7 @@ plugin_runner/tests/test_sandbox.py,sha256=I44rz0sbxqtWm6mAG8fGhneE1yu9M-K3PMkE4
249
249
  pubsub/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
250
250
  pubsub/pubsub.py,sha256=pyTW0JU8mtaqiAV6g6xjZwel1CVy2EonPMU-_vkmhUM,1044
251
251
  settings.py,sha256=to4MXxHXTnDhhPpc8JbPuIMtxPX9cCZFuE5tfBwbROQ,2065
252
- canvas-0.5.0.dist-info/METADATA,sha256=MQz_rKT4LndF75okGzCix01v0eWhWhQ-FAM_RIt8_7w,4605
253
- canvas-0.5.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
254
- canvas-0.5.0.dist-info/entry_points.txt,sha256=VSmSo1IZ3aEfL7enmLmlWSraS_IIkoXNVeyXzgRxFiY,46
255
- canvas-0.5.0.dist-info/RECORD,,
252
+ canvas-0.6.0.dist-info/METADATA,sha256=EF8WA1OYvGaPumByC6Bm1KdOccwx_tqpM-hfGS-Ec5M,4648
253
+ canvas-0.6.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
254
+ canvas-0.6.0.dist-info/entry_points.txt,sha256=VSmSo1IZ3aEfL7enmLmlWSraS_IIkoXNVeyXzgRxFiY,46
255
+ canvas-0.6.0.dist-info/RECORD,,
canvas_sdk/base.py CHANGED
@@ -34,15 +34,18 @@ class Model(BaseModel):
34
34
  class_name = self.__repr_name__() # type: ignore[misc]
35
35
 
36
36
  class_name_article = "an" if class_name.startswith(("A", "E", "I", "O", "U")) else "a"
37
- return [
38
- self._create_error_detail(
39
- "missing",
40
- f"Field '{field}' is required to {method.replace('_', ' ')} {class_name_article} {class_name}",
41
- v,
42
- )
43
- for field in required_fields
44
- if (v := getattr(self, field)) is None
45
- ]
37
+
38
+ error_details = []
39
+ for field in required_fields:
40
+ fields = field.split("|")
41
+ if not all(getattr(self, f) is None for f in fields):
42
+ continue
43
+ field_description = " or ".join([f"'{f}'" for f in fields])
44
+ message = f"Field {field_description} is required to {method.replace('_', ' ')} {class_name_article} {class_name}"
45
+ error = self._create_error_detail("missing", message, None)
46
+ error_details.append(error)
47
+
48
+ return error_details
46
49
 
47
50
  def _validate_before_effect(self, method: str) -> None:
48
51
  self.model_validate(self)
@@ -83,7 +83,7 @@ class _BaseCommand(Model):
83
83
  if name not in base_properties
84
84
  }
85
85
 
86
- def originate(self) -> Effect:
86
+ def originate(self, line_number: int = -1) -> Effect:
87
87
  """Originate a new command in the note body."""
88
88
  self._validate_before_effect("originate")
89
89
  return Effect(
@@ -92,6 +92,7 @@ class _BaseCommand(Model):
92
92
  {
93
93
  "note": self.note_uuid,
94
94
  "data": self.values,
95
+ "line_number": line_number,
95
96
  }
96
97
  ),
97
98
  )
@@ -13,7 +13,13 @@ class AddBannerAlert(_BaseEffect):
13
13
 
14
14
  class Meta:
15
15
  effect_type = EffectType.ADD_BANNER_ALERT
16
- apply_required_fields = ("patient_id", "key", "narrative", "placement", "intent")
16
+ apply_required_fields = (
17
+ "patient_id|patient_filter",
18
+ "key",
19
+ "narrative",
20
+ "placement",
21
+ "intent",
22
+ )
17
23
 
18
24
  class Placement(Enum):
19
25
  CHART = "chart"
@@ -47,4 +53,9 @@ class AddBannerAlert(_BaseEffect):
47
53
  @property
48
54
  def effect_payload(self) -> dict[str, Any]:
49
55
  """The payload of the effect."""
50
- return {"patient": self.patient_id, "key": self.key, "data": self.values}
56
+ return {
57
+ "patient": self.patient_id,
58
+ "patient_filter": self.patient_filter,
59
+ "key": self.key,
60
+ "data": self.values,
61
+ }
@@ -10,7 +10,7 @@ class RemoveBannerAlert(_BaseEffect):
10
10
 
11
11
  class Meta:
12
12
  effect_type = EffectType.REMOVE_BANNER_ALERT
13
- apply_required_fields = ("patient_id", "key")
13
+ apply_required_fields = ("patient_id|patient_filter", "key")
14
14
 
15
15
  patient_id: str | None = None
16
16
  key: str | None = None
@@ -18,4 +18,4 @@ class RemoveBannerAlert(_BaseEffect):
18
18
  @property
19
19
  def effect_payload(self) -> dict[str, Any]:
20
20
  """The payload of the effect."""
21
- return {"patient": self.patient_id, "key": self.key}
21
+ return {"patient": self.patient_id, "patient_filter": self.patient_filter, "key": self.key}
@@ -178,12 +178,28 @@ def test_protocol_that_adds_banner_alert(
178
178
  "placement": [AddBannerAlert.Placement.APPOINTMENT_CARD],
179
179
  "intent": AddBannerAlert.Intent.INFO,
180
180
  },
181
- '{"patient": "uuid", "key": "test-key", "data": {"narrative": "hellooo", "placement": ["appointment_card"], "intent": "info", "href": null}}',
181
+ '{"patient": "uuid", "patient_filter": null, "key": "test-key", "data": {"narrative": "hellooo", "placement": ["appointment_card"], "intent": "info", "href": null}}',
182
+ ),
183
+ (
184
+ AddBannerAlert,
185
+ {
186
+ "patient_filter": {"active": True},
187
+ "key": "test-key",
188
+ "narrative": "hellooo",
189
+ "placement": [AddBannerAlert.Placement.APPOINTMENT_CARD],
190
+ "intent": AddBannerAlert.Intent.INFO,
191
+ },
192
+ '{"patient": null, "patient_filter": {"active": true}, "key": "test-key", "data": {"narrative": "hellooo", "placement": ["appointment_card"], "intent": "info", "href": null}}',
182
193
  ),
183
194
  (
184
195
  RemoveBannerAlert,
185
196
  {"patient_id": "uuid", "key": "testeroo"},
186
- '{"patient": "uuid", "key": "testeroo"}',
197
+ '{"patient": "uuid", "patient_filter": null, "key": "testeroo"}',
198
+ ),
199
+ (
200
+ RemoveBannerAlert,
201
+ {"patient_filter": {"active": True}, "key": "testeroo"},
202
+ '{"patient": null, "patient_filter": {"active": true}, "key": "testeroo"}',
187
203
  ),
188
204
  ],
189
205
  )
@@ -204,7 +220,7 @@ def test_banner_alert_apply_method_succeeds_with_all_required_fields(
204
220
  AddBannerAlert,
205
221
  [
206
222
  "5 validation errors for AddBannerAlert",
207
- "Field 'patient_id' is required to apply an AddBannerAlert [type=missing",
223
+ "Field 'patient_id' or 'patient_filter' is required to apply an AddBannerAlert [type=missing",
208
224
  "Field 'key' is required to apply an AddBannerAlert [type=missing",
209
225
  "Field 'narrative' is required to apply an AddBannerAlert [type=missing",
210
226
  "Field 'placement' is required to apply an AddBannerAlert [type=missing",
@@ -215,7 +231,7 @@ def test_banner_alert_apply_method_succeeds_with_all_required_fields(
215
231
  RemoveBannerAlert,
216
232
  [
217
233
  "2 validation errors for RemoveBannerAlert",
218
- "Field 'patient_id' is required to apply a RemoveBannerAlert [type=missing",
234
+ "Field 'patient_id' or 'patient_filter' is required to apply a RemoveBannerAlert [type=missing",
219
235
  "Field 'key' is required to apply a RemoveBannerAlert [type=missing",
220
236
  ],
221
237
  ),
@@ -10,6 +10,8 @@ class _BaseEffect(Model):
10
10
  A Canvas Effect that changes user behavior or autonomously performs activities on behalf of users.
11
11
  """
12
12
 
13
+ patient_filter: dict | None = None
14
+
13
15
  class Meta:
14
16
  effect_type = EffectType.UNKNOWN_EFFECT
15
17
 
@@ -42,7 +42,7 @@ class ProtocolCard(_BaseEffect):
42
42
 
43
43
  class Meta:
44
44
  effect_type = EffectType.ADD_OR_UPDATE_PROTOCOL_CARD
45
- apply_required_fields = ("patient_id", "key")
45
+ apply_required_fields = ("patient_id|patient_filter", "key")
46
46
 
47
47
  patient_id: str | None = None
48
48
  key: str | None = None
@@ -68,7 +68,12 @@ class ProtocolCard(_BaseEffect):
68
68
  @property
69
69
  def effect_payload(self) -> dict[str, Any]:
70
70
  """The payload of the effect."""
71
- return {"patient": self.patient_id, "key": self.key, "data": self.values}
71
+ return {
72
+ "patient": self.patient_id,
73
+ "patient_filter": self.patient_filter,
74
+ "key": self.key,
75
+ "data": self.values,
76
+ }
72
77
 
73
78
  def add_recommendation(
74
79
  self,
@@ -25,7 +25,7 @@ def test_apply_method_succeeds_with_patient_id_and_key() -> None:
25
25
  applied = p.apply()
26
26
  assert (
27
27
  applied.payload
28
- == '{"patient": "uuid", "key": "something-unique", "data": {"title": "", "narrative": "", "recommendations": [], "status": "due", "feedback_enabled": false}}'
28
+ == '{"patient": "uuid", "patient_filter": null, "key": "something-unique", "data": {"title": "", "narrative": "", "recommendations": [], "status": "due", "feedback_enabled": false}}'
29
29
  )
30
30
 
31
31
 
@@ -38,7 +38,7 @@ def test_apply_method_raises_error_without_patient_id_and_key() -> None:
38
38
 
39
39
  assert "2 validation errors for ProtocolCard" in err_msg
40
40
  assert (
41
- "Field 'patient_id' is required to apply a ProtocolCard [type=missing, input_value=None, input_type=NoneType]"
41
+ "Field 'patient_id' or 'patient_filter' is required to apply a ProtocolCard [type=missing, input_value=None, input_type=NoneType]"
42
42
  in err_msg
43
43
  )
44
44
  assert (
@@ -1,5 +1,5 @@
1
1
  from collections.abc import Container
2
- from typing import TYPE_CHECKING, Type, cast
2
+ from typing import TYPE_CHECKING, Self, Type, cast
3
3
 
4
4
  from django.db import models
5
5
  from django.db.models import Q
@@ -11,25 +11,28 @@ if TYPE_CHECKING:
11
11
  class CommittableModelManager(models.Manager):
12
12
  """A manager for commands that can be committed."""
13
13
 
14
- def get_queryset(self) -> "models.QuerySet":
14
+ def get_queryset(self) -> "CommittableQuerySet":
15
15
  """Return a queryset that filters out deleted objects."""
16
16
  # TODO: Should we just filter these out at the view level?
17
- return super().get_queryset().filter(deleted=False)
17
+ return CommittableQuerySet(self.model, using=self._db).filter(deleted=False)
18
18
 
19
- def committed(self) -> "models.QuerySet":
19
+
20
+ class CommittableQuerySet(models.QuerySet):
21
+ """A queryset for committable objects."""
22
+
23
+ def committed(self) -> "Self":
20
24
  """Return a queryset that filters for objects that have been committed."""
21
- # The committer_id IS set, and the entered_in_error_id IS NOT set
22
25
  return self.filter(committer_id__isnull=False, entered_in_error_id__isnull=True)
23
26
 
24
- def for_patient(self, patient_id: str) -> "models.QuerySet":
27
+ def for_patient(self, patient_id: str) -> "Self":
25
28
  """Return a queryset that filters objects for a specific patient."""
26
29
  return self.filter(patient__id=patient_id)
27
30
 
28
31
 
29
- class ValueSetLookupQuerySet(models.QuerySet):
32
+ class ValueSetLookupQuerySet(CommittableQuerySet):
30
33
  """A QuerySet that can filter objects based on a ValueSet."""
31
34
 
32
- def find(self, value_set: Type["ValueSet"]) -> models.QuerySet:
35
+ def find(self, value_set: Type["ValueSet"]) -> "Self":
33
36
  """
34
37
  Filters conditions, medications, etc. to those found in the inherited ValueSet class that is passed.
35
38
 
@@ -1,6 +1,10 @@
1
1
  from django.db import models
2
2
 
3
- from canvas_sdk.v1.data.base import CommittableModelManager, ValueSetLookupQuerySet
3
+ from canvas_sdk.v1.data.base import (
4
+ CommittableModelManager,
5
+ CommittableQuerySet,
6
+ ValueSetLookupQuerySet,
7
+ )
4
8
  from canvas_sdk.v1.data.patient import Patient
5
9
  from canvas_sdk.v1.data.user import CanvasUser
6
10
 
@@ -19,7 +23,7 @@ class Condition(models.Model):
19
23
  app_label = "canvas_sdk"
20
24
  db_table = "canvas_sdk_data_api_condition_001"
21
25
 
22
- objects = CommittableModelManager.from_queryset(ConditionQuerySet)()
26
+ objects = ConditionQuerySet.as_manager()
23
27
 
24
28
  id = models.UUIDField()
25
29
  dbid = models.BigIntegerField(primary_key=True)
@@ -1,4 +1,4 @@
1
- from typing import Self
1
+ from typing import TYPE_CHECKING, Self
2
2
 
3
3
  from django.db import models
4
4
 
@@ -40,7 +40,7 @@ class ProtocolOverride(models.Model):
40
40
  patient = models.ForeignKey(
41
41
  Patient,
42
42
  on_delete=models.DO_NOTHING,
43
- related_name="allergy_intolerances",
43
+ related_name="protocol_overrides",
44
44
  )
45
45
  protocol_key = models.CharField()
46
46
  is_adjustment = models.BooleanField()
plugin_runner/sandbox.py CHANGED
@@ -59,6 +59,7 @@ ALLOWED_MODULES = frozenset(
59
59
  "operator",
60
60
  "pickletools",
61
61
  "random",
62
+ "rapidfuzz",
62
63
  "re",
63
64
  "requests",
64
65
  "string",
File without changes