canvas 0.1.12b0__py3-none-any.whl → 0.1.15__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 (65) hide show
  1. {canvas-0.1.12b0.dist-info → canvas-0.1.15.dist-info}/METADATA +43 -2
  2. canvas-0.1.15.dist-info/RECORD +95 -0
  3. {canvas-0.1.12b0.dist-info → canvas-0.1.15.dist-info}/WHEEL +1 -1
  4. canvas_cli/apps/plugin/__init__.py +3 -1
  5. canvas_cli/apps/plugin/plugin.py +74 -0
  6. canvas_cli/main.py +5 -3
  7. canvas_cli/templates/plugins/default/{{ cookiecutter.__project_slug }}/protocols/my_protocol.py +3 -0
  8. canvas_generated/data_access_layer/data_access_layer_pb2.py +30 -0
  9. canvas_generated/data_access_layer/data_access_layer_pb2.pyi +23 -0
  10. canvas_generated/data_access_layer/data_access_layer_pb2_grpc.py +66 -0
  11. canvas_generated/messages/effects_pb2.py +28 -0
  12. canvas_generated/messages/effects_pb2.pyi +147 -0
  13. canvas_generated/messages/events_pb2.py +31 -0
  14. {generated → canvas_generated}/messages/events_pb2.pyi +3 -1
  15. {generated → canvas_generated}/messages/plugins_pb2.py +7 -7
  16. canvas_generated/services/plugin_runner_pb2.py +28 -0
  17. {generated → canvas_generated}/services/plugin_runner_pb2.pyi +2 -2
  18. {generated → canvas_generated}/services/plugin_runner_pb2_grpc.py +14 -14
  19. canvas_sdk/base.py +45 -0
  20. canvas_sdk/commands/base.py +61 -41
  21. canvas_sdk/commands/commands/assess.py +6 -2
  22. canvas_sdk/commands/commands/diagnose.py +4 -14
  23. canvas_sdk/commands/commands/goal.py +3 -2
  24. canvas_sdk/commands/commands/history_present_illness.py +2 -1
  25. canvas_sdk/commands/commands/medication_statement.py +6 -2
  26. canvas_sdk/commands/commands/plan.py +2 -1
  27. canvas_sdk/commands/commands/prescribe.py +24 -11
  28. canvas_sdk/commands/commands/questionnaire.py +6 -2
  29. canvas_sdk/commands/commands/reason_for_visit.py +13 -6
  30. canvas_sdk/commands/commands/stop_medication.py +6 -2
  31. canvas_sdk/commands/commands/update_goal.py +4 -1
  32. canvas_sdk/commands/tests/test_utils.py +31 -64
  33. canvas_sdk/commands/tests/tests.py +116 -65
  34. canvas_sdk/data/__init__.py +1 -0
  35. canvas_sdk/data/base.py +22 -0
  36. canvas_sdk/data/data_access_layer_client.py +95 -0
  37. canvas_sdk/data/exceptions.py +16 -0
  38. canvas_sdk/data/patient.py +26 -0
  39. canvas_sdk/data/staff.py +6 -0
  40. canvas_sdk/data/task.py +60 -0
  41. canvas_sdk/effects/__init__.py +1 -1
  42. canvas_sdk/effects/banner_alert/__init__.py +2 -0
  43. canvas_sdk/effects/banner_alert/add_banner_alert.py +49 -0
  44. canvas_sdk/effects/banner_alert/remove_banner_alert.py +20 -0
  45. canvas_sdk/effects/base.py +4 -6
  46. canvas_sdk/events/__init__.py +1 -1
  47. canvas_sdk/handlers/__init__.py +1 -0
  48. canvas_sdk/handlers/base.py +16 -0
  49. canvas_sdk/handlers/cron_task.py +35 -0
  50. canvas_sdk/protocols/base.py +2 -11
  51. canvas_sdk/utils/stats.py +27 -0
  52. logger/__init__.py +2 -0
  53. logger/logger.py +48 -0
  54. canvas-0.1.12b0.dist-info/RECORD +0 -78
  55. canvas_sdk/effects/banner_alert/banner_alert.py +0 -37
  56. canvas_sdk/effects/banner_alert/constants.py +0 -19
  57. generated/messages/effects_pb2.py +0 -28
  58. generated/messages/effects_pb2.pyi +0 -25
  59. generated/messages/events_pb2.py +0 -31
  60. generated/services/plugin_runner_pb2.py +0 -28
  61. {canvas-0.1.12b0.dist-info → canvas-0.1.15.dist-info}/entry_points.txt +0 -0
  62. {generated → canvas_generated}/messages/effects_pb2_grpc.py +0 -0
  63. {generated → canvas_generated}/messages/events_pb2_grpc.py +0 -0
  64. {generated → canvas_generated}/messages/plugins_pb2.pyi +0 -0
  65. {generated → canvas_generated}/messages/plugins_pb2_grpc.py +0 -0
@@ -0,0 +1,31 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: canvas_generated/messages/events.proto
4
+ # Protobuf Python Version: 4.25.0
5
+ """Generated protocol buffer code."""
6
+ from google.protobuf import descriptor as _descriptor
7
+ from google.protobuf import descriptor_pool as _descriptor_pool
8
+ from google.protobuf import symbol_database as _symbol_database
9
+ from google.protobuf.internal import builder as _builder
10
+ # @@protoc_insertion_point(imports)
11
+
12
+ _sym_db = _symbol_database.Default()
13
+
14
+
15
+ from canvas_generated.messages import effects_pb2 as canvas__generated_dot_messages_dot_effects__pb2
16
+
17
+
18
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&canvas_generated/messages/events.proto\x12\x06\x63\x61nvas\x1a\'canvas_generated/messages/effects.proto\"I\n\x05\x45vent\x12\x1f\n\x04type\x18\x01 \x01(\x0e\x32\x11.canvas.EventType\x12\x0e\n\x06target\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontext\x18\x03 \x01(\t\"A\n\rEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x1f\n\x07\x65\x66\x66\x65\x63ts\x18\x02 \x03(\x0b\x32\x0e.canvas.Effect*\xc5=\n\tEventType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x1f\n\x1b\x41LLERGY_INTOLERANCE_CREATED\x10\x01\x12\x1f\n\x1b\x41LLERGY_INTOLERANCE_UPDATED\x10\x02\x12\x18\n\x14\x41PPOINTMENT_CANCELED\x10\x04\x12\x1a\n\x16\x41PPOINTMENT_CHECKED_IN\x10\x05\x12\x17\n\x13\x41PPOINTMENT_CREATED\x10\x06\x12\x19\n\x15\x41PPOINTMENT_NO_SHOWED\x10\x07\x12\x1b\n\x17\x41PPOINTMENT_RESCHEDULED\x10\x08\x12\x18\n\x14\x41PPOINTMENT_RESTORED\x10\t\x12\x17\n\x13\x41PPOINTMENT_UPDATED\x10\n\x12\x1d\n\x19\x42ILLING_LINE_ITEM_CREATED\x10\x0b\x12\x1d\n\x19\x42ILLING_LINE_ITEM_UPDATED\x10\x0c\x12\x16\n\x12\x43ONDITION_ASSESSED\x10\r\x12\x15\n\x11\x43ONDITION_CREATED\x10\x0e\x12\x16\n\x12\x43ONDITION_RESOLVED\x10\x0f\x12\x15\n\x11\x43ONDITION_UPDATED\x10\x10\x12\x13\n\x0f\x43ONSENT_CREATED\x10\x11\x12\x13\n\x0f\x43ONSENT_DELETED\x10\x12\x12\x13\n\x0f\x43ONSENT_UPDATED\x10\x13\x12\x14\n\x10\x43OVERAGE_CREATED\x10\x14\x12\x14\n\x10\x43OVERAGE_UPDATED\x10\x15\x12\x15\n\x11\x45NCOUNTER_CREATED\x10\x16\x12\x15\n\x11\x45NCOUNTER_UPDATED\x10\x17\x12\x1a\n\x16IMAGING_REPORT_CREATED\x10\x1a\x12\x1a\n\x16IMAGING_REPORT_UPDATED\x10\x1b\x12\x18\n\x14IMMUNIZATION_CREATED\x10\x1c\x12\"\n\x1eIMMUNIZATION_STATEMENT_CREATED\x10\x1d\x12\"\n\x1eIMMUNIZATION_STATEMENT_UPDATED\x10\x1e\x12\x18\n\x14IMMUNIZATION_UPDATED\x10\x1f\x12\x17\n\x13INSTRUCTION_CREATED\x10 \x12\x17\n\x13INSTRUCTION_UPDATED\x10!\x12\x15\n\x11INTERVIEW_CREATED\x10\"\x12\x15\n\x11INTERVIEW_UPDATED\x10#\x12\x15\n\x11LAB_ORDER_CREATED\x10$\x12\x15\n\x11LAB_ORDER_UPDATED\x10%\x12\x16\n\x12LAB_REPORT_CREATED\x10&\x12\x16\n\x12LAB_REPORT_UPDATED\x10\'\x12 \n\x1cMEDICATION_LIST_ITEM_CREATED\x10(\x12 \n\x1cMEDICATION_LIST_ITEM_UPDATED\x10)\x12\x13\n\x0fMESSAGE_CREATED\x10*\x12\x13\n\x0fPATIENT_CREATED\x10+\x12\x13\n\x0fPATIENT_UPDATED\x10,\x12\x18\n\x14PRESCRIPTION_CREATED\x10-\x12\x18\n\x14PRESCRIPTION_UPDATED\x10.\x12\x1b\n\x17REFERRAL_REPORT_CREATED\x10/\x12\x1b\n\x17REFERRAL_REPORT_UPDATED\x10\x30\x12\x18\n\x14TASK_COMMENT_CREATED\x10\x33\x12\x10\n\x0cTASK_CREATED\x10\x36\x12\x18\n\x14TASK_LABELS_ADJUSTED\x10\x37\x12\x10\n\x0cTASK_UPDATED\x10\x39\x12\x16\n\x12VITAL_SIGN_CREATED\x10:\x12\x16\n\x12VITAL_SIGN_UPDATED\x10;\x12\x08\n\x04\x43RON\x10<\x12\x19\n\x15PRE_COMMAND_ORIGINATE\x10\x64\x12\x1a\n\x16POST_COMMAND_ORIGINATE\x10\x65\x12\x16\n\x12PRE_COMMAND_UPDATE\x10\x66\x12\x17\n\x13POST_COMMAND_UPDATE\x10g\x12\x16\n\x12PRE_COMMAND_COMMIT\x10h\x12\x17\n\x13POST_COMMAND_COMMIT\x10i\x12\x16\n\x12PRE_COMMAND_DELETE\x10j\x12\x17\n\x13POST_COMMAND_DELETE\x10k\x12\x1e\n\x1aPRE_COMMAND_ENTER_IN_ERROR\x10l\x12\x1f\n\x1bPOST_COMMAND_ENTER_IN_ERROR\x10m\x12\x1e\n\x1aPRE_COMMAND_EXECUTE_ACTION\x10n\x12\x1f\n\x1bPOST_COMMAND_EXECUTE_ACTION\x10o\x12\'\n\"ASSESS_COMMAND__CONDITION_SELECTED\x10\xc8\x01\x12\x31\n,MEDICATION_STATEMENT__MEDICATION__PRE_SEARCH\x10\xc9\x01\x12\x32\n-MEDICATION_STATEMENT__MEDICATION__POST_SEARCH\x10\xca\x01\x12/\n*MEDICATION_STATEMENT__MEDICATION__SELECTED\x10\xcb\x01\x12\"\n\x1d\x41SSESS_COMMAND__PRE_ORIGINATE\x10\xd8\x36\x12#\n\x1e\x41SSESS_COMMAND__POST_ORIGINATE\x10\xd9\x36\x12\x1f\n\x1a\x41SSESS_COMMAND__PRE_UPDATE\x10\xda\x36\x12 \n\x1b\x41SSESS_COMMAND__POST_UPDATE\x10\xdb\x36\x12\x1f\n\x1a\x41SSESS_COMMAND__PRE_COMMIT\x10\xdc\x36\x12 \n\x1b\x41SSESS_COMMAND__POST_COMMIT\x10\xdd\x36\x12\x1f\n\x1a\x41SSESS_COMMAND__PRE_DELETE\x10\xde\x36\x12 \n\x1b\x41SSESS_COMMAND__POST_DELETE\x10\xdf\x36\x12\'\n\"ASSESS_COMMAND__PRE_ENTER_IN_ERROR\x10\xe0\x36\x12(\n#ASSESS_COMMAND__POST_ENTER_IN_ERROR\x10\xe1\x36\x12\'\n\"ASSESS_COMMAND__PRE_EXECUTE_ACTION\x10\xe2\x36\x12(\n#ASSESS_COMMAND__POST_EXECUTE_ACTION\x10\xe3\x36\x12#\n\x1e\x41SSESS__CONDITION__POST_SEARCH\x10\xe4\x36\x12$\n\x1f\x44IAGNOSE__DIAGNOSE__POST_SEARCH\x10\xbcm\x12!\n\x1bGOAL_COMMAND__PRE_ORIGINATE\x10\xd0\x8c\x01\x12\"\n\x1cGOAL_COMMAND__POST_ORIGINATE\x10\xd1\x8c\x01\x12\x1e\n\x18GOAL_COMMAND__PRE_UPDATE\x10\xd2\x8c\x01\x12\x1f\n\x19GOAL_COMMAND__POST_UPDATE\x10\xd3\x8c\x01\x12\x1e\n\x18GOAL_COMMAND__PRE_COMMIT\x10\xd4\x8c\x01\x12\x1f\n\x19GOAL_COMMAND__POST_COMMIT\x10\xd5\x8c\x01\x12\x1e\n\x18GOAL_COMMAND__PRE_DELETE\x10\xd6\x8c\x01\x12\x1f\n\x19GOAL_COMMAND__POST_DELETE\x10\xd7\x8c\x01\x12&\n GOAL_COMMAND__PRE_ENTER_IN_ERROR\x10\xd8\x8c\x01\x12\'\n!GOAL_COMMAND__POST_ENTER_IN_ERROR\x10\xd9\x8c\x01\x12&\n GOAL_COMMAND__PRE_EXECUTE_ACTION\x10\xda\x8c\x01\x12\'\n!GOAL_COMMAND__POST_EXECUTE_ACTION\x10\xdb\x8c\x01\x12\x37\n1HISTORY_OF_PRESENT_ILLNESS_COMMAND__PRE_ORIGINATE\x10\xb8\x94\x01\x12\x38\n2HISTORY_OF_PRESENT_ILLNESS_COMMAND__POST_ORIGINATE\x10\xb9\x94\x01\x12\x34\n.HISTORY_OF_PRESENT_ILLNESS_COMMAND__PRE_UPDATE\x10\xba\x94\x01\x12\x35\n/HISTORY_OF_PRESENT_ILLNESS_COMMAND__POST_UPDATE\x10\xbb\x94\x01\x12\x34\n.HISTORY_OF_PRESENT_ILLNESS_COMMAND__PRE_COMMIT\x10\xbc\x94\x01\x12\x35\n/HISTORY_OF_PRESENT_ILLNESS_COMMAND__POST_COMMIT\x10\xbd\x94\x01\x12\x34\n.HISTORY_OF_PRESENT_ILLNESS_COMMAND__PRE_DELETE\x10\xbe\x94\x01\x12\x35\n/HISTORY_OF_PRESENT_ILLNESS_COMMAND__POST_DELETE\x10\xbf\x94\x01\x12<\n6HISTORY_OF_PRESENT_ILLNESS_COMMAND__PRE_ENTER_IN_ERROR\x10\xc0\x94\x01\x12=\n7HISTORY_OF_PRESENT_ILLNESS_COMMAND__POST_ENTER_IN_ERROR\x10\xc1\x94\x01\x12<\n6HISTORY_OF_PRESENT_ILLNESS_COMMAND__PRE_EXECUTE_ACTION\x10\xc2\x94\x01\x12=\n7HISTORY_OF_PRESENT_ILLNESS_COMMAND__POST_EXECUTE_ACTION\x10\xc3\x94\x01\x12\x31\n+MEDICATION_STATEMENT_COMMAND__PRE_ORIGINATE\x10\xe0\xda\x01\x12\x32\n,MEDICATION_STATEMENT_COMMAND__POST_ORIGINATE\x10\xe1\xda\x01\x12.\n(MEDICATION_STATEMENT_COMMAND__PRE_UPDATE\x10\xe2\xda\x01\x12/\n)MEDICATION_STATEMENT_COMMAND__POST_UPDATE\x10\xe3\xda\x01\x12.\n(MEDICATION_STATEMENT_COMMAND__PRE_COMMIT\x10\xe4\xda\x01\x12/\n)MEDICATION_STATEMENT_COMMAND__POST_COMMIT\x10\xe5\xda\x01\x12.\n(MEDICATION_STATEMENT_COMMAND__PRE_DELETE\x10\xe6\xda\x01\x12/\n)MEDICATION_STATEMENT_COMMAND__POST_DELETE\x10\xe7\xda\x01\x12\x36\n0MEDICATION_STATEMENT_COMMAND__PRE_ENTER_IN_ERROR\x10\xe8\xda\x01\x12\x37\n1MEDICATION_STATEMENT_COMMAND__POST_ENTER_IN_ERROR\x10\xe9\xda\x01\x12\x36\n0MEDICATION_STATEMENT_COMMAND__PRE_EXECUTE_ACTION\x10\xea\xda\x01\x12\x37\n1MEDICATION_STATEMENT_COMMAND__POST_EXECUTE_ACTION\x10\xeb\xda\x01\x12!\n\x1bPLAN_COMMAND__PRE_ORIGINATE\x10\x98\xf2\x01\x12\"\n\x1cPLAN_COMMAND__POST_ORIGINATE\x10\x99\xf2\x01\x12\x1e\n\x18PLAN_COMMAND__PRE_UPDATE\x10\x9a\xf2\x01\x12\x1f\n\x19PLAN_COMMAND__POST_UPDATE\x10\x9b\xf2\x01\x12\x1e\n\x18PLAN_COMMAND__PRE_COMMIT\x10\x9c\xf2\x01\x12\x1f\n\x19PLAN_COMMAND__POST_COMMIT\x10\x9d\xf2\x01\x12\x1e\n\x18PLAN_COMMAND__PRE_DELETE\x10\x9e\xf2\x01\x12\x1f\n\x19PLAN_COMMAND__POST_DELETE\x10\x9f\xf2\x01\x12&\n PLAN_COMMAND__PRE_ENTER_IN_ERROR\x10\xa0\xf2\x01\x12\'\n!PLAN_COMMAND__POST_ENTER_IN_ERROR\x10\xa1\xf2\x01\x12&\n PLAN_COMMAND__PRE_EXECUTE_ACTION\x10\xa2\xf2\x01\x12\'\n!PLAN_COMMAND__POST_EXECUTE_ACTION\x10\xa3\xf2\x01\x12&\n PRESCRIBE_COMMAND__PRE_ORIGINATE\x10\xe8\x81\x02\x12\'\n!PRESCRIBE_COMMAND__POST_ORIGINATE\x10\xe9\x81\x02\x12#\n\x1dPRESCRIBE_COMMAND__PRE_UPDATE\x10\xea\x81\x02\x12$\n\x1ePRESCRIBE_COMMAND__POST_UPDATE\x10\xeb\x81\x02\x12#\n\x1dPRESCRIBE_COMMAND__PRE_COMMIT\x10\xec\x81\x02\x12$\n\x1ePRESCRIBE_COMMAND__POST_COMMIT\x10\xed\x81\x02\x12#\n\x1dPRESCRIBE_COMMAND__PRE_DELETE\x10\xee\x81\x02\x12$\n\x1ePRESCRIBE_COMMAND__POST_DELETE\x10\xef\x81\x02\x12+\n%PRESCRIBE_COMMAND__PRE_ENTER_IN_ERROR\x10\xf0\x81\x02\x12,\n&PRESCRIBE_COMMAND__POST_ENTER_IN_ERROR\x10\xf1\x81\x02\x12+\n%PRESCRIBE_COMMAND__PRE_EXECUTE_ACTION\x10\xf2\x81\x02\x12,\n&PRESCRIBE_COMMAND__POST_EXECUTE_ACTION\x10\xf3\x81\x02\x12\'\n!PRESCRIBE__PRESCRIBE__POST_SEARCH\x10\xf4\x81\x02\x12*\n$QUESTIONNAIRE_COMMAND__PRE_ORIGINATE\x10\xd0\x89\x02\x12+\n%QUESTIONNAIRE_COMMAND__POST_ORIGINATE\x10\xd1\x89\x02\x12\'\n!QUESTIONNAIRE_COMMAND__PRE_UPDATE\x10\xd2\x89\x02\x12(\n\"QUESTIONNAIRE_COMMAND__POST_UPDATE\x10\xd3\x89\x02\x12\'\n!QUESTIONNAIRE_COMMAND__PRE_COMMIT\x10\xd4\x89\x02\x12(\n\"QUESTIONNAIRE_COMMAND__POST_COMMIT\x10\xd5\x89\x02\x12\'\n!QUESTIONNAIRE_COMMAND__PRE_DELETE\x10\xd6\x89\x02\x12(\n\"QUESTIONNAIRE_COMMAND__POST_DELETE\x10\xd7\x89\x02\x12/\n)QUESTIONNAIRE_COMMAND__PRE_ENTER_IN_ERROR\x10\xd8\x89\x02\x12\x30\n*QUESTIONNAIRE_COMMAND__POST_ENTER_IN_ERROR\x10\xd9\x89\x02\x12/\n)QUESTIONNAIRE_COMMAND__PRE_EXECUTE_ACTION\x10\xda\x89\x02\x12\x30\n*QUESTIONNAIRE_COMMAND__POST_EXECUTE_ACTION\x10\xdb\x89\x02\x12/\n)QUESTIONNAIRE__QUESTIONNAIRE__POST_SEARCH\x10\xdc\x89\x02\x12-\n\'REASON_FOR_VISIT_COMMAND__PRE_ORIGINATE\x10\xb8\x91\x02\x12.\n(REASON_FOR_VISIT_COMMAND__POST_ORIGINATE\x10\xb9\x91\x02\x12*\n$REASON_FOR_VISIT_COMMAND__PRE_UPDATE\x10\xba\x91\x02\x12+\n%REASON_FOR_VISIT_COMMAND__POST_UPDATE\x10\xbb\x91\x02\x12*\n$REASON_FOR_VISIT_COMMAND__PRE_COMMIT\x10\xbc\x91\x02\x12+\n%REASON_FOR_VISIT_COMMAND__POST_COMMIT\x10\xbd\x91\x02\x12*\n$REASON_FOR_VISIT_COMMAND__PRE_DELETE\x10\xbe\x91\x02\x12+\n%REASON_FOR_VISIT_COMMAND__POST_DELETE\x10\xbf\x91\x02\x12\x32\n,REASON_FOR_VISIT_COMMAND__PRE_ENTER_IN_ERROR\x10\xc0\x91\x02\x12\x33\n-REASON_FOR_VISIT_COMMAND__POST_ENTER_IN_ERROR\x10\xc1\x91\x02\x12\x32\n,REASON_FOR_VISIT_COMMAND__PRE_EXECUTE_ACTION\x10\xc2\x91\x02\x12\x33\n-REASON_FOR_VISIT_COMMAND__POST_EXECUTE_ACTION\x10\xc3\x91\x02\x12+\n%REASON_FOR_VISIT__CODING__POST_SEARCH\x10\xc4\x91\x02\x12,\n&STOP_MEDICATION_COMMAND__PRE_ORIGINATE\x10\xe0\xd7\x02\x12-\n\'STOP_MEDICATION_COMMAND__POST_ORIGINATE\x10\xe1\xd7\x02\x12)\n#STOP_MEDICATION_COMMAND__PRE_UPDATE\x10\xe2\xd7\x02\x12*\n$STOP_MEDICATION_COMMAND__POST_UPDATE\x10\xe3\xd7\x02\x12)\n#STOP_MEDICATION_COMMAND__PRE_COMMIT\x10\xe4\xd7\x02\x12*\n$STOP_MEDICATION_COMMAND__POST_COMMIT\x10\xe5\xd7\x02\x12)\n#STOP_MEDICATION_COMMAND__PRE_DELETE\x10\xe6\xd7\x02\x12*\n$STOP_MEDICATION_COMMAND__POST_DELETE\x10\xe7\xd7\x02\x12\x31\n+STOP_MEDICATION_COMMAND__PRE_ENTER_IN_ERROR\x10\xe8\xd7\x02\x12\x32\n,STOP_MEDICATION_COMMAND__POST_ENTER_IN_ERROR\x10\xe9\xd7\x02\x12\x31\n+STOP_MEDICATION_COMMAND__PRE_EXECUTE_ACTION\x10\xea\xd7\x02\x12\x32\n,STOP_MEDICATION_COMMAND__POST_EXECUTE_ACTION\x10\xeb\xd7\x02\x12.\n(STOP_MEDICATION__MEDICATION__POST_SEARCH\x10\xec\xd7\x02\x12!\n\x1bTASK_COMMAND__PRE_ORIGINATE\x10\x98\xef\x02\x12\"\n\x1cTASK_COMMAND__POST_ORIGINATE\x10\x99\xef\x02\x12\x1e\n\x18TASK_COMMAND__PRE_UPDATE\x10\x9a\xef\x02\x12\x1f\n\x19TASK_COMMAND__POST_UPDATE\x10\x9b\xef\x02\x12\x1e\n\x18TASK_COMMAND__PRE_COMMIT\x10\x9c\xef\x02\x12\x1f\n\x19TASK_COMMAND__POST_COMMIT\x10\x9d\xef\x02\x12\x1e\n\x18TASK_COMMAND__PRE_DELETE\x10\x9e\xef\x02\x12\x1f\n\x19TASK_COMMAND__POST_DELETE\x10\x9f\xef\x02\x12&\n TASK_COMMAND__PRE_ENTER_IN_ERROR\x10\xa0\xef\x02\x12\'\n!TASK_COMMAND__POST_ENTER_IN_ERROR\x10\xa1\xef\x02\x12&\n TASK_COMMAND__PRE_EXECUTE_ACTION\x10\xa2\xef\x02\x12\'\n!TASK_COMMAND__POST_EXECUTE_ACTION\x10\xa3\xef\x02\x12(\n\"UPDATE_GOAL_COMMAND__PRE_ORIGINATE\x10\xd0\x86\x03\x12)\n#UPDATE_GOAL_COMMAND__POST_ORIGINATE\x10\xd1\x86\x03\x12%\n\x1fUPDATE_GOAL_COMMAND__PRE_UPDATE\x10\xd2\x86\x03\x12&\n UPDATE_GOAL_COMMAND__POST_UPDATE\x10\xd3\x86\x03\x12%\n\x1fUPDATE_GOAL_COMMAND__PRE_COMMIT\x10\xd4\x86\x03\x12&\n UPDATE_GOAL_COMMAND__POST_COMMIT\x10\xd5\x86\x03\x12%\n\x1fUPDATE_GOAL_COMMAND__PRE_DELETE\x10\xd6\x86\x03\x12&\n UPDATE_GOAL_COMMAND__POST_DELETE\x10\xd7\x86\x03\x12-\n\'UPDATE_GOAL_COMMAND__PRE_ENTER_IN_ERROR\x10\xd8\x86\x03\x12.\n(UPDATE_GOAL_COMMAND__POST_ENTER_IN_ERROR\x10\xd9\x86\x03\x12-\n\'UPDATE_GOAL_COMMAND__PRE_EXECUTE_ACTION\x10\xda\x86\x03\x12.\n(UPDATE_GOAL_COMMAND__POST_EXECUTE_ACTION\x10\xdb\x86\x03\x12.\n(UPDATE_GOAL__GOAL_STATEMENT__POST_SEARCH\x10\xdc\x86\x03\x62\x06proto3')
19
+
20
+ _globals = globals()
21
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
22
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'canvas_generated.messages.events_pb2', _globals)
23
+ if _descriptor._USE_C_DESCRIPTORS == False:
24
+ DESCRIPTOR._options = None
25
+ _globals['_EVENTTYPE']._serialized_start=234
26
+ _globals['_EVENTTYPE']._serialized_end=8111
27
+ _globals['_EVENT']._serialized_start=91
28
+ _globals['_EVENT']._serialized_end=164
29
+ _globals['_EVENTRESPONSE']._serialized_start=166
30
+ _globals['_EVENTRESPONSE']._serialized_end=231
31
+ # @@protoc_insertion_point(module_scope)
@@ -1,4 +1,4 @@
1
- from generated.messages import effects_pb2 as _effects_pb2
1
+ from canvas_generated.messages import effects_pb2 as _effects_pb2
2
2
  from google.protobuf.internal import containers as _containers
3
3
  from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
4
4
  from google.protobuf import descriptor as _descriptor
@@ -61,6 +61,7 @@ class EventType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
61
61
  TASK_UPDATED: _ClassVar[EventType]
62
62
  VITAL_SIGN_CREATED: _ClassVar[EventType]
63
63
  VITAL_SIGN_UPDATED: _ClassVar[EventType]
64
+ CRON: _ClassVar[EventType]
64
65
  PRE_COMMAND_ORIGINATE: _ClassVar[EventType]
65
66
  POST_COMMAND_ORIGINATE: _ClassVar[EventType]
66
67
  PRE_COMMAND_UPDATE: _ClassVar[EventType]
@@ -268,6 +269,7 @@ TASK_LABELS_ADJUSTED: EventType
268
269
  TASK_UPDATED: EventType
269
270
  VITAL_SIGN_CREATED: EventType
270
271
  VITAL_SIGN_UPDATED: EventType
272
+ CRON: EventType
271
273
  PRE_COMMAND_ORIGINATE: EventType
272
274
  POST_COMMAND_ORIGINATE: EventType
273
275
  PRE_COMMAND_UPDATE: EventType
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # Generated by the protocol buffer compiler. DO NOT EDIT!
3
- # source: generated/messages/plugins.proto
3
+ # source: canvas_generated/messages/plugins.proto
4
4
  # Protobuf Python Version: 4.25.0
5
5
  """Generated protocol buffer code."""
6
6
  from google.protobuf import descriptor as _descriptor
@@ -14,15 +14,15 @@ _sym_db = _symbol_database.Default()
14
14
 
15
15
 
16
16
 
17
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n generated/messages/plugins.proto\"\x16\n\x14ReloadPluginsRequest\"(\n\x15ReloadPluginsResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x62\x06proto3')
17
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'canvas_generated/messages/plugins.proto\"\x16\n\x14ReloadPluginsRequest\"(\n\x15ReloadPluginsResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x62\x06proto3')
18
18
 
19
19
  _globals = globals()
20
20
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
21
- _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'generated.messages.plugins_pb2', _globals)
21
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'canvas_generated.messages.plugins_pb2', _globals)
22
22
  if _descriptor._USE_C_DESCRIPTORS == False:
23
23
  DESCRIPTOR._options = None
24
- _globals['_RELOADPLUGINSREQUEST']._serialized_start=36
25
- _globals['_RELOADPLUGINSREQUEST']._serialized_end=58
26
- _globals['_RELOADPLUGINSRESPONSE']._serialized_start=60
27
- _globals['_RELOADPLUGINSRESPONSE']._serialized_end=100
24
+ _globals['_RELOADPLUGINSREQUEST']._serialized_start=43
25
+ _globals['_RELOADPLUGINSREQUEST']._serialized_end=65
26
+ _globals['_RELOADPLUGINSRESPONSE']._serialized_start=67
27
+ _globals['_RELOADPLUGINSRESPONSE']._serialized_end=107
28
28
  # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: canvas_generated/services/plugin_runner.proto
4
+ # Protobuf Python Version: 4.25.0
5
+ """Generated protocol buffer code."""
6
+ from google.protobuf import descriptor as _descriptor
7
+ from google.protobuf import descriptor_pool as _descriptor_pool
8
+ from google.protobuf import symbol_database as _symbol_database
9
+ from google.protobuf.internal import builder as _builder
10
+ # @@protoc_insertion_point(imports)
11
+
12
+ _sym_db = _symbol_database.Default()
13
+
14
+
15
+ from canvas_generated.messages import events_pb2 as canvas__generated_dot_messages_dot_events__pb2
16
+ from canvas_generated.messages import plugins_pb2 as canvas__generated_dot_messages_dot_plugins__pb2
17
+
18
+
19
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n-canvas_generated/services/plugin_runner.proto\x12\x06\x63\x61nvas\x1a&canvas_generated/messages/events.proto\x1a\'canvas_generated/messages/plugins.proto2\x87\x01\n\x0cPluginRunner\x12\x35\n\x0bHandleEvent\x12\r.canvas.Event\x1a\x15.canvas.EventResponse0\x01\x12@\n\rReloadPlugins\x12\x15.ReloadPluginsRequest\x1a\x16.ReloadPluginsResponse0\x01\x62\x06proto3')
20
+
21
+ _globals = globals()
22
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
23
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'canvas_generated.services.plugin_runner_pb2', _globals)
24
+ if _descriptor._USE_C_DESCRIPTORS == False:
25
+ DESCRIPTOR._options = None
26
+ _globals['_PLUGINRUNNER']._serialized_start=139
27
+ _globals['_PLUGINRUNNER']._serialized_end=274
28
+ # @@protoc_insertion_point(module_scope)
@@ -1,5 +1,5 @@
1
- from generated.messages import events_pb2 as _events_pb2
2
- from generated.messages import plugins_pb2 as _plugins_pb2
1
+ from canvas_generated.messages import events_pb2 as _events_pb2
2
+ from canvas_generated.messages import plugins_pb2 as _plugins_pb2
3
3
  from google.protobuf import descriptor as _descriptor
4
4
  from typing import ClassVar as _ClassVar
5
5
 
@@ -2,8 +2,8 @@
2
2
  """Client and server classes corresponding to protobuf-defined services."""
3
3
  import grpc
4
4
 
5
- from generated.messages import events_pb2 as generated_dot_messages_dot_events__pb2
6
- from generated.messages import plugins_pb2 as generated_dot_messages_dot_plugins__pb2
5
+ from canvas_generated.messages import events_pb2 as canvas__generated_dot_messages_dot_events__pb2
6
+ from canvas_generated.messages import plugins_pb2 as canvas__generated_dot_messages_dot_plugins__pb2
7
7
 
8
8
 
9
9
  class PluginRunnerStub(object):
@@ -17,13 +17,13 @@ class PluginRunnerStub(object):
17
17
  """
18
18
  self.HandleEvent = channel.unary_stream(
19
19
  '/canvas.PluginRunner/HandleEvent',
20
- request_serializer=generated_dot_messages_dot_events__pb2.Event.SerializeToString,
21
- response_deserializer=generated_dot_messages_dot_events__pb2.EventResponse.FromString,
20
+ request_serializer=canvas__generated_dot_messages_dot_events__pb2.Event.SerializeToString,
21
+ response_deserializer=canvas__generated_dot_messages_dot_events__pb2.EventResponse.FromString,
22
22
  )
23
23
  self.ReloadPlugins = channel.unary_stream(
24
24
  '/canvas.PluginRunner/ReloadPlugins',
25
- request_serializer=generated_dot_messages_dot_plugins__pb2.ReloadPluginsRequest.SerializeToString,
26
- response_deserializer=generated_dot_messages_dot_plugins__pb2.ReloadPluginsResponse.FromString,
25
+ request_serializer=canvas__generated_dot_messages_dot_plugins__pb2.ReloadPluginsRequest.SerializeToString,
26
+ response_deserializer=canvas__generated_dot_messages_dot_plugins__pb2.ReloadPluginsResponse.FromString,
27
27
  )
28
28
 
29
29
 
@@ -47,13 +47,13 @@ def add_PluginRunnerServicer_to_server(servicer, server):
47
47
  rpc_method_handlers = {
48
48
  'HandleEvent': grpc.unary_stream_rpc_method_handler(
49
49
  servicer.HandleEvent,
50
- request_deserializer=generated_dot_messages_dot_events__pb2.Event.FromString,
51
- response_serializer=generated_dot_messages_dot_events__pb2.EventResponse.SerializeToString,
50
+ request_deserializer=canvas__generated_dot_messages_dot_events__pb2.Event.FromString,
51
+ response_serializer=canvas__generated_dot_messages_dot_events__pb2.EventResponse.SerializeToString,
52
52
  ),
53
53
  'ReloadPlugins': grpc.unary_stream_rpc_method_handler(
54
54
  servicer.ReloadPlugins,
55
- request_deserializer=generated_dot_messages_dot_plugins__pb2.ReloadPluginsRequest.FromString,
56
- response_serializer=generated_dot_messages_dot_plugins__pb2.ReloadPluginsResponse.SerializeToString,
55
+ request_deserializer=canvas__generated_dot_messages_dot_plugins__pb2.ReloadPluginsRequest.FromString,
56
+ response_serializer=canvas__generated_dot_messages_dot_plugins__pb2.ReloadPluginsResponse.SerializeToString,
57
57
  ),
58
58
  }
59
59
  generic_handler = grpc.method_handlers_generic_handler(
@@ -77,8 +77,8 @@ class PluginRunner(object):
77
77
  timeout=None,
78
78
  metadata=None):
79
79
  return grpc.experimental.unary_stream(request, target, '/canvas.PluginRunner/HandleEvent',
80
- generated_dot_messages_dot_events__pb2.Event.SerializeToString,
81
- generated_dot_messages_dot_events__pb2.EventResponse.FromString,
80
+ canvas__generated_dot_messages_dot_events__pb2.Event.SerializeToString,
81
+ canvas__generated_dot_messages_dot_events__pb2.EventResponse.FromString,
82
82
  options, channel_credentials,
83
83
  insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
84
84
 
@@ -94,7 +94,7 @@ class PluginRunner(object):
94
94
  timeout=None,
95
95
  metadata=None):
96
96
  return grpc.experimental.unary_stream(request, target, '/canvas.PluginRunner/ReloadPlugins',
97
- generated_dot_messages_dot_plugins__pb2.ReloadPluginsRequest.SerializeToString,
98
- generated_dot_messages_dot_plugins__pb2.ReloadPluginsResponse.FromString,
97
+ canvas__generated_dot_messages_dot_plugins__pb2.ReloadPluginsRequest.SerializeToString,
98
+ canvas__generated_dot_messages_dot_plugins__pb2.ReloadPluginsResponse.FromString,
99
99
  options, channel_credentials,
100
100
  insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
canvas_sdk/base.py ADDED
@@ -0,0 +1,45 @@
1
+ from datetime import date, datetime
2
+ from enum import Enum
3
+ from typing import Any
4
+
5
+ from pydantic import BaseModel, ConfigDict
6
+ from pydantic_core import InitErrorDetails, PydanticCustomError, ValidationError
7
+
8
+
9
+ class Model(BaseModel):
10
+ """A base model that includes validation methods."""
11
+
12
+ class Meta:
13
+ pass
14
+
15
+ model_config = ConfigDict(
16
+ strict=True,
17
+ revalidate_instances="always",
18
+ validate_assignment=True,
19
+ json_encoders={
20
+ date: lambda v: v.isoformat(),
21
+ datetime: lambda v: v.isoformat(),
22
+ Enum: lambda v: v.value,
23
+ },
24
+ )
25
+
26
+ def _get_effect_method_required_fields(self, method: str) -> tuple[str]:
27
+ return getattr(self.Meta, f"{method}_required_fields", tuple())
28
+
29
+ def _create_error_detail(self, type: str, message: str, value: Any) -> InitErrorDetails:
30
+ return InitErrorDetails({"type": PydanticCustomError(type, message), "input": value})
31
+
32
+ def _get_error_details(self, method: str) -> list[InitErrorDetails]:
33
+ required_fields = self._get_effect_method_required_fields(method)
34
+ return [
35
+ self._create_error_detail(
36
+ "missing", f"Field '{field}' is required to {method.replace('_', ' ')} a command", v
37
+ )
38
+ for field in required_fields
39
+ if (v := getattr(self, field)) is None
40
+ ]
41
+
42
+ def _validate_before_effect(self, method: str) -> None:
43
+ self.model_validate(self)
44
+ if error_details := self._get_error_details(method):
45
+ raise ValidationError.from_exception_data(self.__class__.__name__, error_details)
@@ -1,29 +1,51 @@
1
1
  import json
2
+ import re
2
3
  from enum import EnumType
3
- from typing import get_args
4
+ from typing import Literal, get_args
4
5
 
5
- from pydantic import BaseModel, ConfigDict, model_validator
6
- from typing_extensions import Self
6
+ from canvas_sdk.base import Model
7
+ from canvas_sdk.effects import Effect, EffectType
7
8
 
8
- from canvas_sdk.effects import Effect
9
-
10
-
11
- class _BaseCommand(BaseModel):
12
- model_config = ConfigDict(strict=True, validate_assignment=True)
13
9
 
10
+ class _BaseCommand(Model):
14
11
  class Meta:
15
12
  key = ""
16
-
17
- # todo: update int to str as we should use external identifiers
18
- note_id: int | None = None
13
+ originate_required_fields = (
14
+ "user_id",
15
+ "note_uuid",
16
+ )
17
+ edit_required_fields = (
18
+ "user_id",
19
+ "command_uuid",
20
+ )
21
+ delete_required_fields = (
22
+ "user_id",
23
+ "command_uuid",
24
+ )
25
+ commit_required_fields = (
26
+ "user_id",
27
+ "command_uuid",
28
+ )
29
+ enter_in_error_required_fields = (
30
+ "user_id",
31
+ "command_uuid",
32
+ )
33
+
34
+ def constantized_key(self) -> str:
35
+ return re.sub(r"(?<!^)(?=[A-Z])", "_", self.Meta.key).upper()
36
+
37
+ note_uuid: str | None = None
19
38
  command_uuid: str | None = None
20
- user_id: int
39
+ user_id: int | None = None
21
40
 
22
- @model_validator(mode="after")
23
- def _verify_has_note_id_or_command_id(self) -> Self:
24
- if not self.note_id and not self.command_uuid:
25
- raise ValueError("Command should have either a note_id or a command_uuid.")
26
- return self
41
+ def _get_effect_method_required_fields(
42
+ self, method: Literal["originate", "edit", "delete", "commit", "enter_in_error"]
43
+ ) -> tuple[str]:
44
+ base_required_fields: tuple = getattr(
45
+ _BaseCommand.Meta, f"{method}_required_fields", tuple()
46
+ )
47
+ command_required_fields = super()._get_effect_method_required_fields(method)
48
+ return tuple(set(base_required_fields) | set(command_required_fields))
27
49
 
28
50
  @property
29
51
  def values(self) -> dict:
@@ -52,11 +74,12 @@ class _BaseCommand(BaseModel):
52
74
  @classmethod
53
75
  def command_schema(cls) -> dict:
54
76
  """The schema of the command."""
55
- base_properties = {"note_id", "command_uuid", "user_id"}
77
+ base_properties = {"note_uuid", "command_uuid", "user_id"}
56
78
  schema = cls.model_json_schema()
79
+ required_fields: tuple = getattr(cls.Meta, "originate_required_fields", tuple())
57
80
  return {
58
81
  definition.get("commands_api_name", name): {
59
- "required": name in schema["required"],
82
+ "required": name in required_fields,
60
83
  "type": cls._get_property_type(name),
61
84
  "choices": cls._get_property_choices(name, schema),
62
85
  }
@@ -66,23 +89,23 @@ class _BaseCommand(BaseModel):
66
89
 
67
90
  def originate(self) -> Effect:
68
91
  """Originate a new command in the note body."""
69
- if not self.note_id:
70
- raise AttributeError("Note id is required to originate a command")
71
- return {
72
- "type": f"ADD_{self.Meta.key.upper()}_COMMAND",
73
- "payload": {
74
- "user": self.user_id,
75
- "note": self.note_id,
76
- "data": self.values,
77
- },
78
- }
92
+ self._validate_before_effect("originate")
93
+ return Effect(
94
+ type=EffectType.Value(f"ORIGINATE_{self.constantized_key()}_COMMAND"),
95
+ payload=json.dumps(
96
+ {
97
+ "user": self.user_id,
98
+ "note": self.note_uuid,
99
+ "data": self.values,
100
+ }
101
+ ),
102
+ )
79
103
 
80
104
  def edit(self) -> Effect:
81
105
  """Edit the command."""
82
- if not self.command_uuid:
83
- raise AttributeError("Command uuid is required to edit a command")
106
+ self._validate_before_effect("edit")
84
107
  return {
85
- "type": f"EDIT_{self.Meta.key.upper()}_COMMAND",
108
+ "type": f"EDIT_{self.constantized_key()}_COMMAND",
86
109
  "payload": {
87
110
  "user": self.user_id,
88
111
  "command": self.command_uuid,
@@ -92,27 +115,24 @@ class _BaseCommand(BaseModel):
92
115
 
93
116
  def delete(self) -> Effect:
94
117
  """Delete the command."""
95
- if not self.command_uuid:
96
- raise AttributeError("Command uuid is required to delete a command")
118
+ self._validate_before_effect("delete")
97
119
  return {
98
- "type": f"DELETE_{self.Meta.key.upper()}_COMMAND",
120
+ "type": f"DELETE_{self.constantized_key()}_COMMAND",
99
121
  "payload": {"command": self.command_uuid, "user": self.user_id},
100
122
  }
101
123
 
102
124
  def commit(self) -> Effect:
103
125
  """Commit the command."""
104
- if not self.command_uuid:
105
- raise AttributeError("Command uuid is required to commit a command")
126
+ self._validate_before_effect("commit")
106
127
  return {
107
- "type": f"COMMIT_{self.Meta.key.upper()}_COMMAND",
128
+ "type": f"COMMIT_{self.constantized_key()}_COMMAND",
108
129
  "payload": {"command": self.command_uuid, "user": self.user_id},
109
130
  }
110
131
 
111
132
  def enter_in_error(self) -> Effect:
112
133
  """Mark the command as entered-in-error."""
113
- if not self.command_uuid:
114
- raise AttributeError("Command uuid is required to enter in error a command")
134
+ self._validate_before_effect("enter_in_error")
115
135
  return {
116
- "type": f"ENTER_IN_ERROR_{self.Meta.key.upper()}_COMMAND",
136
+ "type": f"ENTER_IN_ERROR_{self.constantized_key()}_COMMAND",
117
137
  "payload": {"command": self.command_uuid, "user": self.user_id},
118
138
  }
@@ -1,21 +1,25 @@
1
1
  from enum import Enum
2
2
 
3
- from canvas_sdk.commands.base import _BaseCommand
4
3
  from pydantic import Field
5
4
 
5
+ from canvas_sdk.commands.base import _BaseCommand
6
+
6
7
 
7
8
  class AssessCommand(_BaseCommand):
8
9
  """A class for managing an Assess command within a specific note."""
9
10
 
10
11
  class Meta:
11
12
  key = "assess"
13
+ originate_required_fields = ("condition_id",)
12
14
 
13
15
  class Status(Enum):
14
16
  IMPROVED = "improved"
15
17
  STABLE = "stable"
16
18
  DETERIORATED = "deteriorated"
17
19
 
18
- condition_id: str = Field(json_schema_extra={"commands_api_name": "condition"})
20
+ condition_id: str | None = Field(
21
+ default=None, json_schema_extra={"commands_api_name": "condition"}
22
+ )
19
23
  background: str | None = None
20
24
  status: Status | None = None
21
25
  narrative: str | None = None
@@ -10,8 +10,11 @@ class DiagnoseCommand(_BaseCommand):
10
10
 
11
11
  class Meta:
12
12
  key = "diagnose"
13
+ originate_required_fields = ("icd10_code",)
13
14
 
14
- icd10_code: str = Field(json_schema_extra={"commands_api_name": "diagnose"})
15
+ icd10_code: str | None = Field(
16
+ default=None, json_schema_extra={"commands_api_name": "diagnose"}
17
+ )
15
18
  background: str | None = None
16
19
  approximate_date_of_onset: datetime | None = None
17
20
  today_assessment: str | None = None
@@ -29,16 +32,3 @@ class DiagnoseCommand(_BaseCommand):
29
32
  ),
30
33
  "today_assessment": self.today_assessment,
31
34
  }
32
-
33
-
34
- # how do we make sure icd10_code is a valid code?
35
-
36
- # idea1:
37
- # create an auto-generated enum class of all possible icd10s, then type the field as that enum
38
- # will require releasing a new version with the new codes every year, and devs will need to update
39
- # to make sure they have the latest version to get the right set of codes.
40
-
41
- # idea2:
42
- # see if we can get ValueSets to play nicely with pydantic
43
-
44
- # idea3: runtime warning after pinging ontologies
@@ -9,6 +9,7 @@ class GoalCommand(_BaseCommand):
9
9
 
10
10
  class Meta:
11
11
  key = "goal"
12
+ originate_required_fields = ("goal_statement", "start_date")
12
13
 
13
14
  class Priority(Enum):
14
15
  HIGH = "high-priority"
@@ -26,7 +27,7 @@ class GoalCommand(_BaseCommand):
26
27
  NO_PROGRESS = "no-progress"
27
28
  NOT_ATTAINABLE = "not-attainable"
28
29
 
29
- goal_statement: str
30
+ goal_statement: str | None = None
30
31
  start_date: datetime | None = None
31
32
  due_date: datetime | None = None
32
33
  achievement_status: AchievementStatus | None = None
@@ -39,7 +40,7 @@ class GoalCommand(_BaseCommand):
39
40
  return {
40
41
  "goal_statement": self.goal_statement,
41
42
  "start_date": (self.start_date.isoformat() if self.start_date else None),
42
- "due_date": (self.due_date.isoformat() if self.start_date else None),
43
+ "due_date": (self.due_date.isoformat() if self.due_date else None),
43
44
  "achievement_status": (
44
45
  self.achievement_status.value if self.achievement_status else None
45
46
  ),
@@ -6,8 +6,9 @@ class HistoryOfPresentIllnessCommand(_BaseCommand):
6
6
 
7
7
  class Meta:
8
8
  key = "hpi"
9
+ originate_required_fields = ("narrative",)
9
10
 
10
- narrative: str
11
+ narrative: str | None = None
11
12
 
12
13
  @property
13
14
  def values(self) -> dict:
@@ -1,14 +1,18 @@
1
- from canvas_sdk.commands.base import _BaseCommand
2
1
  from pydantic import Field
3
2
 
3
+ from canvas_sdk.commands.base import _BaseCommand
4
+
4
5
 
5
6
  class MedicationStatementCommand(_BaseCommand):
6
7
  """A class for managing a MedicationStatement command within a specific note."""
7
8
 
8
9
  class Meta:
9
10
  key = "medicationStatement"
11
+ originate_required_fields = ("fdb_code",)
10
12
 
11
- fdb_code: str = Field(json_schema_extra={"commands_api_name": "medication"})
13
+ fdb_code: str | None = Field(
14
+ default=None, json_schema_extra={"commands_api_name": "medication"}
15
+ )
12
16
  sig: str | None = None
13
17
 
14
18
  @property
@@ -6,8 +6,9 @@ class PlanCommand(_BaseCommand):
6
6
 
7
7
  class Meta:
8
8
  key = "plan"
9
+ originate_required_fields = ("narrative",)
9
10
 
10
- narrative: str
11
+ narrative: str | None = None
11
12
 
12
13
  @property
13
14
  def values(self) -> dict:
@@ -11,23 +11,34 @@ class PrescribeCommand(_BaseCommand):
11
11
 
12
12
  class Meta:
13
13
  key = "prescribe"
14
+ originate_required_fields = (
15
+ "fdb_code",
16
+ "sig",
17
+ "quantity_to_dispense",
18
+ "type_to_dispense",
19
+ "refills",
20
+ "substitutions",
21
+ "prescriber_id",
22
+ )
14
23
 
15
24
  class Substitutions(Enum):
16
25
  ALLOWED = "allowed"
17
26
  NOT_ALLOWED = "not_allowed"
18
27
 
19
- fdb_code: str = Field(json_schema_extra={"commands_api_name": "prescribe"})
28
+ fdb_code: str | None = Field(default=None, json_schema_extra={"commands_api_name": "prescribe"})
20
29
  icd10_codes: list[str] | None = Field(
21
- None, json_schema_extra={"commandsd_api_name": "indications"}
30
+ None, json_schema_extra={"commands_api_name": "indications"}
22
31
  )
23
- sig: str
32
+ sig: str | None = None
24
33
  days_supply: int | None = None
25
- quantity_to_dispense: Decimal
26
- type_to_dispense: str
27
- refills: int
28
- substitutions: Substitutions = Substitutions.ALLOWED # type: ignore
34
+ quantity_to_dispense: Decimal | float | int | None = None
35
+ type_to_dispense: str | None = None
36
+ refills: int | None = None
37
+ substitutions: Substitutions | None = None
29
38
  pharmacy: str | None = None
30
- prescriber_id: str = Field(json_schema_extra={"commands_api_name": "prescriber"})
39
+ prescriber_id: str | None = Field(
40
+ default=None, json_schema_extra={"commands_api_name": "prescriber"}
41
+ )
31
42
  note_to_pharmacist: str | None = None
32
43
 
33
44
  @property
@@ -38,10 +49,12 @@ class PrescribeCommand(_BaseCommand):
38
49
  "icd10_codes": self.icd10_codes,
39
50
  "sig": self.sig,
40
51
  "days_supply": self.days_supply,
41
- "quantity_to_dispense": self.quantity_to_dispense,
42
- "type_to_dispense": self.type_to_dispense,
52
+ "quantity_to_dispense": (
53
+ str(Decimal(self.quantity_to_dispense)) if self.quantity_to_dispense else None
54
+ ),
55
+ # "type_to_dispense": self.type_to_dispense,
43
56
  "refills": self.refills,
44
- "substitutions": self.substitutions,
57
+ "substitutions": self.substitutions.value if self.substitutions else None,
45
58
  "pharmacy": self.pharmacy,
46
59
  "prescriber_id": self.prescriber_id,
47
60
  "note_to_pharmacist": self.note_to_pharmacist,
@@ -1,14 +1,18 @@
1
- from canvas_sdk.commands.base import _BaseCommand
2
1
  from pydantic import Field
3
2
 
3
+ from canvas_sdk.commands.base import _BaseCommand
4
+
4
5
 
5
6
  class QuestionnaireCommand(_BaseCommand):
6
7
  """A class for managing a Questionnaire command within a specific note."""
7
8
 
8
9
  class Meta:
9
10
  key = "questionnaire"
11
+ originate_required_fields = ("questionnaire_id",)
10
12
 
11
- questionnaire_id: str = Field(json_schema_extra={"commands_api_name": "questionnaire"})
13
+ questionnaire_id: str | None = Field(
14
+ default=None, json_schema_extra={"commands_api_name": "questionnaire"}
15
+ )
12
16
  result: str | None = None
13
17
 
14
18
  @property