canvas 0.43.0__py3-none-any.whl → 0.44.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.43.0.dist-info → canvas-0.44.0.dist-info}/METADATA +1 -2
- {canvas-0.43.0.dist-info → canvas-0.44.0.dist-info}/RECORD +18 -14
- canvas_cli/apps/auth/storage.py +65 -0
- canvas_cli/apps/auth/utils.py +6 -42
- canvas_generated/messages/effects_pb2.py +2 -2
- canvas_generated/messages/effects_pb2.pyi +10 -0
- canvas_sdk/effects/note/appointment.py +135 -50
- canvas_sdk/effects/note/base.py +108 -10
- canvas_sdk/effects/note/note.py +81 -22
- canvas_sdk/v1/data/__init__.py +17 -1
- canvas_sdk/v1/data/business_line.py +35 -0
- canvas_sdk/v1/data/patient.py +17 -1
- canvas_sdk/v1/data/patient_consent.py +92 -0
- canvas_sdk/v1/data/payor_specific_charge.py +28 -0
- canvas_sdk/v1/data/user.py +2 -0
- protobufs/canvas_generated/messages/effects.proto +6 -0
- {canvas-0.43.0.dist-info → canvas-0.44.0.dist-info}/WHEEL +0 -0
- {canvas-0.43.0.dist-info → canvas-0.44.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: canvas
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.44.0
|
|
4
4
|
Summary: SDK to customize event-driven actions in your Canvas instance
|
|
5
5
|
Author-email: Canvas Team <engineering@canvasmedical.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -16,7 +16,6 @@ Requires-Dist: frozendict>=2.4.6
|
|
|
16
16
|
Requires-Dist: grpcio<2,>=1.60.1
|
|
17
17
|
Requires-Dist: ipython<9,>=8.21.0
|
|
18
18
|
Requires-Dist: jsonschema<5,>=4.21.1
|
|
19
|
-
Requires-Dist: keyring
|
|
20
19
|
Requires-Dist: protobuf<5,>=4.25.3
|
|
21
20
|
Requires-Dist: psycopg[binary,pool]<4,>=3.2.2
|
|
22
21
|
Requires-Dist: pydantic<3,>=2.6.1
|
|
@@ -3,7 +3,8 @@ canvas_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
3
3
|
canvas_cli/main.py,sha256=L6JQkt1yxy30cA3-M9v7JD8WMW4i0M5GPr9kZetAito,2728
|
|
4
4
|
canvas_cli/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
canvas_cli/apps/auth/__init__.py,sha256=gIwJ2qWvRlLqbiRkudrGqTKV-orlb8OTkG487qoRda4,105
|
|
6
|
-
canvas_cli/apps/auth/
|
|
6
|
+
canvas_cli/apps/auth/storage.py,sha256=D5zZaZo_kDCxXmyjtHsPJOatD0fP5eO1OoHFdxwCxH4,1656
|
|
7
|
+
canvas_cli/apps/auth/utils.py,sha256=kdWpkGY9Qg2G9Pp3caeKgC0_o0FJ6NOIillUa6I7HsE,4233
|
|
7
8
|
canvas_cli/apps/emit/__init__.py,sha256=Fvv6SIbeGV2S5NkigQtamcRwJXIT1n7UcM-wrQoYsRQ,64
|
|
8
9
|
canvas_cli/apps/emit/emit.py,sha256=5a-wfpWI7K5Nq6YQjS4VCMt-10LfZQ5CDFNUeLy9mzQ,2539
|
|
9
10
|
canvas_cli/apps/emit/event_fixtures/ALLERGY_INTOLERANCE_CREATED.ndjson,sha256=C0mKBU_zP19WAn3EVRArQ7Pl3FBCyHXOmE9x7ZIKTss,110
|
|
@@ -91,8 +92,8 @@ canvas_cli/utils/urls/urls.py,sha256=KwWTh5ERrEsZEvdBrZpZB71xtyWkDuglpXUbycWmBOo
|
|
|
91
92
|
canvas_cli/utils/validators/__init__.py,sha256=rBvSR2O1hWkNAnUBdcr-zUkmqT796_A61b9pnvEhwrM,113
|
|
92
93
|
canvas_cli/utils/validators/manifest_schema.py,sha256=HEXid9ZwLSChijVRRzVllSmEhZhr9ekx5Fu9CeKg0yE,6019
|
|
93
94
|
canvas_cli/utils/validators/validators.py,sha256=lrUBQ0sF7seTe_pNGsNgASdr2BGp6g-Qui58V4H9qfQ,1598
|
|
94
|
-
canvas_generated/messages/effects_pb2.py,sha256=
|
|
95
|
-
canvas_generated/messages/effects_pb2.pyi,sha256=
|
|
95
|
+
canvas_generated/messages/effects_pb2.py,sha256=OL0e9pfculEj4eyMAS8PUxv-PdsQrkgw6AoVCwhtAn4,12894
|
|
96
|
+
canvas_generated/messages/effects_pb2.pyi,sha256=fPLdaq3HMoiQ3GHAOJMG3sWlLxgb8UeMqABHBTnRB10,22888
|
|
96
97
|
canvas_generated/messages/effects_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
97
98
|
canvas_generated/messages/events_pb2.py,sha256=jq7YelRp8-f6JSWUVjayLK-lpgEkjxamMwnOAt12_eI,51709
|
|
98
99
|
canvas_generated/messages/events_pb2.pyi,sha256=GFrmww6N2heKMwXwbuTmj3D2jZQ0nrKonnCLUfSPf04,93036
|
|
@@ -169,10 +170,10 @@ canvas_sdk/effects/billing_line_item/add_billing_line_item.py,sha256=_V9P1q-Gxz1
|
|
|
169
170
|
canvas_sdk/effects/billing_line_item/remove_billing_line_item.py,sha256=Ou6gbmpz8pJvgleAmffkeQTQsdInYMtk8rYQb8depb8,628
|
|
170
171
|
canvas_sdk/effects/billing_line_item/update_billing_line_item.py,sha256=rQh3dHgMPibvCrlXZSVUKLNta42ML0qcDVQZOnrefI4,1475
|
|
171
172
|
canvas_sdk/effects/note/__init__.py,sha256=SwxCzB_2qzYhZAjObnD5cYRxC_kBCf68kNkKcQCANXY,294
|
|
172
|
-
canvas_sdk/effects/note/appointment.py,sha256=
|
|
173
|
-
canvas_sdk/effects/note/base.py,sha256=
|
|
173
|
+
canvas_sdk/effects/note/appointment.py,sha256=ca46df8onI-I6szPJWSz_N745PWchKMBcruKiXmxsqk,8034
|
|
174
|
+
canvas_sdk/effects/note/base.py,sha256=3TeUdVsWWl0iJ8fkbCLJ0SjcZ0fEASy50r-ubSXvCpI,7587
|
|
174
175
|
canvas_sdk/effects/note/message.py,sha256=T6p6h4J_I0k2jQSirFtIusydak4tem9yBPOTexvOQ0s,3740
|
|
175
|
-
canvas_sdk/effects/note/note.py,sha256=
|
|
176
|
+
canvas_sdk/effects/note/note.py,sha256=_iVVagZUM4fWOMw3Cy4KXl5rd-1_NgF57QSBl5y9d7w,4610
|
|
176
177
|
canvas_sdk/effects/patient/__init__.py,sha256=POWxjgR3zAawvaP0XVxFAVDmatAySR1kJSWg9_jOfpU,358
|
|
177
178
|
canvas_sdk/effects/patient/base.py,sha256=OXqymxGvRTSd8DxtGQuhORFIxDQYimkXFsYLgjkKNls,4596
|
|
178
179
|
canvas_sdk/effects/patient/create_patient_external_identifier.py,sha256=d03jzJcz2n19_NVo_wAq88iKZRuuM60qsZhGGhL7pGE,1055
|
|
@@ -219,13 +220,14 @@ canvas_sdk/utils/plugins.py,sha256=xxJUeVJBzfQWhlD1kO7HlFWxDAl01kkvbsf07Z08dOM,1
|
|
|
219
220
|
canvas_sdk/v1/__init__.py,sha256=YYXr5tEQlnwMgTXJbOG963tKSPiUOM4mUkX8wuiqp7U,17
|
|
220
221
|
canvas_sdk/v1/apps.py,sha256=4MwQfQu78oX5bUVlyxYzbt4rZODi0ZesXe_3QOsEWO8,170
|
|
221
222
|
canvas_sdk/v1/models.py,sha256=LqeHZ-Hz3EWyM9vYr9uZ9rBYdMXNuuvJqbmojV8EcEM,224
|
|
222
|
-
canvas_sdk/v1/data/__init__.py,sha256=
|
|
223
|
+
canvas_sdk/v1/data/__init__.py,sha256=0vVBA8OG3a1d8s6-_BGZ6eYdriyngA4EOdWaFavc-Ow,4568
|
|
223
224
|
canvas_sdk/v1/data/allergy_intolerance.py,sha256=oVm9OgNT7eGylp-26Y_zw6G109qfkWVGZC0OsLqtlCM,2434
|
|
224
225
|
canvas_sdk/v1/data/appointment.py,sha256=vSHa7GlLCsBS8bhxMxj4hZpYno-wxaaXOIZ2GyYCdGY,2715
|
|
225
226
|
canvas_sdk/v1/data/assessment.py,sha256=7K4ASCda3GGr9pj4lauGdoItyNWD-RpalAXFXa1t7Bk,1618
|
|
226
227
|
canvas_sdk/v1/data/banner_alert.py,sha256=T9Iq_1FWFo_hqxFZnR2xFQ4xDpeBf_aUCvQilrRVh8Q,815
|
|
227
228
|
canvas_sdk/v1/data/base.py,sha256=ZuLj9MSUkTR7L0NMwC5YflhY4Px-SgCvhh5G2Ili0SE,6347
|
|
228
229
|
canvas_sdk/v1/data/billing.py,sha256=E1Bf2SGQCTr4vF3cWQzKgVKvqJmXtzHsyIBcVx8ICMg,2719
|
|
230
|
+
canvas_sdk/v1/data/business_line.py,sha256=8q0BDpKukQVjyHiHkrB3DQTdn4OPJWZhVNNOQ2ULFSY,905
|
|
229
231
|
canvas_sdk/v1/data/care_team.py,sha256=Ce6uXz_g867oYrq1RLvK-KXLubT4MAkomuJIO-GC7Tw,1916
|
|
230
232
|
canvas_sdk/v1/data/claim.py,sha256=IaC-k9AtcQy3JiIPG_NmFKzx7syvrgWS4FAIZl4vcNU,10821
|
|
231
233
|
canvas_sdk/v1/data/claim_line_item.py,sha256=rd8y2A29nWU_8ZAK0W4ITeUCHp2zAzc0fnUIXpVBOoA,4904
|
|
@@ -246,8 +248,10 @@ canvas_sdk/v1/data/message.py,sha256=P95_oeOnaSDEPFoCx6Gcl54-NgYbW8BgIXizl5pBSiQ
|
|
|
246
248
|
canvas_sdk/v1/data/note.py,sha256=E92cgA90hQCAZjIEqFzZOcWJ74RoVEwDpG4Fx35NCkM,6790
|
|
247
249
|
canvas_sdk/v1/data/observation.py,sha256=Hh8UKY1iqIYAqySWhwoj86Gmkbhz90C2m4GIx56ucyo,4034
|
|
248
250
|
canvas_sdk/v1/data/organization.py,sha256=-q1MbjPWihCaoKx6ZkRSY2JD3VNzAHIGGd6foMDwAy8,1031
|
|
249
|
-
canvas_sdk/v1/data/patient.py,sha256=
|
|
251
|
+
canvas_sdk/v1/data/patient.py,sha256=M91WSFFXg03Fu0qP7MRNVvn02EHJ4sMmBZnQMNehnyc,8915
|
|
252
|
+
canvas_sdk/v1/data/patient_consent.py,sha256=l_DJ8la1PFEB8TQMCKeLMjwH98AMSCt2mE24HgVKcqM,2785
|
|
250
253
|
canvas_sdk/v1/data/payment_collection.py,sha256=4ulvp6xWFwiFUrk0utPxy70FPkT1dmOrjwjxQrKYflY,939
|
|
254
|
+
canvas_sdk/v1/data/payor_specific_charge.py,sha256=lhv7qdYR-Z5rR9P_PtIw_LOwhXfidzrT0U7MGKZeZ2s,856
|
|
251
255
|
canvas_sdk/v1/data/posting.py,sha256=1K5GwUIyKEDdN2P5zmOu5PWVjsAZTe6ZLluH0CXTmwE,7841
|
|
252
256
|
canvas_sdk/v1/data/practicelocation.py,sha256=5Z6dq-XFsdo8fqr8nm4b8IvxDMgPgoViHMip8i8o8VE,4308
|
|
253
257
|
canvas_sdk/v1/data/protocol_override.py,sha256=B4FOunuS1h4VvO5m20SCwRHqG1_pAaxMgqrZ2Bwbong,2247
|
|
@@ -256,7 +260,7 @@ canvas_sdk/v1/data/reason_for_visit.py,sha256=lE0Fu5FCMlHPJzdE5fT5MQsuKd0dX2cEMR
|
|
|
256
260
|
canvas_sdk/v1/data/staff.py,sha256=tdVQmAJa-fq9CWixkijZ_u0fLXXAZ5WPf0lYSA83B6A,4955
|
|
257
261
|
canvas_sdk/v1/data/task.py,sha256=wmwgt70ocpboV_ov2GUAFbwcL-qGaotMgOk7DIpe6I0,3634
|
|
258
262
|
canvas_sdk/v1/data/team.py,sha256=9i5EKhq9Nhmx52nt2RLC3qOVRddKsAQbhfoP5Z9xDbE,2861
|
|
259
|
-
canvas_sdk/v1/data/user.py,sha256=
|
|
263
|
+
canvas_sdk/v1/data/user.py,sha256=TPTWrlAPn06ZRkBMyNsRrtQyBIbjSG0y5E00H-gZUzY,535
|
|
260
264
|
canvas_sdk/v1/data/utils.py,sha256=r0vMl4l7hxBFYDWRA_kXKycvFCmEch3SmeTgdsOHDec,279
|
|
261
265
|
canvas_sdk/value_set/__init__.py,sha256=YYXr5tEQlnwMgTXJbOG963tKSPiUOM4mUkX8wuiqp7U,17
|
|
262
266
|
canvas_sdk/value_set/custom.py,sha256=smf5fnPwuW-7H_B49CUfLWnq1QH-PhItvYw0siVsu-o,20173
|
|
@@ -290,13 +294,13 @@ plugin_runner/installation.py,sha256=LLjtnzPk-w4go3UbXnBItJTKz1ajR_5kGQbFXTaWTFU
|
|
|
290
294
|
plugin_runner/load_all_plugins.py,sha256=4T2gW2YljhIx4xfwf1c0F_8oIbE1ubsLj0ShkHRtlVY,5847
|
|
291
295
|
plugin_runner/plugin_runner.py,sha256=Z7ySPJSY4AMGRR0PtdXA_I3z8rxlDpjssuMspHIc79U,21655
|
|
292
296
|
plugin_runner/sandbox.py,sha256=vxfPKkz3A31g_QAD6eqa-CFt7z6xTp4FHu6RBrxoNz8,28589
|
|
293
|
-
protobufs/canvas_generated/messages/effects.proto,sha256=
|
|
297
|
+
protobufs/canvas_generated/messages/effects.proto,sha256=L5QGPfnmiefjD7qsGzKRbHAAyAstQmVvMrndwg1lumQ,8807
|
|
294
298
|
protobufs/canvas_generated/messages/events.proto,sha256=DSxbxKp7BTZpI4M14Sm7-XpHdUnN3bC3RapRqZbxgiQ,48751
|
|
295
299
|
protobufs/canvas_generated/messages/plugins.proto,sha256=oNainUPWFYQjgCX7bJEPI9_VnHC5VZduzOqgR4Q7dNM,109
|
|
296
300
|
protobufs/canvas_generated/services/plugin_runner.proto,sha256=doadBKn5k4xAtOgR-q_pEvW4yzxpUaHNOowMG6CL5GY,304
|
|
297
301
|
pubsub/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
298
302
|
pubsub/pubsub.py,sha256=PHIvJ5SD3M-jQSYeGGSj1FuG6CvP6BQffAoGax9Uudk,1423
|
|
299
|
-
canvas-0.
|
|
300
|
-
canvas-0.
|
|
301
|
-
canvas-0.
|
|
302
|
-
canvas-0.
|
|
303
|
+
canvas-0.44.0.dist-info/METADATA,sha256=OFBUhyUIjSVg9Uukdh7tHEVKu013OLZM5_vO8MkqH0A,4424
|
|
304
|
+
canvas-0.44.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
305
|
+
canvas-0.44.0.dist-info/entry_points.txt,sha256=0Vs_9GmTVUNniH6eDBlRPgofmADMV4BES6Ao26M4AbM,47
|
|
306
|
+
canvas-0.44.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
TOKENS_PATH = Path.home() / ".canvas" / "tokens.json"
|
|
7
|
+
TOKENS_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _load_tokens() -> dict:
|
|
11
|
+
if not TOKENS_PATH.exists():
|
|
12
|
+
return {}
|
|
13
|
+
with TOKENS_PATH.open("r") as f:
|
|
14
|
+
return json.load(f)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _save_tokens(tokens: dict) -> None:
|
|
18
|
+
with TOKENS_PATH.open("w") as f:
|
|
19
|
+
json.dump(tokens, f, indent=2)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _set_value(key: str, value: Any) -> None:
|
|
23
|
+
"""Set a value in the token storage."""
|
|
24
|
+
tokens = _load_tokens()
|
|
25
|
+
tokens[key] = value
|
|
26
|
+
_save_tokens(tokens)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_value(key: str) -> Any | None:
|
|
30
|
+
"""Get value from the token storage."""
|
|
31
|
+
tokens = _load_tokens()
|
|
32
|
+
return tokens.get(key)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _delete_value(key: str) -> None:
|
|
36
|
+
"""Delete a value from the token storage."""
|
|
37
|
+
tokens = _load_tokens()
|
|
38
|
+
tokens.pop(key, None)
|
|
39
|
+
_save_tokens(tokens)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def set_token(host_token_key: str, token: str, exp: datetime) -> None:
|
|
43
|
+
"""Set a token with an expiry date in the token storage."""
|
|
44
|
+
_set_value(host_token_key, {"token": token, "exp_date": exp.isoformat()})
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_token(host_token_key: str) -> str | None:
|
|
48
|
+
"""Get a token from the token storage."""
|
|
49
|
+
value = _get_value(host_token_key)
|
|
50
|
+
|
|
51
|
+
if not value:
|
|
52
|
+
return None
|
|
53
|
+
try:
|
|
54
|
+
exp = value["exp_date"]
|
|
55
|
+
if datetime.fromisoformat(exp) <= datetime.now():
|
|
56
|
+
return None
|
|
57
|
+
except ValueError:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
return value["token"]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def delete_token(host_token_key: str) -> None:
|
|
64
|
+
"""Delete a token from the token storage."""
|
|
65
|
+
_delete_value(host_token_key)
|
canvas_cli/apps/auth/utils.py
CHANGED
|
@@ -3,34 +3,16 @@ from datetime import datetime, timedelta
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from urllib.parse import urlparse
|
|
5
5
|
|
|
6
|
-
import keyring
|
|
7
6
|
import requests
|
|
8
7
|
|
|
8
|
+
from canvas_cli.apps.auth.storage import get_token, set_token
|
|
9
9
|
from canvas_sdk.utils import Http
|
|
10
10
|
|
|
11
|
-
# Keyring namespace we'll use
|
|
12
|
-
KEYRING_SERVICE = __name__
|
|
13
|
-
|
|
14
11
|
CONFIG_PATH = Path.home() / ".canvas" / "credentials.ini"
|
|
15
12
|
|
|
16
13
|
LOCALHOST = "http://localhost:8000"
|
|
17
14
|
|
|
18
15
|
|
|
19
|
-
def get_password(username: str) -> str | None:
|
|
20
|
-
"""Return the stored password for username, or None."""
|
|
21
|
-
return keyring.get_password(KEYRING_SERVICE, username)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def set_password(username: str, password: str) -> None:
|
|
25
|
-
"""Set the password for the given username."""
|
|
26
|
-
keyring.set_password(KEYRING_SERVICE, username=username, password=password)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def delete_password(username: str) -> None:
|
|
30
|
-
"""Delete the password for the given username."""
|
|
31
|
-
keyring.delete_password(KEYRING_SERVICE, username=username)
|
|
32
|
-
|
|
33
|
-
|
|
34
16
|
def get_config() -> configparser.ConfigParser:
|
|
35
17
|
"""Reads the config file and returns a ConfigParser object."""
|
|
36
18
|
config = configparser.ConfigParser()
|
|
@@ -64,7 +46,7 @@ def read_config(host: str, property: str) -> str:
|
|
|
64
46
|
|
|
65
47
|
|
|
66
48
|
def get_api_client_credentials(host: str) -> str:
|
|
67
|
-
"""Either return the given api_key, or fetch it from the
|
|
49
|
+
"""Either return the given api_key, or fetch it from the token storage."""
|
|
68
50
|
hostname = urlparse(host).hostname
|
|
69
51
|
|
|
70
52
|
if not hostname:
|
|
@@ -119,23 +101,6 @@ def request_api_token(host: str, api_client_credentials: str) -> dict:
|
|
|
119
101
|
return token_response.json()
|
|
120
102
|
|
|
121
103
|
|
|
122
|
-
def is_token_valid(host_token_key: str, expiration_date: datetime | None = None) -> bool:
|
|
123
|
-
"""True if the token has not expired yet."""
|
|
124
|
-
token_exp_date_key = f"{host_token_key}|exp_date"
|
|
125
|
-
|
|
126
|
-
if expiration_date:
|
|
127
|
-
if expiration_date <= datetime.now():
|
|
128
|
-
return False
|
|
129
|
-
set_password(token_exp_date_key, expiration_date.isoformat())
|
|
130
|
-
return True
|
|
131
|
-
|
|
132
|
-
stored_expiration_date = get_password(token_exp_date_key)
|
|
133
|
-
return (
|
|
134
|
-
stored_expiration_date is not None
|
|
135
|
-
and datetime.fromisoformat(stored_expiration_date) > datetime.now()
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
|
|
139
104
|
def get_or_request_api_token(host: str | None = None) -> str:
|
|
140
105
|
"""Returns an existing stored token if it has not expired, or requests a new one."""
|
|
141
106
|
if not (host := get_default_host(host)):
|
|
@@ -143,10 +108,9 @@ def get_or_request_api_token(host: str | None = None) -> str:
|
|
|
143
108
|
f"Please specify a host or add one to the configuration file at '{CONFIG_PATH}'"
|
|
144
109
|
)
|
|
145
110
|
|
|
146
|
-
|
|
147
|
-
token = get_password(host_token_key)
|
|
111
|
+
token = get_token(host)
|
|
148
112
|
|
|
149
|
-
if token
|
|
113
|
+
if token:
|
|
150
114
|
return token
|
|
151
115
|
|
|
152
116
|
api_client_credentials = get_api_client_credentials(host)
|
|
@@ -155,9 +119,9 @@ def get_or_request_api_token(host: str | None = None) -> str:
|
|
|
155
119
|
raise Exception(f"A token could not be acquired from the given host '{host}'")
|
|
156
120
|
|
|
157
121
|
token_expiration_date = datetime.now() + timedelta(seconds=token_response["expires_in"])
|
|
158
|
-
if
|
|
122
|
+
if token_expiration_date <= datetime.now():
|
|
159
123
|
raise Exception(f"A valid token could not be acquired from the given host '{host}'")
|
|
160
124
|
|
|
161
125
|
new_token = token_response["access_token"]
|
|
162
|
-
|
|
126
|
+
set_token(host, new_token, token_expiration_date)
|
|
163
127
|
return new_token
|
|
@@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'canvas_generated/messages/effects.proto\x12\x06\x63\x61nvas\"y\n\x06\x45\x66\x66\x65\x63t\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.canvas.EffectType\x12\x0f\n\x07payload\x18\x02 \x01(\t\x12\x13\n\x0bplugin_name\x18\x03 \x01(\t\x12\x11\n\tclassname\x18\x04 \x01(\t\x12\x14\n\x0chandler_name\x18\x05 \x01(\t*\xdc;\n\nEffectType\x12\x12\n\x0eUNKNOWN_EFFECT\x10\x00\x12\x07\n\x03LOG\x10\x01\x12\x14\n\x10\x41\x44\x44_PLAN_COMMAND\x10\x02\x12\x1f\n\x1b\x41UTOCOMPLETE_SEARCH_RESULTS\x10\x03\x12\x14\n\x10\x41\x44\x44_BANNER_ALERT\x10\x04\x12\x17\n\x13REMOVE_BANNER_ALERT\x10\x05\x12\x1c\n\x18ORIGINATE_ASSESS_COMMAND\x10\x06\x12\x17\n\x13\x45\x44IT_ASSESS_COMMAND\x10\x07\x12\x19\n\x15\x44\x45LETE_ASSESS_COMMAND\x10\x08\x12\x19\n\x15\x43OMMIT_ASSESS_COMMAND\x10\t\x12!\n\x1d\x45NTER_IN_ERROR_ASSESS_COMMAND\x10\n\x12\x1e\n\x1aORIGINATE_DIAGNOSE_COMMAND\x10\x0b\x12\x19\n\x15\x45\x44IT_DIAGNOSE_COMMAND\x10\x0c\x12\x1b\n\x17\x44\x45LETE_DIAGNOSE_COMMAND\x10\r\x12\x1b\n\x17\x43OMMIT_DIAGNOSE_COMMAND\x10\x0e\x12#\n\x1f\x45NTER_IN_ERROR_DIAGNOSE_COMMAND\x10\x0f\x12\x1a\n\x16ORIGINATE_GOAL_COMMAND\x10\x10\x12\x15\n\x11\x45\x44IT_GOAL_COMMAND\x10\x11\x12\x17\n\x13\x44\x45LETE_GOAL_COMMAND\x10\x12\x12\x17\n\x13\x43OMMIT_GOAL_COMMAND\x10\x13\x12\x1f\n\x1b\x45NTER_IN_ERROR_GOAL_COMMAND\x10\x14\x12\x19\n\x15ORIGINATE_HPI_COMMAND\x10\x15\x12\x14\n\x10\x45\x44IT_HPI_COMMAND\x10\x16\x12\x16\n\x12\x44\x45LETE_HPI_COMMAND\x10\x17\x12\x16\n\x12\x43OMMIT_HPI_COMMAND\x10\x18\x12\x1e\n\x1a\x45NTER_IN_ERROR_HPI_COMMAND\x10\x19\x12*\n&ORIGINATE_MEDICATION_STATEMENT_COMMAND\x10\x1a\x12%\n!EDIT_MEDICATION_STATEMENT_COMMAND\x10\x1b\x12\'\n#DELETE_MEDICATION_STATEMENT_COMMAND\x10\x1c\x12\'\n#COMMIT_MEDICATION_STATEMENT_COMMAND\x10\x1d\x12/\n+ENTER_IN_ERROR_MEDICATION_STATEMENT_COMMAND\x10\x1e\x12\x1a\n\x16ORIGINATE_PLAN_COMMAND\x10\x1f\x12\x15\n\x11\x45\x44IT_PLAN_COMMAND\x10 \x12\x17\n\x13\x44\x45LETE_PLAN_COMMAND\x10!\x12\x17\n\x13\x43OMMIT_PLAN_COMMAND\x10\"\x12\x1f\n\x1b\x45NTER_IN_ERROR_PLAN_COMMAND\x10#\x12\x1f\n\x1bORIGINATE_PRESCRIBE_COMMAND\x10$\x12\x1a\n\x16\x45\x44IT_PRESCRIBE_COMMAND\x10%\x12\x1c\n\x18\x44\x45LETE_PRESCRIBE_COMMAND\x10&\x12\x1c\n\x18\x43OMMIT_PRESCRIBE_COMMAND\x10\'\x12$\n ENTER_IN_ERROR_PRESCRIBE_COMMAND\x10(\x12#\n\x1fORIGINATE_QUESTIONNAIRE_COMMAND\x10)\x12\x1e\n\x1a\x45\x44IT_QUESTIONNAIRE_COMMAND\x10*\x12 \n\x1c\x44\x45LETE_QUESTIONNAIRE_COMMAND\x10+\x12 \n\x1c\x43OMMIT_QUESTIONNAIRE_COMMAND\x10,\x12(\n$ENTER_IN_ERROR_QUESTIONNAIRE_COMMAND\x10-\x12&\n\"ORIGINATE_REASON_FOR_VISIT_COMMAND\x10.\x12!\n\x1d\x45\x44IT_REASON_FOR_VISIT_COMMAND\x10/\x12#\n\x1f\x44\x45LETE_REASON_FOR_VISIT_COMMAND\x10\x30\x12#\n\x1f\x43OMMIT_REASON_FOR_VISIT_COMMAND\x10\x31\x12+\n\'ENTER_IN_ERROR_REASON_FOR_VISIT_COMMAND\x10\x32\x12%\n!ORIGINATE_STOP_MEDICATION_COMMAND\x10\x33\x12 \n\x1c\x45\x44IT_STOP_MEDICATION_COMMAND\x10\x34\x12\"\n\x1e\x44\x45LETE_STOP_MEDICATION_COMMAND\x10\x35\x12\"\n\x1e\x43OMMIT_STOP_MEDICATION_COMMAND\x10\x36\x12*\n&ENTER_IN_ERROR_STOP_MEDICATION_COMMAND\x10\x37\x12!\n\x1dORIGINATE_UPDATE_GOAL_COMMAND\x10\x38\x12\x1c\n\x18\x45\x44IT_UPDATE_GOAL_COMMAND\x10\x39\x12\x1e\n\x1a\x44\x45LETE_UPDATE_GOAL_COMMAND\x10:\x12\x1e\n\x1a\x43OMMIT_UPDATE_GOAL_COMMAND\x10;\x12&\n\"ENTER_IN_ERROR_UPDATE_GOAL_COMMAND\x10<\x12\x1d\n\x19ORIGINATE_PERFORM_COMMAND\x10=\x12\x18\n\x14\x45\x44IT_PERFORM_COMMAND\x10>\x12\x1a\n\x16\x44\x45LETE_PERFORM_COMMAND\x10?\x12\x1a\n\x16\x43OMMIT_PERFORM_COMMAND\x10@\x12\"\n\x1e\x45NTER_IN_ERROR_PERFORM_COMMAND\x10\x41\x12\x1e\n\x1aORIGINATE_INSTRUCT_COMMAND\x10\x42\x12\x19\n\x15\x45\x44IT_INSTRUCT_COMMAND\x10\x43\x12\x1b\n\x17\x44\x45LETE_INSTRUCT_COMMAND\x10\x44\x12\x1b\n\x17\x43OMMIT_INSTRUCT_COMMAND\x10\x45\x12#\n\x1f\x45NTER_IN_ERROR_INSTRUCT_COMMAND\x10\x46\x12\x1f\n\x1bORIGINATE_LAB_ORDER_COMMAND\x10G\x12\x1a\n\x16\x45\x44IT_LAB_ORDER_COMMAND\x10H\x12\x1c\n\x18\x44\x45LETE_LAB_ORDER_COMMAND\x10I\x12\x1c\n\x18\x43OMMIT_LAB_ORDER_COMMAND\x10J\x12$\n ENTER_IN_ERROR_LAB_ORDER_COMMAND\x10K\x12$\n ORIGINATE_FAMILY_HISTORY_COMMAND\x10L\x12\x1f\n\x1b\x45\x44IT_FAMILY_HISTORY_COMMAND\x10M\x12!\n\x1d\x44\x45LETE_FAMILY_HISTORY_COMMAND\x10N\x12!\n\x1d\x43OMMIT_FAMILY_HISTORY_COMMAND\x10O\x12)\n%ENTER_IN_ERROR_FAMILY_HISTORY_COMMAND\x10P\x12\x1d\n\x19ORIGINATE_ALLERGY_COMMAND\x10Q\x12\x18\n\x14\x45\x44IT_ALLERGY_COMMAND\x10R\x12\x1a\n\x16\x44\x45LETE_ALLERGY_COMMAND\x10S\x12\x1a\n\x16\x43OMMIT_ALLERGY_COMMAND\x10T\x12\"\n\x1e\x45NTER_IN_ERROR_ALLERGY_COMMAND\x10U\x12$\n ORIGINATE_REMOVE_ALLERGY_COMMAND\x10V\x12\x1f\n\x1b\x45\x44IT_REMOVE_ALLERGY_COMMAND\x10W\x12!\n\x1d\x44\x45LETE_REMOVE_ALLERGY_COMMAND\x10X\x12!\n\x1d\x43OMMIT_REMOVE_ALLERGY_COMMAND\x10Y\x12)\n%ENTER_IN_ERROR_REMOVE_ALLERGY_COMMAND\x10Z\x12&\n\"ORIGINATE_SURGICAL_HISTORY_COMMAND\x10[\x12!\n\x1d\x45\x44IT_SURGICAL_HISTORY_COMMAND\x10\\\x12#\n\x1f\x44\x45LETE_SURGICAL_HISTORY_COMMAND\x10]\x12#\n\x1f\x43OMMIT_SURGICAL_HISTORY_COMMAND\x10^\x12+\n\'ENTER_IN_ERROR_SURGICAL_HISTORY_COMMAND\x10_\x12\x0f\n\x0b\x43REATE_TASK\x10\x64\x12\x0f\n\x0bUPDATE_TASK\x10\x65\x12\x17\n\x13\x43REATE_TASK_COMMENT\x10\x66\x12%\n!ORIGINATE_MEDICAL_HISTORY_COMMAND\x10g\x12 \n\x1c\x45\x44IT_MEDICAL_HISTORY_COMMAND\x10h\x12\"\n\x1e\x44\x45LETE_MEDICAL_HISTORY_COMMAND\x10i\x12\"\n\x1e\x43OMMIT_MEDICAL_HISTORY_COMMAND\x10j\x12*\n&ENTER_IN_ERROR_MEDICAL_HISTORY_COMMAND\x10k\x12\x1f\n\x1b\x41\x44\x44_OR_UPDATE_PROTOCOL_CARD\x10n\x12\x1b\n\x16ORIGINATE_TASK_COMMAND\x10\x85\x01\x12\x16\n\x11\x45\x44IT_TASK_COMMAND\x10\x86\x01\x12\x18\n\x13\x44\x45LETE_TASK_COMMAND\x10\x87\x01\x12\x18\n\x13\x43OMMIT_TASK_COMMAND\x10\x88\x01\x12 \n\x1b\x45NTER_IN_ERROR_TASK_COMMAND\x10\x89\x01\x12\x1c\n\x18ORIGINATE_REFILL_COMMAND\x10q\x12\x17\n\x13\x45\x44IT_REFILL_COMMAND\x10r\x12\x19\n\x15\x44\x45LETE_REFILL_COMMAND\x10s\x12\x19\n\x15\x43OMMIT_REFILL_COMMAND\x10t\x12!\n\x1d\x45NTER_IN_ERROR_REFILL_COMMAND\x10u\x12\x1c\n\x18ORIGINATE_VITALS_COMMAND\x10v\x12\x17\n\x13\x45\x44IT_VITALS_COMMAND\x10w\x12\x19\n\x15\x44\x45LETE_VITALS_COMMAND\x10x\x12\x19\n\x15\x43OMMIT_VITALS_COMMAND\x10y\x12!\n\x1d\x45NTER_IN_ERROR_VITALS_COMMAND\x10z\x12&\n\"ORIGINATE_UPDATE_DIAGNOSIS_COMMAND\x10{\x12!\n\x1d\x45\x44IT_UPDATE_DIAGNOSIS_COMMAND\x10|\x12#\n\x1f\x44\x45LETE_UPDATE_DIAGNOSIS_COMMAND\x10}\x12#\n\x1f\x43OMMIT_UPDATE_DIAGNOSIS_COMMAND\x10~\x12+\n\'ENTER_IN_ERROR_UPDATE_DIAGNOSIS_COMMAND\x10\x7f\x12!\n\x1cORIGINATE_CLOSE_GOAL_COMMAND\x10\x80\x01\x12\x1c\n\x17\x45\x44IT_CLOSE_GOAL_COMMAND\x10\x81\x01\x12\x1e\n\x19\x44\x45LETE_CLOSE_GOAL_COMMAND\x10\x82\x01\x12\x1e\n\x19\x43OMMIT_CLOSE_GOAL_COMMAND\x10\x83\x01\x12&\n!ENTER_IN_ERROR_CLOSE_GOAL_COMMAND\x10\x84\x01\x12\x1c\n\x17ORIGINATE_REFER_COMMAND\x10\x8b\x01\x12\x17\n\x12\x45\x44IT_REFER_COMMAND\x10\x8c\x01\x12\x19\n\x14\x44\x45LETE_REFER_COMMAND\x10\x8d\x01\x12\x19\n\x14\x43OMMIT_REFER_COMMAND\x10\x8e\x01\x12!\n\x1c\x45NTER_IN_ERROR_REFER_COMMAND\x10\x8f\x01\x12(\n#ORIGINATE_CHANGE_MEDICATION_COMMAND\x10\x96\x01\x12#\n\x1e\x45\x44IT_CHANGE_MEDICATION_COMMAND\x10\x97\x01\x12%\n DELETE_CHANGE_MEDICATION_COMMAND\x10\x98\x01\x12%\n COMMIT_CHANGE_MEDICATION_COMMAND\x10\x99\x01\x12-\n(ENTER_IN_ERROR_CHANGE_MEDICATION_COMMAND\x10\x9a\x01\x12 \n\x1b\x43REATE_QUESTIONNAIRE_RESULT\x10\x8a\x01\x12-\n(ANNOTATE_PATIENT_CHART_CONDITION_RESULTS\x10\xc8\x01\x12%\n ANNOTATE_CLAIM_CONDITION_RESULTS\x10\xac\x02\x12(\n#SHOW_PATIENT_CHART_SUMMARY_SECTIONS\x10\x90\x03\x12\"\n\x1dSHOW_PATIENT_PROFILE_SECTIONS\x10\xf4\x03\x12\x37\n2PATIENT_PROFILE__ADD_PHARMACY__POST_SEARCH_RESULTS\x10\xf5\x03\x12)\n$SEND_SURESCRIPTS_ELIGIBILITY_REQUEST\x10\xd8\x04\x12\x30\n+SEND_SURESCRIPTS_MEDICATION_HISTORY_REQUEST\x10\xd9\x04\x12&\n!SEND_SURESCRIPTS_BENEFITS_REQUEST\x10\xda\x04\x12\x1b\n\x16ORIGINATE_EXAM_COMMAND\x10\xbc\x05\x12\x16\n\x11\x45\x44IT_EXAM_COMMAND\x10\xbd\x05\x12\x18\n\x13\x44\x45LETE_EXAM_COMMAND\x10\xbe\x05\x12\x18\n\x13\x43OMMIT_EXAM_COMMAND\x10\xbf\x05\x12 \n\x1b\x45NTER_IN_ERROR_EXAM_COMMAND\x10\xc0\x05\x12\x1a\n\x15ORIGINATE_ROS_COMMAND\x10\xa0\x06\x12\x15\n\x10\x45\x44IT_ROS_COMMAND\x10\xa1\x06\x12\x17\n\x12\x44\x45LETE_ROS_COMMAND\x10\xa2\x06\x12\x17\n\x12\x43OMMIT_ROS_COMMAND\x10\xa3\x06\x12\x1f\n\x1a\x45NTER_IN_ERROR_ROS_COMMAND\x10\xa4\x06\x12,\n\'ORIGINATE_STRUCTURED_ASSESSMENT_COMMAND\x10\x84\x07\x12\'\n\"EDIT_STRUCTURED_ASSESSMENT_COMMAND\x10\x85\x07\x12)\n$DELETE_STRUCTURED_ASSESSMENT_COMMAND\x10\x86\x07\x12)\n$COMMIT_STRUCTURED_ASSESSMENT_COMMAND\x10\x87\x07\x12\x31\n,ENTER_IN_ERROR_STRUCTURED_ASSESSMENT_COMMAND\x10\x88\x07\x12 \n\x1bORIGINATE_FOLLOW_UP_COMMAND\x10\x89\x07\x12\x1b\n\x16\x45\x44IT_FOLLOW_UP_COMMAND\x10\x8a\x07\x12\x1d\n\x18\x44\x45LETE_FOLLOW_UP_COMMAND\x10\x8b\x07\x12\x1d\n\x18\x43OMMIT_FOLLOW_UP_COMMAND\x10\x8c\x07\x12%\n ENTER_IN_ERROR_FOLLOW_UP_COMMAND\x10\x8d\x07\x12$\n\x1fORIGINATE_IMAGING_ORDER_COMMAND\x10\xcc\x08\x12\x1f\n\x1a\x45\x44IT_IMAGING_ORDER_COMMAND\x10\xcd\x08\x12!\n\x1c\x44\x45LETE_IMAGING_ORDER_COMMAND\x10\xce\x08\x12!\n\x1c\x43OMMIT_IMAGING_ORDER_COMMAND\x10\xcf\x08\x12)\n$ENTER_IN_ERROR_IMAGING_ORDER_COMMAND\x10\xd0\x08\x12(\n#ORIGINATE_RESOLVE_CONDITION_COMMAND\x10\xb0\t\x12#\n\x1e\x45\x44IT_RESOLVE_CONDITION_COMMAND\x10\xb1\t\x12%\n DELETE_RESOLVE_CONDITION_COMMAND\x10\xb2\t\x12%\n COMMIT_RESOLVE_CONDITION_COMMAND\x10\xb3\t\x12-\n(ENTER_IN_ERROR_RESOLVE_CONDITION_COMMAND\x10\xb4\t\x12*\n%ORIGINATE_ADJUST_PRESCRIPTION_COMMAND\x10\x94\n\x12%\n EDIT_ADJUST_PRESCRIPTION_COMMAND\x10\x95\n\x12\'\n\"DELETE_ADJUST_PRESCRIPTION_COMMAND\x10\x96\n\x12\'\n\"COMMIT_ADJUST_PRESCRIPTION_COMMAND\x10\x97\n\x12/\n*ENTER_IN_ERROR_ADJUST_PRESCRIPTION_COMMAND\x10\x98\n\x12+\n&ORIGINATE_CHART_SECTION_REVIEW_COMMAND\x10\xf8\n\x12\x17\n\x12SHOW_ACTION_BUTTON\x10\xe8\x07\x12 \n\x1bPATIENT_PORTAL__FORM_RESULT\x10\xd0\x0f\x12.\n)PATIENT_PORTAL__APPOINTMENT_IS_CANCELABLE\x10\xd1\x0f\x12\x31\n,PATIENT_PORTAL__APPOINTMENT_IS_RESCHEDULABLE\x10\xd2\x0f\x12\x32\n-PATIENT_PORTAL__APPOINTMENT_SHOW_MEETING_LINK\x10\xd3\x0f\x12 \n\x1bPATIENT_PORTAL__SEND_INVITE\x10\xd4\x0f\x12=\n8PATIENT_PORTAL__APPOINTMENTS__SLOTS__POST_SEARCH_RESULTS\x10\xd5\x0f\x12M\nHPATIENT_PORTAL__APPOINTMENTS__FORM_APPOINTMENT_TYPES__PRE_SEARCH_RESULTS\x10\xd6\x0f\x12N\nIPATIENT_PORTAL__APPOINTMENTS__FORM_APPOINTMENT_TYPES__POST_SEARCH_RESULTS\x10\xd7\x0f\x12\x45\n@PATIENT_PORTAL__APPOINTMENTS__FORM_LOCATIONS__PRE_SEARCH_RESULTS\x10\xd8\x0f\x12\x46\nAPATIENT_PORTAL__APPOINTMENTS__FORM_LOCATIONS__POST_SEARCH_RESULTS\x10\xd9\x0f\x12\x45\n@PATIENT_PORTAL__APPOINTMENTS__FORM_PROVIDERS__PRE_SEARCH_RESULTS\x10\xda\x0f\x12\x46\nAPATIENT_PORTAL__APPOINTMENTS__FORM_PROVIDERS__POST_SEARCH_RESULTS\x10\xdb\x0f\x12.\n)PATIENT_PORTAL__APPLICATION_CONFIGURATION\x10\xdc\x0f\x12\x1a\n\x15\x41\x44\x44_BILLING_LINE_ITEM\x10\xee\x0f\x12\x1d\n\x18UPDATE_BILLING_LINE_ITEM\x10\xef\x0f\x12\x1d\n\x18REMOVE_BILLING_LINE_ITEM\x10\xf0\x0f\x12#\n\x1eSHOW_PATIENT_PORTAL_MENU_ITEMS\x10\xb4\x10\x12\x12\n\rPORTAL_WIDGET\x10\xb5\x10\x12\x11\n\x0cLAUNCH_MODAL\x10\xb8\x17\x12\x18\n\x13SIMPLE_API_RESPONSE\x10\xa0\x1f\x12 \n\x1bSIMPLE_API_WEBSOCKET_ACCEPT\x10\xa1\x1f\x12\x1e\n\x19SIMPLE_API_WEBSOCKET_DENY\x10\xa2\x1f\x12#\n\x1eSIMPLE_API_WEBSOCKET_BROADCAST\x10\xa3\x1f\x12\x10\n\x0bUPDATE_USER\x10\x88\'\x12\x10\n\x0b\x43REATE_NOTE\x10\xf0.\x12\x17\n\x12\x43REATE_APPOINTMENT\x10\xf1.\x12\x1a\n\x15\x43REATE_SCHEDULE_EVENT\x10\xf2.\x12\x13\n\x0e\x43REATE_PATIENT\x10\xf3.\x12\x13\n\x0e\x43REATE_MESSAGE\x10\xf4.\x12\x11\n\x0cSEND_MESSAGE\x10\xf5.\x12\x1c\n\x17\x43REATE_AND_SEND_MESSAGE\x10\xf6.\x12\x11\n\x0c\x45\x44IT_MESSAGE\x10\xf7.\x12/\n*PATIENT_METADATA__CREATE_ADDITIONAL_FIELDS\x10\xfa.\x12\x1c\n\x17UPSERT_PATIENT_METADATA\x10\xfb.\x12\'\n\"CREATE_PATIENT_EXTERNAL_IDENTIFIER\x10\xfc.b\x06proto3')
|
|
17
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'canvas_generated/messages/effects.proto\x12\x06\x63\x61nvas\"y\n\x06\x45\x66\x66\x65\x63t\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.canvas.EffectType\x12\x0f\n\x07payload\x18\x02 \x01(\t\x12\x13\n\x0bplugin_name\x18\x03 \x01(\t\x12\x11\n\tclassname\x18\x04 \x01(\t\x12\x14\n\x0chandler_name\x18\x05 \x01(\t*\xd8<\n\nEffectType\x12\x12\n\x0eUNKNOWN_EFFECT\x10\x00\x12\x07\n\x03LOG\x10\x01\x12\x14\n\x10\x41\x44\x44_PLAN_COMMAND\x10\x02\x12\x1f\n\x1b\x41UTOCOMPLETE_SEARCH_RESULTS\x10\x03\x12\x14\n\x10\x41\x44\x44_BANNER_ALERT\x10\x04\x12\x17\n\x13REMOVE_BANNER_ALERT\x10\x05\x12\x1c\n\x18ORIGINATE_ASSESS_COMMAND\x10\x06\x12\x17\n\x13\x45\x44IT_ASSESS_COMMAND\x10\x07\x12\x19\n\x15\x44\x45LETE_ASSESS_COMMAND\x10\x08\x12\x19\n\x15\x43OMMIT_ASSESS_COMMAND\x10\t\x12!\n\x1d\x45NTER_IN_ERROR_ASSESS_COMMAND\x10\n\x12\x1e\n\x1aORIGINATE_DIAGNOSE_COMMAND\x10\x0b\x12\x19\n\x15\x45\x44IT_DIAGNOSE_COMMAND\x10\x0c\x12\x1b\n\x17\x44\x45LETE_DIAGNOSE_COMMAND\x10\r\x12\x1b\n\x17\x43OMMIT_DIAGNOSE_COMMAND\x10\x0e\x12#\n\x1f\x45NTER_IN_ERROR_DIAGNOSE_COMMAND\x10\x0f\x12\x1a\n\x16ORIGINATE_GOAL_COMMAND\x10\x10\x12\x15\n\x11\x45\x44IT_GOAL_COMMAND\x10\x11\x12\x17\n\x13\x44\x45LETE_GOAL_COMMAND\x10\x12\x12\x17\n\x13\x43OMMIT_GOAL_COMMAND\x10\x13\x12\x1f\n\x1b\x45NTER_IN_ERROR_GOAL_COMMAND\x10\x14\x12\x19\n\x15ORIGINATE_HPI_COMMAND\x10\x15\x12\x14\n\x10\x45\x44IT_HPI_COMMAND\x10\x16\x12\x16\n\x12\x44\x45LETE_HPI_COMMAND\x10\x17\x12\x16\n\x12\x43OMMIT_HPI_COMMAND\x10\x18\x12\x1e\n\x1a\x45NTER_IN_ERROR_HPI_COMMAND\x10\x19\x12*\n&ORIGINATE_MEDICATION_STATEMENT_COMMAND\x10\x1a\x12%\n!EDIT_MEDICATION_STATEMENT_COMMAND\x10\x1b\x12\'\n#DELETE_MEDICATION_STATEMENT_COMMAND\x10\x1c\x12\'\n#COMMIT_MEDICATION_STATEMENT_COMMAND\x10\x1d\x12/\n+ENTER_IN_ERROR_MEDICATION_STATEMENT_COMMAND\x10\x1e\x12\x1a\n\x16ORIGINATE_PLAN_COMMAND\x10\x1f\x12\x15\n\x11\x45\x44IT_PLAN_COMMAND\x10 \x12\x17\n\x13\x44\x45LETE_PLAN_COMMAND\x10!\x12\x17\n\x13\x43OMMIT_PLAN_COMMAND\x10\"\x12\x1f\n\x1b\x45NTER_IN_ERROR_PLAN_COMMAND\x10#\x12\x1f\n\x1bORIGINATE_PRESCRIBE_COMMAND\x10$\x12\x1a\n\x16\x45\x44IT_PRESCRIBE_COMMAND\x10%\x12\x1c\n\x18\x44\x45LETE_PRESCRIBE_COMMAND\x10&\x12\x1c\n\x18\x43OMMIT_PRESCRIBE_COMMAND\x10\'\x12$\n ENTER_IN_ERROR_PRESCRIBE_COMMAND\x10(\x12#\n\x1fORIGINATE_QUESTIONNAIRE_COMMAND\x10)\x12\x1e\n\x1a\x45\x44IT_QUESTIONNAIRE_COMMAND\x10*\x12 \n\x1c\x44\x45LETE_QUESTIONNAIRE_COMMAND\x10+\x12 \n\x1c\x43OMMIT_QUESTIONNAIRE_COMMAND\x10,\x12(\n$ENTER_IN_ERROR_QUESTIONNAIRE_COMMAND\x10-\x12&\n\"ORIGINATE_REASON_FOR_VISIT_COMMAND\x10.\x12!\n\x1d\x45\x44IT_REASON_FOR_VISIT_COMMAND\x10/\x12#\n\x1f\x44\x45LETE_REASON_FOR_VISIT_COMMAND\x10\x30\x12#\n\x1f\x43OMMIT_REASON_FOR_VISIT_COMMAND\x10\x31\x12+\n\'ENTER_IN_ERROR_REASON_FOR_VISIT_COMMAND\x10\x32\x12%\n!ORIGINATE_STOP_MEDICATION_COMMAND\x10\x33\x12 \n\x1c\x45\x44IT_STOP_MEDICATION_COMMAND\x10\x34\x12\"\n\x1e\x44\x45LETE_STOP_MEDICATION_COMMAND\x10\x35\x12\"\n\x1e\x43OMMIT_STOP_MEDICATION_COMMAND\x10\x36\x12*\n&ENTER_IN_ERROR_STOP_MEDICATION_COMMAND\x10\x37\x12!\n\x1dORIGINATE_UPDATE_GOAL_COMMAND\x10\x38\x12\x1c\n\x18\x45\x44IT_UPDATE_GOAL_COMMAND\x10\x39\x12\x1e\n\x1a\x44\x45LETE_UPDATE_GOAL_COMMAND\x10:\x12\x1e\n\x1a\x43OMMIT_UPDATE_GOAL_COMMAND\x10;\x12&\n\"ENTER_IN_ERROR_UPDATE_GOAL_COMMAND\x10<\x12\x1d\n\x19ORIGINATE_PERFORM_COMMAND\x10=\x12\x18\n\x14\x45\x44IT_PERFORM_COMMAND\x10>\x12\x1a\n\x16\x44\x45LETE_PERFORM_COMMAND\x10?\x12\x1a\n\x16\x43OMMIT_PERFORM_COMMAND\x10@\x12\"\n\x1e\x45NTER_IN_ERROR_PERFORM_COMMAND\x10\x41\x12\x1e\n\x1aORIGINATE_INSTRUCT_COMMAND\x10\x42\x12\x19\n\x15\x45\x44IT_INSTRUCT_COMMAND\x10\x43\x12\x1b\n\x17\x44\x45LETE_INSTRUCT_COMMAND\x10\x44\x12\x1b\n\x17\x43OMMIT_INSTRUCT_COMMAND\x10\x45\x12#\n\x1f\x45NTER_IN_ERROR_INSTRUCT_COMMAND\x10\x46\x12\x1f\n\x1bORIGINATE_LAB_ORDER_COMMAND\x10G\x12\x1a\n\x16\x45\x44IT_LAB_ORDER_COMMAND\x10H\x12\x1c\n\x18\x44\x45LETE_LAB_ORDER_COMMAND\x10I\x12\x1c\n\x18\x43OMMIT_LAB_ORDER_COMMAND\x10J\x12$\n ENTER_IN_ERROR_LAB_ORDER_COMMAND\x10K\x12$\n ORIGINATE_FAMILY_HISTORY_COMMAND\x10L\x12\x1f\n\x1b\x45\x44IT_FAMILY_HISTORY_COMMAND\x10M\x12!\n\x1d\x44\x45LETE_FAMILY_HISTORY_COMMAND\x10N\x12!\n\x1d\x43OMMIT_FAMILY_HISTORY_COMMAND\x10O\x12)\n%ENTER_IN_ERROR_FAMILY_HISTORY_COMMAND\x10P\x12\x1d\n\x19ORIGINATE_ALLERGY_COMMAND\x10Q\x12\x18\n\x14\x45\x44IT_ALLERGY_COMMAND\x10R\x12\x1a\n\x16\x44\x45LETE_ALLERGY_COMMAND\x10S\x12\x1a\n\x16\x43OMMIT_ALLERGY_COMMAND\x10T\x12\"\n\x1e\x45NTER_IN_ERROR_ALLERGY_COMMAND\x10U\x12$\n ORIGINATE_REMOVE_ALLERGY_COMMAND\x10V\x12\x1f\n\x1b\x45\x44IT_REMOVE_ALLERGY_COMMAND\x10W\x12!\n\x1d\x44\x45LETE_REMOVE_ALLERGY_COMMAND\x10X\x12!\n\x1d\x43OMMIT_REMOVE_ALLERGY_COMMAND\x10Y\x12)\n%ENTER_IN_ERROR_REMOVE_ALLERGY_COMMAND\x10Z\x12&\n\"ORIGINATE_SURGICAL_HISTORY_COMMAND\x10[\x12!\n\x1d\x45\x44IT_SURGICAL_HISTORY_COMMAND\x10\\\x12#\n\x1f\x44\x45LETE_SURGICAL_HISTORY_COMMAND\x10]\x12#\n\x1f\x43OMMIT_SURGICAL_HISTORY_COMMAND\x10^\x12+\n\'ENTER_IN_ERROR_SURGICAL_HISTORY_COMMAND\x10_\x12\x0f\n\x0b\x43REATE_TASK\x10\x64\x12\x0f\n\x0bUPDATE_TASK\x10\x65\x12\x17\n\x13\x43REATE_TASK_COMMENT\x10\x66\x12%\n!ORIGINATE_MEDICAL_HISTORY_COMMAND\x10g\x12 \n\x1c\x45\x44IT_MEDICAL_HISTORY_COMMAND\x10h\x12\"\n\x1e\x44\x45LETE_MEDICAL_HISTORY_COMMAND\x10i\x12\"\n\x1e\x43OMMIT_MEDICAL_HISTORY_COMMAND\x10j\x12*\n&ENTER_IN_ERROR_MEDICAL_HISTORY_COMMAND\x10k\x12\x1f\n\x1b\x41\x44\x44_OR_UPDATE_PROTOCOL_CARD\x10n\x12\x1b\n\x16ORIGINATE_TASK_COMMAND\x10\x85\x01\x12\x16\n\x11\x45\x44IT_TASK_COMMAND\x10\x86\x01\x12\x18\n\x13\x44\x45LETE_TASK_COMMAND\x10\x87\x01\x12\x18\n\x13\x43OMMIT_TASK_COMMAND\x10\x88\x01\x12 \n\x1b\x45NTER_IN_ERROR_TASK_COMMAND\x10\x89\x01\x12\x1c\n\x18ORIGINATE_REFILL_COMMAND\x10q\x12\x17\n\x13\x45\x44IT_REFILL_COMMAND\x10r\x12\x19\n\x15\x44\x45LETE_REFILL_COMMAND\x10s\x12\x19\n\x15\x43OMMIT_REFILL_COMMAND\x10t\x12!\n\x1d\x45NTER_IN_ERROR_REFILL_COMMAND\x10u\x12\x1c\n\x18ORIGINATE_VITALS_COMMAND\x10v\x12\x17\n\x13\x45\x44IT_VITALS_COMMAND\x10w\x12\x19\n\x15\x44\x45LETE_VITALS_COMMAND\x10x\x12\x19\n\x15\x43OMMIT_VITALS_COMMAND\x10y\x12!\n\x1d\x45NTER_IN_ERROR_VITALS_COMMAND\x10z\x12&\n\"ORIGINATE_UPDATE_DIAGNOSIS_COMMAND\x10{\x12!\n\x1d\x45\x44IT_UPDATE_DIAGNOSIS_COMMAND\x10|\x12#\n\x1f\x44\x45LETE_UPDATE_DIAGNOSIS_COMMAND\x10}\x12#\n\x1f\x43OMMIT_UPDATE_DIAGNOSIS_COMMAND\x10~\x12+\n\'ENTER_IN_ERROR_UPDATE_DIAGNOSIS_COMMAND\x10\x7f\x12!\n\x1cORIGINATE_CLOSE_GOAL_COMMAND\x10\x80\x01\x12\x1c\n\x17\x45\x44IT_CLOSE_GOAL_COMMAND\x10\x81\x01\x12\x1e\n\x19\x44\x45LETE_CLOSE_GOAL_COMMAND\x10\x82\x01\x12\x1e\n\x19\x43OMMIT_CLOSE_GOAL_COMMAND\x10\x83\x01\x12&\n!ENTER_IN_ERROR_CLOSE_GOAL_COMMAND\x10\x84\x01\x12\x1c\n\x17ORIGINATE_REFER_COMMAND\x10\x8b\x01\x12\x17\n\x12\x45\x44IT_REFER_COMMAND\x10\x8c\x01\x12\x19\n\x14\x44\x45LETE_REFER_COMMAND\x10\x8d\x01\x12\x19\n\x14\x43OMMIT_REFER_COMMAND\x10\x8e\x01\x12!\n\x1c\x45NTER_IN_ERROR_REFER_COMMAND\x10\x8f\x01\x12(\n#ORIGINATE_CHANGE_MEDICATION_COMMAND\x10\x96\x01\x12#\n\x1e\x45\x44IT_CHANGE_MEDICATION_COMMAND\x10\x97\x01\x12%\n DELETE_CHANGE_MEDICATION_COMMAND\x10\x98\x01\x12%\n COMMIT_CHANGE_MEDICATION_COMMAND\x10\x99\x01\x12-\n(ENTER_IN_ERROR_CHANGE_MEDICATION_COMMAND\x10\x9a\x01\x12 \n\x1b\x43REATE_QUESTIONNAIRE_RESULT\x10\x8a\x01\x12-\n(ANNOTATE_PATIENT_CHART_CONDITION_RESULTS\x10\xc8\x01\x12%\n ANNOTATE_CLAIM_CONDITION_RESULTS\x10\xac\x02\x12(\n#SHOW_PATIENT_CHART_SUMMARY_SECTIONS\x10\x90\x03\x12\"\n\x1dSHOW_PATIENT_PROFILE_SECTIONS\x10\xf4\x03\x12\x37\n2PATIENT_PROFILE__ADD_PHARMACY__POST_SEARCH_RESULTS\x10\xf5\x03\x12)\n$SEND_SURESCRIPTS_ELIGIBILITY_REQUEST\x10\xd8\x04\x12\x30\n+SEND_SURESCRIPTS_MEDICATION_HISTORY_REQUEST\x10\xd9\x04\x12&\n!SEND_SURESCRIPTS_BENEFITS_REQUEST\x10\xda\x04\x12\x1b\n\x16ORIGINATE_EXAM_COMMAND\x10\xbc\x05\x12\x16\n\x11\x45\x44IT_EXAM_COMMAND\x10\xbd\x05\x12\x18\n\x13\x44\x45LETE_EXAM_COMMAND\x10\xbe\x05\x12\x18\n\x13\x43OMMIT_EXAM_COMMAND\x10\xbf\x05\x12 \n\x1b\x45NTER_IN_ERROR_EXAM_COMMAND\x10\xc0\x05\x12\x1a\n\x15ORIGINATE_ROS_COMMAND\x10\xa0\x06\x12\x15\n\x10\x45\x44IT_ROS_COMMAND\x10\xa1\x06\x12\x17\n\x12\x44\x45LETE_ROS_COMMAND\x10\xa2\x06\x12\x17\n\x12\x43OMMIT_ROS_COMMAND\x10\xa3\x06\x12\x1f\n\x1a\x45NTER_IN_ERROR_ROS_COMMAND\x10\xa4\x06\x12,\n\'ORIGINATE_STRUCTURED_ASSESSMENT_COMMAND\x10\x84\x07\x12\'\n\"EDIT_STRUCTURED_ASSESSMENT_COMMAND\x10\x85\x07\x12)\n$DELETE_STRUCTURED_ASSESSMENT_COMMAND\x10\x86\x07\x12)\n$COMMIT_STRUCTURED_ASSESSMENT_COMMAND\x10\x87\x07\x12\x31\n,ENTER_IN_ERROR_STRUCTURED_ASSESSMENT_COMMAND\x10\x88\x07\x12 \n\x1bORIGINATE_FOLLOW_UP_COMMAND\x10\x89\x07\x12\x1b\n\x16\x45\x44IT_FOLLOW_UP_COMMAND\x10\x8a\x07\x12\x1d\n\x18\x44\x45LETE_FOLLOW_UP_COMMAND\x10\x8b\x07\x12\x1d\n\x18\x43OMMIT_FOLLOW_UP_COMMAND\x10\x8c\x07\x12%\n ENTER_IN_ERROR_FOLLOW_UP_COMMAND\x10\x8d\x07\x12$\n\x1fORIGINATE_IMAGING_ORDER_COMMAND\x10\xcc\x08\x12\x1f\n\x1a\x45\x44IT_IMAGING_ORDER_COMMAND\x10\xcd\x08\x12!\n\x1c\x44\x45LETE_IMAGING_ORDER_COMMAND\x10\xce\x08\x12!\n\x1c\x43OMMIT_IMAGING_ORDER_COMMAND\x10\xcf\x08\x12)\n$ENTER_IN_ERROR_IMAGING_ORDER_COMMAND\x10\xd0\x08\x12(\n#ORIGINATE_RESOLVE_CONDITION_COMMAND\x10\xb0\t\x12#\n\x1e\x45\x44IT_RESOLVE_CONDITION_COMMAND\x10\xb1\t\x12%\n DELETE_RESOLVE_CONDITION_COMMAND\x10\xb2\t\x12%\n COMMIT_RESOLVE_CONDITION_COMMAND\x10\xb3\t\x12-\n(ENTER_IN_ERROR_RESOLVE_CONDITION_COMMAND\x10\xb4\t\x12*\n%ORIGINATE_ADJUST_PRESCRIPTION_COMMAND\x10\x94\n\x12%\n EDIT_ADJUST_PRESCRIPTION_COMMAND\x10\x95\n\x12\'\n\"DELETE_ADJUST_PRESCRIPTION_COMMAND\x10\x96\n\x12\'\n\"COMMIT_ADJUST_PRESCRIPTION_COMMAND\x10\x97\n\x12/\n*ENTER_IN_ERROR_ADJUST_PRESCRIPTION_COMMAND\x10\x98\n\x12+\n&ORIGINATE_CHART_SECTION_REVIEW_COMMAND\x10\xf8\n\x12\x17\n\x12SHOW_ACTION_BUTTON\x10\xe8\x07\x12 \n\x1bPATIENT_PORTAL__FORM_RESULT\x10\xd0\x0f\x12.\n)PATIENT_PORTAL__APPOINTMENT_IS_CANCELABLE\x10\xd1\x0f\x12\x31\n,PATIENT_PORTAL__APPOINTMENT_IS_RESCHEDULABLE\x10\xd2\x0f\x12\x32\n-PATIENT_PORTAL__APPOINTMENT_SHOW_MEETING_LINK\x10\xd3\x0f\x12 \n\x1bPATIENT_PORTAL__SEND_INVITE\x10\xd4\x0f\x12=\n8PATIENT_PORTAL__APPOINTMENTS__SLOTS__POST_SEARCH_RESULTS\x10\xd5\x0f\x12M\nHPATIENT_PORTAL__APPOINTMENTS__FORM_APPOINTMENT_TYPES__PRE_SEARCH_RESULTS\x10\xd6\x0f\x12N\nIPATIENT_PORTAL__APPOINTMENTS__FORM_APPOINTMENT_TYPES__POST_SEARCH_RESULTS\x10\xd7\x0f\x12\x45\n@PATIENT_PORTAL__APPOINTMENTS__FORM_LOCATIONS__PRE_SEARCH_RESULTS\x10\xd8\x0f\x12\x46\nAPATIENT_PORTAL__APPOINTMENTS__FORM_LOCATIONS__POST_SEARCH_RESULTS\x10\xd9\x0f\x12\x45\n@PATIENT_PORTAL__APPOINTMENTS__FORM_PROVIDERS__PRE_SEARCH_RESULTS\x10\xda\x0f\x12\x46\nAPATIENT_PORTAL__APPOINTMENTS__FORM_PROVIDERS__POST_SEARCH_RESULTS\x10\xdb\x0f\x12.\n)PATIENT_PORTAL__APPLICATION_CONFIGURATION\x10\xdc\x0f\x12\x1a\n\x15\x41\x44\x44_BILLING_LINE_ITEM\x10\xee\x0f\x12\x1d\n\x18UPDATE_BILLING_LINE_ITEM\x10\xef\x0f\x12\x1d\n\x18REMOVE_BILLING_LINE_ITEM\x10\xf0\x0f\x12#\n\x1eSHOW_PATIENT_PORTAL_MENU_ITEMS\x10\xb4\x10\x12\x12\n\rPORTAL_WIDGET\x10\xb5\x10\x12\x11\n\x0cLAUNCH_MODAL\x10\xb8\x17\x12\x18\n\x13SIMPLE_API_RESPONSE\x10\xa0\x1f\x12 \n\x1bSIMPLE_API_WEBSOCKET_ACCEPT\x10\xa1\x1f\x12\x1e\n\x19SIMPLE_API_WEBSOCKET_DENY\x10\xa2\x1f\x12#\n\x1eSIMPLE_API_WEBSOCKET_BROADCAST\x10\xa3\x1f\x12\x10\n\x0bUPDATE_USER\x10\x88\'\x12\x10\n\x0b\x43REATE_NOTE\x10\xf0.\x12\x10\n\x0bUPDATE_NOTE\x10\xfd.\x12\x17\n\x12\x43REATE_APPOINTMENT\x10\xf1.\x12\x17\n\x12UPDATE_APPOINTMENT\x10\xfe.\x12\x17\n\x12\x43\x41NCEL_APPOINTMENT\x10\xff.\x12\x1a\n\x15\x43REATE_SCHEDULE_EVENT\x10\xf2.\x12\x1a\n\x15UPDATE_SCHEDULE_EVENT\x10\x80/\x12\x1a\n\x15\x44\x45LETE_SCHEDULE_EVENT\x10\x81/\x12\x13\n\x0e\x43REATE_PATIENT\x10\xf3.\x12\x13\n\x0e\x43REATE_MESSAGE\x10\xf4.\x12\x11\n\x0cSEND_MESSAGE\x10\xf5.\x12\x1c\n\x17\x43REATE_AND_SEND_MESSAGE\x10\xf6.\x12\x11\n\x0c\x45\x44IT_MESSAGE\x10\xf7.\x12/\n*PATIENT_METADATA__CREATE_ADDITIONAL_FIELDS\x10\xfa.\x12\x1c\n\x17UPSERT_PATIENT_METADATA\x10\xfb.\x12\'\n\"CREATE_PATIENT_EXTERNAL_IDENTIFIER\x10\xfc.b\x06proto3')
|
|
18
18
|
|
|
19
19
|
_globals = globals()
|
|
20
20
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -22,7 +22,7 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'canvas_generated.messages.e
|
|
|
22
22
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
23
23
|
DESCRIPTOR._options = None
|
|
24
24
|
_globals['_EFFECTTYPE']._serialized_start=175
|
|
25
|
-
_globals['_EFFECTTYPE']._serialized_end=
|
|
25
|
+
_globals['_EFFECTTYPE']._serialized_end=7943
|
|
26
26
|
_globals['_EFFECT']._serialized_start=51
|
|
27
27
|
_globals['_EFFECT']._serialized_end=172
|
|
28
28
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -218,8 +218,13 @@ class EffectType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
|
|
218
218
|
SIMPLE_API_WEBSOCKET_BROADCAST: _ClassVar[EffectType]
|
|
219
219
|
UPDATE_USER: _ClassVar[EffectType]
|
|
220
220
|
CREATE_NOTE: _ClassVar[EffectType]
|
|
221
|
+
UPDATE_NOTE: _ClassVar[EffectType]
|
|
221
222
|
CREATE_APPOINTMENT: _ClassVar[EffectType]
|
|
223
|
+
UPDATE_APPOINTMENT: _ClassVar[EffectType]
|
|
224
|
+
CANCEL_APPOINTMENT: _ClassVar[EffectType]
|
|
222
225
|
CREATE_SCHEDULE_EVENT: _ClassVar[EffectType]
|
|
226
|
+
UPDATE_SCHEDULE_EVENT: _ClassVar[EffectType]
|
|
227
|
+
DELETE_SCHEDULE_EVENT: _ClassVar[EffectType]
|
|
223
228
|
CREATE_PATIENT: _ClassVar[EffectType]
|
|
224
229
|
CREATE_MESSAGE: _ClassVar[EffectType]
|
|
225
230
|
SEND_MESSAGE: _ClassVar[EffectType]
|
|
@@ -439,8 +444,13 @@ SIMPLE_API_WEBSOCKET_DENY: EffectType
|
|
|
439
444
|
SIMPLE_API_WEBSOCKET_BROADCAST: EffectType
|
|
440
445
|
UPDATE_USER: EffectType
|
|
441
446
|
CREATE_NOTE: EffectType
|
|
447
|
+
UPDATE_NOTE: EffectType
|
|
442
448
|
CREATE_APPOINTMENT: EffectType
|
|
449
|
+
UPDATE_APPOINTMENT: EffectType
|
|
450
|
+
CANCEL_APPOINTMENT: EffectType
|
|
443
451
|
CREATE_SCHEDULE_EVENT: EffectType
|
|
452
|
+
UPDATE_SCHEDULE_EVENT: EffectType
|
|
453
|
+
DELETE_SCHEDULE_EVENT: EffectType
|
|
444
454
|
CREATE_PATIENT: EffectType
|
|
445
455
|
CREATE_MESSAGE: EffectType
|
|
446
456
|
SEND_MESSAGE: EffectType
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import json
|
|
1
2
|
from typing import Any
|
|
2
3
|
from uuid import UUID
|
|
3
4
|
|
|
4
5
|
from pydantic_core import InitErrorDetails
|
|
5
6
|
|
|
7
|
+
from canvas_sdk.effects import Effect
|
|
6
8
|
from canvas_sdk.effects.note.base import AppointmentABC
|
|
7
9
|
from canvas_sdk.v1.data import NoteType, Patient
|
|
8
10
|
from canvas_sdk.v1.data.note import NoteTypeCategories
|
|
@@ -13,7 +15,7 @@ class ScheduleEvent(AppointmentABC):
|
|
|
13
15
|
Effect to create a schedule event.
|
|
14
16
|
|
|
15
17
|
Attributes:
|
|
16
|
-
note_type_id (UUID | str): The ID of the note type.
|
|
18
|
+
note_type_id (UUID | str | None): The ID of the note type.
|
|
17
19
|
patient_id (str | None): The ID of the patient, if applicable.
|
|
18
20
|
description (str | None): The description of the schedule event, if applicable.
|
|
19
21
|
"""
|
|
@@ -21,7 +23,7 @@ class ScheduleEvent(AppointmentABC):
|
|
|
21
23
|
class Meta:
|
|
22
24
|
effect_type = "SCHEDULE_EVENT"
|
|
23
25
|
|
|
24
|
-
note_type_id: UUID | str
|
|
26
|
+
note_type_id: UUID | str | None = None
|
|
25
27
|
patient_id: str | None = None
|
|
26
28
|
description: str | None = None
|
|
27
29
|
|
|
@@ -37,68 +39,102 @@ class ScheduleEvent(AppointmentABC):
|
|
|
37
39
|
"""
|
|
38
40
|
errors = super()._get_error_details(method)
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.filter(id=self.note_type_id)
|
|
43
|
-
.first()
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
if not note_type:
|
|
42
|
+
# note_type_id is required for create
|
|
43
|
+
if method == "create" and not self.note_type_id:
|
|
47
44
|
errors.append(
|
|
48
45
|
self._create_error_detail(
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
"missing",
|
|
47
|
+
"Field 'note_type_id' is required to create a schedule event.",
|
|
48
|
+
None,
|
|
52
49
|
)
|
|
53
50
|
)
|
|
54
51
|
return errors
|
|
55
52
|
|
|
56
|
-
if
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
self.note_type_id,
|
|
62
|
-
)
|
|
53
|
+
if self.note_type_id:
|
|
54
|
+
note_type = (
|
|
55
|
+
NoteType.objects.values("category", "is_patient_required", "allow_custom_title")
|
|
56
|
+
.filter(id=self.note_type_id)
|
|
57
|
+
.first()
|
|
63
58
|
)
|
|
64
59
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
if not note_type:
|
|
61
|
+
errors.append(
|
|
62
|
+
self._create_error_detail(
|
|
63
|
+
"value",
|
|
64
|
+
f"Note type with ID {self.note_type_id} does not exist.",
|
|
65
|
+
self.note_type_id,
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
return errors
|
|
69
|
+
|
|
70
|
+
if note_type["category"] != NoteTypeCategories.SCHEDULE_EVENT:
|
|
71
|
+
errors.append(
|
|
72
|
+
self._create_error_detail(
|
|
73
|
+
"value",
|
|
74
|
+
f"Schedule event note type must be of type: {NoteTypeCategories.SCHEDULE_EVENT}.",
|
|
75
|
+
self.note_type_id,
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if note_type["is_patient_required"] and not self.patient_id:
|
|
80
|
+
errors.append(
|
|
81
|
+
self._create_error_detail(
|
|
82
|
+
"value",
|
|
83
|
+
"Patient ID is required for this note type.",
|
|
84
|
+
self.note_type_id,
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if not note_type["allow_custom_title"] and self.description:
|
|
89
|
+
errors.append(
|
|
90
|
+
self._create_error_detail(
|
|
91
|
+
"value",
|
|
92
|
+
"Description is not allowed for this note type.",
|
|
93
|
+
self.note_type_id,
|
|
94
|
+
)
|
|
71
95
|
)
|
|
72
|
-
)
|
|
73
96
|
|
|
74
|
-
|
|
97
|
+
# Validate patient exists if provided
|
|
98
|
+
if self.patient_id and not Patient.objects.filter(id=self.patient_id).exists():
|
|
75
99
|
errors.append(
|
|
76
100
|
self._create_error_detail(
|
|
77
101
|
"value",
|
|
78
|
-
"
|
|
79
|
-
self.
|
|
102
|
+
f"Patient with ID {self.patient_id} does not exist.",
|
|
103
|
+
self.patient_id,
|
|
80
104
|
)
|
|
81
105
|
)
|
|
82
106
|
|
|
83
107
|
return errors
|
|
84
108
|
|
|
109
|
+
def delete(self) -> Effect:
|
|
110
|
+
"""Send a DELETE effect for the schedule event."""
|
|
111
|
+
self._validate_before_effect("delete")
|
|
112
|
+
return Effect(
|
|
113
|
+
type=f"DELETE_{self.Meta.effect_type}",
|
|
114
|
+
payload=json.dumps(
|
|
115
|
+
{
|
|
116
|
+
"data": self.values,
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
)
|
|
120
|
+
|
|
85
121
|
|
|
86
122
|
class Appointment(AppointmentABC):
|
|
87
123
|
"""
|
|
88
|
-
Effect to create an appointment.
|
|
124
|
+
Effect to create or update an appointment.
|
|
89
125
|
|
|
90
126
|
Attributes:
|
|
91
|
-
appointment_note_type_id (UUID | str): The ID of the appointment note type.
|
|
127
|
+
appointment_note_type_id (UUID | str | None): The ID of the appointment note type.
|
|
92
128
|
meeting_link (str | None): The meeting link for the appointment, if any.
|
|
93
|
-
patient_id (str): The ID of the patient.
|
|
129
|
+
patient_id (str | None): The ID of the patient.
|
|
94
130
|
"""
|
|
95
131
|
|
|
96
132
|
class Meta:
|
|
97
133
|
effect_type = "APPOINTMENT"
|
|
98
134
|
|
|
99
|
-
appointment_note_type_id: UUID | str
|
|
135
|
+
appointment_note_type_id: UUID | str | None = None
|
|
100
136
|
meeting_link: str | None = None
|
|
101
|
-
patient_id: str
|
|
137
|
+
patient_id: str | None = None
|
|
102
138
|
|
|
103
139
|
def _get_error_details(self, method: Any) -> list[InitErrorDetails]:
|
|
104
140
|
"""
|
|
@@ -112,37 +148,86 @@ class Appointment(AppointmentABC):
|
|
|
112
148
|
"""
|
|
113
149
|
errors = super()._get_error_details(method)
|
|
114
150
|
|
|
115
|
-
if
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
151
|
+
if method == "create":
|
|
152
|
+
if not self.appointment_note_type_id:
|
|
153
|
+
errors.append(
|
|
154
|
+
self._create_error_detail(
|
|
155
|
+
"missing",
|
|
156
|
+
"Field 'appointment_note_type_id' is required to create an appointment.",
|
|
157
|
+
None,
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
if not self.patient_id:
|
|
161
|
+
errors.append(
|
|
162
|
+
self._create_error_detail(
|
|
163
|
+
"missing",
|
|
164
|
+
"Field 'patient_id' is required to create an appointment.",
|
|
165
|
+
None,
|
|
166
|
+
)
|
|
121
167
|
)
|
|
122
|
-
)
|
|
123
168
|
|
|
124
|
-
|
|
125
|
-
id=self.appointment_note_type_id
|
|
126
|
-
)
|
|
127
|
-
if category != NoteTypeCategories.ENCOUNTER:
|
|
169
|
+
if method == "update" and self.patient_id:
|
|
128
170
|
errors.append(
|
|
129
171
|
self._create_error_detail(
|
|
130
172
|
"value",
|
|
131
|
-
|
|
132
|
-
|
|
173
|
+
"Field 'patient_id' cannot be updated for an existing appointment.",
|
|
174
|
+
None,
|
|
133
175
|
)
|
|
134
176
|
)
|
|
135
177
|
|
|
136
|
-
if not
|
|
178
|
+
if self.patient_id and not Patient.objects.filter(id=self.patient_id).exists():
|
|
137
179
|
errors.append(
|
|
138
180
|
self._create_error_detail(
|
|
139
181
|
"value",
|
|
140
|
-
"
|
|
141
|
-
self.
|
|
182
|
+
"Patient with ID {self.patient_id} does not exist.",
|
|
183
|
+
self.patient_id,
|
|
142
184
|
)
|
|
143
185
|
)
|
|
144
186
|
|
|
187
|
+
if self.appointment_note_type_id:
|
|
188
|
+
try:
|
|
189
|
+
category, is_scheduleable = NoteType.objects.values_list(
|
|
190
|
+
"category", "is_scheduleable"
|
|
191
|
+
).get(id=self.appointment_note_type_id)
|
|
192
|
+
if category != NoteTypeCategories.ENCOUNTER:
|
|
193
|
+
errors.append(
|
|
194
|
+
self._create_error_detail(
|
|
195
|
+
"value",
|
|
196
|
+
f"Appointment note type must be of type, {NoteTypeCategories.ENCOUNTER} but got: {category}.",
|
|
197
|
+
self.appointment_note_type_id,
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
if not is_scheduleable:
|
|
202
|
+
errors.append(
|
|
203
|
+
self._create_error_detail(
|
|
204
|
+
"value",
|
|
205
|
+
"Appointment note type must be scheduleable.",
|
|
206
|
+
self.appointment_note_type_id,
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
except NoteType.DoesNotExist:
|
|
210
|
+
errors.append(
|
|
211
|
+
self._create_error_detail(
|
|
212
|
+
"value",
|
|
213
|
+
f"Note type with ID {self.appointment_note_type_id} does not exist.",
|
|
214
|
+
self.appointment_note_type_id,
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
|
|
145
218
|
return errors
|
|
146
219
|
|
|
220
|
+
def cancel(self) -> Effect:
|
|
221
|
+
"""Send a CANCEL effect for the appointment."""
|
|
222
|
+
self._validate_before_effect("cancel")
|
|
223
|
+
return Effect(
|
|
224
|
+
type=f"CANCEL_{self.Meta.effect_type}",
|
|
225
|
+
payload=json.dumps(
|
|
226
|
+
{
|
|
227
|
+
"data": self.values,
|
|
228
|
+
}
|
|
229
|
+
),
|
|
230
|
+
)
|
|
231
|
+
|
|
147
232
|
|
|
148
233
|
__exports__ = ("ScheduleEvent", "Appointment")
|
canvas_sdk/effects/note/base.py
CHANGED
|
@@ -10,7 +10,7 @@ from pydantic_core import InitErrorDetails
|
|
|
10
10
|
from canvas_generated.messages.effects_pb2 import Effect
|
|
11
11
|
from canvas_sdk.base import TrackableFieldsModel
|
|
12
12
|
from canvas_sdk.v1.data import PracticeLocation, Staff
|
|
13
|
-
from canvas_sdk.v1.data.appointment import AppointmentProgressStatus
|
|
13
|
+
from canvas_sdk.v1.data.appointment import Appointment, AppointmentProgressStatus
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@dataclass
|
|
@@ -32,6 +32,7 @@ class NoteOrAppointmentABC(TrackableFieldsModel, ABC):
|
|
|
32
32
|
Base class for all note effects.
|
|
33
33
|
|
|
34
34
|
Attributes:
|
|
35
|
+
instance_id (UUID | str): The unique identifier for the note or appointment instance.
|
|
35
36
|
practice_location_id (UUID | str): The ID of the practice location.
|
|
36
37
|
provider_id (str): The ID of the provider.
|
|
37
38
|
"""
|
|
@@ -39,8 +40,9 @@ class NoteOrAppointmentABC(TrackableFieldsModel, ABC):
|
|
|
39
40
|
class Meta:
|
|
40
41
|
effect_type = None
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
instance_id: UUID | str | None = None
|
|
44
|
+
practice_location_id: UUID | str | None = None
|
|
45
|
+
provider_id: str | None = None
|
|
44
46
|
|
|
45
47
|
def _get_error_details(self, method: Any) -> list[InitErrorDetails]:
|
|
46
48
|
"""
|
|
@@ -54,7 +56,46 @@ class NoteOrAppointmentABC(TrackableFieldsModel, ABC):
|
|
|
54
56
|
"""
|
|
55
57
|
errors = super()._get_error_details(method)
|
|
56
58
|
|
|
57
|
-
if
|
|
59
|
+
if method == "create":
|
|
60
|
+
if self.instance_id:
|
|
61
|
+
errors.append(
|
|
62
|
+
self._create_error_detail(
|
|
63
|
+
"value",
|
|
64
|
+
"Instance ID should not be provided for create effects.",
|
|
65
|
+
self.instance_id,
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
if not self.practice_location_id:
|
|
69
|
+
errors.append(
|
|
70
|
+
self._create_error_detail(
|
|
71
|
+
"value",
|
|
72
|
+
"Practice location ID is required.",
|
|
73
|
+
self.practice_location_id,
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
if not self.provider_id:
|
|
77
|
+
errors.append(
|
|
78
|
+
self._create_error_detail(
|
|
79
|
+
"value",
|
|
80
|
+
"Provider ID is required.",
|
|
81
|
+
self.provider_id,
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
else:
|
|
86
|
+
if not self.instance_id:
|
|
87
|
+
errors.append(
|
|
88
|
+
self._create_error_detail(
|
|
89
|
+
"missing",
|
|
90
|
+
"Field 'instance_id' is required to update or cancel/delete a note or appointment.",
|
|
91
|
+
None,
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if (
|
|
96
|
+
self.practice_location_id
|
|
97
|
+
and not PracticeLocation.objects.filter(id=self.practice_location_id).exists()
|
|
98
|
+
):
|
|
58
99
|
errors.append(
|
|
59
100
|
self._create_error_detail(
|
|
60
101
|
"value",
|
|
@@ -63,7 +104,7 @@ class NoteOrAppointmentABC(TrackableFieldsModel, ABC):
|
|
|
63
104
|
)
|
|
64
105
|
)
|
|
65
106
|
|
|
66
|
-
if not Staff.objects.filter(id=self.provider_id).exists():
|
|
107
|
+
if self.provider_id and not Staff.objects.filter(id=self.provider_id).exists():
|
|
67
108
|
errors.append(
|
|
68
109
|
self._create_error_detail(
|
|
69
110
|
"value",
|
|
@@ -75,7 +116,7 @@ class NoteOrAppointmentABC(TrackableFieldsModel, ABC):
|
|
|
75
116
|
return errors
|
|
76
117
|
|
|
77
118
|
def create(self) -> Effect:
|
|
78
|
-
"""
|
|
119
|
+
"""Send a CREATE effect for the note or appointment."""
|
|
79
120
|
self._validate_before_effect("create")
|
|
80
121
|
return Effect(
|
|
81
122
|
type=f"CREATE_{self.Meta.effect_type}",
|
|
@@ -86,10 +127,27 @@ class NoteOrAppointmentABC(TrackableFieldsModel, ABC):
|
|
|
86
127
|
),
|
|
87
128
|
)
|
|
88
129
|
|
|
130
|
+
def update(self) -> Effect:
|
|
131
|
+
"""Send an UPDATE effect for the note or appointment."""
|
|
132
|
+
self._validate_before_effect("update")
|
|
133
|
+
|
|
134
|
+
# Check if any fields were actually modified
|
|
135
|
+
if self._dirty_keys == {"instance_id"}:
|
|
136
|
+
raise ValueError("No fields have been modified. Nothing to update.")
|
|
137
|
+
|
|
138
|
+
return Effect(
|
|
139
|
+
type=f"UPDATE_{self.Meta.effect_type}",
|
|
140
|
+
payload=json.dumps(
|
|
141
|
+
{
|
|
142
|
+
"data": self.values,
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
|
|
89
147
|
|
|
90
148
|
class AppointmentABC(NoteOrAppointmentABC, ABC):
|
|
91
149
|
"""
|
|
92
|
-
Base class for appointment
|
|
150
|
+
Base class for appointment create/update effects.
|
|
93
151
|
|
|
94
152
|
Attributes:
|
|
95
153
|
start_time (datetime.datetime): The start time of the appointment.
|
|
@@ -105,11 +163,48 @@ class AppointmentABC(NoteOrAppointmentABC, ABC):
|
|
|
105
163
|
"external_identifiers",
|
|
106
164
|
]
|
|
107
165
|
|
|
108
|
-
start_time: datetime.datetime
|
|
109
|
-
duration_minutes: int
|
|
166
|
+
start_time: datetime.datetime | None = None
|
|
167
|
+
duration_minutes: int | None = None
|
|
110
168
|
status: AppointmentProgressStatus | None = None
|
|
111
169
|
external_identifiers: list[AppointmentIdentifier] | None = None
|
|
112
170
|
|
|
171
|
+
def _get_error_details(self, method: Any) -> list[InitErrorDetails]:
|
|
172
|
+
"""Validates the appointment instance and returns a list of error details if validation fails."""
|
|
173
|
+
errors = super()._get_error_details(method)
|
|
174
|
+
|
|
175
|
+
if method == "create":
|
|
176
|
+
# Additional required fields for appointment creation
|
|
177
|
+
if not self.start_time:
|
|
178
|
+
errors.append(
|
|
179
|
+
self._create_error_detail(
|
|
180
|
+
"missing",
|
|
181
|
+
"Field 'start_time' is required to create an appointment.",
|
|
182
|
+
None,
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if not self.duration_minutes:
|
|
187
|
+
errors.append(
|
|
188
|
+
self._create_error_detail(
|
|
189
|
+
"missing",
|
|
190
|
+
"Field 'duration_minutes' is required to create an appointment.",
|
|
191
|
+
None,
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Validate appointment exists for update/delete
|
|
196
|
+
else:
|
|
197
|
+
if self.instance_id and not Appointment.objects.filter(id=self.instance_id).exists():
|
|
198
|
+
errors.append(
|
|
199
|
+
self._create_error_detail(
|
|
200
|
+
"value",
|
|
201
|
+
f"Appointment with ID {self.instance_id} does not exist.",
|
|
202
|
+
self.instance_id,
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return errors
|
|
207
|
+
|
|
113
208
|
@property
|
|
114
209
|
def values(self) -> dict:
|
|
115
210
|
"""
|
|
@@ -117,7 +212,10 @@ class AppointmentABC(NoteOrAppointmentABC, ABC):
|
|
|
117
212
|
"""
|
|
118
213
|
values = super().values
|
|
119
214
|
|
|
120
|
-
|
|
215
|
+
# Handle external_identifiers separately since it's excluded from dirty tracking
|
|
216
|
+
# because it can be a complex type
|
|
217
|
+
# Only include if explicitly set (not None)
|
|
218
|
+
if self.external_identifiers is not None:
|
|
121
219
|
values["external_identifiers"] = [
|
|
122
220
|
{"system": identifier.system, "value": identifier.value}
|
|
123
221
|
for identifier in self.external_identifiers
|
canvas_sdk/effects/note/note.py
CHANGED
|
@@ -5,6 +5,7 @@ from uuid import UUID
|
|
|
5
5
|
from pydantic_core import InitErrorDetails
|
|
6
6
|
|
|
7
7
|
from canvas_sdk.effects.note.base import NoteOrAppointmentABC
|
|
8
|
+
from canvas_sdk.v1.data import Note as NoteModel
|
|
8
9
|
from canvas_sdk.v1.data import NoteType, Patient
|
|
9
10
|
from canvas_sdk.v1.data.note import NoteTypeCategories
|
|
10
11
|
|
|
@@ -22,9 +23,10 @@ class Note(NoteOrAppointmentABC):
|
|
|
22
23
|
class Meta:
|
|
23
24
|
effect_type = "NOTE"
|
|
24
25
|
|
|
25
|
-
note_type_id: UUID | str
|
|
26
|
-
datetime_of_service: datetime.datetime
|
|
27
|
-
patient_id: str
|
|
26
|
+
note_type_id: UUID | str | None = None
|
|
27
|
+
datetime_of_service: datetime.datetime | None = None
|
|
28
|
+
patient_id: str | None = None
|
|
29
|
+
title: str | None = None
|
|
28
30
|
|
|
29
31
|
def _get_error_details(self, method: Any) -> list[InitErrorDetails]:
|
|
30
32
|
"""
|
|
@@ -38,33 +40,90 @@ class Note(NoteOrAppointmentABC):
|
|
|
38
40
|
"""
|
|
39
41
|
errors = super()._get_error_details(method)
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
if method == "create":
|
|
44
|
+
if not self.note_type_id:
|
|
45
|
+
errors.append(
|
|
46
|
+
self._create_error_detail(
|
|
47
|
+
"missing",
|
|
48
|
+
"Field 'note_type_id' is required to create a note.",
|
|
49
|
+
None,
|
|
50
|
+
)
|
|
51
|
+
)
|
|
44
52
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
if not self.datetime_of_service:
|
|
54
|
+
errors.append(
|
|
55
|
+
self._create_error_detail(
|
|
56
|
+
"missing",
|
|
57
|
+
"Field 'datetime_of_service' is required to create a note.",
|
|
58
|
+
None,
|
|
59
|
+
)
|
|
51
60
|
)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
|
|
62
|
+
if not self.patient_id:
|
|
63
|
+
errors.append(
|
|
64
|
+
self._create_error_detail(
|
|
65
|
+
"missing",
|
|
66
|
+
"Field 'patient_id' is required to create a note.",
|
|
67
|
+
None,
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
elif method == "update":
|
|
71
|
+
if self.note_type_id:
|
|
72
|
+
errors.append(
|
|
73
|
+
self._create_error_detail(
|
|
74
|
+
"invalid",
|
|
75
|
+
"Field 'note_type_id' cannot be updated for a note.",
|
|
76
|
+
self.note_type_id,
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
if self.patient_id:
|
|
80
|
+
errors.append(
|
|
81
|
+
self._create_error_detail(
|
|
82
|
+
"invalid",
|
|
83
|
+
"Field 'patient_id' cannot be updated for a note.",
|
|
84
|
+
self.patient_id,
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if self.instance_id and not NoteModel.objects.filter(id=self.instance_id).exists():
|
|
59
89
|
errors.append(
|
|
60
90
|
self._create_error_detail(
|
|
61
91
|
"value",
|
|
62
|
-
f"
|
|
63
|
-
self.
|
|
92
|
+
f"Note with ID {self.instance_id} does not exist.",
|
|
93
|
+
self.instance_id,
|
|
64
94
|
)
|
|
65
95
|
)
|
|
66
96
|
|
|
67
|
-
if
|
|
97
|
+
if self.note_type_id:
|
|
98
|
+
note_type_category = (
|
|
99
|
+
NoteType.objects.values_list("category", flat=True)
|
|
100
|
+
.filter(id=self.note_type_id)
|
|
101
|
+
.first()
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if not note_type_category:
|
|
105
|
+
errors.append(
|
|
106
|
+
self._create_error_detail(
|
|
107
|
+
"value",
|
|
108
|
+
f"Note type with ID {self.note_type_id} does not exist.",
|
|
109
|
+
self.note_type_id,
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
elif note_type_category in (
|
|
113
|
+
NoteTypeCategories.APPOINTMENT,
|
|
114
|
+
NoteTypeCategories.SCHEDULE_EVENT,
|
|
115
|
+
NoteTypeCategories.MESSAGE,
|
|
116
|
+
NoteTypeCategories.LETTER,
|
|
117
|
+
):
|
|
118
|
+
errors.append(
|
|
119
|
+
self._create_error_detail(
|
|
120
|
+
"value",
|
|
121
|
+
f"Visit note type cannot be of type: {note_type_category}.",
|
|
122
|
+
self.note_type_id,
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if self.patient_id and not Patient.objects.filter(id=self.patient_id).exists():
|
|
68
127
|
errors.append(
|
|
69
128
|
self._create_error_detail(
|
|
70
129
|
"value",
|
canvas_sdk/v1/data/__init__.py
CHANGED
|
@@ -3,6 +3,7 @@ from .appointment import Appointment, AppointmentExternalIdentifier
|
|
|
3
3
|
from .assessment import Assessment
|
|
4
4
|
from .banner_alert import BannerAlert
|
|
5
5
|
from .billing import BillingLineItem, BillingLineItemModifier
|
|
6
|
+
from .business_line import BusinessLine
|
|
6
7
|
from .care_team import CareTeamMembership, CareTeamRole
|
|
7
8
|
from .claim import Claim, ClaimCoverage, ClaimPatient, ClaimQueue
|
|
8
9
|
from .claim_line_item import ClaimLineItem
|
|
@@ -24,7 +25,11 @@ from .lab import (
|
|
|
24
25
|
LabValue,
|
|
25
26
|
LabValueCoding,
|
|
26
27
|
)
|
|
27
|
-
from .line_item_transaction import
|
|
28
|
+
from .line_item_transaction import (
|
|
29
|
+
LineItemTransfer,
|
|
30
|
+
NewLineItemAdjustment,
|
|
31
|
+
NewLineItemPayment,
|
|
32
|
+
)
|
|
28
33
|
from .medication import Medication, MedicationCoding
|
|
29
34
|
from .message import Message, MessageAttachment, MessageTransmission
|
|
30
35
|
from .note import Note, NoteType
|
|
@@ -44,7 +49,13 @@ from .patient import (
|
|
|
44
49
|
PatientMetadata,
|
|
45
50
|
PatientSetting,
|
|
46
51
|
)
|
|
52
|
+
from .patient_consent import (
|
|
53
|
+
PatientConsent,
|
|
54
|
+
PatientConsentCoding,
|
|
55
|
+
PatientConsentRejectionCoding,
|
|
56
|
+
)
|
|
47
57
|
from .payment_collection import PaymentCollection
|
|
58
|
+
from .payor_specific_charge import PayorSpecificCharge
|
|
48
59
|
from .posting import (
|
|
49
60
|
BasePosting,
|
|
50
61
|
BaseRemittanceAdvice,
|
|
@@ -80,6 +91,7 @@ __all__ = __exports__ = (
|
|
|
80
91
|
"BaseRemittanceAdvice",
|
|
81
92
|
"BillingLineItem",
|
|
82
93
|
"BillingLineItemModifier",
|
|
94
|
+
"BusinessLine",
|
|
83
95
|
"BulkPatientPosting",
|
|
84
96
|
"CanvasUser",
|
|
85
97
|
"CareTeamMembership",
|
|
@@ -136,6 +148,10 @@ __all__ = __exports__ = (
|
|
|
136
148
|
"PatientPosting",
|
|
137
149
|
"PatientSetting",
|
|
138
150
|
"PatientMetadata",
|
|
151
|
+
"PatientConsent",
|
|
152
|
+
"PatientConsentCoding",
|
|
153
|
+
"PatientConsentRejectionCoding",
|
|
154
|
+
"PayorSpecificCharge",
|
|
139
155
|
"PaymentCollection",
|
|
140
156
|
"PracticeLocation",
|
|
141
157
|
"PracticeLocationSetting",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BusinessLineState(models.TextChoices):
|
|
5
|
+
"""BusinessLineStatus."""
|
|
6
|
+
|
|
7
|
+
STATE_SUCCESS = "success", "Success"
|
|
8
|
+
STATE_PENDING = "pending", "Pending"
|
|
9
|
+
STATE_ERROR = "error", "Deleted"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BusinessLine(models.Model):
|
|
13
|
+
"""Business Line."""
|
|
14
|
+
|
|
15
|
+
class Meta:
|
|
16
|
+
managed = False
|
|
17
|
+
db_table = "canvas_sdk_data_api_businessline_001"
|
|
18
|
+
|
|
19
|
+
id = models.UUIDField()
|
|
20
|
+
dbid = models.BigIntegerField(primary_key=True)
|
|
21
|
+
name = models.CharField()
|
|
22
|
+
description = models.TextField()
|
|
23
|
+
area_code = models.CharField()
|
|
24
|
+
subdomain = models.CharField()
|
|
25
|
+
active = models.BooleanField()
|
|
26
|
+
state = models.CharField(max_length=20, choices=BusinessLineState)
|
|
27
|
+
organization = models.ForeignKey(
|
|
28
|
+
"v1.Organization", on_delete=models.DO_NOTHING, related_name="business_lines"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
__exports__ = (
|
|
33
|
+
"BusinessLineState",
|
|
34
|
+
"BusinessLine",
|
|
35
|
+
)
|
canvas_sdk/v1/data/patient.py
CHANGED
|
@@ -52,6 +52,9 @@ class Patient(models.Model):
|
|
|
52
52
|
first_name = models.CharField()
|
|
53
53
|
last_name = models.CharField()
|
|
54
54
|
birth_date = models.DateField()
|
|
55
|
+
business_line = models.ForeignKey(
|
|
56
|
+
"v1.BusinessLine", on_delete=models.DO_NOTHING, related_name="patients"
|
|
57
|
+
)
|
|
55
58
|
sex_at_birth = models.CharField(choices=SexAtBirth.choices)
|
|
56
59
|
created = models.DateTimeField()
|
|
57
60
|
modified = models.DateTimeField()
|
|
@@ -129,6 +132,16 @@ class Patient(models.Model):
|
|
|
129
132
|
return None
|
|
130
133
|
return pharmacy_setting
|
|
131
134
|
|
|
135
|
+
@property
|
|
136
|
+
def preferred_full_name(self) -> str:
|
|
137
|
+
"""Returns the patient's preferred full name, taking nickname into consideration."""
|
|
138
|
+
return " ".join(n for n in (self.preferred_first_name, self.last_name, self.suffix) if n)
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def preferred_first_name(self) -> str:
|
|
142
|
+
"""Returns the patient's preferred first name, taking nickname into consideration."""
|
|
143
|
+
return self.nickname or self.first_name
|
|
144
|
+
|
|
132
145
|
|
|
133
146
|
class PatientContactPoint(models.Model):
|
|
134
147
|
"""A class representing a patient contact point."""
|
|
@@ -197,7 +210,10 @@ class PatientExternalIdentifier(models.Model):
|
|
|
197
210
|
created = models.DateTimeField()
|
|
198
211
|
modified = models.DateTimeField()
|
|
199
212
|
patient = models.ForeignKey(
|
|
200
|
-
"v1.Patient",
|
|
213
|
+
"v1.Patient",
|
|
214
|
+
related_name="external_identifiers",
|
|
215
|
+
on_delete=models.DO_NOTHING,
|
|
216
|
+
null=True,
|
|
201
217
|
)
|
|
202
218
|
use = models.CharField()
|
|
203
219
|
identifier_type = models.CharField()
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PatientConsentRejectionCoding(models.Model):
|
|
5
|
+
"""Patient Consent Rejection Coding."""
|
|
6
|
+
|
|
7
|
+
class Meta:
|
|
8
|
+
managed = False
|
|
9
|
+
db_table = "canvas_sdk_data_api_patientconsentrejectioncoding_001"
|
|
10
|
+
|
|
11
|
+
dbid = models.BigIntegerField(primary_key=True)
|
|
12
|
+
system = models.CharField()
|
|
13
|
+
version = models.CharField()
|
|
14
|
+
code = models.CharField()
|
|
15
|
+
display = models.CharField()
|
|
16
|
+
user_selected = models.BooleanField()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PatientConsentExpirationRule(models.TextChoices):
|
|
20
|
+
"""PatientConsentExpirationRule."""
|
|
21
|
+
|
|
22
|
+
NEVER = "never", "Never"
|
|
23
|
+
IN_ONE_YEAR = "in_one_year", "In one year"
|
|
24
|
+
END_OF_YEAR = "end_of_year", "End of year"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PatientConsentCoding(models.Model):
|
|
28
|
+
"""Patient Consent Coding."""
|
|
29
|
+
|
|
30
|
+
class Meta:
|
|
31
|
+
managed = False
|
|
32
|
+
db_table = "canvas_sdk_data_api_patientconsentcoding_001"
|
|
33
|
+
|
|
34
|
+
dbid = models.BigIntegerField(primary_key=True)
|
|
35
|
+
system = models.CharField()
|
|
36
|
+
version = models.CharField()
|
|
37
|
+
code = models.CharField()
|
|
38
|
+
display = models.CharField()
|
|
39
|
+
user_selected = models.BooleanField()
|
|
40
|
+
expiration_rule = models.CharField(choices=PatientConsentExpirationRule, max_length=255)
|
|
41
|
+
is_mandatory = models.BooleanField()
|
|
42
|
+
is_proof_required = models.BooleanField()
|
|
43
|
+
show_in_patient_portal = models.BooleanField()
|
|
44
|
+
summary = models.TextField()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class PatientConsentStatus(models.TextChoices):
|
|
48
|
+
"""PatientConsentStatus."""
|
|
49
|
+
|
|
50
|
+
ACCEPTED = "accepted", "Accepted"
|
|
51
|
+
ACCEPTED_VIA_PORTAL = (
|
|
52
|
+
"accepted_via_patient_portal",
|
|
53
|
+
"Accepted Via Patient Portal",
|
|
54
|
+
)
|
|
55
|
+
REJECTED = "rejected", "Rejected"
|
|
56
|
+
REJECTED_VIA_PORTAL = "rejected_via_patient_portal", "Rejected Via Patient Portal"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class PatientConsent(models.Model):
|
|
60
|
+
"""Patient Consent."""
|
|
61
|
+
|
|
62
|
+
class Meta:
|
|
63
|
+
managed = False
|
|
64
|
+
db_table = "canvas_sdk_data_api_patientconsent_001"
|
|
65
|
+
|
|
66
|
+
id = models.UUIDField()
|
|
67
|
+
dbid = models.BigIntegerField(primary_key=True)
|
|
68
|
+
patient = models.ForeignKey(
|
|
69
|
+
"v1.Patient", on_delete=models.DO_NOTHING, related_name="patient_consent"
|
|
70
|
+
)
|
|
71
|
+
category = models.ForeignKey(
|
|
72
|
+
"v1.PatientConsentCoding",
|
|
73
|
+
on_delete=models.DO_NOTHING,
|
|
74
|
+
related_name="patient_consent",
|
|
75
|
+
)
|
|
76
|
+
state = models.CharField(choices=PatientConsentStatus, max_length=255)
|
|
77
|
+
effective_date = models.DateField()
|
|
78
|
+
expired_date = models.DateField()
|
|
79
|
+
rejection_reason = models.ForeignKey(
|
|
80
|
+
"v1.PatientConsentRejectionCoding",
|
|
81
|
+
on_delete=models.DO_NOTHING,
|
|
82
|
+
null=True,
|
|
83
|
+
related_name="patient_consents",
|
|
84
|
+
)
|
|
85
|
+
originator = models.ForeignKey("v1.CanvasUser", on_delete=models.DO_NOTHING, related_name="+")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
__exports__ = (
|
|
89
|
+
"PatientConsent",
|
|
90
|
+
"PatientConsentCoding",
|
|
91
|
+
"PatientConsentRejectionCoding",
|
|
92
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PayorSpecificCharge(models.Model):
|
|
5
|
+
"""Payor Specific Charge."""
|
|
6
|
+
|
|
7
|
+
class Meta:
|
|
8
|
+
managed = False
|
|
9
|
+
db_table = "canvas_sdk_data_quality_and_revenue_payorspecificcharge_001"
|
|
10
|
+
|
|
11
|
+
dbid = models.BigIntegerField(primary_key=True)
|
|
12
|
+
transactor = models.ForeignKey(
|
|
13
|
+
"v1.Transactor", related_name="specific_charges", on_delete=models.DO_NOTHING
|
|
14
|
+
)
|
|
15
|
+
# uncomment this when ChargeDescriptionMaster is added to data module
|
|
16
|
+
# charge = models.ForeignKey(
|
|
17
|
+
# "v1.ChargeDescriptionMaster",
|
|
18
|
+
# related_name="transactor_charges",
|
|
19
|
+
# on_delete=models.DO_NOTHING,
|
|
20
|
+
# )
|
|
21
|
+
|
|
22
|
+
charge_amount = models.DecimalField()
|
|
23
|
+
effective_date = models.DateField()
|
|
24
|
+
end_date = models.DateField()
|
|
25
|
+
part_of_capitated_set = models.BooleanField()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__exports__ = ("PayorSpecificCharge",)
|
canvas_sdk/v1/data/user.py
CHANGED
|
@@ -11,6 +11,8 @@ class CanvasUser(models.Model):
|
|
|
11
11
|
dbid = models.BigIntegerField(db_column="dbid", primary_key=True)
|
|
12
12
|
email = models.EmailField(db_column="email")
|
|
13
13
|
phone_number = models.CharField(db_column="phone_number", max_length=255)
|
|
14
|
+
last_invite_date_time = models.DateTimeField()
|
|
15
|
+
is_portal_registered = models.BooleanField()
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
__exports__ = ("CanvasUser",)
|
|
@@ -269,8 +269,14 @@ enum EffectType {
|
|
|
269
269
|
UPDATE_USER = 5000;
|
|
270
270
|
|
|
271
271
|
CREATE_NOTE = 6000;
|
|
272
|
+
UPDATE_NOTE = 6013;
|
|
273
|
+
|
|
272
274
|
CREATE_APPOINTMENT = 6001;
|
|
275
|
+
UPDATE_APPOINTMENT = 6014;
|
|
276
|
+
CANCEL_APPOINTMENT = 6015;
|
|
273
277
|
CREATE_SCHEDULE_EVENT = 6002;
|
|
278
|
+
UPDATE_SCHEDULE_EVENT = 6016;
|
|
279
|
+
DELETE_SCHEDULE_EVENT = 6017;
|
|
274
280
|
|
|
275
281
|
CREATE_PATIENT = 6003;
|
|
276
282
|
|
|
File without changes
|
|
File without changes
|