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.
- {canvas-0.5.0.dist-info → canvas-0.6.0.dist-info}/METADATA +2 -1
- {canvas-0.5.0.dist-info → canvas-0.6.0.dist-info}/RECORD +17 -17
- canvas_sdk/base.py +12 -9
- canvas_sdk/commands/base.py +2 -1
- canvas_sdk/effects/banner_alert/add_banner_alert.py +13 -2
- canvas_sdk/effects/banner_alert/remove_banner_alert.py +2 -2
- canvas_sdk/effects/banner_alert/tests.py +20 -4
- canvas_sdk/effects/base.py +2 -0
- canvas_sdk/effects/protocol_card/protocol_card.py +7 -2
- canvas_sdk/effects/protocol_card/tests.py +2 -2
- canvas_sdk/v1/data/base.py +11 -8
- canvas_sdk/v1/data/condition.py +6 -2
- canvas_sdk/v1/data/patient.py +1 -1
- canvas_sdk/v1/data/protocol_override.py +1 -1
- plugin_runner/sandbox.py +1 -0
- {canvas-0.5.0.dist-info → canvas-0.6.0.dist-info}/WHEEL +0 -0
- {canvas-0.5.0.dist-info → canvas-0.6.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: canvas
|
|
3
|
-
Version: 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=
|
|
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=
|
|
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=
|
|
151
|
-
canvas_sdk/effects/banner_alert/remove_banner_alert.py,sha256=
|
|
152
|
-
canvas_sdk/effects/banner_alert/tests.py,sha256=
|
|
153
|
-
canvas_sdk/effects/base.py,sha256=
|
|
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=
|
|
157
|
-
canvas_sdk/effects/protocol_card/tests.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
185
|
-
canvas_sdk/v1/data/protocol_override.py,sha256=
|
|
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=
|
|
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.
|
|
253
|
-
canvas-0.
|
|
254
|
-
canvas-0.
|
|
255
|
-
canvas-0.
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
for
|
|
44
|
-
|
|
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)
|
canvas_sdk/commands/base.py
CHANGED
|
@@ -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 = (
|
|
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 {
|
|
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
|
),
|
canvas_sdk/effects/base.py
CHANGED
|
@@ -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 {
|
|
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 (
|
canvas_sdk/v1/data/base.py
CHANGED
|
@@ -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) -> "
|
|
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
|
|
17
|
+
return CommittableQuerySet(self.model, using=self._db).filter(deleted=False)
|
|
18
18
|
|
|
19
|
-
|
|
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) -> "
|
|
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(
|
|
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"]) ->
|
|
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
|
|
canvas_sdk/v1/data/condition.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
from django.db import models
|
|
2
2
|
|
|
3
|
-
from canvas_sdk.v1.data.base import
|
|
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 =
|
|
26
|
+
objects = ConditionQuerySet.as_manager()
|
|
23
27
|
|
|
24
28
|
id = models.UUIDField()
|
|
25
29
|
dbid = models.BigIntegerField(primary_key=True)
|
canvas_sdk/v1/data/patient.py
CHANGED
|
@@ -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="
|
|
43
|
+
related_name="protocol_overrides",
|
|
44
44
|
)
|
|
45
45
|
protocol_key = models.CharField()
|
|
46
46
|
is_adjustment = models.BooleanField()
|
plugin_runner/sandbox.py
CHANGED
|
File without changes
|
|
File without changes
|