canvas 0.35.1__py3-none-any.whl → 0.37.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.

Files changed (42) hide show
  1. {canvas-0.35.1.dist-info → canvas-0.37.0.dist-info}/METADATA +1 -1
  2. {canvas-0.35.1.dist-info → canvas-0.37.0.dist-info}/RECORD +42 -34
  3. canvas_cli/apps/plugin/plugin.py +3 -2
  4. canvas_cli/utils/validators/manifest_schema.py +15 -1
  5. canvas_generated/messages/effects_pb2.py +2 -2
  6. canvas_generated/messages/effects_pb2.pyi +8 -0
  7. canvas_generated/messages/events_pb2.py +2 -2
  8. canvas_generated/messages/events_pb2.pyi +26 -0
  9. canvas_sdk/caching/__init__.py +1 -0
  10. canvas_sdk/caching/base.py +127 -0
  11. canvas_sdk/caching/client.py +24 -0
  12. canvas_sdk/caching/exceptions.py +21 -0
  13. canvas_sdk/caching/plugins.py +20 -0
  14. canvas_sdk/caching/utils.py +28 -0
  15. canvas_sdk/commands/__init__.py +2 -0
  16. canvas_sdk/commands/commands/chart_section_review.py +23 -0
  17. canvas_sdk/commands/commands/prescribe.py +3 -0
  18. canvas_sdk/effects/launch_modal.py +1 -0
  19. canvas_sdk/effects/simple_api.py +61 -2
  20. canvas_sdk/handlers/simple_api/websocket.py +79 -0
  21. canvas_sdk/value_set/custom.py +1 -1
  22. canvas_sdk/value_set/v2022/allergy.py +1 -1
  23. canvas_sdk/value_set/v2022/assessment.py +1 -1
  24. canvas_sdk/value_set/v2022/communication.py +1 -1
  25. canvas_sdk/value_set/v2022/condition.py +1 -1
  26. canvas_sdk/value_set/v2022/device.py +1 -1
  27. canvas_sdk/value_set/v2022/diagnostic_study.py +1 -1
  28. canvas_sdk/value_set/v2022/encounter.py +1 -1
  29. canvas_sdk/value_set/v2022/immunization.py +1 -1
  30. canvas_sdk/value_set/v2022/individual_characteristic.py +1 -1
  31. canvas_sdk/value_set/v2022/intervention.py +1 -1
  32. canvas_sdk/value_set/v2022/laboratory_test.py +1 -1
  33. canvas_sdk/value_set/v2022/medication.py +1 -1
  34. canvas_sdk/value_set/v2022/physical_exam.py +1 -1
  35. canvas_sdk/value_set/v2022/procedure.py +1 -1
  36. plugin_runner/plugin_runner.py +15 -1
  37. plugin_runner/sandbox.py +5 -0
  38. protobufs/canvas_generated/messages/effects.proto +6 -0
  39. protobufs/canvas_generated/messages/events.proto +14 -12
  40. settings.py +28 -0
  41. {canvas-0.35.1.dist-info → canvas-0.37.0.dist-info}/WHEEL +0 -0
  42. {canvas-0.35.1.dist-info → canvas-0.37.0.dist-info}/entry_points.txt +0 -0
@@ -32,6 +32,7 @@ from canvas_generated.services.plugin_runner_pb2_grpc import (
32
32
  from canvas_sdk.effects import Effect
33
33
  from canvas_sdk.effects.simple_api import Response
34
34
  from canvas_sdk.events import Event, EventRequest, EventResponse, EventType
35
+ from canvas_sdk.handlers.simple_api.websocket import DenyConnection
35
36
  from canvas_sdk.protocols import ClinicalQualityMeasure
36
37
  from canvas_sdk.utils import metrics
37
38
  from canvas_sdk.utils.metrics import measured
@@ -180,7 +181,11 @@ class PluginRunner(PluginRunnerServicer):
180
181
  plugin_name = event.target.id
181
182
  # filter only for the plugin(s) that were created/updated
182
183
  relevant_plugins = [p for p in relevant_plugins if p.startswith(f"{plugin_name}:")]
183
- elif event_type in {EventType.SIMPLE_API_AUTHENTICATE, EventType.SIMPLE_API_REQUEST}:
184
+ elif event_type in {
185
+ EventType.SIMPLE_API_AUTHENTICATE,
186
+ EventType.SIMPLE_API_REQUEST,
187
+ EventType.SIMPLE_API_WEBSOCKET_AUTHENTICATE,
188
+ }:
184
189
  # The target plugin's name will be part of the home-app URL path, so other plugins that
185
190
  # respond to SimpleAPI request events are not relevant
186
191
  plugin_name = event.context["plugin_name"]
@@ -267,6 +272,15 @@ class PluginRunner(PluginRunnerServicer):
267
272
  f" {event.context['path']}"
268
273
  )
269
274
  effect_list = [Response(status_code=HTTPStatus.INTERNAL_SERVER_ERROR).apply()]
275
+ if event.type == EventType.SIMPLE_API_WEBSOCKET_AUTHENTICATE:
276
+ if len(relevant_plugin_handlers) == 0:
277
+ effect_list = [DenyConnection().apply()]
278
+ elif len(relevant_plugin_handlers) > 1:
279
+ log.error(
280
+ f"Multiple handlers responded to {EventType.Name(EventType.SIMPLE_API_WEBSOCKET_AUTHENTICATE)}"
281
+ f" {event.context['channel']}"
282
+ )
283
+ effect_list = [DenyConnection().apply()]
270
284
 
271
285
  # Don't log anything if a plugin handler didn't actually run.
272
286
  if relevant_plugins:
plugin_runner/sandbox.py CHANGED
@@ -81,6 +81,7 @@ SAFE_EXTERNAL_DUNDER_READ_ATTRIBUTES = {
81
81
  }
82
82
 
83
83
  CANVAS_TOP_LEVEL_MODULES = (
84
+ "canvas_sdk.caching",
84
85
  "canvas_sdk.commands",
85
86
  "canvas_sdk.effects",
86
87
  "canvas_sdk.events",
@@ -130,6 +131,10 @@ STANDARD_LIBRARY_MODULES = {
130
131
  "b64decode",
131
132
  "b64encode",
132
133
  },
134
+ "collections": {
135
+ "Counter",
136
+ "defaultdict",
137
+ },
133
138
  "datetime": {
134
139
  "date",
135
140
  "datetime",
@@ -233,6 +233,8 @@ enum EffectType {
233
233
  COMMIT_ADJUST_PRESCRIPTION_COMMAND = 1303;
234
234
  ENTER_IN_ERROR_ADJUST_PRESCRIPTION_COMMAND = 1304;
235
235
 
236
+ ORIGINATE_CHART_SECTION_REVIEW_COMMAND = 1400;
237
+
236
238
  SHOW_ACTION_BUTTON = 1000;
237
239
 
238
240
  PATIENT_PORTAL__FORM_RESULT = 2000;
@@ -260,6 +262,9 @@ enum EffectType {
260
262
  LAUNCH_MODAL = 3000;
261
263
 
262
264
  SIMPLE_API_RESPONSE = 4000;
265
+ SIMPLE_API_WEBSOCKET_ACCEPT = 4001;
266
+ SIMPLE_API_WEBSOCKET_DENY = 4002;
267
+ SIMPLE_API_WEBSOCKET_BROADCAST = 4003;
263
268
 
264
269
  UPDATE_USER = 5000;
265
270
 
@@ -268,6 +273,7 @@ enum EffectType {
268
273
  CREATE_SCHEDULE_EVENT = 6002;
269
274
 
270
275
  CREATE_PATIENT = 6003;
276
+
271
277
  }
272
278
 
273
279
  message Effect {
@@ -157,6 +157,8 @@ enum EventType {
157
157
  ADJUST_PRESCRIPTION__PHARMACY__POST_SEARCH = 2017;
158
158
  ADJUST_PRESCRIPTION__CHANGE_MEDICATION_TO__PRE_SEARCH = 2018;
159
159
  ADJUST_PRESCRIPTION__CHANGE_MEDICATION_TO__POST_SEARCH = 2019;
160
+ ADJUST_PRESCRIPTION__SUPERVISING_PROVIDER__PRE_SEARCH = 2021;
161
+ ADJUST_PRESCRIPTION__SUPERVISING_PROVIDER__POST_SEARCH = 2022;
160
162
 
161
163
  // ADJUST_PROTOCOL_COMMAND__PRE_ORIGINATE = 3000;
162
164
  // ADJUST_PROTOCOL_COMMAND__POST_ORIGINATE = 3001;
@@ -244,18 +246,12 @@ enum EventType {
244
246
  CANCEL_PRESCRIPTION__SELECTED_PRESCRIPTION__PRE_SEARCH = 8012;
245
247
  CANCEL_PRESCRIPTION__SELECTED_PRESCRIPTION__POST_SEARCH = 8013;
246
248
 
247
- // CHART_SECTION_REVIEW_COMMAND__PRE_ORIGINATE = 9000;
248
- // CHART_SECTION_REVIEW_COMMAND__POST_ORIGINATE = 9001;
249
- // CHART_SECTION_REVIEW_COMMAND__PRE_UPDATE = 9002;
250
- // CHART_SECTION_REVIEW_COMMAND__POST_UPDATE = 9003;
251
- // CHART_SECTION_REVIEW_COMMAND__PRE_COMMIT = 9004;
252
- // CHART_SECTION_REVIEW_COMMAND__POST_COMMIT = 9005;
253
- // CHART_SECTION_REVIEW_COMMAND__PRE_DELETE = 9006;
254
- // CHART_SECTION_REVIEW_COMMAND__POST_DELETE = 9007;
255
- // CHART_SECTION_REVIEW_COMMAND__PRE_ENTER_IN_ERROR = 9008;
256
- // CHART_SECTION_REVIEW_COMMAND__POST_ENTER_IN_ERROR = 9009;
257
- // CHART_SECTION_REVIEW_COMMAND__PRE_EXECUTE_ACTION = 9010;
258
- // CHART_SECTION_REVIEW_COMMAND__POST_EXECUTE_ACTION = 9011;
249
+ CHART_SECTION_REVIEW_COMMAND__PRE_ORIGINATE = 9000;
250
+ CHART_SECTION_REVIEW_COMMAND__POST_ORIGINATE = 9001;
251
+ CHART_SECTION_REVIEW_COMMAND__PRE_ENTER_IN_ERROR = 9002;
252
+ CHART_SECTION_REVIEW_COMMAND__POST_ENTER_IN_ERROR = 9003;
253
+ CHART_SECTION_REVIEW_COMMAND__PRE_EXECUTE_ACTION = 9004;
254
+ CHART_SECTION_REVIEW_COMMAND__POST_EXECUTE_ACTION = 9005;
259
255
 
260
256
 
261
257
  CLIPBOARD_COMMAND__PRE_ORIGINATE = 53000;
@@ -663,6 +659,8 @@ enum EventType {
663
659
  PRESCRIBE__INDICATIONS__POST_SEARCH = 33015;
664
660
  PRESCRIBE__PHARMACY__PRE_SEARCH = 33016;
665
661
  PRESCRIBE__PHARMACY__POST_SEARCH = 33017;
662
+ PRESCRIBE__SUPERVISING_PROVIDER__POST_SEARCH = 33019;
663
+ PRESCRIBE__SUPERVISING_PROVIDER__PRE_SEARCH = 33020;
666
664
 
667
665
  QUESTIONNAIRE_COMMAND__PRE_ORIGINATE = 34000;
668
666
  QUESTIONNAIRE_COMMAND__POST_ORIGINATE = 34001;
@@ -763,6 +761,8 @@ enum EventType {
763
761
  REFILL__INDICATIONS__POST_SEARCH = 39015;
764
762
  REFILL__PHARMACY__PRE_SEARCH = 39016;
765
763
  REFILL__PHARMACY__POST_SEARCH = 39017;
764
+ REFILL__SUPERVISING_PROVIDER__PRE_SEARCH = 39019;
765
+ REFILL__SUPERVISING_PROVIDER__POST_SEARCH = 39020;
766
766
 
767
767
  REMOVE_ALLERGY_COMMAND__PRE_ORIGINATE = 40000;
768
768
  REMOVE_ALLERGY_COMMAND__POST_ORIGINATE = 40001;
@@ -1093,6 +1093,8 @@ enum EventType {
1093
1093
 
1094
1094
  SIMPLE_API_AUTHENTICATE = 130000;
1095
1095
  SIMPLE_API_REQUEST = 130001;
1096
+ SIMPLE_API_WEBSOCKET_AUTHENTICATE = 130002;
1097
+
1096
1098
  }
1097
1099
 
1098
1100
  message Event {
settings.py CHANGED
@@ -9,6 +9,7 @@ from env_tools import env_to_bool
9
9
  load_dotenv()
10
10
 
11
11
  BASE_DIR = Path(__file__).resolve().parent.resolve()
12
+ FOURTEEN_DAYS = 60 * 60 * 24 * 14
12
13
 
13
14
  ENV = os.getenv("ENV", "development")
14
15
  IS_PRODUCTION = ENV == "production"
@@ -24,6 +25,10 @@ INTEGRATION_TEST_CLIENT_SECRET = os.getenv("INTEGRATION_TEST_CLIENT_SECRET")
24
25
 
25
26
  GRAPHQL_ENDPOINT = os.getenv("GRAPHQL_ENDPOINT", "http://localhost:8000/plugins-graphql")
26
27
  REDIS_ENDPOINT = os.getenv("REDIS_ENDPOINT", f"redis://{APP_NAME}-redis:6379")
28
+ CANVAS_SDK_PLUGINS_CACHE_LOCATION = os.getenv(
29
+ "CANVAS_SDK_PLUGINS_CACHE_LOCATION", "plugin_io_plugins_cache"
30
+ )
31
+ CANVAS_SDK_CACHE_TIMEOUT_SECONDS = int(os.getenv("CANVAS_SDK_CACHE_TIMEOUT", FOURTEEN_DAYS))
27
32
 
28
33
 
29
34
  METRICS_ENABLED = env_to_bool("PLUGINS_METRICS_ENABLED", not IS_SCRIPT)
@@ -110,3 +115,26 @@ TEMPLATES = [
110
115
  "OPTIONS": {},
111
116
  },
112
117
  ]
118
+
119
+
120
+ if IS_TESTING:
121
+ CACHES = {
122
+ "default": {
123
+ "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
124
+ },
125
+ "plugins": {
126
+ "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
127
+ "KEY_PREFIX": CUSTOMER_IDENTIFIER,
128
+ "LOCATION": "plugins_cache",
129
+ "TIMEOUT": CANVAS_SDK_CACHE_TIMEOUT_SECONDS,
130
+ },
131
+ }
132
+ else:
133
+ CACHES = {
134
+ "plugins": {
135
+ "BACKEND": "django.core.cache.backends.db.DatabaseCache",
136
+ "KEY_PREFIX": CUSTOMER_IDENTIFIER,
137
+ "LOCATION": CANVAS_SDK_PLUGINS_CACHE_LOCATION,
138
+ "TIMEOUT": CANVAS_SDK_CACHE_TIMEOUT_SECONDS,
139
+ }
140
+ }