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.
- {canvas-0.11.1.dist-info → canvas-0.13.0.dist-info}/METADATA +1 -1
- {canvas-0.11.1.dist-info → canvas-0.13.0.dist-info}/RECORD +38 -20
- canvas_cli/apps/plugin/plugin.py +13 -4
- canvas_cli/templates/plugins/application/cookiecutter.json +4 -0
- canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/CANVAS_MANIFEST.json +28 -0
- canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/README.md +11 -0
- canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/applications/__init__.py +0 -0
- canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/applications/my_application.py +12 -0
- canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/assets/python-logo.png +0 -0
- canvas_cli/utils/validators/manifest_schema.py +18 -1
- canvas_generated/messages/effects_pb2.py +2 -2
- canvas_generated/messages/effects_pb2.pyi +36 -0
- canvas_generated/messages/events_pb2.py +2 -2
- canvas_generated/messages/events_pb2.pyi +10 -0
- canvas_sdk/commands/__init__.py +6 -0
- canvas_sdk/commands/commands/exam.py +9 -0
- canvas_sdk/commands/commands/review_of_systems.py +9 -0
- canvas_sdk/commands/commands/structured_assessment.py +9 -0
- canvas_sdk/effects/__init__.py +3 -1
- canvas_sdk/effects/launch_modal.py +24 -0
- canvas_sdk/effects/patient_portal/__init__.py +0 -0
- canvas_sdk/effects/patient_portal/intake_form_results.py +24 -0
- canvas_sdk/effects/show_button.py +28 -0
- canvas_sdk/handlers/action_button.py +55 -0
- canvas_sdk/handlers/application.py +29 -0
- canvas_sdk/handlers/base.py +8 -1
- canvas_sdk/protocols/base.py +3 -1
- canvas_sdk/v1/data/__init__.py +4 -0
- canvas_sdk/v1/data/note.py +3 -4
- canvas_sdk/v1/data/organization.py +29 -0
- canvas_sdk/v1/data/practicelocation.py +105 -0
- plugin_runner/plugin_runner.py +37 -23
- plugin_runner/sandbox.py +2 -6
- plugin_runner/tests/test_application.py +65 -0
- plugin_runner/tests/test_plugin_runner.py +4 -4
- plugin_runner/tests/test_sandbox.py +2 -2
- {canvas-0.11.1.dist-info → canvas-0.13.0.dist-info}/WHEEL +0 -0
- {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.
|
|
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
|
-
|
|
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
|
|
147
|
+
assert EVENT_HANDLER_MAP == {}
|
|
148
148
|
load_plugins()
|
|
149
|
-
assert EventType.Name(EventType.UNKNOWN) in
|
|
150
|
-
assert
|
|
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,
|
|
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,
|
|
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()
|
|
File without changes
|
|
File without changes
|