canvas 0.43.0__py3-none-any.whl → 0.44.1__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.4
2
2
  Name: canvas
3
- Version: 0.43.0
3
+ Version: 0.44.1
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/utils.py,sha256=IH5oZB3pdlb4_FRfCZKkNTncx_kdKagpiBqlhtM8h2U,5434
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=arYOr5QxbXA4NC1MogGhHoVtqXrrihrsgWXoD8W4MAQ,12678
95
- canvas_generated/messages/effects_pb2.pyi,sha256=3N4SsSCV4PHVMopFt7hWojRRfgE6mCQLMBxb5zl55DQ,22505
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=uUiquciUdppc_R_wP_UkPeh4TQlzZRjtRRmGb0Nr-_A,4749
173
- canvas_sdk/effects/note/base.py,sha256=b_zCtU7_5y5rexDD0sJg3wmzwRMeUUvKUXJe-laSUIU,3856
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=s8ASykdGtCHobg8AyHHAKdnDGW0k4NUXPC3J17qHt98,2392
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,15 +220,16 @@ 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=SIHywHsxBBVrrPPFhnRlvxZOE3BZWFYUbMVA49XhqIU,4208
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
- canvas_sdk/v1/data/claim.py,sha256=IaC-k9AtcQy3JiIPG_NmFKzx7syvrgWS4FAIZl4vcNU,10821
232
+ canvas_sdk/v1/data/claim.py,sha256=5j9Hfi8bp9TVKfYmyamUQgw3Fa7EaJBOGdBND4w2HWI,10901
231
233
  canvas_sdk/v1/data/claim_line_item.py,sha256=rd8y2A29nWU_8ZAK0W4ITeUCHp2zAzc0fnUIXpVBOoA,4904
232
234
  canvas_sdk/v1/data/command.py,sha256=fLxQMhLN7HT7cMtnrFGLNoZNyZFIK2zgKasYrivYhr0,1822
233
235
  canvas_sdk/v1/data/common.py,sha256=u5W3_7kXAGPe7guEBwRouKp8FMRCx9iZL6ei8QMmtwU,4880
@@ -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=wO_oo3nzb2h8u68v7Te6BCEdww90tNmU2E-bc3D1t3o,8317
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=Go2Mxso7RlMr0uhamPMenKt75RsyJCPnd9BscHxt6OA,435
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=aMdW-xJsjPHfTsYZvAgu_cDIJqrqsdd3onBkJFKy5qM,8662
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.43.0.dist-info/METADATA,sha256=lfvZjIaAiCkRAJ0C3TGnPy-lB7u6yyojOUq4zWBZzMU,4447
300
- canvas-0.43.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
301
- canvas-0.43.0.dist-info/entry_points.txt,sha256=0Vs_9GmTVUNniH6eDBlRPgofmADMV4BES6Ao26M4AbM,47
302
- canvas-0.43.0.dist-info/RECORD,,
303
+ canvas-0.44.1.dist-info/METADATA,sha256=aZwZM-cpatcS9QQ_LA1cNVBY0f5IRPO9BWMRB6TcPeY,4424
304
+ canvas-0.44.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
305
+ canvas-0.44.1.dist-info/entry_points.txt,sha256=0Vs_9GmTVUNniH6eDBlRPgofmADMV4BES6Ao26M4AbM,47
306
+ canvas-0.44.1.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)
@@ -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 keyring."""
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
- host_token_key = f"{host}|token"
147
- token = get_password(host_token_key)
111
+ token = get_token(host)
148
112
 
149
- if token and is_token_valid(host_token_key):
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 not is_token_valid(host_token_key, token_expiration_date):
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
- set_password(host_token_key, new_token)
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=7819
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
- note_type = (
41
- NoteType.objects.values("category", "is_patient_required", "allow_custom_title")
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
- "value",
50
- f"Note type with ID {self.note_type_id} does not exist.",
51
- self.note_type_id,
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 note_type["category"] != NoteTypeCategories.SCHEDULE_EVENT:
57
- errors.append(
58
- self._create_error_detail(
59
- "value",
60
- f"Schedule event note type must be of type: {NoteTypeCategories.SCHEDULE_EVENT}.",
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
- if note_type["is_patient_required"] and not self.patient_id:
66
- errors.append(
67
- self._create_error_detail(
68
- "value",
69
- "Patient ID is required for this note type.",
70
- self.note_type_id,
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
- if not note_type["allow_custom_title"] and self.description:
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
- "Description is not allowed for this note type.",
79
- self.note_type_id,
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 not Patient.objects.filter(id=self.patient_id).exists():
116
- errors.append(
117
- self._create_error_detail(
118
- "value",
119
- "Patient with ID {self.patient_id} does not exist.",
120
- self.patient_id,
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
- category, is_scheduleable = NoteType.objects.values_list("category", "is_scheduleable").get(
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
- f"Appointment note type must be of type, {NoteTypeCategories.ENCOUNTER} but got: {category}.",
132
- self.appointment_note_type_id,
173
+ "Field 'patient_id' cannot be updated for an existing appointment.",
174
+ None,
133
175
  )
134
176
  )
135
177
 
136
- if not is_scheduleable:
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
- "Appointment note type must be scheduleable.",
141
- self.appointment_note_type_id,
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")
@@ -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
- practice_location_id: UUID | str
43
- provider_id: str
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 not PracticeLocation.objects.filter(id=self.practice_location_id).exists():
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
- """Originate a new command in the note body."""
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 creation effects.
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
- if self.external_identifiers:
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
@@ -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
- note_type_category = (
42
- NoteType.objects.values_list("category", flat=True).filter(id=self.note_type_id).first()
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
- if not note_type_category:
46
- errors.append(
47
- self._create_error_detail(
48
- "value",
49
- f"Note type with ID {self.note_type_id} does not exist.",
50
- self.note_type_id,
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
- elif note_type_category in (
54
- NoteTypeCategories.APPOINTMENT,
55
- NoteTypeCategories.SCHEDULE_EVENT,
56
- NoteTypeCategories.MESSAGE,
57
- NoteTypeCategories.LETTER,
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"Visit note type cannot be of type: {note_type_category}.",
63
- self.note_type_id,
92
+ f"Note with ID {self.instance_id} does not exist.",
93
+ self.instance_id,
64
94
  )
65
95
  )
66
96
 
67
- if not Patient.objects.filter(id=self.patient_id).exists():
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",
@@ -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 LineItemTransfer, NewLineItemAdjustment, NewLineItemPayment
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
+ )
@@ -25,17 +25,20 @@ class InstallmentPlan(models.Model):
25
25
 
26
26
  class Meta:
27
27
  managed = False
28
- db_table = "canvas_sdk_data_api_installmentplan_001"
28
+ db_table = "canvas_sdk_data_quality_and_revenue_installmentplan_001"
29
+
30
+ dbid = models.BigIntegerField(primary_key=True)
29
31
 
30
32
  creator = models.ForeignKey("v1.CanvasUser", on_delete=models.CASCADE)
31
33
  patient = models.ForeignKey(
32
34
  "v1.Patient", on_delete=models.CASCADE, related_name="installment_plans"
33
35
  )
34
36
  total_amount = models.DecimalField(max_digits=8, decimal_places=2)
35
- status = models.CharField(choices=InstallmentPlanStatus.choices)
37
+ status = models.CharField(choices=InstallmentPlanStatus.choices, max_length=10)
36
38
  expected_payoff_date = models.DateField()
37
- created_at = models.DateTimeField()
38
- updated_at = models.DateTimeField()
39
+
40
+ created = models.DateTimeField()
41
+ modified = models.DateTimeField()
39
42
 
40
43
 
41
44
  class ClaimQueueColumns(models.TextChoices):
@@ -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", related_name="external_identifiers", on_delete=models.DO_NOTHING, null=True
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",)
@@ -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