canvas 0.11.1__py3-none-any.whl → 0.13.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 (38) hide show
  1. {canvas-0.11.1.dist-info → canvas-0.13.0.dist-info}/METADATA +1 -1
  2. {canvas-0.11.1.dist-info → canvas-0.13.0.dist-info}/RECORD +38 -20
  3. canvas_cli/apps/plugin/plugin.py +13 -4
  4. canvas_cli/templates/plugins/application/cookiecutter.json +4 -0
  5. canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/CANVAS_MANIFEST.json +28 -0
  6. canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/README.md +11 -0
  7. canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/applications/__init__.py +0 -0
  8. canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/applications/my_application.py +12 -0
  9. canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/assets/python-logo.png +0 -0
  10. canvas_cli/utils/validators/manifest_schema.py +18 -1
  11. canvas_generated/messages/effects_pb2.py +2 -2
  12. canvas_generated/messages/effects_pb2.pyi +36 -0
  13. canvas_generated/messages/events_pb2.py +2 -2
  14. canvas_generated/messages/events_pb2.pyi +10 -0
  15. canvas_sdk/commands/__init__.py +6 -0
  16. canvas_sdk/commands/commands/exam.py +9 -0
  17. canvas_sdk/commands/commands/review_of_systems.py +9 -0
  18. canvas_sdk/commands/commands/structured_assessment.py +9 -0
  19. canvas_sdk/effects/__init__.py +3 -1
  20. canvas_sdk/effects/launch_modal.py +24 -0
  21. canvas_sdk/effects/patient_portal/__init__.py +0 -0
  22. canvas_sdk/effects/patient_portal/intake_form_results.py +24 -0
  23. canvas_sdk/effects/show_button.py +28 -0
  24. canvas_sdk/handlers/action_button.py +55 -0
  25. canvas_sdk/handlers/application.py +29 -0
  26. canvas_sdk/handlers/base.py +8 -1
  27. canvas_sdk/protocols/base.py +3 -1
  28. canvas_sdk/v1/data/__init__.py +4 -0
  29. canvas_sdk/v1/data/note.py +3 -4
  30. canvas_sdk/v1/data/organization.py +29 -0
  31. canvas_sdk/v1/data/practicelocation.py +105 -0
  32. plugin_runner/plugin_runner.py +37 -23
  33. plugin_runner/sandbox.py +2 -6
  34. plugin_runner/tests/test_application.py +65 -0
  35. plugin_runner/tests/test_plugin_runner.py +4 -4
  36. plugin_runner/tests/test_sandbox.py +2 -2
  37. {canvas-0.11.1.dist-info → canvas-0.13.0.dist-info}/WHEEL +0 -0
  38. {canvas-0.11.1.dist-info → canvas-0.13.0.dist-info}/entry_points.txt +0 -0
plugin_runner/sandbox.py CHANGED
@@ -93,7 +93,6 @@ class Sandbox:
93
93
 
94
94
  source_code: str
95
95
  namespace: str
96
- module_name: str | None
97
96
 
98
97
  class Transformer(RestrictingNodeTransformer):
99
98
  """A node transformer for customizing the sandbox compiler."""
@@ -200,19 +199,16 @@ class Sandbox:
200
199
  # Impossible Case only ctx Load, Store and Del are defined in ast.
201
200
  raise NotImplementedError(f"Unknown ctx type: {type(node.ctx)}")
202
201
 
203
- def __init__(
204
- self, source_code: str, namespace: str | None = None, module_name: str | None = None
205
- ) -> None:
202
+ def __init__(self, source_code: str, namespace: str | None = None) -> None:
206
203
  if source_code is None:
207
204
  raise TypeError("source_code may not be None")
208
- self.module_name = module_name
209
205
  self.namespace = namespace or "protocols"
210
206
  self.source_code = source_code
211
207
 
212
208
  @cached_property
213
209
  def package_name(self) -> str | None:
214
210
  """Return the root package name."""
215
- return self.module_name.split(".")[0] if self.module_name else None
211
+ return self.namespace.split(".")[0] if self.namespace else None
216
212
 
217
213
  @cached_property
218
214
  def scope(self) -> dict[str, Any]:
@@ -0,0 +1,65 @@
1
+ import pytest
2
+
3
+ from canvas_sdk.effects import Effect
4
+ from canvas_sdk.effects.launch_modal import LaunchModalEffect
5
+ from canvas_sdk.events import Event, EventRequest, EventType
6
+ from canvas_sdk.handlers.application import Application
7
+
8
+
9
+ class TestApplication(Application):
10
+ """A concrete implementation of the Application class for testing."""
11
+
12
+ def on_open(self) -> Effect:
13
+ """Handle the application open event by returning a mock effect."""
14
+ return LaunchModalEffect(url="https://example.com").apply()
15
+
16
+
17
+ @pytest.fixture
18
+ def app_instance(event: Event) -> TestApplication:
19
+ """Provide an instance of the TestApplication with a mocked event."""
20
+ app = TestApplication(event)
21
+ return app
22
+
23
+
24
+ def test_compute_event_not_targeted() -> None:
25
+ """Test that compute filters out events not targeted for the app."""
26
+ request = EventRequest(type=EventType.APPLICATION__ON_OPEN, target="some_identifier")
27
+ event = Event(request)
28
+ app = TestApplication(event)
29
+
30
+ result = app.compute()
31
+
32
+ assert result == [], "Expected no effects if the event target is not the app identifier"
33
+
34
+
35
+ def test_compute_event_targeted() -> None:
36
+ """Test that compute processes events targeted for the app."""
37
+ request = EventRequest(
38
+ type=EventType.APPLICATION__ON_OPEN,
39
+ target=f"{TestApplication.__module__}:{TestApplication.__qualname__}",
40
+ )
41
+ event = Event(request)
42
+ app = TestApplication(event)
43
+ result = app.compute()
44
+
45
+ assert len(result) == 1, "Expected a single effect if the event target is the app identifier"
46
+ assert isinstance(result[0], Effect), "Effect should be an instance of Effect"
47
+
48
+
49
+ def test_identifier_property() -> None:
50
+ """Test the identifier property of the Application class."""
51
+ expected_identifier = f"{TestApplication.__module__}:{TestApplication.__qualname__}"
52
+ request = EventRequest(
53
+ type=EventType.APPLICATION__ON_OPEN,
54
+ target=f"{TestApplication.__module__}:{TestApplication.__qualname__}",
55
+ )
56
+ event = Event(request)
57
+ app = TestApplication(event)
58
+
59
+ assert app.identifier == expected_identifier, "The identifier property is incorrect"
60
+
61
+
62
+ def test_abstract_method_on_open() -> None:
63
+ """Test that the abstract method on_open must be implemented."""
64
+ with pytest.raises(TypeError):
65
+ Application(Event(EventRequest(type=EventType.UNKNOWN))) # type: ignore[abstract]
@@ -9,7 +9,7 @@ from canvas_generated.messages.effects_pb2 import EffectType
9
9
  from canvas_generated.messages.plugins_pb2 import ReloadPluginsRequest
10
10
  from canvas_sdk.events import EventRequest, EventType
11
11
  from plugin_runner.plugin_runner import (
12
- EVENT_PROTOCOL_MAP,
12
+ EVENT_HANDLER_MAP,
13
13
  LOADED_PLUGINS,
14
14
  PluginRunner,
15
15
  load_plugins,
@@ -144,10 +144,10 @@ def test_remove_plugin_should_be_removed_from_loaded_plugins(setup_test_plugin:
144
144
  @pytest.mark.parametrize("setup_test_plugin", ["example_plugin"], indirect=True)
145
145
  def test_load_plugins_should_refresh_event_protocol_map(setup_test_plugin: Path) -> None:
146
146
  """Test that the event protocol map is refreshed when loading plugins."""
147
- assert EVENT_PROTOCOL_MAP == {}
147
+ assert EVENT_HANDLER_MAP == {}
148
148
  load_plugins()
149
- assert EventType.Name(EventType.UNKNOWN) in EVENT_PROTOCOL_MAP
150
- assert EVENT_PROTOCOL_MAP[EventType.Name(EventType.UNKNOWN)] == [
149
+ assert EventType.Name(EventType.UNKNOWN) in EVENT_HANDLER_MAP
150
+ assert EVENT_HANDLER_MAP[EventType.Name(EventType.UNKNOWN)] == [
151
151
  "example_plugin:example_plugin.protocols.my_protocol:Protocol"
152
152
  ]
153
153
 
@@ -100,7 +100,7 @@ print("Hello, Sandbox!")
100
100
 
101
101
  def test_sandbox_module_name_imports_within_package() -> None:
102
102
  """Test that modules within the same package can be imported."""
103
- sandbox_module_a = Sandbox(source_code=SOURCE_CODE_MODULE_OS, module_name="os.a")
103
+ sandbox_module_a = Sandbox(source_code=SOURCE_CODE_MODULE_OS, namespace="os.a")
104
104
  result = sandbox_module_a.execute()
105
105
 
106
106
  assert "os" in result
@@ -108,6 +108,6 @@ def test_sandbox_module_name_imports_within_package() -> None:
108
108
 
109
109
  def test_sandbox_denies_module_name_import_outside_package() -> None:
110
110
  """Test that modules outside the root package cannot be imported."""
111
- sandbox_module_a = Sandbox(source_code=SOURCE_CODE_MODULE_OS, module_name="module.a")
111
+ sandbox_module_a = Sandbox(source_code=SOURCE_CODE_MODULE_OS, namespace="module.a")
112
112
  with pytest.raises(ImportError, match="os' is not an allowed import."):
113
113
  sandbox_module_a.execute()