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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: canvas
3
- Version: 0.13.3
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=hvORKOxKesEFMnvrcu-QAzWDDaMXEpnkOGifVulkh9k,9029
101
- canvas_generated/messages/effects_pb2.pyi,sha256=AULAWggal2prDVKBg82axRO2uzQBzj0Qp9cvHn8FUOg,15588
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=Toz3-9Im7ZsT5_i8ToS3U6LLFm3qCerkb95EwPc-alA,321
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=PGKmFXjTbTQW7WeJbrv4vLbZh7g7UDWfoqnjANg5sVk,989
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=Ah3A1j6vO8wi4HPRYVlxF1w-GqbuTvLmDCQvLQK3Oy8,7020
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=BETY7stpf_DZdSBLSSBc_NeAWcLU0m_cptG8C8_rk1Q,3937
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=6Jw2Akcl1KQpXb01FxDvI_ZToJzOcpABwFkXK3fl8BU,3434
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=UG0jV2Sht7ftEJjqEyLKn9fld0jAtblqO7oJdfjNYvA,2144
278
- canvas-0.13.3.dist-info/METADATA,sha256=vh349TC_eBl1Bi0NgHhlypsxT1gkx2598CJtbrBBtS4,4793
279
- canvas-0.13.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
280
- canvas-0.13.3.dist-info/entry_points.txt,sha256=VSmSo1IZ3aEfL7enmLmlWSraS_IIkoXNVeyXzgRxFiY,46
281
- canvas-0.13.3.dist-info/RECORD,,
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*\x83(\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(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')
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=5276
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
+ }
@@ -5,3 +5,4 @@ from canvas_sdk.v1.data.condition import Condition
5
5
  from canvas_sdk.v1.data.note import Note
6
6
  from canvas_sdk.v1.data.patient import Patient
7
7
  from canvas_sdk.v1.data.user import CanvasUser
8
+ from canvas_sdk.v1.data.questionnaire import Interview
@@ -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
- s3 = boto3.client("s3")
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
- s3.download_fileobj(
108
- "canvas-client-media",
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
- mock_s3_client = MagicMock()
110
- mocker.patch("boto3.client", return_value=mock_s3_client)
111
-
112
- plugin_package = "plugins/plugin1.tar.gz"
113
- with download_plugin(plugin_package) as plugin_path:
114
- assert plugin_path.exists()
115
-
116
- mock_s3_client.download_fileobj.assert_called_once_with(
117
- "canvas-client-media", f"{settings.CUSTOMER_IDENTIFIER}/{plugin_package}", mocker.ANY
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