canvas 0.32.0__py3-none-any.whl → 0.33.1__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 (257) hide show
  1. {canvas-0.32.0.dist-info → canvas-0.33.1.dist-info}/METADATA +2 -1
  2. canvas-0.33.1.dist-info/RECORD +272 -0
  3. canvas_generated/messages/effects_pb2.py +2 -2
  4. canvas_generated/messages/effects_pb2.pyi +4 -0
  5. canvas_sdk/__init__.py +3 -0
  6. canvas_sdk/commands/__init__.py +1 -1
  7. canvas_sdk/commands/base.py +3 -0
  8. canvas_sdk/commands/commands/__init__.py +1 -0
  9. canvas_sdk/commands/commands/adjust_prescription.py +3 -0
  10. canvas_sdk/commands/commands/allergy.py +7 -0
  11. canvas_sdk/commands/commands/assess.py +2 -0
  12. canvas_sdk/commands/commands/close_goal.py +3 -0
  13. canvas_sdk/commands/commands/diagnose.py +3 -0
  14. canvas_sdk/commands/commands/exam.py +3 -0
  15. canvas_sdk/commands/commands/family_history.py +3 -0
  16. canvas_sdk/commands/commands/follow_up.py +3 -0
  17. canvas_sdk/commands/commands/goal.py +3 -0
  18. canvas_sdk/commands/commands/history_present_illness.py +3 -0
  19. canvas_sdk/commands/commands/imaging_order.py +3 -0
  20. canvas_sdk/commands/commands/instruct.py +3 -0
  21. canvas_sdk/commands/commands/lab_order.py +3 -0
  22. canvas_sdk/commands/commands/medical_history.py +3 -0
  23. canvas_sdk/commands/commands/medication_statement.py +2 -0
  24. canvas_sdk/commands/commands/past_surgical_history.py +3 -0
  25. canvas_sdk/commands/commands/perform.py +3 -0
  26. canvas_sdk/commands/commands/plan.py +3 -0
  27. canvas_sdk/commands/commands/prescribe.py +8 -0
  28. canvas_sdk/commands/commands/questionnaire/__init__.py +3 -13
  29. canvas_sdk/commands/commands/questionnaire/question.py +10 -0
  30. canvas_sdk/commands/commands/reason_for_visit.py +3 -0
  31. canvas_sdk/commands/commands/refer.py +3 -0
  32. canvas_sdk/commands/commands/refill.py +3 -0
  33. canvas_sdk/commands/commands/remove_allergy.py +3 -0
  34. canvas_sdk/commands/commands/resolve_condition.py +3 -0
  35. canvas_sdk/commands/commands/review_of_systems.py +3 -0
  36. canvas_sdk/commands/commands/stop_medication.py +3 -0
  37. canvas_sdk/commands/commands/structured_assessment.py +3 -0
  38. canvas_sdk/commands/commands/task.py +7 -0
  39. canvas_sdk/commands/commands/update_diagnosis.py +3 -0
  40. canvas_sdk/commands/commands/update_goal.py +3 -0
  41. canvas_sdk/commands/commands/vitals.py +3 -0
  42. canvas_sdk/commands/constants.py +8 -0
  43. canvas_sdk/effects/__init__.py +1 -1
  44. canvas_sdk/effects/banner_alert/__init__.py +1 -1
  45. canvas_sdk/effects/banner_alert/add_banner_alert.py +3 -0
  46. canvas_sdk/effects/banner_alert/remove_banner_alert.py +3 -0
  47. canvas_sdk/effects/base.py +7 -0
  48. canvas_sdk/effects/billing_line_item/__init__.py +5 -1
  49. canvas_sdk/effects/billing_line_item/add_billing_line_item.py +3 -0
  50. canvas_sdk/effects/billing_line_item/remove_billing_line_item.py +3 -0
  51. canvas_sdk/effects/billing_line_item/update_billing_line_item.py +3 -0
  52. canvas_sdk/effects/launch_modal.py +3 -0
  53. canvas_sdk/effects/patient_chart_summary_configuration.py +3 -0
  54. canvas_sdk/effects/patient_portal/__init__.py +1 -0
  55. canvas_sdk/effects/patient_portal/application_configuration.py +3 -0
  56. canvas_sdk/effects/patient_portal/form_result.py +3 -0
  57. canvas_sdk/effects/patient_portal_menu_configuration.py +3 -0
  58. canvas_sdk/effects/patient_profile_configuration.py +6 -1
  59. canvas_sdk/effects/protocol_card/__init__.py +1 -1
  60. canvas_sdk/effects/protocol_card/protocol_card.py +6 -0
  61. canvas_sdk/effects/questionnaire_result.py +3 -0
  62. canvas_sdk/effects/send_invite.py +46 -0
  63. canvas_sdk/effects/show_button.py +3 -0
  64. canvas_sdk/effects/simple_api.py +9 -0
  65. canvas_sdk/effects/surescripts/__init__.py +2 -2
  66. canvas_sdk/effects/surescripts/surescripts_messages.py +7 -0
  67. canvas_sdk/effects/task/__init__.py +6 -1
  68. canvas_sdk/effects/task/task.py +8 -0
  69. canvas_sdk/effects/update_user.py +81 -0
  70. canvas_sdk/effects/widgets/__init__.py +1 -1
  71. canvas_sdk/effects/widgets/portal_widget.py +3 -0
  72. canvas_sdk/events/__init__.py +6 -1
  73. canvas_sdk/events/base.py +3 -0
  74. canvas_sdk/handlers/__init__.py +1 -1
  75. canvas_sdk/handlers/action_button.py +6 -0
  76. canvas_sdk/handlers/application.py +3 -0
  77. canvas_sdk/handlers/base.py +3 -0
  78. canvas_sdk/handlers/cron_task.py +3 -0
  79. canvas_sdk/handlers/simple_api/__init__.py +3 -2
  80. canvas_sdk/handlers/simple_api/api.py +26 -1
  81. canvas_sdk/handlers/simple_api/exceptions.py +10 -0
  82. canvas_sdk/handlers/simple_api/security.py +21 -5
  83. canvas_sdk/handlers/simple_api/tools.py +9 -0
  84. canvas_sdk/protocols/__init__.py +1 -1
  85. canvas_sdk/protocols/base.py +3 -0
  86. canvas_sdk/protocols/clinical_quality_measure.py +6 -1
  87. canvas_sdk/protocols/timeframe.py +3 -0
  88. canvas_sdk/questionnaires/__init__.py +1 -1
  89. canvas_sdk/questionnaires/utils.py +7 -0
  90. canvas_sdk/templates/__init__.py +1 -1
  91. canvas_sdk/templates/utils.py +3 -0
  92. canvas_sdk/utils/__init__.py +1 -1
  93. canvas_sdk/utils/http.py +69 -35
  94. canvas_sdk/utils/plugins.py +4 -0
  95. canvas_sdk/utils/stats.py +11 -0
  96. canvas_sdk/v1/__init__.py +1 -0
  97. canvas_sdk/v1/apps.py +3 -0
  98. canvas_sdk/v1/data/__init__.py +2 -2
  99. canvas_sdk/v1/data/allergy_intolerance.py +3 -0
  100. canvas_sdk/v1/data/appointment.py +7 -0
  101. canvas_sdk/v1/data/assessment.py +3 -0
  102. canvas_sdk/v1/data/banner_alert.py +3 -0
  103. canvas_sdk/v1/data/base.py +3 -0
  104. canvas_sdk/v1/data/billing.py +7 -0
  105. canvas_sdk/v1/data/care_team.py +7 -0
  106. canvas_sdk/v1/data/command.py +3 -0
  107. canvas_sdk/v1/data/common.py +18 -0
  108. canvas_sdk/v1/data/condition.py +7 -0
  109. canvas_sdk/v1/data/coverage.py +14 -0
  110. canvas_sdk/v1/data/detected_issue.py +3 -0
  111. canvas_sdk/v1/data/device.py +3 -0
  112. canvas_sdk/v1/data/imaging.py +7 -0
  113. canvas_sdk/v1/data/lab.py +16 -0
  114. canvas_sdk/v1/data/medication.py +3 -0
  115. canvas_sdk/v1/data/note.py +9 -0
  116. canvas_sdk/v1/data/observation.py +9 -0
  117. canvas_sdk/v1/data/organization.py +3 -0
  118. canvas_sdk/v1/data/patient.py +20 -3
  119. canvas_sdk/v1/data/practicelocation.py +7 -0
  120. canvas_sdk/v1/data/protocol_override.py +7 -0
  121. canvas_sdk/v1/data/questionnaire.py +16 -3
  122. canvas_sdk/v1/data/reason_for_visit.py +3 -0
  123. canvas_sdk/v1/data/staff.py +3 -0
  124. canvas_sdk/v1/data/task.py +12 -0
  125. canvas_sdk/v1/data/team.py +8 -1
  126. canvas_sdk/v1/data/user.py +5 -1
  127. canvas_sdk/v1/models.py +2 -0
  128. canvas_sdk/value_set/__init__.py +1 -0
  129. canvas_sdk/value_set/_utilities.py +16 -0
  130. canvas_sdk/value_set/custom.py +4 -0
  131. canvas_sdk/value_set/hcc2018.py +3 -0
  132. canvas_sdk/value_set/v2022/__init__.py +1 -0
  133. canvas_sdk/value_set/v2022/adverse_event.py +3 -0
  134. canvas_sdk/value_set/v2022/allergy.py +5 -0
  135. canvas_sdk/value_set/v2022/assessment.py +5 -0
  136. canvas_sdk/value_set/v2022/communication.py +5 -0
  137. canvas_sdk/value_set/v2022/condition.py +5 -0
  138. canvas_sdk/value_set/v2022/device.py +5 -0
  139. canvas_sdk/value_set/v2022/diagnostic_study.py +5 -0
  140. canvas_sdk/value_set/v2022/encounter.py +5 -0
  141. canvas_sdk/value_set/v2022/immunization.py +5 -0
  142. canvas_sdk/value_set/v2022/individual_characteristic.py +5 -0
  143. canvas_sdk/value_set/v2022/intervention.py +5 -0
  144. canvas_sdk/value_set/v2022/laboratory_test.py +5 -0
  145. canvas_sdk/value_set/v2022/medication.py +5 -0
  146. canvas_sdk/value_set/v2022/physical_exam.py +5 -0
  147. canvas_sdk/value_set/v2022/procedure.py +5 -0
  148. canvas_sdk/value_set/v2022/symptom.py +3 -0
  149. canvas_sdk/value_set/value_set.py +9 -0
  150. canvas_sdk/views/__init__.py +1 -0
  151. logger/__init__.py +2 -0
  152. logger/logger.py +3 -0
  153. plugin_runner/aws_headers.py +1 -1
  154. plugin_runner/load_all_plugins.py +202 -0
  155. plugin_runner/plugin_runner.py +26 -24
  156. plugin_runner/sandbox.py +497 -115
  157. protobufs/canvas_generated/messages/effects.proto +3 -0
  158. settings.py +5 -2
  159. canvas-0.32.0.dist-info/RECORD +0 -364
  160. canvas_cli/apps/auth/tests.py +0 -155
  161. canvas_cli/apps/plugin/tests.py +0 -85
  162. canvas_cli/conftest.py +0 -28
  163. canvas_cli/tests.py +0 -217
  164. canvas_cli/utils/context/tests.py +0 -131
  165. canvas_cli/utils/print/tests.py +0 -69
  166. canvas_cli/utils/urls/tests.py +0 -12
  167. canvas_cli/utils/validators/tests.py +0 -37
  168. canvas_sdk/commands/tests/protocol/__init__.py +0 -0
  169. canvas_sdk/commands/tests/protocol/tests.py +0 -83
  170. canvas_sdk/commands/tests/schema/__init__.py +0 -0
  171. canvas_sdk/commands/tests/schema/tests.py +0 -108
  172. canvas_sdk/commands/tests/test_base_command.py +0 -81
  173. canvas_sdk/commands/tests/test_utils.py +0 -375
  174. canvas_sdk/commands/tests/unit/__init__.py +0 -0
  175. canvas_sdk/commands/tests/unit/tests.py +0 -278
  176. canvas_sdk/effects/banner_alert/tests.py +0 -288
  177. canvas_sdk/effects/protocol_card/tests.py +0 -191
  178. canvas_sdk/questionnaires/tests/__init__.py +0 -0
  179. canvas_sdk/questionnaires/tests/test_utils.py +0 -74
  180. canvas_sdk/templates/tests/__init__.py +0 -0
  181. canvas_sdk/templates/tests/test_utils.py +0 -43
  182. canvas_sdk/tests/__init__.py +0 -0
  183. canvas_sdk/tests/handlers/__init__.py +0 -0
  184. canvas_sdk/tests/handlers/test_simple_api.py +0 -1167
  185. canvas_sdk/utils/tests.py +0 -72
  186. canvas_sdk/value_set/tests/test_value_sets.py +0 -72
  187. plugin_runner/tests/__init__.py +0 -0
  188. plugin_runner/tests/fixtures/plugins/example_plugin/CANVAS_MANIFEST.json +0 -29
  189. plugin_runner/tests/fixtures/plugins/example_plugin/README.md +0 -12
  190. plugin_runner/tests/fixtures/plugins/example_plugin/__init__.py +0 -0
  191. plugin_runner/tests/fixtures/plugins/example_plugin/protocols/__init__.py +0 -0
  192. plugin_runner/tests/fixtures/plugins/example_plugin/protocols/my_protocol.py +0 -18
  193. plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/CANVAS_MANIFEST.json +0 -38
  194. plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/README.md +0 -11
  195. plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/protocols/__init__.py +0 -0
  196. plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/protocols/my_protocol.py +0 -33
  197. plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/templates/__init__.py +0 -3
  198. plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/templates/base.py +0 -6
  199. plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/utils/__init__.py +0 -5
  200. plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/utils/base.py +0 -4
  201. plugin_runner/tests/fixtures/plugins/test_load_questionnaire/CANVAS_MANIFEST.json +0 -52
  202. plugin_runner/tests/fixtures/plugins/test_load_questionnaire/README.md +0 -11
  203. plugin_runner/tests/fixtures/plugins/test_load_questionnaire/protocols/__init__.py +0 -0
  204. plugin_runner/tests/fixtures/plugins/test_load_questionnaire/protocols/my_protocol.py +0 -39
  205. plugin_runner/tests/fixtures/plugins/test_load_questionnaire/questionnaires/example_questionnaire.yml +0 -61
  206. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/CANVAS_MANIFEST.json +0 -29
  207. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/README.md +0 -12
  208. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/other_module/__init__.py +0 -0
  209. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/other_module/base.py +0 -10
  210. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/protocols/__init__.py +0 -0
  211. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/protocols/my_protocol.py +0 -18
  212. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/CANVAS_MANIFEST.json +0 -29
  213. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/README.md +0 -12
  214. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/other_module/__init__.py +0 -0
  215. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/other_module/base.py +0 -10
  216. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/protocols/__init__.py +0 -0
  217. plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/protocols/my_protocol.py +0 -18
  218. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/CANVAS_MANIFEST.json +0 -29
  219. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/README.md +0 -12
  220. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/other_module/__init__.py +0 -0
  221. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/other_module/base.py +0 -3
  222. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/protocols/__init__.py +0 -0
  223. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/protocols/my_protocol.py +0 -18
  224. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/CANVAS_MANIFEST.json +0 -29
  225. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/README.md +0 -12
  226. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/other_module/__init__.py +0 -0
  227. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/other_module/base.py +0 -6
  228. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/protocols/__init__.py +0 -0
  229. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/protocols/my_protocol.py +0 -18
  230. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/CANVAS_MANIFEST.json +0 -29
  231. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/README.md +0 -12
  232. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/other_module/__init__.py +0 -0
  233. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/other_module/base.py +0 -8
  234. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/protocols/__init__.py +0 -0
  235. plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/protocols/my_protocol.py +0 -18
  236. plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/CANVAS_MANIFEST.json +0 -29
  237. plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/README.md +0 -12
  238. plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/other_module/__init__.py +0 -0
  239. plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/other_module/base.py +0 -3
  240. plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/protocols/__init__.py +0 -0
  241. plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/protocols/my_protocol.py +0 -18
  242. plugin_runner/tests/fixtures/plugins/test_render_template/CANVAS_MANIFEST.json +0 -47
  243. plugin_runner/tests/fixtures/plugins/test_render_template/README.md +0 -11
  244. plugin_runner/tests/fixtures/plugins/test_render_template/protocols/__init__.py +0 -0
  245. plugin_runner/tests/fixtures/plugins/test_render_template/protocols/my_protocol.py +0 -43
  246. plugin_runner/tests/fixtures/plugins/test_render_template/templates/template.html +0 -10
  247. plugin_runner/tests/fixtures/plugins/test_simple_api/CANVAS_MANIFEST.json +0 -47
  248. plugin_runner/tests/fixtures/plugins/test_simple_api/README.md +0 -11
  249. plugin_runner/tests/fixtures/plugins/test_simple_api/__init__.py +0 -0
  250. plugin_runner/tests/fixtures/plugins/test_simple_api/protocols/__init__.py +0 -0
  251. plugin_runner/tests/fixtures/plugins/test_simple_api/protocols/my_protocol.py +0 -43
  252. plugin_runner/tests/test_application.py +0 -65
  253. plugin_runner/tests/test_plugin_installer.py +0 -127
  254. plugin_runner/tests/test_plugin_runner.py +0 -388
  255. plugin_runner/tests/test_sandbox.py +0 -137
  256. {canvas-0.32.0.dist-info → canvas-0.33.1.dist-info}/WHEEL +0 -0
  257. {canvas-0.32.0.dist-info → canvas-0.33.1.dist-info}/entry_points.txt +0 -0
@@ -1,278 +0,0 @@
1
- import uuid
2
-
3
- import pytest
4
- from pydantic import ValidationError
5
- from typer.testing import CliRunner
6
-
7
- from canvas_sdk.commands import (
8
- AssessCommand,
9
- DiagnoseCommand,
10
- GoalCommand,
11
- HistoryOfPresentIllnessCommand,
12
- MedicationStatementCommand,
13
- PlanCommand,
14
- PrescribeCommand,
15
- QuestionnaireCommand,
16
- ReasonForVisitCommand,
17
- StopMedicationCommand,
18
- UpdateGoalCommand,
19
- )
20
- from canvas_sdk.commands.base import _BaseCommand
21
- from canvas_sdk.commands.tests.test_utils import (
22
- fake,
23
- get_field_type,
24
- raises_none_error_for_effect_method,
25
- raises_wrong_type_error,
26
- )
27
-
28
- runner = CliRunner()
29
-
30
-
31
- @pytest.mark.parametrize(
32
- "Command,fields_to_test",
33
- [
34
- (AssessCommand, ("condition_id", "background", "status", "narrative")),
35
- (
36
- DiagnoseCommand,
37
- ("icd10_code", "background", "approximate_date_of_onset", "today_assessment"),
38
- ),
39
- (
40
- GoalCommand,
41
- (
42
- "goal_statement",
43
- "start_date",
44
- "due_date",
45
- "achievement_status",
46
- "priority",
47
- "progress",
48
- ),
49
- ),
50
- (HistoryOfPresentIllnessCommand, ("narrative",)),
51
- (MedicationStatementCommand, ("fdb_code", "sig")),
52
- (PlanCommand, ("narrative", "command_uuid")),
53
- (
54
- PrescribeCommand,
55
- (
56
- "fdb_code",
57
- "icd10_codes",
58
- "sig",
59
- "days_supply",
60
- "type_to_dispense",
61
- "refills",
62
- "substitutions",
63
- "pharmacy",
64
- "prescriber_id",
65
- "note_to_pharmacist",
66
- ),
67
- ),
68
- (QuestionnaireCommand, ("questionnaire_id", "result")),
69
- (ReasonForVisitCommand, ("coding", "comment")),
70
- (StopMedicationCommand, ("medication_id", "rationale")),
71
- (
72
- UpdateGoalCommand,
73
- (
74
- "goal_id",
75
- "due_date",
76
- "achievement_status",
77
- "priority",
78
- "progress",
79
- ),
80
- ),
81
- ],
82
- )
83
- def test_command_raises_generic_error_when_kwarg_given_incorrect_type(
84
- Command: type[_BaseCommand],
85
- fields_to_test: tuple[str],
86
- ) -> None:
87
- """Test that Command raises a generic error when a kwarg is given an incorrect type."""
88
- for field in fields_to_test:
89
- raises_wrong_type_error(Command, field)
90
-
91
- for method in ["originate", "edit", "delete", "commit", "enter_in_error"]:
92
- raises_none_error_for_effect_method(Command, method)
93
-
94
-
95
- @pytest.mark.parametrize(
96
- "Command,err_kwargs,valid_kwargs",
97
- [
98
- (
99
- PlanCommand,
100
- {"narrative": "yo", "note_uuid": 1},
101
- {"narrative": "yo", "note_uuid": "00000000-0000-0000-0000-000000000000"},
102
- ),
103
- (
104
- PlanCommand,
105
- {"narrative": "yo", "note_uuid": "5", "command_uuid": 5},
106
- {"narrative": "yo", "note_uuid": "5", "command_uuid": "5"},
107
- ),
108
- (
109
- ReasonForVisitCommand,
110
- {"note_uuid": "00000000-0000-0000-0000-000000000000", "structured": True},
111
- {
112
- "note_uuid": "00000000-0000-0000-0000-000000000000",
113
- "structured": False,
114
- },
115
- ),
116
- (
117
- ReasonForVisitCommand,
118
- {
119
- "note_uuid": "00000000-0000-0000-0000-000000000000",
120
- "coding": {"code": "x"},
121
- },
122
- {"note_uuid": "00000000-0000-0000-0000-000000000000"},
123
- ),
124
- (
125
- ReasonForVisitCommand,
126
- {
127
- "note_uuid": "00000000-0000-0000-0000-000000000000",
128
- "coding": {"code": 1, "system": "y"},
129
- },
130
- {"note_uuid": "00000000-0000-0000-0000-000000000000"},
131
- ),
132
- (
133
- ReasonForVisitCommand,
134
- {
135
- "note_uuid": "00000000-0000-0000-0000-000000000000",
136
- "coding": {"code": None, "system": "y"},
137
- },
138
- {"note_uuid": "00000000-0000-0000-0000-000000000000"},
139
- ),
140
- (
141
- ReasonForVisitCommand,
142
- {
143
- "note_uuid": "00000000-0000-0000-0000-000000000000",
144
- "coding": {"system": "y"},
145
- },
146
- {"note_uuid": "00000000-0000-0000-0000-000000000000"},
147
- ),
148
- (
149
- ReasonForVisitCommand,
150
- {
151
- "note_uuid": "00000000-0000-0000-0000-000000000000",
152
- "coding": {"code": "x", "system": 1},
153
- },
154
- {"note_uuid": "00000000-0000-0000-0000-000000000000"},
155
- ),
156
- (
157
- ReasonForVisitCommand,
158
- {
159
- "note_uuid": "00000000-0000-0000-0000-000000000000",
160
- "coding": {"code": "x", "system": None},
161
- },
162
- {"note_uuid": "00000000-0000-0000-0000-000000000000"},
163
- ),
164
- (
165
- ReasonForVisitCommand,
166
- {
167
- "note_uuid": "00000000-0000-0000-0000-000000000000",
168
- "coding": {"code": "x", "system": "y", "display": 1},
169
- },
170
- {"note_uuid": "00000000-0000-0000-0000-000000000000"},
171
- ),
172
- ],
173
- )
174
- def test_command_raises_specific_error_when_kwarg_given_incorrect_type(
175
- Command: type[PlanCommand] | type[ReasonForVisitCommand],
176
- err_kwargs: dict,
177
- valid_kwargs: dict,
178
- ) -> None:
179
- """Test that Command raises a specific error when a kwarg is given an incorrect type."""
180
- with pytest.raises(ValidationError):
181
- cmd = Command(**err_kwargs)
182
- cmd.originate()
183
- cmd.command_uuid = str(uuid.uuid4())
184
- cmd.edit()
185
-
186
- cmd = Command(**valid_kwargs)
187
- if len(err_kwargs) < len(valid_kwargs):
188
- return
189
- key, value = list(err_kwargs.items())[-1]
190
- with pytest.raises(ValidationError):
191
- setattr(cmd, key, value)
192
- cmd.originate()
193
- cmd.command_uuid = str(uuid.uuid4())
194
- cmd.edit()
195
-
196
-
197
- @pytest.mark.parametrize(
198
- "Command,fields_to_test",
199
- [
200
- (AssessCommand, ("condition_id", "background", "status", "narrative")),
201
- (
202
- DiagnoseCommand,
203
- ("icd10_code", "background", "approximate_date_of_onset", "today_assessment"),
204
- ),
205
- (
206
- GoalCommand,
207
- (
208
- "goal_statement",
209
- "start_date",
210
- "due_date",
211
- "achievement_status",
212
- "priority",
213
- "progress",
214
- ),
215
- ),
216
- (HistoryOfPresentIllnessCommand, ("narrative",)),
217
- (MedicationStatementCommand, ("fdb_code", "sig")),
218
- (PlanCommand, ("narrative", "command_uuid", "note_uuid")),
219
- (
220
- PrescribeCommand,
221
- (
222
- "fdb_code",
223
- "icd10_codes",
224
- "sig",
225
- "days_supply",
226
- "quantity_to_dispense",
227
- "type_to_dispense",
228
- "refills",
229
- "substitutions",
230
- "pharmacy",
231
- "prescriber_id",
232
- "note_to_pharmacist",
233
- ),
234
- ),
235
- (QuestionnaireCommand, ("questionnaire_id", "result")),
236
- (ReasonForVisitCommand, ("coding", "comment")),
237
- (StopMedicationCommand, ("medication_id", "rationale")),
238
- (
239
- UpdateGoalCommand,
240
- (
241
- "goal_id",
242
- "due_date",
243
- "achievement_status",
244
- "priority",
245
- "progress",
246
- ),
247
- ),
248
- ],
249
- )
250
- def test_command_allows_kwarg_with_correct_type(
251
- Command: type[_BaseCommand],
252
- fields_to_test: tuple[str],
253
- ) -> None:
254
- """Test that Command allows a kwarg with the correct type."""
255
- schema = Command.model_json_schema()
256
-
257
- for field in fields_to_test:
258
- field_type = get_field_type(schema["properties"][field])
259
-
260
- init_field_value = fake({"type": field_type}, Command)
261
- init_kwargs = {field: init_field_value}
262
- cmd = Command(**init_kwargs)
263
- assert getattr(cmd, field) == init_field_value
264
-
265
- updated_field_value = fake({"type": field_type}, Command)
266
- setattr(cmd, field, updated_field_value)
267
- assert getattr(cmd, field) == updated_field_value
268
-
269
- for method in ["originate", "edit", "delete", "commit", "enter_in_error"]:
270
- required_fields = {
271
- k: v
272
- for k, v in schema["properties"].items()
273
- if k in Command()._get_effect_method_required_fields(method)
274
- }
275
- base = {field: fake(props, Command) for field, props in required_fields.items()}
276
- cmd = Command(**base)
277
- effect = getattr(cmd, method)()
278
- assert effect is not None
@@ -1,288 +0,0 @@
1
- import shutil
2
- from collections.abc import Generator
3
- from contextlib import chdir
4
- from datetime import datetime
5
- from pathlib import Path
6
- from typing import Any
7
-
8
- import pytest
9
- import requests
10
- from django.core.exceptions import ImproperlyConfigured
11
- from pydantic import ValidationError
12
- from typer.testing import CliRunner
13
-
14
- import settings
15
- from canvas_cli.apps.plugin.plugin import _build_package, plugin_url
16
- from canvas_cli.main import app
17
- from canvas_sdk.commands.tests.test_utils import MaskedValue, wait_for_log
18
- from canvas_sdk.effects.banner_alert import AddBannerAlert, RemoveBannerAlert
19
-
20
- runner = CliRunner()
21
-
22
-
23
- @pytest.fixture(scope="session")
24
- def token() -> MaskedValue:
25
- """Get a valid token."""
26
- response = requests.post(
27
- f"{settings.INTEGRATION_TEST_URL}/auth/token/",
28
- headers={"Content-Type": "application/x-www-form-urlencoded"},
29
- data={
30
- "grant_type": "client_credentials",
31
- "client_id": settings.INTEGRATION_TEST_CLIENT_ID,
32
- "client_secret": settings.INTEGRATION_TEST_CLIENT_SECRET,
33
- },
34
- )
35
- response.raise_for_status()
36
-
37
- return MaskedValue(response.json()["access_token"])
38
-
39
-
40
- @pytest.fixture(scope="session")
41
- def first_patient_id(token: MaskedValue) -> dict:
42
- """Get the first patient id."""
43
- headers = {
44
- "Authorization": f"Bearer {token.value}",
45
- "Content-Type": "application/json",
46
- "Accept": "application/json",
47
- }
48
- response = requests.get(f"{settings.INTEGRATION_TEST_URL}/api/Patient", headers=headers)
49
- response.raise_for_status()
50
- patients = response.json()
51
- return patients["entry"][0]["resource"]["key"]
52
-
53
-
54
- @pytest.fixture(scope="session")
55
- def plugin_name() -> str:
56
- """Get the plugin name to be used."""
57
- return f"addbanneralert{datetime.now().timestamp()}".replace(".", "")
58
-
59
-
60
- @pytest.fixture(scope="session")
61
- def write_and_install_protocol_and_clean_up(
62
- first_patient_id: str, plugin_name: str, token: MaskedValue
63
- ) -> Generator[Any, Any, Any]:
64
- """Write the protocol code, install the plugin, and clean up after the test."""
65
- if not settings.INTEGRATION_TEST_URL:
66
- raise ImproperlyConfigured("INTEGRATION_TEST_URL is not set")
67
-
68
- # write the protocol
69
- with chdir(Path("./custom-plugins")):
70
- runner.invoke(app, "init", input=plugin_name)
71
-
72
- protocol_code = f"""
73
- from canvas_sdk.effects.banner_alert import AddBannerAlert
74
- from canvas_sdk.events import EventType
75
- from canvas_sdk.protocols import BaseProtocol
76
-
77
- class Protocol(BaseProtocol):
78
- RESPONDS_TO = EventType.Name(EventType.ENCOUNTER_CREATED)
79
- def compute(self):
80
- return [
81
- AddBannerAlert(
82
- patient_id="{first_patient_id}",
83
- key="{plugin_name}",
84
- narrative="this is a test",
85
- placement=[AddBannerAlert.Placement.CHART],
86
- intent=AddBannerAlert.Intent.INFO,
87
- ).apply()
88
- ]
89
- """
90
-
91
- with open(f"./custom-plugins/{plugin_name}/protocols/my_protocol.py", "w") as protocol:
92
- protocol.write(protocol_code)
93
-
94
- with open(_build_package(Path(f"./custom-plugins/{plugin_name}")), "rb") as package:
95
- message_received_event, thread, ws = wait_for_log(
96
- settings.INTEGRATION_TEST_URL,
97
- token.value,
98
- f"Loading plugin '{plugin_name}",
99
- )
100
-
101
- # install the plugin
102
- response = requests.post(
103
- plugin_url(settings.INTEGRATION_TEST_URL),
104
- data={"is_enabled": True},
105
- files={"package": package},
106
- headers={"Authorization": f"Bearer {token.value}"},
107
- )
108
- response.raise_for_status()
109
-
110
- message_received_event.wait(timeout=15.0)
111
-
112
- # unfortunately sometimes the log websocket just doesn't return any
113
- # messages, so asserting on the state of the timeout here causes failures
114
- # even though the delay itself will cause the waiting test to pass (because
115
- # the plugin has been loaded).
116
- # timeout_not_hit = message_received_event.wait(timeout=15.0)
117
- # if not timeout_not_hit:
118
- # ws.close()
119
- # assert timeout_not_hit, f"plugin loading message timeout hit: Loading plugin '{plugin_name}"
120
- yield
121
-
122
- ws.close()
123
- thread.join()
124
-
125
- # clean up
126
- if Path(f"./custom-plugins/{plugin_name}").exists():
127
- shutil.rmtree(Path(f"./custom-plugins/{plugin_name}"))
128
-
129
- # disable
130
- response = requests.patch(
131
- plugin_url(settings.INTEGRATION_TEST_URL, plugin_name),
132
- data={"is_enabled": False},
133
- headers={
134
- "Authorization": f"Bearer {token.value}",
135
- },
136
- )
137
- response.raise_for_status()
138
-
139
- # delete
140
- response = requests.delete(
141
- plugin_url(settings.INTEGRATION_TEST_URL, plugin_name),
142
- headers={"Authorization": f"Bearer {token.value}"},
143
- )
144
- response.raise_for_status()
145
-
146
- # confirm no more banner
147
- response = requests.get(
148
- f"{settings.INTEGRATION_TEST_URL}/api/BannerAlert/?patient__key={first_patient_id}",
149
- headers={
150
- "Authorization": f"Bearer {token.value}",
151
- },
152
- )
153
- response.raise_for_status()
154
- patient_banners_none = response.json()
155
- patient_banner = next(
156
- (b for b in patient_banners_none["results"] if b["key"] == plugin_name), None
157
- )
158
- assert patient_banner is None
159
-
160
-
161
- @pytest.mark.integtest
162
- def test_protocol_that_adds_banner_alert(
163
- write_and_install_protocol_and_clean_up: None,
164
- token: MaskedValue,
165
- plugin_name: str,
166
- first_patient_id: str,
167
- ) -> None:
168
- """Test that the protocol adds a banner alert."""
169
- # trigger the event
170
- response = requests.post(
171
- f"{settings.INTEGRATION_TEST_URL}/api/Note/",
172
- headers={
173
- "Authorization": f"Bearer {token.value}",
174
- "Content-Type": "application/json",
175
- "Accept": "application/json",
176
- },
177
- json={
178
- "patient": 1,
179
- "provider": 1,
180
- "note_type": "office",
181
- "note_type_version": 1,
182
- "lastModifiedBySessionKey": "8fee3c03a525cebee1d8a6b8e63dd4dg",
183
- },
184
- )
185
- response.raise_for_status()
186
-
187
- response = requests.get(
188
- f"{settings.INTEGRATION_TEST_URL}/api/BannerAlert/?patient__key={first_patient_id}",
189
- headers={
190
- "Authorization": f"Bearer {token.value}",
191
- },
192
- )
193
- response.raise_for_status()
194
-
195
- patient_banners = response.json()
196
- assert patient_banners["count"] > 0
197
-
198
- patient_banner = next(b for b in patient_banners["results"] if b["key"] == plugin_name)
199
- assert patient_banner["pluginName"] == plugin_name
200
- assert patient_banner["narrative"] == "this is a test"
201
- assert patient_banner["placement"] == ["chart"]
202
- assert patient_banner["intent"] == "info"
203
- assert patient_banner["href"] is None
204
- assert patient_banner["status"] == "active"
205
-
206
-
207
- @pytest.mark.parametrize(
208
- "Effect,params,expected_payload",
209
- [
210
- (
211
- AddBannerAlert,
212
- {
213
- "patient_id": "uuid",
214
- "key": "test-key",
215
- "narrative": "hellooo",
216
- "placement": [AddBannerAlert.Placement.APPOINTMENT_CARD],
217
- "intent": AddBannerAlert.Intent.INFO,
218
- },
219
- '{"patient": "uuid", "patient_filter": null, "key": "test-key", "data": {"narrative": "hellooo", "placement": ["appointment_card"], "intent": "info", "href": null}}',
220
- ),
221
- (
222
- AddBannerAlert,
223
- {
224
- "patient_filter": {"active": True},
225
- "key": "test-key",
226
- "narrative": "hellooo",
227
- "placement": [AddBannerAlert.Placement.APPOINTMENT_CARD],
228
- "intent": AddBannerAlert.Intent.INFO,
229
- },
230
- '{"patient": null, "patient_filter": {"active": true}, "key": "test-key", "data": {"narrative": "hellooo", "placement": ["appointment_card"], "intent": "info", "href": null}}',
231
- ),
232
- (
233
- RemoveBannerAlert,
234
- {"patient_id": "uuid", "key": "testeroo"},
235
- '{"patient": "uuid", "patient_filter": null, "key": "testeroo"}',
236
- ),
237
- (
238
- RemoveBannerAlert,
239
- {"patient_filter": {"active": True}, "key": "testeroo"},
240
- '{"patient": null, "patient_filter": {"active": true}, "key": "testeroo"}',
241
- ),
242
- ],
243
- )
244
- def test_banner_alert_apply_method_succeeds_with_all_required_fields(
245
- Effect: type[AddBannerAlert] | type[RemoveBannerAlert], params: dict, expected_payload: str
246
- ) -> None:
247
- """Test that the apply method succeeds with all required fields."""
248
- b = Effect()
249
- for k, v in params.items():
250
- setattr(b, k, v)
251
- applied = b.apply()
252
- assert applied.payload == expected_payload
253
-
254
-
255
- @pytest.mark.parametrize(
256
- "Effect,expected_err_msgs",
257
- [
258
- (
259
- AddBannerAlert,
260
- [
261
- "5 validation errors for AddBannerAlert",
262
- "Field 'patient_id' or 'patient_filter' is required to apply an AddBannerAlert [type=missing",
263
- "Field 'key' is required to apply an AddBannerAlert [type=missing",
264
- "Field 'narrative' is required to apply an AddBannerAlert [type=missing",
265
- "Field 'placement' is required to apply an AddBannerAlert [type=missing",
266
- "Field 'intent' is required to apply an AddBannerAlert [type=missing",
267
- ],
268
- ),
269
- (
270
- RemoveBannerAlert,
271
- [
272
- "2 validation errors for RemoveBannerAlert",
273
- "Field 'patient_id' or 'patient_filter' is required to apply a RemoveBannerAlert [type=missing",
274
- "Field 'key' is required to apply a RemoveBannerAlert [type=missing",
275
- ],
276
- ),
277
- ],
278
- )
279
- def test_banner_alert_apply_method_raises_error_without_required_fields(
280
- Effect: type[AddBannerAlert] | type[RemoveBannerAlert], expected_err_msgs: str
281
- ) -> None:
282
- """Test that the apply method raises an error when missing required fields."""
283
- b = Effect()
284
- with pytest.raises(ValidationError) as e:
285
- b.apply()
286
- err_msg = repr(e.value)
287
- for expected in expected_err_msgs:
288
- assert expected in err_msg