canvas 0.1.15__py3-none-any.whl → 0.2.10__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.

Files changed (94) hide show
  1. {canvas-0.1.15.dist-info → canvas-0.2.10.dist-info}/METADATA +7 -1
  2. canvas-0.2.10.dist-info/RECORD +143 -0
  3. canvas_cli/apps/plugin/plugin.py +51 -9
  4. canvas_cli/apps/plugin/tests.py +51 -0
  5. canvas_cli/tests.py +193 -4
  6. canvas_cli/utils/validators/manifest_schema.py +1 -0
  7. canvas_generated/messages/effects_pb2.py +2 -2
  8. canvas_generated/messages/effects_pb2.pyi +138 -0
  9. canvas_generated/messages/events_pb2.py +3 -3
  10. canvas_generated/messages/events_pb2.pyi +616 -0
  11. canvas_sdk/__init__.py +7 -0
  12. canvas_sdk/base.py +6 -2
  13. canvas_sdk/commands/__init__.py +26 -0
  14. canvas_sdk/commands/base.py +35 -32
  15. canvas_sdk/commands/commands/allergy.py +49 -0
  16. canvas_sdk/commands/commands/assess.py +1 -1
  17. canvas_sdk/commands/commands/close_goal.py +22 -0
  18. canvas_sdk/commands/commands/diagnose.py +3 -3
  19. canvas_sdk/commands/commands/family_history.py +18 -0
  20. canvas_sdk/commands/commands/goal.py +3 -3
  21. canvas_sdk/commands/commands/history_present_illness.py +1 -1
  22. canvas_sdk/commands/commands/instruct.py +17 -0
  23. canvas_sdk/commands/commands/lab_order.py +33 -0
  24. canvas_sdk/commands/commands/medical_history.py +34 -0
  25. canvas_sdk/commands/commands/medication_statement.py +1 -1
  26. canvas_sdk/commands/commands/past_surgical_history.py +28 -0
  27. canvas_sdk/commands/commands/perform.py +17 -0
  28. canvas_sdk/commands/commands/plan.py +2 -2
  29. canvas_sdk/commands/commands/prescribe.py +10 -7
  30. canvas_sdk/commands/commands/questionnaire.py +1 -1
  31. canvas_sdk/commands/commands/refill.py +16 -0
  32. canvas_sdk/commands/commands/remove_allergy.py +26 -0
  33. canvas_sdk/commands/commands/stop_medication.py +1 -1
  34. canvas_sdk/commands/commands/task.py +52 -0
  35. canvas_sdk/commands/commands/update_diagnosis.py +27 -0
  36. canvas_sdk/commands/commands/update_goal.py +1 -1
  37. canvas_sdk/commands/commands/vitals.py +78 -0
  38. canvas_sdk/commands/constants.py +7 -0
  39. canvas_sdk/commands/tests/protocol/__init__.py +0 -0
  40. canvas_sdk/commands/tests/protocol/tests.py +55 -0
  41. canvas_sdk/commands/tests/schema/__init__.py +0 -0
  42. canvas_sdk/commands/tests/schema/tests.py +104 -0
  43. canvas_sdk/commands/tests/test_utils.py +170 -6
  44. canvas_sdk/commands/tests/unit/__init__.py +0 -0
  45. canvas_sdk/commands/tests/{tests.py → unit/tests.py} +20 -194
  46. canvas_sdk/data/client.py +82 -0
  47. canvas_sdk/data/patient.py +1 -21
  48. canvas_sdk/effects/banner_alert/add_banner_alert.py +8 -7
  49. canvas_sdk/effects/banner_alert/remove_banner_alert.py +3 -2
  50. canvas_sdk/effects/banner_alert/tests.py +224 -0
  51. canvas_sdk/effects/base.py +3 -5
  52. canvas_sdk/effects/patient_chart_summary_configuration.py +39 -0
  53. canvas_sdk/effects/protocol_card/__init__.py +1 -0
  54. canvas_sdk/effects/protocol_card/protocol_card.py +83 -0
  55. canvas_sdk/effects/protocol_card/tests.py +184 -0
  56. canvas_sdk/handlers/base.py +14 -1
  57. canvas_sdk/protocols/base.py +14 -0
  58. canvas_sdk/protocols/clinical_quality_measure.py +41 -0
  59. canvas_sdk/utils/db.py +17 -0
  60. canvas_sdk/v1/__init__.py +0 -0
  61. canvas_sdk/v1/data/__init__.py +3 -0
  62. canvas_sdk/v1/data/allergy_intolerance.py +63 -0
  63. canvas_sdk/v1/data/base.py +47 -0
  64. canvas_sdk/v1/data/condition.py +48 -0
  65. canvas_sdk/v1/data/lab.py +96 -0
  66. canvas_sdk/v1/data/medication.py +54 -0
  67. canvas_sdk/v1/data/patient.py +49 -0
  68. canvas_sdk/v1/data/user.py +10 -0
  69. canvas_sdk/value_set/tests/test_value_sets.py +65 -0
  70. canvas_sdk/value_set/v2022/adverse_event.py +33 -0
  71. canvas_sdk/value_set/v2022/allergy.py +232 -0
  72. canvas_sdk/value_set/v2022/assessment.py +215 -0
  73. canvas_sdk/value_set/v2022/communication.py +325 -0
  74. canvas_sdk/value_set/v2022/condition.py +40654 -0
  75. canvas_sdk/value_set/v2022/device.py +174 -0
  76. canvas_sdk/value_set/v2022/diagnostic_study.py +4967 -0
  77. canvas_sdk/value_set/v2022/encounter.py +2564 -0
  78. canvas_sdk/value_set/v2022/immunization.py +341 -0
  79. canvas_sdk/value_set/v2022/individual_characteristic.py +307 -0
  80. canvas_sdk/value_set/v2022/intervention.py +1356 -0
  81. canvas_sdk/value_set/v2022/laboratory_test.py +1250 -0
  82. canvas_sdk/value_set/v2022/medication.py +5130 -0
  83. canvas_sdk/value_set/v2022/physical_exam.py +201 -0
  84. canvas_sdk/value_set/v2022/procedure.py +4037 -0
  85. canvas_sdk/value_set/v2022/symptom.py +176 -0
  86. canvas_sdk/value_set/value_set.py +91 -0
  87. canvas-0.1.15.dist-info/RECORD +0 -95
  88. canvas_generated/data_access_layer/data_access_layer_pb2.py +0 -30
  89. canvas_generated/data_access_layer/data_access_layer_pb2.pyi +0 -23
  90. canvas_generated/data_access_layer/data_access_layer_pb2_grpc.py +0 -66
  91. canvas_sdk/data/data_access_layer_client.py +0 -95
  92. canvas_sdk/data/exceptions.py +0 -16
  93. {canvas-0.1.15.dist-info → canvas-0.2.10.dist-info}/WHEEL +0 -0
  94. {canvas-0.1.15.dist-info → canvas-0.2.10.dist-info}/entry_points.txt +0 -0
@@ -1,11 +1,7 @@
1
- import decimal
2
- from datetime import datetime
3
-
4
1
  import pytest
5
- import requests
6
2
  from pydantic import ValidationError
3
+ from typer.testing import CliRunner
7
4
 
8
- import settings
9
5
  from canvas_sdk.commands import (
10
6
  AssessCommand,
11
7
  DiagnoseCommand,
@@ -19,15 +15,16 @@ from canvas_sdk.commands import (
19
15
  StopMedicationCommand,
20
16
  UpdateGoalCommand,
21
17
  )
22
- from canvas_sdk.commands.constants import Coding
18
+ from canvas_sdk.commands.base import _BaseCommand
23
19
  from canvas_sdk.commands.tests.test_utils import (
24
- MaskedValue,
25
20
  fake,
26
21
  get_field_type,
27
22
  raises_none_error_for_effect_method,
28
23
  raises_wrong_type_error,
29
24
  )
30
25
 
26
+ runner = CliRunner()
27
+
31
28
 
32
29
  @pytest.mark.parametrize(
33
30
  "Command,fields_to_test",
@@ -50,7 +47,7 @@ from canvas_sdk.commands.tests.test_utils import (
50
47
  ),
51
48
  (HistoryOfPresentIllnessCommand, ("narrative",)),
52
49
  (MedicationStatementCommand, ("fdb_code", "sig")),
53
- (PlanCommand, ("narrative", "user_id", "command_uuid")),
50
+ (PlanCommand, ("narrative", "command_uuid")),
54
51
  (
55
52
  PrescribeCommand,
56
53
  (
@@ -82,19 +79,7 @@ from canvas_sdk.commands.tests.test_utils import (
82
79
  ],
83
80
  )
84
81
  def test_command_raises_generic_error_when_kwarg_given_incorrect_type(
85
- Command: (
86
- AssessCommand
87
- | DiagnoseCommand
88
- | GoalCommand
89
- | HistoryOfPresentIllnessCommand
90
- | MedicationStatementCommand
91
- | PlanCommand
92
- | PrescribeCommand
93
- | QuestionnaireCommand
94
- | ReasonForVisitCommand
95
- | StopMedicationCommand
96
- | UpdateGoalCommand
97
- ),
82
+ Command: _BaseCommand,
98
83
  fields_to_test: tuple[str],
99
84
  ) -> None:
100
85
  for field in fields_to_test:
@@ -109,29 +94,22 @@ def test_command_raises_generic_error_when_kwarg_given_incorrect_type(
109
94
  [
110
95
  (
111
96
  PlanCommand,
112
- {"narrative": "yo", "user_id": 5, "note_uuid": 1},
97
+ {"narrative": "yo", "note_uuid": 1},
113
98
  "1 validation error for PlanCommand\nnote_uuid\n Input should be a valid string [type=string_type",
114
- {"narrative": "yo", "note_uuid": "00000000-0000-0000-0000-000000000000", "user_id": 1},
99
+ {"narrative": "yo", "note_uuid": "00000000-0000-0000-0000-000000000000"},
115
100
  ),
116
101
  (
117
102
  PlanCommand,
118
- {"narrative": "yo", "user_id": 5, "note_uuid": "5", "command_uuid": 5},
103
+ {"narrative": "yo", "note_uuid": "5", "command_uuid": 5},
119
104
  "1 validation error for PlanCommand\ncommand_uuid\n Input should be a valid string [type=string_type",
120
- {"narrative": "yo", "user_id": 5, "note_uuid": "5", "command_uuid": "5"},
121
- ),
122
- (
123
- PlanCommand,
124
- {"narrative": "yo", "note_uuid": "5", "command_uuid": "4", "user_id": "5"},
125
- "1 validation error for PlanCommand\nuser_id\n Input should be a valid integer [type=int_type",
126
- {"narrative": "yo", "note_uuid": "5", "command_uuid": "4", "user_id": 5},
105
+ {"narrative": "yo", "note_uuid": "5", "command_uuid": "5"},
127
106
  ),
128
107
  (
129
108
  ReasonForVisitCommand,
130
- {"note_uuid": "00000000-0000-0000-0000-000000000000", "user_id": 1, "structured": True},
109
+ {"note_uuid": "00000000-0000-0000-0000-000000000000", "structured": True},
131
110
  "1 validation error for ReasonForVisitCommand\n Structured RFV should have a coding",
132
111
  {
133
112
  "note_uuid": "00000000-0000-0000-0000-000000000000",
134
- "user_id": 1,
135
113
  "structured": False,
136
114
  },
137
115
  ),
@@ -139,71 +117,64 @@ def test_command_raises_generic_error_when_kwarg_given_incorrect_type(
139
117
  ReasonForVisitCommand,
140
118
  {
141
119
  "note_uuid": "00000000-0000-0000-0000-000000000000",
142
- "user_id": 1,
143
120
  "coding": {"code": "x"},
144
121
  },
145
122
  "1 validation error for ReasonForVisitCommand\ncoding.system\n Field required [type=missing",
146
- {"note_uuid": "00000000-0000-0000-0000-000000000000", "user_id": 1},
123
+ {"note_uuid": "00000000-0000-0000-0000-000000000000"},
147
124
  ),
148
125
  (
149
126
  ReasonForVisitCommand,
150
127
  {
151
128
  "note_uuid": "00000000-0000-0000-0000-000000000000",
152
- "user_id": 1,
153
129
  "coding": {"code": 1, "system": "y"},
154
130
  },
155
131
  "1 validation error for ReasonForVisitCommand\ncoding.code\n Input should be a valid string [type=string_type",
156
- {"note_uuid": "00000000-0000-0000-0000-000000000000", "user_id": 1},
132
+ {"note_uuid": "00000000-0000-0000-0000-000000000000"},
157
133
  ),
158
134
  (
159
135
  ReasonForVisitCommand,
160
136
  {
161
137
  "note_uuid": "00000000-0000-0000-0000-000000000000",
162
- "user_id": 1,
163
138
  "coding": {"code": None, "system": "y"},
164
139
  },
165
140
  "1 validation error for ReasonForVisitCommand\ncoding.code\n Input should be a valid string [type=string_type",
166
- {"note_uuid": "00000000-0000-0000-0000-000000000000", "user_id": 1},
141
+ {"note_uuid": "00000000-0000-0000-0000-000000000000"},
167
142
  ),
168
143
  (
169
144
  ReasonForVisitCommand,
170
145
  {
171
146
  "note_uuid": "00000000-0000-0000-0000-000000000000",
172
- "user_id": 1,
173
147
  "coding": {"system": "y"},
174
148
  },
175
149
  "1 validation error for ReasonForVisitCommand\ncoding.code\n Field required [type=missing",
176
- {"note_uuid": "00000000-0000-0000-0000-000000000000", "user_id": 1},
150
+ {"note_uuid": "00000000-0000-0000-0000-000000000000"},
177
151
  ),
178
152
  (
179
153
  ReasonForVisitCommand,
180
154
  {
181
155
  "note_uuid": "00000000-0000-0000-0000-000000000000",
182
- "user_id": 1,
183
156
  "coding": {"code": "x", "system": 1},
184
157
  },
185
158
  "1 validation error for ReasonForVisitCommand\ncoding.system\n Input should be a valid string [type=string_type",
186
- {"note_uuid": "00000000-0000-0000-0000-000000000000", "user_id": 1},
159
+ {"note_uuid": "00000000-0000-0000-0000-000000000000"},
187
160
  ),
188
161
  (
189
162
  ReasonForVisitCommand,
190
163
  {
191
164
  "note_uuid": "00000000-0000-0000-0000-000000000000",
192
- "user_id": 1,
193
165
  "coding": {"code": "x", "system": None},
194
166
  },
195
167
  "1 validation error for ReasonForVisitCommand\ncoding.system\n Input should be a valid string [type=string_type",
196
- {"note_uuid": "00000000-0000-0000-0000-000000000000", "user_id": 1},
168
+ {"note_uuid": "00000000-0000-0000-0000-000000000000"},
197
169
  ),
198
170
  (
199
171
  ReasonForVisitCommand,
200
172
  {
201
173
  "note_uuid": "00000000-0000-0000-0000-000000000000",
202
- "user_id": 1,
203
174
  "coding": {"code": "x", "system": "y", "display": 1},
204
175
  },
205
176
  "1 validation error for ReasonForVisitCommand\ncoding.display\n Input should be a valid string [type=string_type",
206
- {"note_uuid": "00000000-0000-0000-0000-000000000000", "user_id": 1},
177
+ {"note_uuid": "00000000-0000-0000-0000-000000000000"},
207
178
  ),
208
179
  ],
209
180
  )
@@ -251,7 +222,7 @@ def test_command_raises_specific_error_when_kwarg_given_incorrect_type(
251
222
  ),
252
223
  (HistoryOfPresentIllnessCommand, ("narrative",)),
253
224
  (MedicationStatementCommand, ("fdb_code", "sig")),
254
- (PlanCommand, ("narrative", "user_id", "command_uuid", "note_uuid")),
225
+ (PlanCommand, ("narrative", "command_uuid", "note_uuid")),
255
226
  (
256
227
  PrescribeCommand,
257
228
  (
@@ -284,19 +255,7 @@ def test_command_raises_specific_error_when_kwarg_given_incorrect_type(
284
255
  ],
285
256
  )
286
257
  def test_command_allows_kwarg_with_correct_type(
287
- Command: (
288
- AssessCommand
289
- | DiagnoseCommand
290
- | GoalCommand
291
- | HistoryOfPresentIllnessCommand
292
- | MedicationStatementCommand
293
- | PlanCommand
294
- | PrescribeCommand
295
- | QuestionnaireCommand
296
- | ReasonForVisitCommand
297
- | StopMedicationCommand
298
- | UpdateGoalCommand
299
- ),
258
+ Command: _BaseCommand,
300
259
  fields_to_test: tuple[str],
301
260
  ) -> None:
302
261
  schema = Command.model_json_schema()
@@ -323,136 +282,3 @@ def test_command_allows_kwarg_with_correct_type(
323
282
  cmd = Command(**base)
324
283
  effect = getattr(cmd, method)()
325
284
  assert effect is not None
326
-
327
-
328
- @pytest.fixture(scope="session")
329
- def token() -> MaskedValue:
330
- return MaskedValue(
331
- requests.post(
332
- f"{settings.INTEGRATION_TEST_URL}/auth/token/",
333
- headers={"Content-Type": "application/x-www-form-urlencoded"},
334
- data={
335
- "grant_type": "client_credentials",
336
- "client_id": settings.INTEGRATION_TEST_CLIENT_ID,
337
- "client_secret": settings.INTEGRATION_TEST_CLIENT_SECRET,
338
- },
339
- ).json()["access_token"]
340
- )
341
-
342
-
343
- @pytest.fixture
344
- def note_uuid(token: MaskedValue) -> str:
345
- headers = {
346
- "Authorization": f"Bearer {token.value}",
347
- "Content-Type": "application/json",
348
- "Accept": "application/json",
349
- }
350
- data = {
351
- "patient": 1,
352
- "provider": 1,
353
- "note_type": "office",
354
- "note_type_version": 1,
355
- "lastModifiedBySessionKey": "8fee3c03a525cebee1d8a6b8e63dd4dg",
356
- }
357
- note = requests.post(
358
- f"{settings.INTEGRATION_TEST_URL}/api/Note/", headers=headers, json=data
359
- ).json()
360
- return note["externallyExposableId"]
361
-
362
-
363
- @pytest.fixture
364
- def command_type_map() -> dict[str, type]:
365
- return {
366
- "AutocompleteField": str,
367
- "MultiLineTextField": str,
368
- "TextField": str,
369
- "ChoiceField": str,
370
- "DateField": datetime,
371
- "ApproximateDateField": datetime,
372
- "IntegerField": int,
373
- "DecimalField": decimal.Decimal,
374
- }
375
-
376
-
377
- @pytest.mark.integtest
378
- @pytest.mark.parametrize(
379
- "Command",
380
- [
381
- (AssessCommand),
382
- (DiagnoseCommand),
383
- (GoalCommand),
384
- (HistoryOfPresentIllnessCommand),
385
- (MedicationStatementCommand),
386
- (PlanCommand),
387
- (PrescribeCommand),
388
- (QuestionnaireCommand),
389
- (ReasonForVisitCommand),
390
- (StopMedicationCommand),
391
- (UpdateGoalCommand),
392
- ],
393
- )
394
- def test_command_schema_matches_command_api(
395
- token: MaskedValue,
396
- command_type_map: dict[str, str],
397
- note_uuid: str,
398
- Command: (
399
- AssessCommand
400
- | DiagnoseCommand
401
- | GoalCommand
402
- | HistoryOfPresentIllnessCommand
403
- | MedicationStatementCommand
404
- | PlanCommand
405
- | PrescribeCommand
406
- | QuestionnaireCommand
407
- | ReasonForVisitCommand
408
- | StopMedicationCommand
409
- | UpdateGoalCommand
410
- ),
411
- ) -> None:
412
- # first create the command in the new note
413
- data = {"noteKey": note_uuid, "schemaKey": Command.Meta.key}
414
- headers = {"Authorization": f"Bearer {token.value}"}
415
- url = f"{settings.INTEGRATION_TEST_URL}/core/api/v1/commands/"
416
- command_resp = requests.post(url, headers=headers, data=data).json()
417
- assert "uuid" in command_resp
418
- command_uuid = command_resp["uuid"]
419
-
420
- # next, request the fields of the newly created command
421
- url = f"{settings.INTEGRATION_TEST_URL}/core/api/v1/commands/{command_uuid}/fields/"
422
- command_fields_resp = requests.get(url, headers=headers).json()
423
- assert command_fields_resp["schema"] == Command.Meta.key
424
-
425
- command_fields = command_fields_resp["fields"]
426
- if Command.Meta.key == "questionnaire":
427
- # questionnaire's fields vary per questionnaire, so just check the first two fields which never vary
428
- command_fields = command_fields[:2]
429
- expected_fields = Command.command_schema()
430
- assert len(command_fields) == len(expected_fields)
431
-
432
- for actual_field in command_fields:
433
- name = actual_field["name"]
434
- assert name in expected_fields
435
- expected_field = expected_fields[name]
436
-
437
- assert expected_field["required"] == actual_field["required"]
438
-
439
- expected_type = expected_field["type"]
440
- if expected_type is Coding:
441
- expected_type = expected_type.__annotations__["code"]
442
-
443
- actual_type = command_type_map.get(actual_field["type"])
444
- if actual_field["type"] == "AutocompleteField" and name[-1] == "s":
445
- # this condition initially created for Prescribe.indications,
446
- # but could apply to other AutocompleteField fields that are lists
447
- # making the assumption here that if the field ends in 's' (like indications), it is a list
448
- actual_type = list[actual_type] # type: ignore
449
-
450
- assert expected_type == actual_type
451
-
452
- if (choices := actual_field["choices"]) is None:
453
- assert expected_field["choices"] is None
454
- continue
455
-
456
- assert len(expected_field["choices"]) == len(choices)
457
- for choice in choices:
458
- assert choice["value"] in expected_field["choices"]
@@ -0,0 +1,82 @@
1
+ from typing import Any, cast
2
+
3
+ from gql import Client, gql
4
+ from gql.transport.aiohttp import AIOHTTPTransport
5
+
6
+ from settings import GRAPHQL_ENDPOINT
7
+
8
+
9
+ class _CanvasGQLClient:
10
+ """
11
+ This is a GraphQL client that can be used to query home-app in order to fetch data for use in plugins.
12
+
13
+ Usage Examples:
14
+
15
+ A query with no parameters:
16
+
17
+ TEST_QUERY_NO_PARAMS = '''
18
+ query PatientsAll {
19
+ patients {
20
+ edges {
21
+ node {
22
+ firstName
23
+ lastName
24
+ birthDate
25
+ }
26
+ }
27
+ }
28
+ }
29
+ '''
30
+
31
+ client = _CanvasGQLClient()
32
+ result = client.query(TEST_QUERY_NO_PARAMS)
33
+ print(result) # returns dictionary
34
+
35
+ A query with parameters:
36
+
37
+ TEST_QUERY_WITH_PARAMS = '''
38
+ query PatientGet($patientKey: String!) {
39
+ patient(patientKey: $patientKey) {
40
+ firstName
41
+ lastName
42
+ birthDate
43
+ }
44
+ }
45
+ '''
46
+
47
+ client = _CanvasGQLClient()
48
+ result = client.query(TEST_QUERY_NO_PARAMS)
49
+ print(result)
50
+
51
+ For use in plugins, it is included in the instantiation of Protocol class. This means
52
+ it can simply be referred to as self.client in plugin code.
53
+ """
54
+
55
+ def __init__(self) -> None:
56
+ self.client = Client(
57
+ transport=AIOHTTPTransport(url=cast(str, GRAPHQL_ENDPOINT)),
58
+ # TODO: follow the documentation in the link below to specify a
59
+ # cached copy of the schema
60
+ # https://gql.readthedocs.io/en/stable/usage/validation.html#using-a-provided-schema
61
+ fetch_schema_from_transport=False,
62
+ )
63
+
64
+ def query(
65
+ self,
66
+ gql_query: str,
67
+ variables: dict[str, Any] | None = None,
68
+ extra_args: dict[str, Any] | None = None
69
+ ) -> dict[str, Any]:
70
+ if variables is None:
71
+ query_variables = {}
72
+ else:
73
+ query_variables = variables
74
+
75
+ return self.client.execute(
76
+ gql(gql_query),
77
+ variable_values=query_variables,
78
+ extra_args=extra_args,
79
+ )
80
+
81
+
82
+ GQL_CLIENT = _CanvasGQLClient()
@@ -1,26 +1,6 @@
1
- from datetime import date
2
- from typing import Self
3
-
4
1
  from canvas_sdk.data import DataModel
5
2
 
6
- from .data_access_layer_client import DAL_CLIENT
7
-
8
3
 
9
4
  class Patient(DataModel):
10
- """Patient model."""
11
-
12
5
  id: str | None = None
13
- first_name: str | None = None
14
- last_name: str | None = None
15
- birth_date: date | None = None
16
-
17
- @classmethod
18
- def get(cls, id: str) -> Self:
19
- """Given an ID, get the Patient from the Data Access Layer."""
20
- patient = DAL_CLIENT.get_patient(id)
21
- return cls(
22
- id=patient.id,
23
- first_name=patient.first_name or None,
24
- last_name=patient.last_name or None,
25
- birth_date=date.fromisoformat(patient.birth_date) if patient.birth_date else None,
26
- )
6
+ # TODO - populate more attributes
@@ -13,6 +13,7 @@ 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
17
 
17
18
  class Placement(Enum):
18
19
  CHART = "chart"
@@ -26,11 +27,11 @@ class AddBannerAlert(_BaseEffect):
26
27
  WARNING = "warning"
27
28
  ALERT = "alert"
28
29
 
29
- patient_id: str
30
- key: str
31
- narrative: str = Field(max_length=90)
32
- placement: list[Placement] = Field(min_length=1)
33
- intent: Intent
30
+ patient_id: str | None = None
31
+ key: str | None = None
32
+ narrative: str | None = Field(max_length=90, default=None)
33
+ placement: list[Placement] | None = Field(min_length=1, default=None)
34
+ intent: Intent | None = None
34
35
  href: str | None = None
35
36
 
36
37
  @property
@@ -38,8 +39,8 @@ class AddBannerAlert(_BaseEffect):
38
39
  """The BannerAlert's values."""
39
40
  return {
40
41
  "narrative": self.narrative,
41
- "placement": [p.value for p in self.placement],
42
- "intent": self.intent.value,
42
+ "placement": [p.value for p in self.placement] if self.placement else None,
43
+ "intent": self.intent.value if self.intent else None,
43
44
  "href": self.href,
44
45
  }
45
46
 
@@ -10,9 +10,10 @@ 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
14
 
14
- patient_id: str
15
- key: str
15
+ patient_id: str | None = None
16
+ key: str | None = None
16
17
 
17
18
  @property
18
19
  def effect_payload(self) -> dict[str, Any]: