canvas 0.13.3__py3-none-any.whl → 0.14.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.13.3.dist-info → canvas-0.14.0.dist-info}/METADATA +1 -3
- {canvas-0.13.3.dist-info → canvas-0.14.0.dist-info}/RECORD +14 -12
- canvas_generated/messages/effects_pb2.py +2 -2
- canvas_generated/messages/effects_pb2.pyi +2 -0
- canvas_sdk/effects/questionnaire_result.py +29 -0
- canvas_sdk/models/__init__.py +1 -0
- canvas_sdk/v1/data/command.py +13 -0
- plugin_runner/aws_headers.py +77 -0
- plugin_runner/plugin_installer.py +25 -7
- plugin_runner/tests/test_plugin_installer.py +10 -12
- plugin_runner/tests/test_sandbox.py +13 -1
- settings.py +4 -0
- {canvas-0.13.3.dist-info → canvas-0.14.0.dist-info}/WHEEL +0 -0
- {canvas-0.13.3.dist-info → canvas-0.14.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.14.0
|
|
4
4
|
Summary: SDK to customize event-driven actions in your Canvas instance
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Canvas Team
|
|
@@ -10,8 +10,6 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
-
Requires-Dist: boto3 (>=1.35.88,<2.0.0)
|
|
14
|
-
Requires-Dist: boto3-stubs[s3] (>=1.35.88,<2.0.0)
|
|
15
13
|
Requires-Dist: cookiecutter
|
|
16
14
|
Requires-Dist: cron-converter (>=1.2.1,<2.0.0)
|
|
17
15
|
Requires-Dist: deprecation (>=2.1.0,<3.0.0)
|
|
@@ -97,8 +97,8 @@ canvas_cli/utils/validators/__init__.py,sha256=rBvSR2O1hWkNAnUBdcr-zUkmqT796_A61
|
|
|
97
97
|
canvas_cli/utils/validators/manifest_schema.py,sha256=6PQmK2AdMWxrziBtv6jcpyoNk3SQEZsJCt3PrI0ZYm4,3961
|
|
98
98
|
canvas_cli/utils/validators/tests.py,sha256=KhaOdbxGUK2QwL2KnycAJJkqclYQSXF7CKg-scSf0DU,1259
|
|
99
99
|
canvas_cli/utils/validators/validators.py,sha256=lrUBQ0sF7seTe_pNGsNgASdr2BGp6g-Qui58V4H9qfQ,1598
|
|
100
|
-
canvas_generated/messages/effects_pb2.py,sha256=
|
|
101
|
-
canvas_generated/messages/effects_pb2.pyi,sha256=
|
|
100
|
+
canvas_generated/messages/effects_pb2.py,sha256=_fAq_TZ_82aC1qNdilFTZ53glzUJhqZ8ze19qnOJJqU,9082
|
|
101
|
+
canvas_generated/messages/effects_pb2.pyi,sha256=UraC7VQ8GeymsfiMl5q8e0csHOKq2xv47vagCPTSe9o,15683
|
|
102
102
|
canvas_generated/messages/effects_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
103
103
|
canvas_generated/messages/events_pb2.py,sha256=5xnMRMlqBTkxM3h8EVUW3EiNZ1LNoBGvMWFlwdf2epI,39868
|
|
104
104
|
canvas_generated/messages/events_pb2.pyi,sha256=HVqS44OgMYGL47svBJE9iQbLXaOm-EoMvBYvdP6Ha3c,71175
|
|
@@ -162,6 +162,7 @@ canvas_sdk/effects/patient_profile_configuration.py,sha256=ZXBx_PnJP-KKRIwuDI4tS
|
|
|
162
162
|
canvas_sdk/effects/protocol_card/__init__.py,sha256=AwXAARybFhVIfYwqiRThG6Ne3ARnKgvuvuGUknWpwTo,134
|
|
163
163
|
canvas_sdk/effects/protocol_card/protocol_card.py,sha256=aL8lXRBm1ByU-X0xzgBepxKnUTLvmr-OC7G-C-EBjy8,2596
|
|
164
164
|
canvas_sdk/effects/protocol_card/tests.py,sha256=ZiDZTjn-Z6UpEfR1S-6XDcJXlPlTPpLbg2PgvkQD8Uk,6815
|
|
165
|
+
canvas_sdk/effects/questionnaire_result.py,sha256=dXJE6la5aP5xLCnT9iMCOS6PiKu0Zz733Ku2lqa4CnM,816
|
|
165
166
|
canvas_sdk/effects/show_button.py,sha256=JnW9nM8S_GUXIOufs-uef3pg0HPDxSbF0l51Wh1Xxgw,715
|
|
166
167
|
canvas_sdk/effects/surescripts/surescripts_messages.py,sha256=tMG5ry2mZA5pwO0uGJfYZnJuVpj5MVhS2lYT5IRsb1Y,2582
|
|
167
168
|
canvas_sdk/effects/task/task.py,sha256=olG7gJN3CC1JKrGU4SdVvRq8qLduPuiNz80C_dTaY58,2837
|
|
@@ -172,7 +173,7 @@ canvas_sdk/handlers/action_button.py,sha256=8q7zo96k_4SV4JUHc_GoqbBksXW2dk_EE33H
|
|
|
172
173
|
canvas_sdk/handlers/application.py,sha256=8HwgJeHThCpif-vLAfgQU0En3Y07AEyONPIWkLUoqyM,910
|
|
173
174
|
canvas_sdk/handlers/base.py,sha256=E5UCnckZJM2N7G-NEWh5Wj2BmSARM6dNxS26Ly8AKug,1277
|
|
174
175
|
canvas_sdk/handlers/cron_task.py,sha256=Q4_D3bDKE5hyEpCf0JnGSgZlnLh_g9RSP3oEfIuLJ9Y,945
|
|
175
|
-
canvas_sdk/models/__init__.py,sha256=
|
|
176
|
+
canvas_sdk/models/__init__.py,sha256=o2axmFmIcYihBwPCeErDyjiEKCdN1rVuJWr7P81AXnM,376
|
|
176
177
|
canvas_sdk/protocols/__init__.py,sha256=3u9zet5D4DX4V953tLCoN1xhaOhAUCwGwscMv-7IIxo,186
|
|
177
178
|
canvas_sdk/protocols/base.py,sha256=sbm0uOk3PPfPemqBmHh2hawE5utC6no46EmvyMN8Y7Q,179
|
|
178
179
|
canvas_sdk/protocols/clinical_quality_measure.py,sha256=39ddtSIBCFB4DF7Kpuh2uDwSxH-mV7sjrsVrQY7tEuo,4835
|
|
@@ -188,7 +189,7 @@ canvas_sdk/v1/data/__init__.py,sha256=Ea0apSYEz2UEhlZT9bmLH0rqmh75-2C0AsxbqDIfYn
|
|
|
188
189
|
canvas_sdk/v1/data/allergy_intolerance.py,sha256=BJD8eDVpdwxPTM2ebdZeCLG8-lafS_kxFHhPJsnZY7g,2099
|
|
189
190
|
canvas_sdk/v1/data/base.py,sha256=ZMZEpwkeg3ewl3QsbPAF7-z1MqqWu5hCR-IibWQCiHI,6189
|
|
190
191
|
canvas_sdk/v1/data/billing.py,sha256=sAC5IMV526pdEZ9FwEmN7dz30F0kOHqH7Rh_JBwReuQ,2076
|
|
191
|
-
canvas_sdk/v1/data/command.py,sha256=
|
|
192
|
+
canvas_sdk/v1/data/command.py,sha256=Tk4oA-QBhq8p38xUClrv6Wp0uaIbpFW1iHDZV356_Y0,1636
|
|
192
193
|
canvas_sdk/v1/data/common.py,sha256=OvF4Oxo3BqE1Cv2lfyA1MXp_q_VHwoihyiJ0ETfa5lY,3459
|
|
193
194
|
canvas_sdk/v1/data/condition.py,sha256=g_kBv5zV3ynUg8fZbBqIrVTUBbl70r7pED-YxmnZ540,1930
|
|
194
195
|
canvas_sdk/v1/data/detected_issue.py,sha256=IvxHrBa2YK_ylHNi2HGSK0ji2yk92C_6EDDnsMh6CnY,1790
|
|
@@ -232,8 +233,9 @@ logger/__init__.py,sha256=9o2iRCjzFEhfULgXvrrECRFK-4IslWJTqKKjTCEUbq8,61
|
|
|
232
233
|
logger/logger.py,sha256=axf7UffBJtETjwDCtmi1IaaJKsvcFj8zaLfouGsq68A,1847
|
|
233
234
|
plugin_runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
234
235
|
plugin_runner/authentication.py,sha256=SDPso2AogtLAV_H0LuMDp99IMZuF3oTq-Q_AXAvJ8uc,1116
|
|
236
|
+
plugin_runner/aws_headers.py,sha256=DenX_nAMVhXMJZw88PLZbqJsi5_XriNtr3jE-eJqHY4,2773
|
|
235
237
|
plugin_runner/exceptions.py,sha256=YnRZiQVzbU3HrVlmEXLje_np99009YnhTRVHHyBCtqc,433
|
|
236
|
-
plugin_runner/plugin_installer.py,sha256=
|
|
238
|
+
plugin_runner/plugin_installer.py,sha256=QNc222WQHJk9d8YE8Npi3mlkCApbYVH4M9VZX5-AYxw,7676
|
|
237
239
|
plugin_runner/plugin_runner.py,sha256=EZ3snfYZ5kirlELY0LH6stcz6NuaJQ_WJE8aKi59EVU,15269
|
|
238
240
|
plugin_runner/plugin_synchronizer.py,sha256=GcY2oKwJc8beDoAJwo-0MkY4rxNzoF-Bk1bF9MOtO6U,2851
|
|
239
241
|
plugin_runner/sandbox.py,sha256=JUbphdu1iY0rPU6DdMEjFA32BBsZXHUz1KguCYpmrug,9384
|
|
@@ -269,13 +271,13 @@ plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/other_module/bas
|
|
|
269
271
|
plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/protocols/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
270
272
|
plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/protocols/my_protocol.py,sha256=V_6DPXsJaD12zGyKqGjYY2JFtKEk4E0eIAgBWax_MGc,629
|
|
271
273
|
plugin_runner/tests/test_application.py,sha256=ZirW14UGorfkQ_VSbnoKtmTvx6lHFS2EqndYNczIQfo,2391
|
|
272
|
-
plugin_runner/tests/test_plugin_installer.py,sha256=
|
|
274
|
+
plugin_runner/tests/test_plugin_installer.py,sha256=JNzLTAl7K8vFYe_WIt-_-VxuxoD_EdLnkceJWVfFvYA,3999
|
|
273
275
|
plugin_runner/tests/test_plugin_runner.py,sha256=htO1Pu-73AusrBP1EPeaeVPXcDYyHSEb-aHpSZFTz04,7029
|
|
274
|
-
plugin_runner/tests/test_sandbox.py,sha256=
|
|
276
|
+
plugin_runner/tests/test_sandbox.py,sha256=GX_nKgpU2R6s3V__dogkqEhLOXcqsQlic2kjwSwl_30,3835
|
|
275
277
|
pubsub/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
276
278
|
pubsub/pubsub.py,sha256=pyTW0JU8mtaqiAV6g6xjZwel1CVy2EonPMU-_vkmhUM,1044
|
|
277
|
-
settings.py,sha256=
|
|
278
|
-
canvas-0.
|
|
279
|
-
canvas-0.
|
|
280
|
-
canvas-0.
|
|
281
|
-
canvas-0.
|
|
279
|
+
settings.py,sha256=S_nlo7S2UxJUXrKRCv2hRXXysKrjJ9pxjT2Amb-JSao,2392
|
|
280
|
+
canvas-0.14.0.dist-info/METADATA,sha256=SQTZ2hB4GgCqEI8hSV_QsRNJXpnsjzmlpkzjerlkaaM,4703
|
|
281
|
+
canvas-0.14.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
282
|
+
canvas-0.14.0.dist-info/entry_points.txt,sha256=VSmSo1IZ3aEfL7enmLmlWSraS_IIkoXNVeyXzgRxFiY,46
|
|
283
|
+
canvas-0.14.0.dist-info/RECORD,,
|
|
@@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'canvas_generated/messages/effects.proto\x12\x06\x63\x61nvas\"c\n\x06\x45\x66\x66\x65\x63t\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.canvas.EffectType\x12\x0f\n\x07payload\x18\x02 \x01(\t\x12\x13\n\x0bplugin_name\x18\x03 \x01(\t\x12\x11\n\tclassname\x18\x04 \x01(\t*\
|
|
17
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'canvas_generated/messages/effects.proto\x12\x06\x63\x61nvas\"c\n\x06\x45\x66\x66\x65\x63t\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.canvas.EffectType\x12\x0f\n\x07payload\x18\x02 \x01(\t\x12\x13\n\x0bplugin_name\x18\x03 \x01(\t\x12\x11\n\tclassname\x18\x04 \x01(\t*\xa5(\n\nEffectType\x12\x12\n\x0eUNKNOWN_EFFECT\x10\x00\x12\x07\n\x03LOG\x10\x01\x12\x14\n\x10\x41\x44\x44_PLAN_COMMAND\x10\x02\x12\x1f\n\x1b\x41UTOCOMPLETE_SEARCH_RESULTS\x10\x03\x12\x14\n\x10\x41\x44\x44_BANNER_ALERT\x10\x04\x12\x17\n\x13REMOVE_BANNER_ALERT\x10\x05\x12\x1c\n\x18ORIGINATE_ASSESS_COMMAND\x10\x06\x12\x17\n\x13\x45\x44IT_ASSESS_COMMAND\x10\x07\x12\x19\n\x15\x44\x45LETE_ASSESS_COMMAND\x10\x08\x12\x19\n\x15\x43OMMIT_ASSESS_COMMAND\x10\t\x12!\n\x1d\x45NTER_IN_ERROR_ASSESS_COMMAND\x10\n\x12\x1e\n\x1aORIGINATE_DIAGNOSE_COMMAND\x10\x0b\x12\x19\n\x15\x45\x44IT_DIAGNOSE_COMMAND\x10\x0c\x12\x1b\n\x17\x44\x45LETE_DIAGNOSE_COMMAND\x10\r\x12\x1b\n\x17\x43OMMIT_DIAGNOSE_COMMAND\x10\x0e\x12#\n\x1f\x45NTER_IN_ERROR_DIAGNOSE_COMMAND\x10\x0f\x12\x1a\n\x16ORIGINATE_GOAL_COMMAND\x10\x10\x12\x15\n\x11\x45\x44IT_GOAL_COMMAND\x10\x11\x12\x17\n\x13\x44\x45LETE_GOAL_COMMAND\x10\x12\x12\x17\n\x13\x43OMMIT_GOAL_COMMAND\x10\x13\x12\x1f\n\x1b\x45NTER_IN_ERROR_GOAL_COMMAND\x10\x14\x12\x19\n\x15ORIGINATE_HPI_COMMAND\x10\x15\x12\x14\n\x10\x45\x44IT_HPI_COMMAND\x10\x16\x12\x16\n\x12\x44\x45LETE_HPI_COMMAND\x10\x17\x12\x16\n\x12\x43OMMIT_HPI_COMMAND\x10\x18\x12\x1e\n\x1a\x45NTER_IN_ERROR_HPI_COMMAND\x10\x19\x12*\n&ORIGINATE_MEDICATION_STATEMENT_COMMAND\x10\x1a\x12%\n!EDIT_MEDICATION_STATEMENT_COMMAND\x10\x1b\x12\'\n#DELETE_MEDICATION_STATEMENT_COMMAND\x10\x1c\x12\'\n#COMMIT_MEDICATION_STATEMENT_COMMAND\x10\x1d\x12/\n+ENTER_IN_ERROR_MEDICATION_STATEMENT_COMMAND\x10\x1e\x12\x1a\n\x16ORIGINATE_PLAN_COMMAND\x10\x1f\x12\x15\n\x11\x45\x44IT_PLAN_COMMAND\x10 \x12\x17\n\x13\x44\x45LETE_PLAN_COMMAND\x10!\x12\x17\n\x13\x43OMMIT_PLAN_COMMAND\x10\"\x12\x1f\n\x1b\x45NTER_IN_ERROR_PLAN_COMMAND\x10#\x12\x1f\n\x1bORIGINATE_PRESCRIBE_COMMAND\x10$\x12\x1a\n\x16\x45\x44IT_PRESCRIBE_COMMAND\x10%\x12\x1c\n\x18\x44\x45LETE_PRESCRIBE_COMMAND\x10&\x12\x1c\n\x18\x43OMMIT_PRESCRIBE_COMMAND\x10\'\x12$\n ENTER_IN_ERROR_PRESCRIBE_COMMAND\x10(\x12#\n\x1fORIGINATE_QUESTIONNAIRE_COMMAND\x10)\x12\x1e\n\x1a\x45\x44IT_QUESTIONNAIRE_COMMAND\x10*\x12 \n\x1c\x44\x45LETE_QUESTIONNAIRE_COMMAND\x10+\x12 \n\x1c\x43OMMIT_QUESTIONNAIRE_COMMAND\x10,\x12(\n$ENTER_IN_ERROR_QUESTIONNAIRE_COMMAND\x10-\x12&\n\"ORIGINATE_REASON_FOR_VISIT_COMMAND\x10.\x12!\n\x1d\x45\x44IT_REASON_FOR_VISIT_COMMAND\x10/\x12#\n\x1f\x44\x45LETE_REASON_FOR_VISIT_COMMAND\x10\x30\x12#\n\x1f\x43OMMIT_REASON_FOR_VISIT_COMMAND\x10\x31\x12+\n\'ENTER_IN_ERROR_REASON_FOR_VISIT_COMMAND\x10\x32\x12%\n!ORIGINATE_STOP_MEDICATION_COMMAND\x10\x33\x12 \n\x1c\x45\x44IT_STOP_MEDICATION_COMMAND\x10\x34\x12\"\n\x1e\x44\x45LETE_STOP_MEDICATION_COMMAND\x10\x35\x12\"\n\x1e\x43OMMIT_STOP_MEDICATION_COMMAND\x10\x36\x12*\n&ENTER_IN_ERROR_STOP_MEDICATION_COMMAND\x10\x37\x12!\n\x1dORIGINATE_UPDATE_GOAL_COMMAND\x10\x38\x12\x1c\n\x18\x45\x44IT_UPDATE_GOAL_COMMAND\x10\x39\x12\x1e\n\x1a\x44\x45LETE_UPDATE_GOAL_COMMAND\x10:\x12\x1e\n\x1a\x43OMMIT_UPDATE_GOAL_COMMAND\x10;\x12&\n\"ENTER_IN_ERROR_UPDATE_GOAL_COMMAND\x10<\x12\x1d\n\x19ORIGINATE_PERFORM_COMMAND\x10=\x12\x18\n\x14\x45\x44IT_PERFORM_COMMAND\x10>\x12\x1a\n\x16\x44\x45LETE_PERFORM_COMMAND\x10?\x12\x1a\n\x16\x43OMMIT_PERFORM_COMMAND\x10@\x12\"\n\x1e\x45NTER_IN_ERROR_PERFORM_COMMAND\x10\x41\x12\x1e\n\x1aORIGINATE_INSTRUCT_COMMAND\x10\x42\x12\x19\n\x15\x45\x44IT_INSTRUCT_COMMAND\x10\x43\x12\x1b\n\x17\x44\x45LETE_INSTRUCT_COMMAND\x10\x44\x12\x1b\n\x17\x43OMMIT_INSTRUCT_COMMAND\x10\x45\x12#\n\x1f\x45NTER_IN_ERROR_INSTRUCT_COMMAND\x10\x46\x12\x1f\n\x1bORIGINATE_LAB_ORDER_COMMAND\x10G\x12\x1a\n\x16\x45\x44IT_LAB_ORDER_COMMAND\x10H\x12\x1c\n\x18\x44\x45LETE_LAB_ORDER_COMMAND\x10I\x12\x1c\n\x18\x43OMMIT_LAB_ORDER_COMMAND\x10J\x12$\n ENTER_IN_ERROR_LAB_ORDER_COMMAND\x10K\x12$\n ORIGINATE_FAMILY_HISTORY_COMMAND\x10L\x12\x1f\n\x1b\x45\x44IT_FAMILY_HISTORY_COMMAND\x10M\x12!\n\x1d\x44\x45LETE_FAMILY_HISTORY_COMMAND\x10N\x12!\n\x1d\x43OMMIT_FAMILY_HISTORY_COMMAND\x10O\x12)\n%ENTER_IN_ERROR_FAMILY_HISTORY_COMMAND\x10P\x12\x1d\n\x19ORIGINATE_ALLERGY_COMMAND\x10Q\x12\x18\n\x14\x45\x44IT_ALLERGY_COMMAND\x10R\x12\x1a\n\x16\x44\x45LETE_ALLERGY_COMMAND\x10S\x12\x1a\n\x16\x43OMMIT_ALLERGY_COMMAND\x10T\x12\"\n\x1e\x45NTER_IN_ERROR_ALLERGY_COMMAND\x10U\x12$\n ORIGINATE_REMOVE_ALLERGY_COMMAND\x10V\x12\x1f\n\x1b\x45\x44IT_REMOVE_ALLERGY_COMMAND\x10W\x12!\n\x1d\x44\x45LETE_REMOVE_ALLERGY_COMMAND\x10X\x12!\n\x1d\x43OMMIT_REMOVE_ALLERGY_COMMAND\x10Y\x12)\n%ENTER_IN_ERROR_REMOVE_ALLERGY_COMMAND\x10Z\x12&\n\"ORIGINATE_SURGICAL_HISTORY_COMMAND\x10[\x12!\n\x1d\x45\x44IT_SURGICAL_HISTORY_COMMAND\x10\\\x12#\n\x1f\x44\x45LETE_SURGICAL_HISTORY_COMMAND\x10]\x12#\n\x1f\x43OMMIT_SURGICAL_HISTORY_COMMAND\x10^\x12+\n\'ENTER_IN_ERROR_SURGICAL_HISTORY_COMMAND\x10_\x12\x0f\n\x0b\x43REATE_TASK\x10\x64\x12\x0f\n\x0bUPDATE_TASK\x10\x65\x12\x17\n\x13\x43REATE_TASK_COMMENT\x10\x66\x12%\n!ORIGINATE_MEDICAL_HISTORY_COMMAND\x10g\x12 \n\x1c\x45\x44IT_MEDICAL_HISTORY_COMMAND\x10h\x12\"\n\x1e\x44\x45LETE_MEDICAL_HISTORY_COMMAND\x10i\x12\"\n\x1e\x43OMMIT_MEDICAL_HISTORY_COMMAND\x10j\x12*\n&ENTER_IN_ERROR_MEDICAL_HISTORY_COMMAND\x10k\x12\x1f\n\x1b\x41\x44\x44_OR_UPDATE_PROTOCOL_CARD\x10n\x12\x1b\n\x16ORIGINATE_TASK_COMMAND\x10\x85\x01\x12\x16\n\x11\x45\x44IT_TASK_COMMAND\x10\x86\x01\x12\x18\n\x13\x44\x45LETE_TASK_COMMAND\x10\x87\x01\x12\x18\n\x13\x43OMMIT_TASK_COMMAND\x10\x88\x01\x12 \n\x1b\x45NTER_IN_ERROR_TASK_COMMAND\x10\x89\x01\x12\x1c\n\x18ORIGINATE_REFILL_COMMAND\x10q\x12\x17\n\x13\x45\x44IT_REFILL_COMMAND\x10r\x12\x19\n\x15\x44\x45LETE_REFILL_COMMAND\x10s\x12\x19\n\x15\x43OMMIT_REFILL_COMMAND\x10t\x12!\n\x1d\x45NTER_IN_ERROR_REFILL_COMMAND\x10u\x12\x1c\n\x18ORIGINATE_VITALS_COMMAND\x10v\x12\x17\n\x13\x45\x44IT_VITALS_COMMAND\x10w\x12\x19\n\x15\x44\x45LETE_VITALS_COMMAND\x10x\x12\x19\n\x15\x43OMMIT_VITALS_COMMAND\x10y\x12!\n\x1d\x45NTER_IN_ERROR_VITALS_COMMAND\x10z\x12&\n\"ORIGINATE_UPDATE_DIAGNOSIS_COMMAND\x10{\x12!\n\x1d\x45\x44IT_UPDATE_DIAGNOSIS_COMMAND\x10|\x12#\n\x1f\x44\x45LETE_UPDATE_DIAGNOSIS_COMMAND\x10}\x12#\n\x1f\x43OMMIT_UPDATE_DIAGNOSIS_COMMAND\x10~\x12+\n\'ENTER_IN_ERROR_UPDATE_DIAGNOSIS_COMMAND\x10\x7f\x12!\n\x1cORIGINATE_CLOSE_GOAL_COMMAND\x10\x80\x01\x12\x1c\n\x17\x45\x44IT_CLOSE_GOAL_COMMAND\x10\x81\x01\x12\x1e\n\x19\x44\x45LETE_CLOSE_GOAL_COMMAND\x10\x82\x01\x12\x1e\n\x19\x43OMMIT_CLOSE_GOAL_COMMAND\x10\x83\x01\x12&\n!ENTER_IN_ERROR_CLOSE_GOAL_COMMAND\x10\x84\x01\x12 \n\x1b\x43REATE_QUESTIONNAIRE_RESULT\x10\x8a\x01\x12-\n(ANNOTATE_PATIENT_CHART_CONDITION_RESULTS\x10\xc8\x01\x12%\n ANNOTATE_CLAIM_CONDITION_RESULTS\x10\xac\x02\x12(\n#SHOW_PATIENT_CHART_SUMMARY_SECTIONS\x10\x90\x03\x12\"\n\x1dSHOW_PATIENT_PROFILE_SECTIONS\x10\xf4\x03\x12\x37\n2PATIENT_PROFILE__ADD_PHARMACY__POST_SEARCH_RESULTS\x10\xf5\x03\x12)\n$SEND_SURESCRIPTS_ELIGIBILITY_REQUEST\x10\xd8\x04\x12\x30\n+SEND_SURESCRIPTS_MEDICATION_HISTORY_REQUEST\x10\xd9\x04\x12&\n!SEND_SURESCRIPTS_BENEFITS_REQUEST\x10\xda\x04\x12\x1b\n\x16ORIGINATE_EXAM_COMMAND\x10\xbc\x05\x12\x16\n\x11\x45\x44IT_EXAM_COMMAND\x10\xbd\x05\x12\x18\n\x13\x44\x45LETE_EXAM_COMMAND\x10\xbe\x05\x12\x18\n\x13\x43OMMIT_EXAM_COMMAND\x10\xbf\x05\x12 \n\x1b\x45NTER_IN_ERROR_EXAM_COMMAND\x10\xc0\x05\x12\x1a\n\x15ORIGINATE_ROS_COMMAND\x10\xa0\x06\x12\x15\n\x10\x45\x44IT_ROS_COMMAND\x10\xa1\x06\x12\x17\n\x12\x44\x45LETE_ROS_COMMAND\x10\xa2\x06\x12\x17\n\x12\x43OMMIT_ROS_COMMAND\x10\xa3\x06\x12\x1f\n\x1a\x45NTER_IN_ERROR_ROS_COMMAND\x10\xa4\x06\x12,\n\'ORIGINATE_STRUCTURED_ASSESSMENT_COMMAND\x10\x84\x07\x12\'\n\"EDIT_STRUCTURED_ASSESSMENT_COMMAND\x10\x85\x07\x12)\n$DELETE_STRUCTURED_ASSESSMENT_COMMAND\x10\x86\x07\x12)\n$COMMIT_STRUCTURED_ASSESSMENT_COMMAND\x10\x87\x07\x12\x31\n,ENTER_IN_ERROR_STRUCTURED_ASSESSMENT_COMMAND\x10\x88\x07\x12\x17\n\x12SHOW_ACTION_BUTTON\x10\xe8\x07\x12(\n#PATIENT_PORTAL__INTAKE_FORM_RESULTS\x10\xd0\x0f\x12\x11\n\x0cLAUNCH_MODAL\x10\xb8\x17\x62\x06proto3')
|
|
18
18
|
|
|
19
19
|
_globals = globals()
|
|
20
20
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -22,7 +22,7 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'canvas_generated.messages.e
|
|
|
22
22
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
23
23
|
DESCRIPTOR._options = None
|
|
24
24
|
_globals['_EFFECTTYPE']._serialized_start=153
|
|
25
|
-
_globals['_EFFECTTYPE']._serialized_end=
|
|
25
|
+
_globals['_EFFECTTYPE']._serialized_end=5310
|
|
26
26
|
_globals['_EFFECT']._serialized_start=51
|
|
27
27
|
_globals['_EFFECT']._serialized_end=150
|
|
28
28
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -137,6 +137,7 @@ class EffectType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
|
|
137
137
|
DELETE_CLOSE_GOAL_COMMAND: _ClassVar[EffectType]
|
|
138
138
|
COMMIT_CLOSE_GOAL_COMMAND: _ClassVar[EffectType]
|
|
139
139
|
ENTER_IN_ERROR_CLOSE_GOAL_COMMAND: _ClassVar[EffectType]
|
|
140
|
+
CREATE_QUESTIONNAIRE_RESULT: _ClassVar[EffectType]
|
|
140
141
|
ANNOTATE_PATIENT_CHART_CONDITION_RESULTS: _ClassVar[EffectType]
|
|
141
142
|
ANNOTATE_CLAIM_CONDITION_RESULTS: _ClassVar[EffectType]
|
|
142
143
|
SHOW_PATIENT_CHART_SUMMARY_SECTIONS: _ClassVar[EffectType]
|
|
@@ -293,6 +294,7 @@ EDIT_CLOSE_GOAL_COMMAND: EffectType
|
|
|
293
294
|
DELETE_CLOSE_GOAL_COMMAND: EffectType
|
|
294
295
|
COMMIT_CLOSE_GOAL_COMMAND: EffectType
|
|
295
296
|
ENTER_IN_ERROR_CLOSE_GOAL_COMMAND: EffectType
|
|
297
|
+
CREATE_QUESTIONNAIRE_RESULT: EffectType
|
|
296
298
|
ANNOTATE_PATIENT_CHART_CONDITION_RESULTS: EffectType
|
|
297
299
|
ANNOTATE_CLAIM_CONDITION_RESULTS: EffectType
|
|
298
300
|
SHOW_PATIENT_CHART_SUMMARY_SECTIONS: EffectType
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from canvas_sdk.effects.base import EffectType, _BaseEffect
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CreateQuestionnaireResult(_BaseEffect):
|
|
7
|
+
"""CreateQuestionnaireResult effect."""
|
|
8
|
+
|
|
9
|
+
class Meta:
|
|
10
|
+
effect_type = EffectType.CREATE_QUESTIONNAIRE_RESULT
|
|
11
|
+
|
|
12
|
+
interview_id: str | None = None
|
|
13
|
+
score: float | None = None
|
|
14
|
+
abnormal: bool | None = None
|
|
15
|
+
narrative: str | None = None
|
|
16
|
+
code_system: str | None = None
|
|
17
|
+
code: str | None = None
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def values(self) -> dict[str, Any]:
|
|
21
|
+
"""Make the payload."""
|
|
22
|
+
return {
|
|
23
|
+
"interview_id": self.interview_id,
|
|
24
|
+
"score": self.score,
|
|
25
|
+
"abnormal": self.abnormal or False,
|
|
26
|
+
"narrative": self.narrative or "",
|
|
27
|
+
"code_system": self.code_system,
|
|
28
|
+
"code": self.code,
|
|
29
|
+
}
|
canvas_sdk/models/__init__.py
CHANGED
canvas_sdk/v1/data/command.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from django.apps import apps
|
|
1
2
|
from django.db import models
|
|
2
3
|
|
|
3
4
|
from canvas_sdk.v1.data.patient import Patient
|
|
@@ -25,3 +26,15 @@ class Command(models.Model):
|
|
|
25
26
|
schema_key = models.TextField()
|
|
26
27
|
data = models.JSONField()
|
|
27
28
|
origination_source = models.CharField()
|
|
29
|
+
anchor_object_type = models.CharField()
|
|
30
|
+
anchor_object_dbid = models.BigIntegerField()
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def anchor_object(self) -> models.Model | None:
|
|
34
|
+
"""
|
|
35
|
+
Use the anchor_object_type and anchor_object_dbid to get the anchor object for the command.
|
|
36
|
+
"""
|
|
37
|
+
# TODO: Is the anchor object type enough here, or do we need a mapping? The home-app model
|
|
38
|
+
# names might not exactly match the plugins model names.
|
|
39
|
+
anchor_model = apps.get_model(app_label="canvas_sdk", model_name=self.anchor_object_type)
|
|
40
|
+
return anchor_model.objects.get(dbid=self.anchor_object_dbid)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Shamelessly cribbed from https://charemza.name/blog/posts/aws/python/you-might-not-need-boto-3/
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
import hashlib
|
|
5
|
+
import hmac
|
|
6
|
+
import urllib.parse
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def aws_sig_v4_headers(
|
|
10
|
+
access_key_id: str,
|
|
11
|
+
secret_access_key: str,
|
|
12
|
+
pre_auth_headers: dict[str, str],
|
|
13
|
+
service: str,
|
|
14
|
+
region: str,
|
|
15
|
+
host: str,
|
|
16
|
+
method: str,
|
|
17
|
+
path: str,
|
|
18
|
+
query: dict[str, str],
|
|
19
|
+
payload: bytes,
|
|
20
|
+
) -> dict[str, str]:
|
|
21
|
+
"""Constructs signed headers for use in requests made to AWS."""
|
|
22
|
+
algorithm = "AWS4-HMAC-SHA256"
|
|
23
|
+
now = datetime.datetime.utcnow()
|
|
24
|
+
amzdate = now.strftime("%Y%m%dT%H%M%SZ")
|
|
25
|
+
datestamp = now.strftime("%Y%m%d")
|
|
26
|
+
payload_hash = hashlib.sha256(payload).hexdigest()
|
|
27
|
+
credential_scope = f"{datestamp}/{region}/{service}/aws4_request"
|
|
28
|
+
|
|
29
|
+
pre_auth_headers_lower = {
|
|
30
|
+
header_key.lower(): " ".join(header_value.split())
|
|
31
|
+
for header_key, header_value in pre_auth_headers.items()
|
|
32
|
+
}
|
|
33
|
+
required_headers = {
|
|
34
|
+
"host": host,
|
|
35
|
+
"x-amz-content-sha256": payload_hash,
|
|
36
|
+
"x-amz-date": amzdate,
|
|
37
|
+
}
|
|
38
|
+
headers = {**pre_auth_headers_lower, **required_headers}
|
|
39
|
+
header_keys = sorted(headers.keys())
|
|
40
|
+
signed_headers = ";".join(header_keys)
|
|
41
|
+
|
|
42
|
+
def signature() -> str:
|
|
43
|
+
def canonical_request() -> str:
|
|
44
|
+
canonical_uri = urllib.parse.quote(path, safe="/~")
|
|
45
|
+
quoted_query = sorted(
|
|
46
|
+
(urllib.parse.quote(key, safe="~"), urllib.parse.quote(value, safe="~"))
|
|
47
|
+
for key, value in query.items()
|
|
48
|
+
)
|
|
49
|
+
canonical_querystring = "&".join(f"{key}={value}" for key, value in quoted_query)
|
|
50
|
+
canonical_headers = "".join(f"{key}:{headers[key]}\n" for key in header_keys)
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
f"{method}\n{canonical_uri}\n{canonical_querystring}\n"
|
|
54
|
+
+ f"{canonical_headers}\n{signed_headers}\n{payload_hash}"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def sign(key: bytes, msg: str) -> bytes:
|
|
58
|
+
return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
|
|
59
|
+
|
|
60
|
+
string_to_sign = (
|
|
61
|
+
f"{algorithm}\n{amzdate}\n{credential_scope}\n"
|
|
62
|
+
+ hashlib.sha256(canonical_request().encode("utf-8")).hexdigest()
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
date_key = sign(("AWS4" + secret_access_key).encode("utf-8"), datestamp)
|
|
66
|
+
region_key = sign(date_key, region)
|
|
67
|
+
service_key = sign(region_key, service)
|
|
68
|
+
request_key = sign(service_key, "aws4_request")
|
|
69
|
+
return sign(request_key, string_to_sign).hex()
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
**pre_auth_headers,
|
|
73
|
+
"x-amz-date": amzdate,
|
|
74
|
+
"x-amz-content-sha256": payload_hash,
|
|
75
|
+
"Authorization": f"{algorithm} Credential={access_key_id}/{credential_scope}, "
|
|
76
|
+
f"SignedHeaders={signed_headers}, Signature=" + signature(),
|
|
77
|
+
}
|
|
@@ -9,12 +9,13 @@ from pathlib import Path
|
|
|
9
9
|
from typing import Any, TypedDict
|
|
10
10
|
from urllib import parse
|
|
11
11
|
|
|
12
|
-
import boto3
|
|
13
12
|
import psycopg
|
|
13
|
+
import requests
|
|
14
14
|
from psycopg import Connection
|
|
15
15
|
from psycopg.rows import dict_row
|
|
16
16
|
|
|
17
17
|
import settings
|
|
18
|
+
from plugin_runner.aws_headers import aws_sig_v4_headers
|
|
18
19
|
from plugin_runner.exceptions import InvalidPluginFormat, PluginInstallationError
|
|
19
20
|
|
|
20
21
|
# Plugin "packages" include this prefix in the database record for the plugin and the S3 bucket key.
|
|
@@ -98,17 +99,34 @@ def _extract_rows_to_dict(rows: list) -> dict[str, PluginAttributes]:
|
|
|
98
99
|
@contextmanager
|
|
99
100
|
def download_plugin(plugin_package: str) -> Generator:
|
|
100
101
|
"""Download the plugin package from the S3 bucket."""
|
|
101
|
-
|
|
102
|
+
method = "GET"
|
|
103
|
+
host = f"s3-{settings.AWS_REGION}.amazonaws.com"
|
|
104
|
+
bucket = settings.MEDIA_S3_BUCKET_NAME
|
|
105
|
+
customer_identifier = settings.CUSTOMER_IDENTIFIER
|
|
106
|
+
path = f"/{bucket}/{customer_identifier}/{plugin_package}"
|
|
107
|
+
payload = b"This is required for the AWS headers because it is part of the signature"
|
|
108
|
+
pre_auth_headers: dict[str, str] = {}
|
|
109
|
+
query: dict[str, str] = {}
|
|
110
|
+
headers = aws_sig_v4_headers(
|
|
111
|
+
settings.AWS_ACCESS_KEY_ID,
|
|
112
|
+
settings.AWS_SECRET_ACCESS_KEY,
|
|
113
|
+
pre_auth_headers,
|
|
114
|
+
"s3",
|
|
115
|
+
settings.AWS_REGION,
|
|
116
|
+
host,
|
|
117
|
+
method,
|
|
118
|
+
path,
|
|
119
|
+
query,
|
|
120
|
+
payload,
|
|
121
|
+
)
|
|
122
|
+
|
|
102
123
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
103
124
|
prefix_dir = Path(temp_dir) / UPLOAD_TO_PREFIX
|
|
104
125
|
prefix_dir.mkdir() # create an intermediate directory reflecting the prefix
|
|
105
126
|
download_path = Path(temp_dir) / plugin_package
|
|
106
127
|
with open(download_path, "wb") as download_file:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
f"{settings.CUSTOMER_IDENTIFIER}/{plugin_package}",
|
|
110
|
-
download_file,
|
|
111
|
-
)
|
|
128
|
+
response = requests.request(method=method, url=f"https://{host}{path}", headers=headers)
|
|
129
|
+
download_file.write(response.content)
|
|
112
130
|
yield download_path
|
|
113
131
|
|
|
114
132
|
|
|
@@ -2,11 +2,10 @@ import json
|
|
|
2
2
|
import tarfile
|
|
3
3
|
import tempfile
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from unittest.mock import MagicMock
|
|
5
|
+
from unittest.mock import MagicMock, patch
|
|
6
6
|
|
|
7
7
|
from pytest_mock import MockerFixture
|
|
8
8
|
|
|
9
|
-
import settings
|
|
10
9
|
from plugin_runner.plugin_installer import (
|
|
11
10
|
PluginAttributes,
|
|
12
11
|
_extract_rows_to_dict,
|
|
@@ -106,13 +105,12 @@ def test_plugin_installation_from_tarball(mocker: MockerFixture) -> None:
|
|
|
106
105
|
|
|
107
106
|
def test_download(mocker: MockerFixture) -> None:
|
|
108
107
|
"""Test that the plugin package can be written to disk, mocking out S3."""
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
)
|
|
108
|
+
mock_response = MagicMock()
|
|
109
|
+
mock_response.status_code = 200
|
|
110
|
+
mock_response.content = b"some content in a file"
|
|
111
|
+
with patch("requests.request", return_value=mock_response) as mock_request:
|
|
112
|
+
plugin_package = "plugins/plugin1.tar.gz"
|
|
113
|
+
with download_plugin(plugin_package) as plugin_path:
|
|
114
|
+
assert plugin_path.exists()
|
|
115
|
+
assert plugin_path.read_bytes() == b"some content in a file"
|
|
116
|
+
mock_request.assert_called_once()
|
|
@@ -14,6 +14,11 @@ import os
|
|
|
14
14
|
result = os.listdir('.')
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
+
CODE_WITH_PLUGIN_RUNNER_SETTING_IMPORT = """
|
|
18
|
+
import settings
|
|
19
|
+
result = settings.AWS_SECRET_ACCESS_KEY
|
|
20
|
+
"""
|
|
21
|
+
|
|
17
22
|
CODE_WITH_ALLOWED_IMPORT = """
|
|
18
23
|
import json
|
|
19
24
|
result = json.dumps({"key": "value"})
|
|
@@ -39,7 +44,14 @@ def test_valid_code_execution() -> None:
|
|
|
39
44
|
def test_disallowed_import() -> None:
|
|
40
45
|
"""Test that restricted imports are not allowed."""
|
|
41
46
|
sandbox = Sandbox(CODE_WITH_RESTRICTED_IMPORT)
|
|
42
|
-
with pytest.raises(ImportError, match="os' is not an allowed import."):
|
|
47
|
+
with pytest.raises(ImportError, match="'os' is not an allowed import."):
|
|
48
|
+
sandbox.execute()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_plugin_runner_settings_import() -> None:
|
|
52
|
+
"""Test that imports of plugin runner settings are not allowed."""
|
|
53
|
+
sandbox = Sandbox(CODE_WITH_PLUGIN_RUNNER_SETTING_IMPORT)
|
|
54
|
+
with pytest.raises(ImportError, match="'settings' is not an allowed import."):
|
|
43
55
|
sandbox.execute()
|
|
44
56
|
|
|
45
57
|
|
settings.py
CHANGED
|
@@ -49,6 +49,10 @@ else:
|
|
|
49
49
|
|
|
50
50
|
DATABASES = {"default": database_dict}
|
|
51
51
|
|
|
52
|
+
AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID", "")
|
|
53
|
+
AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY", "")
|
|
54
|
+
AWS_REGION = os.getenv("AWS_REGION", "us-west-2")
|
|
55
|
+
MEDIA_S3_BUCKET_NAME = os.getenv("MEDIA_S3_BUCKET_NAME", "canvas-client-media")
|
|
52
56
|
|
|
53
57
|
PLUGIN_RUNNER_SIGNING_KEY = os.getenv("PLUGIN_RUNNER_SIGNING_KEY", "")
|
|
54
58
|
|
|
File without changes
|
|
File without changes
|