canvas 0.1.14__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: canvas
3
- Version: 0.1.14
3
+ Version: 0.1.15
4
4
  Summary: SDK to customize event-driven actions in your Canvas instance
5
5
  License: MIT
6
6
  Author: Canvas Team
@@ -6,7 +6,7 @@ canvas_cli/apps/auth/utils.py,sha256=IH5oZB3pdlb4_FRfCZKkNTncx_kdKagpiBqlhtM8h2U
6
6
  canvas_cli/apps/logs/__init__.py,sha256=ehY9SRb6nBw81xZF50yyBlUZJtNR2VeVSNI5sFuWJ7o,64
7
7
  canvas_cli/apps/logs/logs.py,sha256=Ixue8Z1wgxABunVIx6TzmsH6oZ0FPf2H51Sd3nFUnAI,1969
8
8
  canvas_cli/apps/plugin/__init__.py,sha256=G_nLsu6cdko5OjatnbqUyEboGcNlGGLwpZmCBxMKdfo,236
9
- canvas_cli/apps/plugin/plugin.py,sha256=irJdh1WGHSnzuBPp2TuKc18dpPtxtvrv9IA5IqgdB4g,11391
9
+ canvas_cli/apps/plugin/plugin.py,sha256=MBA1JAFN6wknm5l1A1Tmb3jXNQj3L_67mLkakp1gRxE,11380
10
10
  canvas_cli/apps/plugin/tests.py,sha256=_8fHTGlQermt4AVs20nsTYcs2bDRNEqr9CGE29pD6YQ,1035
11
11
  canvas_cli/conftest.py,sha256=pGvVS6VT0Zll_Lp3ezLh2jykOk-2HTBVH8RP5FwLUVw,930
12
12
  canvas_cli/main.py,sha256=lUCh5M7u0KJ9CWOL_oPa0J4284ggxHxteURYv6dGU4g,3790
@@ -30,6 +30,9 @@ canvas_cli/utils/validators/__init__.py,sha256=rBvSR2O1hWkNAnUBdcr-zUkmqT796_A61
30
30
  canvas_cli/utils/validators/manifest_schema.py,sha256=SIMpr_vTV8dtkO9cjsRnZZtRm5tgGkPR6QewTG8CztI,3117
31
31
  canvas_cli/utils/validators/tests.py,sha256=cZHLSx7oteOfLOoU1tXGvw88n6itcvUT2B3ZBg-bmEY,1206
32
32
  canvas_cli/utils/validators/validators.py,sha256=RKDKxIvZ1IuCPVuhQ1KHdpwu7aewAqT9ZX-nA-LZDdg,1614
33
+ canvas_generated/data_access_layer/data_access_layer_pb2.py,sha256=zifzwTsVs1pwNoBGEAlX_qJllIruk1gIeUSONQdvbP4,1673
34
+ canvas_generated/data_access_layer/data_access_layer_pb2.pyi,sha256=mJNO3x6UYhPMurn2aGBuYJYS6tFigd3znPknndy4dpk,868
35
+ canvas_generated/data_access_layer/data_access_layer_pb2_grpc.py,sha256=nmmH2hPYLcaAVOK_SMgX1ruZmiQGSJ3aRRnxiUVMftc,2828
33
36
  canvas_generated/messages/effects_pb2.py,sha256=NX__XT0xZk3G2zchK-BW5huCVtoZ4jBW-F2hcYNK98Q,4376
34
37
  canvas_generated/messages/effects_pb2.pyi,sha256=6YkzAOo6xnbWqxvv4gHdz1K6cz52JVQCRejpLVaX2y0,6640
35
38
  canvas_generated/messages/effects_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
@@ -62,7 +65,9 @@ canvas_sdk/commands/tests/test_utils.py,sha256=008nUdp5UCTyyFXI58KSgIofMt9njEbpX
62
65
  canvas_sdk/commands/tests/tests.py,sha256=lawX4BKlibIhc04mZJWNsWKSzyVr_fDSrAHIN89pmYo,15613
63
66
  canvas_sdk/data/__init__.py,sha256=dmqlcw11-0DCbH2lOebwBCq6njSQ6BDhxygmncUgZRs,28
64
67
  canvas_sdk/data/base.py,sha256=XbUctLoEdY8egrPzw_x2Ox41nurubh8sl4Kjbhu8rTE,795
65
- canvas_sdk/data/patient.py,sha256=IhJxZoQHJPK2O7d7v0UVUTZ_8FYAXZ_sl2BUVvgsztY,130
68
+ canvas_sdk/data/data_access_layer_client.py,sha256=ujd6LnC7IzOvD6_IVpDQOQgaz_ZDtClwUN5j360e0P8,3400
69
+ canvas_sdk/data/exceptions.py,sha256=PRSD0N7AzqajebSK-vfJDaN8BGBEG5WTxejZaEMDZ6Y,305
70
+ canvas_sdk/data/patient.py,sha256=k0pmxkPUYYoHzWWtQf6h1__0cn1dy42NRcFsetMxs3M,742
66
71
  canvas_sdk/data/staff.py,sha256=zmwKZlndqFdwsE3j14DD79MSzM9doIkeqqzToGDBjTs,168
67
72
  canvas_sdk/data/task.py,sha256=VeOh45M6tQ0IPwPr03yK6EYa8xox3iJhoRBzqUdcq2k,1756
68
73
  canvas_sdk/effects/__init__.py,sha256=oChQi5y_8mK2zDiNMwWYp6CaQX1_zbmwXILPF7iSt-4,69
@@ -84,7 +89,7 @@ canvas_sdk/utils/tests.py,sha256=t3MScbIfzXkQttMIvj0dRzJlFVS8LFU8WgWRrChM-H0,193
84
89
  canvas_sdk/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
90
  logger/__init__.py,sha256=sf54RfJ48tCbXm5jTHAea3WWuSH5-AXRHxTNNKVuxgA,60
86
91
  logger/logger.py,sha256=u103k5-yP62ifK1Hcp-PDTRrw_vOfLGeC-5raxNmBC0,1428
87
- canvas-0.1.14.dist-info/METADATA,sha256=Wx2__EHrsmOGVYPZj-WQVdJPKgOi8GTc8kN4iiRtiDk,4253
88
- canvas-0.1.14.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
89
- canvas-0.1.14.dist-info/entry_points.txt,sha256=VSmSo1IZ3aEfL7enmLmlWSraS_IIkoXNVeyXzgRxFiY,46
90
- canvas-0.1.14.dist-info/RECORD,,
92
+ canvas-0.1.15.dist-info/METADATA,sha256=1sbQDXfVO4ofQAcawRy1drfTEbYzVzUdBzDMz1ZupsU,4253
93
+ canvas-0.1.15.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
94
+ canvas-0.1.15.dist-info/entry_points.txt,sha256=VSmSo1IZ3aEfL7enmLmlWSraS_IIkoXNVeyXzgRxFiY,46
95
+ canvas-0.1.15.dist-info/RECORD,,
@@ -204,8 +204,8 @@ def enable(
204
204
  print(f"Failed to connect to {host}")
205
205
  raise typer.Exit(1)
206
206
 
207
- if r.status_code == requests.codes.no_content:
208
- print(r.text)
207
+ if r.ok:
208
+ print(f"Plugin {name} successfully enabled!")
209
209
  else:
210
210
  print(f"Status code {r.status_code}: {r.text}")
211
211
  raise typer.Exit(1)
@@ -241,8 +241,8 @@ def disable(
241
241
  print(f"Failed to connect to {host}")
242
242
  raise typer.Exit(1)
243
243
 
244
- if r.status_code == requests.codes.no_content:
245
- print(r.text)
244
+ if r.ok:
245
+ print(f"Plugin {name} successfully disabled!")
246
246
  else:
247
247
  print(f"Status code {r.status_code}: {r.text}")
248
248
  raise typer.Exit(1)
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: canvas_generated/data_access_layer/data_access_layer.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
+
16
+
17
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n:canvas_generated/data_access_layer/data_access_layer.proto\"\x10\n\x02ID\x12\n\n\x02id\x18\x01 \x01(\t\"\x8b\x01\n\x07Patient\x12\n\n\x02id\x18\x01 \x01(\t\x12\x17\n\nfirst_name\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tlast_name\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nbirth_date\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_first_nameB\x0c\n\n_last_nameB\r\n\x0b_birth_date20\n\x0f\x44\x61taAccessLayer\x12\x1d\n\nGetPatient\x12\x03.ID\x1a\x08.Patient\"\x00\x62\x06proto3')
18
+
19
+ _globals = globals()
20
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
21
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'canvas_generated.data_access_layer.data_access_layer_pb2', _globals)
22
+ if _descriptor._USE_C_DESCRIPTORS == False:
23
+ DESCRIPTOR._options = None
24
+ _globals['_ID']._serialized_start=62
25
+ _globals['_ID']._serialized_end=78
26
+ _globals['_PATIENT']._serialized_start=81
27
+ _globals['_PATIENT']._serialized_end=220
28
+ _globals['_DATAACCESSLAYER']._serialized_start=222
29
+ _globals['_DATAACCESSLAYER']._serialized_end=270
30
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,23 @@
1
+ from google.protobuf import descriptor as _descriptor
2
+ from google.protobuf import message as _message
3
+ from typing import ClassVar as _ClassVar, Optional as _Optional
4
+
5
+ DESCRIPTOR: _descriptor.FileDescriptor
6
+
7
+ class ID(_message.Message):
8
+ __slots__ = ("id",)
9
+ ID_FIELD_NUMBER: _ClassVar[int]
10
+ id: str
11
+ def __init__(self, id: _Optional[str] = ...) -> None: ...
12
+
13
+ class Patient(_message.Message):
14
+ __slots__ = ("id", "first_name", "last_name", "birth_date")
15
+ ID_FIELD_NUMBER: _ClassVar[int]
16
+ FIRST_NAME_FIELD_NUMBER: _ClassVar[int]
17
+ LAST_NAME_FIELD_NUMBER: _ClassVar[int]
18
+ BIRTH_DATE_FIELD_NUMBER: _ClassVar[int]
19
+ id: str
20
+ first_name: str
21
+ last_name: str
22
+ birth_date: str
23
+ def __init__(self, id: _Optional[str] = ..., first_name: _Optional[str] = ..., last_name: _Optional[str] = ..., birth_date: _Optional[str] = ...) -> None: ...
@@ -0,0 +1,66 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+
5
+ from canvas_generated.data_access_layer import data_access_layer_pb2 as canvas__generated_dot_data__access__layer_dot_data__access__layer__pb2
6
+
7
+
8
+ class DataAccessLayerStub(object):
9
+ """Missing associated documentation comment in .proto file."""
10
+
11
+ def __init__(self, channel):
12
+ """Constructor.
13
+
14
+ Args:
15
+ channel: A grpc.Channel.
16
+ """
17
+ self.GetPatient = channel.unary_unary(
18
+ '/DataAccessLayer/GetPatient',
19
+ request_serializer=canvas__generated_dot_data__access__layer_dot_data__access__layer__pb2.ID.SerializeToString,
20
+ response_deserializer=canvas__generated_dot_data__access__layer_dot_data__access__layer__pb2.Patient.FromString,
21
+ )
22
+
23
+
24
+ class DataAccessLayerServicer(object):
25
+ """Missing associated documentation comment in .proto file."""
26
+
27
+ def GetPatient(self, request, context):
28
+ """Missing associated documentation comment in .proto file."""
29
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
30
+ context.set_details('Method not implemented!')
31
+ raise NotImplementedError('Method not implemented!')
32
+
33
+
34
+ def add_DataAccessLayerServicer_to_server(servicer, server):
35
+ rpc_method_handlers = {
36
+ 'GetPatient': grpc.unary_unary_rpc_method_handler(
37
+ servicer.GetPatient,
38
+ request_deserializer=canvas__generated_dot_data__access__layer_dot_data__access__layer__pb2.ID.FromString,
39
+ response_serializer=canvas__generated_dot_data__access__layer_dot_data__access__layer__pb2.Patient.SerializeToString,
40
+ ),
41
+ }
42
+ generic_handler = grpc.method_handlers_generic_handler(
43
+ 'DataAccessLayer', rpc_method_handlers)
44
+ server.add_generic_rpc_handlers((generic_handler,))
45
+
46
+
47
+ # This class is part of an EXPERIMENTAL API.
48
+ class DataAccessLayer(object):
49
+ """Missing associated documentation comment in .proto file."""
50
+
51
+ @staticmethod
52
+ def GetPatient(request,
53
+ target,
54
+ options=(),
55
+ channel_credentials=None,
56
+ call_credentials=None,
57
+ insecure=False,
58
+ compression=None,
59
+ wait_for_ready=None,
60
+ timeout=None,
61
+ metadata=None):
62
+ return grpc.experimental.unary_unary(request, target, '/DataAccessLayer/GetPatient',
63
+ canvas__generated_dot_data__access__layer_dot_data__access__layer__pb2.ID.SerializeToString,
64
+ canvas__generated_dot_data__access__layer_dot_data__access__layer__pb2.Patient.FromString,
65
+ options, channel_credentials,
66
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@@ -0,0 +1,95 @@
1
+ """
2
+ Data Access Layer client.
3
+
4
+ This module is primarily responsible for executing calls to the gRPC service so that such details
5
+ are abstracted away from callers. The return values of the methods on the client class are protobufs
6
+ which must be mapped to user-facing objects.
7
+ """
8
+
9
+ import functools
10
+ from collections.abc import Callable
11
+ from types import FunctionType
12
+ from typing import Any
13
+
14
+ import grpc
15
+ from grpc import StatusCode
16
+
17
+ from canvas_generated.data_access_layer.data_access_layer_pb2 import ID, Patient
18
+ from canvas_generated.data_access_layer.data_access_layer_pb2_grpc import (
19
+ DataAccessLayerStub,
20
+ )
21
+ from settings import DAL_TARGET
22
+
23
+ from . import exceptions
24
+ from .exceptions import DataModuleError
25
+
26
+
27
+ class _DataAccessLayerClientMeta(type):
28
+ """
29
+ Metaclass for the Data Access Layer client class.
30
+
31
+ Wraps all methods of a class with a gRPC error handler.
32
+ """
33
+
34
+ def __new__(cls, name: str, bases: tuple, attrs: dict) -> type:
35
+ for attr_name, attr_value in attrs.items():
36
+ if isinstance(attr_value, FunctionType):
37
+ attrs[attr_name] = cls.handle_grpc_errors(attr_value)
38
+ return super().__new__(cls, name, bases, attrs)
39
+
40
+ @classmethod
41
+ def handle_grpc_errors(cls, func: Callable[..., Any]) -> Callable[..., Any]:
42
+ """
43
+ Decorator that wraps a try-except block around all class methods. gRPC errors are mapped to
44
+ a defined set of exceptions from a Data Access Layer exception hierarchy.
45
+ """
46
+
47
+ @functools.wraps(func)
48
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
49
+ try:
50
+ return func(*args, **kwargs)
51
+ except grpc.RpcError as error:
52
+ # gRPC exceptions aren't tightly defined, so we'll try to get a status code and
53
+ # error details, and handle it if we can't
54
+ try:
55
+ status_code = error.code()
56
+ except Exception:
57
+ status_code = None
58
+
59
+ try:
60
+ error_details = error.details()
61
+ except Exception:
62
+ error_details = ""
63
+
64
+ # Map more gRPC status codes to exception types as needed
65
+ match status_code:
66
+ case StatusCode.NOT_FOUND:
67
+ raise exceptions.DataModuleNotFoundError(error_details) from error
68
+ case _:
69
+ raise exceptions.DataModuleError from error
70
+ except Exception as exception:
71
+ raise DataModuleError from exception
72
+
73
+ return wrapper
74
+
75
+
76
+ class _DataAccessLayerClient(metaclass=_DataAccessLayerClientMeta):
77
+ """
78
+ Data Access Layer client.
79
+
80
+ Do not instantiate -- just import the global variable DAL_CLIENT.
81
+ """
82
+
83
+ def __init__(self) -> None:
84
+ self._channel = grpc.insecure_channel(DAL_TARGET)
85
+ self._stub = DataAccessLayerStub(self._channel)
86
+
87
+ def get_patient(self, id: str) -> Patient:
88
+ """Given an ID, get the Patient from the Data Access Layer."""
89
+ return self._stub.GetPatient(ID(id=id))
90
+
91
+
92
+ # There should only be one instantiation of the client, so this global will act as a singleton in a
93
+ # way. This is the value that should be imported; no one should be instantiating the DAL client
94
+ # (hence the underscore notation indicating that the class is "private").
95
+ DAL_CLIENT = _DataAccessLayerClient()
@@ -0,0 +1,16 @@
1
+ """Data Access Layer exceptions."""
2
+
3
+
4
+ class DataModuleError(RuntimeError):
5
+ """
6
+ General Data Access Layer error; base class and also used to represent errors of indeterminate
7
+ cause.
8
+ """
9
+
10
+ pass
11
+
12
+
13
+ class DataModuleNotFoundError(DataModuleError):
14
+ """Object not found error."""
15
+
16
+ pass
@@ -1,6 +1,26 @@
1
+ from datetime import date
2
+ from typing import Self
3
+
1
4
  from canvas_sdk.data import DataModel
2
5
 
6
+ from .data_access_layer_client import DAL_CLIENT
7
+
3
8
 
4
9
  class Patient(DataModel):
10
+ """Patient model."""
11
+
5
12
  id: str | None = None
6
- # TODO - populate more attributes
13
+ first_name: str | None = None
14
+ last_name: str | None = None
15
+ birth_date: date | None = None
16
+
17
+ @classmethod
18
+ def get(cls, id: str) -> Self:
19
+ """Given an ID, get the Patient from the Data Access Layer."""
20
+ patient = DAL_CLIENT.get_patient(id)
21
+ return cls(
22
+ id=patient.id,
23
+ first_name=patient.first_name or None,
24
+ last_name=patient.last_name or None,
25
+ birth_date=date.fromisoformat(patient.birth_date) if patient.birth_date else None,
26
+ )