canvas 0.51.0__py3-none-any.whl → 0.52.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.51.0.dist-info → canvas-0.52.0.dist-info}/METADATA +1 -1
- {canvas-0.51.0.dist-info → canvas-0.52.0.dist-info}/RECORD +15 -13
- canvas_sdk/effects/task/task.py +35 -4
- canvas_sdk/v1/data/__init__.py +7 -1
- canvas_sdk/v1/data/command.py +3 -0
- canvas_sdk/v1/data/imaging.py +36 -7
- canvas_sdk/v1/data/lab.py +8 -4
- canvas_sdk/v1/data/note.py +9 -1
- canvas_sdk/v1/data/referral.py +102 -0
- canvas_sdk/v1/data/service_provider.py +54 -0
- canvas_sdk/v1/data/staff.py +76 -1
- plugin_runner/allowed-module-imports.json +13 -1
- plugin_runner/sandbox.py +33 -1
- {canvas-0.51.0.dist-info → canvas-0.52.0.dist-info}/WHEEL +0 -0
- {canvas-0.51.0.dist-info → canvas-0.52.0.dist-info}/entry_points.txt +0 -0
|
@@ -192,7 +192,7 @@ canvas_sdk/effects/protocol_card/protocol_card.py,sha256=kYkyl0gUi147VJUm-mgrrtR
|
|
|
192
192
|
canvas_sdk/effects/surescripts/__init__.py,sha256=vD-g2zW8HzGMfxcaure5ZhrQNP72iIDROAsCrzLH3y4,349
|
|
193
193
|
canvas_sdk/effects/surescripts/surescripts_messages.py,sha256=7Cd_93SJVCgrneXadubUSjLQwS0N2lNp2ISH_6a24cs,2746
|
|
194
194
|
canvas_sdk/effects/task/__init__.py,sha256=KlIinP5QbWMkoHGWPTg6Wi-TolPWW7TnEMVZ-h0Z3YE,168
|
|
195
|
-
canvas_sdk/effects/task/task.py,sha256=
|
|
195
|
+
canvas_sdk/effects/task/task.py,sha256=Q_MGMCNbNOJf9xf9upMhwwLNxlozGzDqtUI7gmyIiHw,4350
|
|
196
196
|
canvas_sdk/effects/widgets/__init__.py,sha256=vuMvOTrHNC-6tiIpdJ3Ct59EeBd9RUFZeFN9-vMHr4c,83
|
|
197
197
|
canvas_sdk/effects/widgets/portal_widget.py,sha256=5K8gqbS9OoF6P7oKvsWEOV9ij448VH3JEQu8YcIYwOQ,2441
|
|
198
198
|
canvas_sdk/events/__init__.py,sha256=eIJeJL52DUc1It1Dm6E8TrLzW-p2buKrsxEsy1UhYpU,270
|
|
@@ -224,7 +224,7 @@ canvas_sdk/utils/plugins.py,sha256=xxJUeVJBzfQWhlD1kO7HlFWxDAl01kkvbsf07Z08dOM,1
|
|
|
224
224
|
canvas_sdk/v1/__init__.py,sha256=YYXr5tEQlnwMgTXJbOG963tKSPiUOM4mUkX8wuiqp7U,17
|
|
225
225
|
canvas_sdk/v1/apps.py,sha256=4MwQfQu78oX5bUVlyxYzbt4rZODi0ZesXe_3QOsEWO8,170
|
|
226
226
|
canvas_sdk/v1/models.py,sha256=LqeHZ-Hz3EWyM9vYr9uZ9rBYdMXNuuvJqbmojV8EcEM,224
|
|
227
|
-
canvas_sdk/v1/data/__init__.py,sha256=
|
|
227
|
+
canvas_sdk/v1/data/__init__.py,sha256=z2v3leRHvkPEjULJq2OxlTzURXI7-o9r1z2QokUUaP4,5281
|
|
228
228
|
canvas_sdk/v1/data/allergy_intolerance.py,sha256=KXfU7UqTdRfHUFrP8yncQGMvlRPEt2K6TrC_y7JYgiE,2453
|
|
229
229
|
canvas_sdk/v1/data/appointment.py,sha256=UXvn6wYEgsphhTQaFbFg-UE7Bumy_UR-AQFnsSrL5Dk,2658
|
|
230
230
|
canvas_sdk/v1/data/assessment.py,sha256=vnVoaKWoJGCfaYn6WilxpI5cTgkvM647hFIuTyi_AgY,1663
|
|
@@ -236,7 +236,7 @@ canvas_sdk/v1/data/care_team.py,sha256=vQh2QS8T6JvYL8seMVpVFI9qQLpZRo6NT5lMgnSf0
|
|
|
236
236
|
canvas_sdk/v1/data/charge_description_master.py,sha256=dyxa4Fiwts6wTyoDR-blbPZ_D95pojDLWPrNbmemx2Y,856
|
|
237
237
|
canvas_sdk/v1/data/claim.py,sha256=xCuZ1SSiQ_LxUX-0GoxwKstEZwi87cnqjn8QB6m86S8,11530
|
|
238
238
|
canvas_sdk/v1/data/claim_line_item.py,sha256=5WyTqlWSdQSDKyGWwRv5Dyn24_etyp8Zg_Q8aNgGL6o,5072
|
|
239
|
-
canvas_sdk/v1/data/command.py,sha256=
|
|
239
|
+
canvas_sdk/v1/data/command.py,sha256=G7qtNypiTK274QQExKLbnij4gM71HZMs8f79VFYvIiY,1944
|
|
240
240
|
canvas_sdk/v1/data/common.py,sha256=u5W3_7kXAGPe7guEBwRouKp8FMRCx9iZL6ei8QMmtwU,4880
|
|
241
241
|
canvas_sdk/v1/data/compound_medication.py,sha256=a_m_hlRyZyF_c6FIIEUuX34hSslikd7RueLmjbRC12s,2239
|
|
242
242
|
canvas_sdk/v1/data/condition.py,sha256=u9Vgo8h5TYhof-GYPpezpP3YdN_lfCAw4jCN4QSqpfE,2376
|
|
@@ -245,13 +245,13 @@ canvas_sdk/v1/data/detected_issue.py,sha256=k-tOX6fpzfRf9WmG7neHPEeWrpZUdTiTEqYz
|
|
|
245
245
|
canvas_sdk/v1/data/device.py,sha256=5YnCApQR3ZprGO9zCsSP-4spwo4HVRDSTY3QCNwqYlo,1932
|
|
246
246
|
canvas_sdk/v1/data/discount.py,sha256=Bqa0PcytSdqZ0MJor9EShtRUmNWGcBKtivGCmjk9Pbw,618
|
|
247
247
|
canvas_sdk/v1/data/fields.py,sha256=bI7mPI_3B36ntyoVQ6vuDq8eGLoUIkwZZr14lTC-4To,1104
|
|
248
|
-
canvas_sdk/v1/data/imaging.py,sha256=
|
|
248
|
+
canvas_sdk/v1/data/imaging.py,sha256=N0OkJjmHI3jtv1w8RBhqGZGDGidNpf1ZwT0ypm1o60M,5246
|
|
249
249
|
canvas_sdk/v1/data/invoice.py,sha256=F5j_Hvu-YwqhHFkp6FDui8vx6GumCAgj14YgDvfyeYU,1877
|
|
250
|
-
canvas_sdk/v1/data/lab.py,sha256=
|
|
250
|
+
canvas_sdk/v1/data/lab.py,sha256=8FPnApgcEfZ5f62387EgY6RZd8za2wps5LMzCB0_x6U,12969
|
|
251
251
|
canvas_sdk/v1/data/line_item_transaction.py,sha256=OJA_3DLA1RF0v8jCgQ8SHaoqij_Z97Iatfi5i6CVEis,2406
|
|
252
252
|
canvas_sdk/v1/data/medication.py,sha256=QMAUqqldJeQK9IuaoO5mWFp3IEjbqcqrxkzIgFZ4O0U,2333
|
|
253
253
|
canvas_sdk/v1/data/message.py,sha256=EKc2kkWGuzrgBH3sDUa9jiNpRP7J1ZdWE6BnPhY2gvs,2268
|
|
254
|
-
canvas_sdk/v1/data/note.py,sha256=
|
|
254
|
+
canvas_sdk/v1/data/note.py,sha256=ZZN0CoQbXmXxzo1cb7bpiXBcy7kLpTzZueGgdd6H95Q,9301
|
|
255
255
|
canvas_sdk/v1/data/observation.py,sha256=l1EXGQ8FBBInxQthsmY1Oms9v73ni_J7SAP5M8Hmkvg,3881
|
|
256
256
|
canvas_sdk/v1/data/organization.py,sha256=gt0KZC1hklwWf57vCxs2K0qPnLpMyyNFWNL7-WZ6AqI,1159
|
|
257
257
|
canvas_sdk/v1/data/patient.py,sha256=g1y1Mq38obqYOMv8NaJ_WpzieO1XAjoqhGsCUG8GGBs,9183
|
|
@@ -263,7 +263,9 @@ canvas_sdk/v1/data/practicelocation.py,sha256=O-avErANUE3KwUB_vlRbTe9MywVj2jfBgc
|
|
|
263
263
|
canvas_sdk/v1/data/protocol_override.py,sha256=zSDHngf9Pqz4irYN_BoX5wy-uJc83NFunPBJnUfS1OI,2259
|
|
264
264
|
canvas_sdk/v1/data/questionnaire.py,sha256=4vQFbBjnvfXwvb73Q0ES64bnWME6P-Oy_9E9ioWAOg8,7209
|
|
265
265
|
canvas_sdk/v1/data/reason_for_visit.py,sha256=diXbeGUUc_XBmIDpzubxesc6Nhj5F87zw2oYurPSE3M,669
|
|
266
|
-
canvas_sdk/v1/data/
|
|
266
|
+
canvas_sdk/v1/data/referral.py,sha256=qVPHWqDlJoBzrYsNhP6z2NQ2Wpus_OxW8LwHYpLlM-M,3589
|
|
267
|
+
canvas_sdk/v1/data/service_provider.py,sha256=LQfQ0esg97NpmSYnftsWqYUJyJIQOZKmpZFNnGsxXoQ,1917
|
|
268
|
+
canvas_sdk/v1/data/staff.py,sha256=iVqBXwANp1qYEL39DIpveKbuqd1nw1oSDQ917xw8xgk,8302
|
|
267
269
|
canvas_sdk/v1/data/task.py,sha256=9jSqr7e7ODJTs4hanWsOD-1Rb8gByJOuLta90V9V_hY,3480
|
|
268
270
|
canvas_sdk/v1/data/team.py,sha256=J4s98sS7UuUw9jJCe-_7eikr1dV4SQARDzK5oxmsO_o,2832
|
|
269
271
|
canvas_sdk/v1/data/user.py,sha256=14_yDFXQ38DF7quZLY5pXmL90_0wfYUTZvEkfKEAgAY,477
|
|
@@ -293,7 +295,7 @@ canvas_sdk/views/__init__.py,sha256=YYXr5tEQlnwMgTXJbOG963tKSPiUOM4mUkX8wuiqp7U,
|
|
|
293
295
|
logger/__init__.py,sha256=uMIOf1CDdKSmBKxKwO4CmDpvnJITci76IMXENOwSHEo,85
|
|
294
296
|
logger/logger.py,sha256=l5W5jn3vGQt6TM7OxOpoMy-vC7RNNA2pGnQJJgHeXWA,2068
|
|
295
297
|
plugin_runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
296
|
-
plugin_runner/allowed-module-imports.json,sha256=
|
|
298
|
+
plugin_runner/allowed-module-imports.json,sha256=j6-KDxn9oJLZXfLwXBSg35znDOUPHqluxu5CcOC1bOA,35350
|
|
297
299
|
plugin_runner/authentication.py,sha256=UyyhXajokVFH866dpDhoTlXS9Cg7y0sQltn0_LcwXrY,1131
|
|
298
300
|
plugin_runner/aws_headers.py,sha256=wZ8584E1fTW0CdGxOCnLSF8alH27z-URcUyoc6y6ohg,2782
|
|
299
301
|
plugin_runner/exceptions.py,sha256=YnRZiQVzbU3HrVlmEXLje_np99009YnhTRVHHyBCtqc,433
|
|
@@ -301,14 +303,14 @@ plugin_runner/generate_allowed_imports.py,sha256=LQuVxL_j5n0Sj-KgR4Q8D9mj0xfuDqz
|
|
|
301
303
|
plugin_runner/installation.py,sha256=LLjtnzPk-w4go3UbXnBItJTKz1ajR_5kGQbFXTaWTFU,7693
|
|
302
304
|
plugin_runner/load_all_plugins.py,sha256=4T2gW2YljhIx4xfwf1c0F_8oIbE1ubsLj0ShkHRtlVY,5847
|
|
303
305
|
plugin_runner/plugin_runner.py,sha256=PqtvyUHOSvIHRW97zX_NKEjszJ1GVmETXxkZhzkZoe0,21975
|
|
304
|
-
plugin_runner/sandbox.py,sha256=
|
|
306
|
+
plugin_runner/sandbox.py,sha256=uFVmKLMNkOEo3lZhAcfjxKYHzUpHzaRPxfFnAhlOb50,30189
|
|
305
307
|
protobufs/canvas_generated/messages/effects.proto,sha256=zTCelFeZ2ajsQPadiWFPOfqqpEB2ekSiLrdqmSd6tbY,9316
|
|
306
308
|
protobufs/canvas_generated/messages/events.proto,sha256=XBMsexTQb_4ZnvRF_u4ghEfKpuHN7eU6CJJmHGM-ThU,51507
|
|
307
309
|
protobufs/canvas_generated/messages/plugins.proto,sha256=oNainUPWFYQjgCX7bJEPI9_VnHC5VZduzOqgR4Q7dNM,109
|
|
308
310
|
protobufs/canvas_generated/services/plugin_runner.proto,sha256=doadBKn5k4xAtOgR-q_pEvW4yzxpUaHNOowMG6CL5GY,304
|
|
309
311
|
pubsub/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
310
312
|
pubsub/pubsub.py,sha256=PHIvJ5SD3M-jQSYeGGSj1FuG6CvP6BQffAoGax9Uudk,1423
|
|
311
|
-
canvas-0.
|
|
312
|
-
canvas-0.
|
|
313
|
-
canvas-0.
|
|
314
|
-
canvas-0.
|
|
313
|
+
canvas-0.52.0.dist-info/METADATA,sha256=YFNhTGJ5dcp-vkquOd6o9i3bk7ENveYs-LN6R_7yzdk,4645
|
|
314
|
+
canvas-0.52.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
315
|
+
canvas-0.52.0.dist-info/entry_points.txt,sha256=0Vs_9GmTVUNniH6eDBlRPgofmADMV4BES6Ao26M4AbM,47
|
|
316
|
+
canvas-0.52.0.dist-info/RECORD,,
|
canvas_sdk/effects/task/task.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from enum import Enum
|
|
3
|
-
from typing import Any, cast
|
|
2
|
+
from enum import Enum, StrEnum
|
|
3
|
+
from typing import Any, Self, cast
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from pydantic import model_validator
|
|
4
7
|
|
|
5
8
|
from canvas_sdk.effects.base import EffectType, _BaseEffect
|
|
6
9
|
|
|
@@ -18,10 +21,17 @@ class AddTask(_BaseEffect):
|
|
|
18
21
|
An Effect that will create a Task in Canvas.
|
|
19
22
|
"""
|
|
20
23
|
|
|
24
|
+
class LinkableObjectType(StrEnum):
|
|
25
|
+
"""Types of objects that can be linked to a Task."""
|
|
26
|
+
|
|
27
|
+
REFERRAL = "REFERRAL"
|
|
28
|
+
IMAGING = "IMAGING"
|
|
29
|
+
|
|
21
30
|
class Meta:
|
|
22
31
|
effect_type = EffectType.CREATE_TASK
|
|
23
32
|
apply_required_fields = ("title",)
|
|
24
33
|
|
|
34
|
+
id: str | UUID | None = None
|
|
25
35
|
assignee_id: str | None = None
|
|
26
36
|
team_id: str | None = None
|
|
27
37
|
patient_id: str | None = None
|
|
@@ -29,11 +39,28 @@ class AddTask(_BaseEffect):
|
|
|
29
39
|
due: datetime | None = None
|
|
30
40
|
status: TaskStatus = TaskStatus.OPEN
|
|
31
41
|
labels: list[str] = []
|
|
42
|
+
linked_object_id: str | UUID | None = None
|
|
43
|
+
linked_object_type: LinkableObjectType | None = None
|
|
44
|
+
|
|
45
|
+
@model_validator(mode="after")
|
|
46
|
+
def check_needed_together_fields(self) -> Self:
|
|
47
|
+
"""Check that linked_object_id and linked_object_type are set together."""
|
|
48
|
+
if self.linked_object_id is not None and self.linked_object_type is None:
|
|
49
|
+
raise ValueError(
|
|
50
|
+
"'linked_object_id' must be set with 'linked_object_type' if it is provided"
|
|
51
|
+
)
|
|
52
|
+
if self.linked_object_id is None and self.linked_object_type is not None:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
"'linked_object_type' must be set with 'linked_object_id' if it is provided"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return self
|
|
32
58
|
|
|
33
59
|
@property
|
|
34
60
|
def values(self) -> dict[str, Any]:
|
|
35
61
|
"""The values for Task addition."""
|
|
36
62
|
return {
|
|
63
|
+
"id": str(self.id) if self.id else None,
|
|
37
64
|
"patient": {"id": self.patient_id},
|
|
38
65
|
"due": self.due.isoformat() if self.due else None,
|
|
39
66
|
"assignee": {"id": self.assignee_id},
|
|
@@ -41,6 +68,10 @@ class AddTask(_BaseEffect):
|
|
|
41
68
|
"title": self.title,
|
|
42
69
|
"status": self.status.value,
|
|
43
70
|
"labels": self.labels,
|
|
71
|
+
"linked_object": {
|
|
72
|
+
"id": str(self.linked_object_id) if self.linked_object_id else None,
|
|
73
|
+
"type": self.linked_object_type.value if self.linked_object_type else None,
|
|
74
|
+
},
|
|
44
75
|
}
|
|
45
76
|
|
|
46
77
|
|
|
@@ -57,12 +88,12 @@ class AddTaskComment(_BaseEffect):
|
|
|
57
88
|
)
|
|
58
89
|
|
|
59
90
|
body: str | None = None
|
|
60
|
-
task_id: str | None = None
|
|
91
|
+
task_id: str | UUID | None = None
|
|
61
92
|
|
|
62
93
|
@property
|
|
63
94
|
def values(self) -> dict[str, Any]:
|
|
64
95
|
"""The values for adding a task comment."""
|
|
65
|
-
return {"task": {"id": self.task_id}, "body": self.body}
|
|
96
|
+
return {"task": {"id": str(self.task_id) if self.task_id else None}, "body": self.body}
|
|
66
97
|
|
|
67
98
|
|
|
68
99
|
class UpdateTask(_BaseEffect):
|
canvas_sdk/v1/data/__init__.py
CHANGED
|
@@ -80,7 +80,9 @@ from .questionnaire import (
|
|
|
80
80
|
ResponseOptionSet,
|
|
81
81
|
)
|
|
82
82
|
from .reason_for_visit import ReasonForVisitSettingCoding
|
|
83
|
-
from .
|
|
83
|
+
from .referral import Referral, ReferralReport
|
|
84
|
+
from .service_provider import ServiceProvider
|
|
85
|
+
from .staff import Staff, StaffAddress, StaffContactPoint, StaffPhoto, StaffRole
|
|
84
86
|
from .task import Task, TaskComment, TaskLabel, TaskTaskLabel
|
|
85
87
|
from .team import Team, TeamContactPoint
|
|
86
88
|
from .user import CanvasUser
|
|
@@ -172,11 +174,15 @@ __all__ = __exports__ = (
|
|
|
172
174
|
"Questionnaire",
|
|
173
175
|
"QuestionnaireQuestionMap",
|
|
174
176
|
"ReasonForVisitSettingCoding",
|
|
177
|
+
"Referral",
|
|
178
|
+
"ReferralReport",
|
|
175
179
|
"ResponseOption",
|
|
176
180
|
"ResponseOptionSet",
|
|
181
|
+
"ServiceProvider",
|
|
177
182
|
"Staff",
|
|
178
183
|
"StaffAddress",
|
|
179
184
|
"StaffPhoto",
|
|
185
|
+
"StaffRole",
|
|
180
186
|
"StaffContactPoint",
|
|
181
187
|
"Task",
|
|
182
188
|
"TaskComment",
|
canvas_sdk/v1/data/command.py
CHANGED
|
@@ -40,6 +40,9 @@ class Command(IdentifiableModel):
|
|
|
40
40
|
"""
|
|
41
41
|
# TODO: Is the anchor object type enough here, or do we need a mapping? The home-app model
|
|
42
42
|
# names might not exactly match the plugins model names.
|
|
43
|
+
if not self.anchor_object_type or not self.anchor_object_dbid:
|
|
44
|
+
return None
|
|
45
|
+
|
|
43
46
|
anchor_model = apps.get_model(
|
|
44
47
|
app_label=self._meta.app_label, model_name=self.anchor_object_type
|
|
45
48
|
)
|
canvas_sdk/v1/data/imaging.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
1
3
|
from django.db import models
|
|
2
4
|
|
|
3
5
|
from canvas_sdk.v1.data.base import IdentifiableModel
|
|
@@ -7,6 +9,7 @@ from canvas_sdk.v1.data.common import (
|
|
|
7
9
|
ReviewPatientCommunicationMethod,
|
|
8
10
|
ReviewStatus,
|
|
9
11
|
)
|
|
12
|
+
from canvas_sdk.v1.data.task import Task
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class ImagingOrder(IdentifiableModel):
|
|
@@ -30,20 +33,39 @@ class ImagingOrder(IdentifiableModel):
|
|
|
30
33
|
patient = models.ForeignKey(
|
|
31
34
|
"v1.Patient", on_delete=models.DO_NOTHING, related_name="imaging_orders", null=True
|
|
32
35
|
)
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
note = models.ForeignKey(
|
|
37
|
+
"v1.Note", on_delete=models.DO_NOTHING, related_name="imaging_orders", null=True
|
|
38
|
+
)
|
|
35
39
|
imaging = models.CharField(max_length=1024)
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
imaging_center = models.ForeignKey(
|
|
41
|
+
"v1.ServiceProvider", on_delete=models.DO_NOTHING, related_name="imaging_orders", null=True
|
|
42
|
+
)
|
|
38
43
|
note_to_radiologist = models.CharField(max_length=1024)
|
|
39
44
|
internal_comment = models.CharField(max_length=1024)
|
|
40
45
|
status = models.CharField(choices=OrderStatus.choices, max_length=30)
|
|
41
46
|
date_time_ordered = models.DateTimeField()
|
|
42
47
|
priority = models.CharField(max_length=255)
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
ordering_provider = models.ForeignKey(
|
|
49
|
+
"v1.Staff", on_delete=models.DO_NOTHING, related_name="imaging_orders", null=True
|
|
50
|
+
)
|
|
45
51
|
delegated = models.BooleanField(default=False)
|
|
46
52
|
|
|
53
|
+
task_ids = models.CharField(max_length=1024)
|
|
54
|
+
|
|
55
|
+
def get_task_objects(self) -> "models.QuerySet[Task]":
|
|
56
|
+
"""Convert task IDs to Task objects."""
|
|
57
|
+
if self.task_ids:
|
|
58
|
+
task_ids = (
|
|
59
|
+
json.loads(self.task_ids) if isinstance(self.task_ids, str) else self.task_ids
|
|
60
|
+
)
|
|
61
|
+
return Task.objects.filter(id__in=task_ids)
|
|
62
|
+
return Task.objects.none()
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def task_list(self) -> list[Task]:
|
|
66
|
+
"""Convenience property to get task objects."""
|
|
67
|
+
return list(self.get_task_objects())
|
|
68
|
+
|
|
47
69
|
|
|
48
70
|
class ImagingReview(IdentifiableModel):
|
|
49
71
|
"""Model to read ImagingReview data."""
|
|
@@ -97,7 +119,14 @@ class ImagingReport(IdentifiableModel):
|
|
|
97
119
|
patient = models.ForeignKey(
|
|
98
120
|
"v1.Patient", on_delete=models.DO_NOTHING, related_name="imaging_results", null=True
|
|
99
121
|
)
|
|
100
|
-
order = models.ForeignKey(
|
|
122
|
+
order = models.ForeignKey(
|
|
123
|
+
ImagingOrder,
|
|
124
|
+
on_delete=models.DO_NOTHING,
|
|
125
|
+
related_name="results",
|
|
126
|
+
default=None,
|
|
127
|
+
blank=True,
|
|
128
|
+
null=True,
|
|
129
|
+
)
|
|
101
130
|
source = models.CharField(choices=ImagingReportSource.choices, max_length=18)
|
|
102
131
|
name = models.CharField(max_length=255)
|
|
103
132
|
result_date = models.DateField()
|
canvas_sdk/v1/data/lab.py
CHANGED
|
@@ -208,8 +208,8 @@ class LabOrder(IdentifiableModel):
|
|
|
208
208
|
"v1.Patient", on_delete=models.DO_NOTHING, related_name="lab_orders", null=True
|
|
209
209
|
)
|
|
210
210
|
ontology_lab_partner = models.CharField(max_length=128)
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
|
|
212
|
+
note = models.ForeignKey("v1.Note", on_delete=models.DO_NOTHING, null=True)
|
|
213
213
|
comment = models.CharField(max_length=128)
|
|
214
214
|
requisition_number = models.CharField(max_length=32)
|
|
215
215
|
is_patient_bill = models.BooleanField(null=True)
|
|
@@ -224,8 +224,10 @@ class LabOrder(IdentifiableModel):
|
|
|
224
224
|
)
|
|
225
225
|
courtesy_copy_number = models.CharField(max_length=32)
|
|
226
226
|
courtesy_copy_text = models.CharField(max_length=64)
|
|
227
|
-
ordering_provider = models.ForeignKey(
|
|
228
|
-
|
|
227
|
+
ordering_provider = models.ForeignKey(
|
|
228
|
+
Staff, on_delete=models.DO_NOTHING, related_name="lab_orders", null=True
|
|
229
|
+
)
|
|
230
|
+
parent_order = models.ForeignKey("v1.LabOrder", on_delete=models.DO_NOTHING, null=True)
|
|
229
231
|
healthgorilla_id = models.CharField(max_length=40)
|
|
230
232
|
manual_processing_status = models.CharField(
|
|
231
233
|
choices=ManualProcessingStatus.choices, null=True, max_length=16
|
|
@@ -233,6 +235,8 @@ class LabOrder(IdentifiableModel):
|
|
|
233
235
|
manual_processing_comment = models.TextField(null=True)
|
|
234
236
|
labcorp_abn_url = models.URLField()
|
|
235
237
|
|
|
238
|
+
reports = models.ManyToManyField("v1.LabReport", through="v1.LabTest")
|
|
239
|
+
|
|
236
240
|
|
|
237
241
|
class LabOrderReason(Model):
|
|
238
242
|
"""A class representing a lab order reason."""
|
canvas_sdk/v1/data/note.py
CHANGED
|
@@ -2,6 +2,7 @@ from django.contrib.postgres.fields import ArrayField
|
|
|
2
2
|
from django.db import models
|
|
3
3
|
|
|
4
4
|
from canvas_sdk.v1.data.base import IdentifiableModel
|
|
5
|
+
from canvas_sdk.v1.data.claim import Claim
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class NoteTypeCategories(models.TextChoices):
|
|
@@ -183,6 +184,13 @@ class Note(IdentifiableModel):
|
|
|
183
184
|
datetime_of_service = models.DateTimeField()
|
|
184
185
|
place_of_service = models.CharField(max_length=255)
|
|
185
186
|
|
|
187
|
+
def get_claim(self) -> Claim | None:
|
|
188
|
+
"""
|
|
189
|
+
Get the most recent claim for this note.
|
|
190
|
+
Returns the latest claim ordered by created date, or None if no claims exist.
|
|
191
|
+
"""
|
|
192
|
+
return self.claims.order_by("-created").first()
|
|
193
|
+
|
|
186
194
|
|
|
187
195
|
class NoteStateChangeEvent(IdentifiableModel):
|
|
188
196
|
"""NoteStateChangeEvent."""
|
|
@@ -209,7 +217,7 @@ class CurrentNoteStateEvent(IdentifiableModel):
|
|
|
209
217
|
db_table = "canvas_sdk_data_current_note_state_001"
|
|
210
218
|
|
|
211
219
|
state = models.CharField(choices=NoteStates.choices, max_length=3)
|
|
212
|
-
note = models.ForeignKey("v1.Note", on_delete=models.DO_NOTHING, related_name="
|
|
220
|
+
note = models.ForeignKey("v1.Note", on_delete=models.DO_NOTHING, related_name="current_state")
|
|
213
221
|
|
|
214
222
|
def editable(self) -> bool:
|
|
215
223
|
"""Returns a boolean to indicate if the related note can be edited."""
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from django.db import models
|
|
4
|
+
|
|
5
|
+
from canvas_sdk.v1.data.base import IdentifiableModel
|
|
6
|
+
from canvas_sdk.v1.data.task import Task
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Referral(IdentifiableModel):
|
|
10
|
+
"""Referral."""
|
|
11
|
+
|
|
12
|
+
class Meta:
|
|
13
|
+
db_table = "canvas_sdk_data_api_referral_001"
|
|
14
|
+
|
|
15
|
+
created = models.DateTimeField()
|
|
16
|
+
modified = models.DateTimeField()
|
|
17
|
+
originator = models.ForeignKey(
|
|
18
|
+
"v1.CanvasUser", on_delete=models.DO_NOTHING, null=True, related_name="+"
|
|
19
|
+
)
|
|
20
|
+
deleted = models.BooleanField()
|
|
21
|
+
committer = models.ForeignKey(
|
|
22
|
+
"v1.CanvasUser", on_delete=models.DO_NOTHING, null=True, related_name="+"
|
|
23
|
+
)
|
|
24
|
+
entered_in_error = models.ForeignKey(
|
|
25
|
+
"v1.CanvasUser", on_delete=models.DO_NOTHING, null=True, related_name="+"
|
|
26
|
+
)
|
|
27
|
+
patient = models.ForeignKey("v1.Patient", on_delete=models.DO_NOTHING)
|
|
28
|
+
note = models.ForeignKey("v1.Note", on_delete=models.DO_NOTHING)
|
|
29
|
+
service_provider = models.ForeignKey(
|
|
30
|
+
"v1.ServiceProvider",
|
|
31
|
+
on_delete=models.CASCADE,
|
|
32
|
+
related_name="referrals",
|
|
33
|
+
null=True,
|
|
34
|
+
blank=True,
|
|
35
|
+
)
|
|
36
|
+
assessments = models.ManyToManyField("v1.Assessment", related_name="referrals", blank=True)
|
|
37
|
+
clinical_question = models.CharField(max_length=50)
|
|
38
|
+
priority = models.CharField(max_length=255)
|
|
39
|
+
include_visit_note = models.BooleanField()
|
|
40
|
+
notes = models.TextField()
|
|
41
|
+
date_referred = models.DateTimeField()
|
|
42
|
+
forwarded = models.BooleanField()
|
|
43
|
+
internal_comment = models.TextField()
|
|
44
|
+
internal_task_comment = models.OneToOneField(
|
|
45
|
+
"v1.TaskComment", on_delete=models.SET_NULL, null=True, related_name="referral"
|
|
46
|
+
)
|
|
47
|
+
ignored = models.BooleanField()
|
|
48
|
+
|
|
49
|
+
task_ids = models.CharField(max_length=1024)
|
|
50
|
+
|
|
51
|
+
def get_task_objects(self) -> "models.QuerySet[Task]":
|
|
52
|
+
"""Convert task IDs to Task objects."""
|
|
53
|
+
if self.task_ids:
|
|
54
|
+
task_ids = (
|
|
55
|
+
json.loads(self.task_ids) if isinstance(self.task_ids, str) else self.task_ids
|
|
56
|
+
)
|
|
57
|
+
return Task.objects.filter(id__in=task_ids)
|
|
58
|
+
return Task.objects.none()
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def task_list(self) -> list[Task]:
|
|
62
|
+
"""Convenience property to get task objects."""
|
|
63
|
+
return list(self.get_task_objects())
|
|
64
|
+
|
|
65
|
+
def __str__(self) -> str:
|
|
66
|
+
return f"Referral {self.id}"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ReferralReport(IdentifiableModel):
|
|
70
|
+
"""ReferralReport."""
|
|
71
|
+
|
|
72
|
+
class Meta:
|
|
73
|
+
db_table = "canvas_sdk_data_api_referralreport_001"
|
|
74
|
+
|
|
75
|
+
created = models.DateTimeField(auto_now_add=True)
|
|
76
|
+
modified = models.DateTimeField(auto_now=True)
|
|
77
|
+
originator = models.ForeignKey(
|
|
78
|
+
"v1.CanvasUser", on_delete=models.DO_NOTHING, null=True, related_name="+"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
review_mode = models.CharField(max_length=2)
|
|
82
|
+
assigned_by = models.ForeignKey(
|
|
83
|
+
"v1.CanvasUser", on_delete=models.DO_NOTHING, null=True, related_name="+"
|
|
84
|
+
)
|
|
85
|
+
junked = models.BooleanField()
|
|
86
|
+
requires_signature = models.BooleanField()
|
|
87
|
+
assigned_date = models.DateTimeField(null=True)
|
|
88
|
+
team_assigned_date = models.DateTimeField(null=True)
|
|
89
|
+
team = models.ForeignKey("v1.Team", on_delete=models.DO_NOTHING, null=True)
|
|
90
|
+
patient = models.ForeignKey(
|
|
91
|
+
"v1.Patient", on_delete=models.DO_NOTHING, related_name="referral_reports"
|
|
92
|
+
)
|
|
93
|
+
referral = models.ForeignKey(
|
|
94
|
+
Referral, on_delete=models.DO_NOTHING, related_name="reports", null=True
|
|
95
|
+
)
|
|
96
|
+
specialty = models.CharField(max_length=250)
|
|
97
|
+
original_date = models.DateField(null=True)
|
|
98
|
+
comment = models.TextField()
|
|
99
|
+
priority = models.BooleanField(default=False)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
__exports__ = ("Referral", "ReferralReport")
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from functools import cached_property
|
|
2
|
+
|
|
3
|
+
from django.db import models
|
|
4
|
+
|
|
5
|
+
from canvas_sdk.v1.data.base import IdentifiableModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ServiceProvider(IdentifiableModel):
|
|
9
|
+
"""ServiceProvider."""
|
|
10
|
+
|
|
11
|
+
class Meta:
|
|
12
|
+
db_table = "canvas_sdk_data_data_integration_serviceprovider_001"
|
|
13
|
+
|
|
14
|
+
first_name = models.CharField(max_length=512)
|
|
15
|
+
# organizations won't have a last name
|
|
16
|
+
last_name = models.CharField(max_length=512, default="", blank=True)
|
|
17
|
+
business_fax = models.CharField(max_length=512, null=True, blank=True)
|
|
18
|
+
business_phone = models.CharField(max_length=512, null=True, blank=True)
|
|
19
|
+
business_address = models.CharField(max_length=512, null=True, blank=True)
|
|
20
|
+
specialty = models.CharField(max_length=512)
|
|
21
|
+
practice_name = models.CharField(max_length=512, null=True, blank=True)
|
|
22
|
+
notes = models.TextField(default="", null=True, blank=True)
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def full_name(self) -> str:
|
|
26
|
+
"""Service provider full name."""
|
|
27
|
+
return f"{self.first_name} {self.last_name}"
|
|
28
|
+
|
|
29
|
+
@cached_property
|
|
30
|
+
def full_name_and_specialty(self) -> str:
|
|
31
|
+
"""Service provider full name and specialty."""
|
|
32
|
+
name_components: list[str] = []
|
|
33
|
+
|
|
34
|
+
# Note 1: if firstName is (TBD) then insert at the end instead of the beginning
|
|
35
|
+
if self.first_name != "(TBD)":
|
|
36
|
+
name_components.append(self.first_name)
|
|
37
|
+
|
|
38
|
+
if self.first_name != self.last_name:
|
|
39
|
+
name_components.append(self.last_name)
|
|
40
|
+
|
|
41
|
+
if self.practice_name and self.practice_name != "(TBD)":
|
|
42
|
+
name_components.append(f"({self.practice_name}),")
|
|
43
|
+
|
|
44
|
+
if self.specialty not in [self.first_name, self.last_name, self.practice_name]:
|
|
45
|
+
name_components.append(self.specialty)
|
|
46
|
+
|
|
47
|
+
# see Note 1
|
|
48
|
+
if self.first_name == "(TBD)":
|
|
49
|
+
name_components.append(self.first_name)
|
|
50
|
+
|
|
51
|
+
return " ".join(name_components)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__exports__ = ("ServiceProvider",)
|
canvas_sdk/v1/data/staff.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from functools import cached_property
|
|
2
|
+
|
|
1
3
|
from django.contrib.postgres.fields import ArrayField
|
|
2
4
|
from django.db import models
|
|
5
|
+
from django.db.models.enums import TextChoices
|
|
3
6
|
from timezone_utils.fields import TimeZoneField
|
|
4
7
|
|
|
5
8
|
from canvas_sdk.v1.data.base import IdentifiableModel, Model
|
|
@@ -82,6 +85,47 @@ class Staff(Model):
|
|
|
82
85
|
photo = self.photos.first()
|
|
83
86
|
return photo.url if photo else "https://d3hn0m4rbsz438.cloudfront.net/avatar1.png"
|
|
84
87
|
|
|
88
|
+
@cached_property
|
|
89
|
+
def full_name(self) -> str:
|
|
90
|
+
"""Return Staff's first + last name."""
|
|
91
|
+
return f"{self.first_name} {self.last_name}"
|
|
92
|
+
|
|
93
|
+
@cached_property
|
|
94
|
+
def top_clinical_role(self) -> "StaffRole | None":
|
|
95
|
+
"""Returns the topmost clinical role to assist in determining privilege levels.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
StaffRole | None: the topmost clinical role of the staff member.
|
|
99
|
+
"""
|
|
100
|
+
roles = [
|
|
101
|
+
role
|
|
102
|
+
for role in self.roles.all()
|
|
103
|
+
if role.domain in StaffRole.RoleDomain.clinical_domains()
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
if not roles:
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
return roles[0]
|
|
110
|
+
|
|
111
|
+
@cached_property
|
|
112
|
+
def top_role_abbreviation(self) -> str | None:
|
|
113
|
+
"""Returns the abbreviation string for the topmost role that the Staff object has.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Optional[str]: The abbreviation for the topmost role, if available.
|
|
117
|
+
"""
|
|
118
|
+
return self.top_clinical_role.public_abbreviation if self.top_clinical_role else None
|
|
119
|
+
|
|
120
|
+
@cached_property
|
|
121
|
+
def credentialed_name(self) -> str:
|
|
122
|
+
"""Returns the full name of the staff member, suffixed with their topmost credential.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
str: The credentialed full name of the staff member.
|
|
126
|
+
"""
|
|
127
|
+
return " ".join(filter(bool, [self.full_name, self.top_role_abbreviation or ""]))
|
|
128
|
+
|
|
85
129
|
|
|
86
130
|
class StaffContactPoint(IdentifiableModel):
|
|
87
131
|
"""StaffContactPoint."""
|
|
@@ -136,4 +180,35 @@ class StaffPhoto(Model):
|
|
|
136
180
|
title = models.CharField(max_length=255, blank=True, default="")
|
|
137
181
|
|
|
138
182
|
|
|
139
|
-
|
|
183
|
+
class StaffRole(Model):
|
|
184
|
+
"""StaffRole."""
|
|
185
|
+
|
|
186
|
+
class Meta:
|
|
187
|
+
db_table = "canvas_sdk_data_api_staffrole_001"
|
|
188
|
+
|
|
189
|
+
class RoleDomain(TextChoices):
|
|
190
|
+
CLINICAL = "CLI", "Clinical"
|
|
191
|
+
ADMINISTRATIVE = "ADM", "Administrative"
|
|
192
|
+
HYBRID = "HYB", "Hybrid"
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def clinical_domains() -> list["StaffRole.RoleDomain"]:
|
|
196
|
+
"""Return a list of clinical role domains."""
|
|
197
|
+
return [StaffRole.RoleDomain.CLINICAL, StaffRole.RoleDomain.HYBRID]
|
|
198
|
+
|
|
199
|
+
class RoleType(TextChoices):
|
|
200
|
+
NON_LICENSED = "NON-LICENSED", "Non-Licensed"
|
|
201
|
+
LICENSED = "LICENSED", "Licensed"
|
|
202
|
+
PROVIDER = "PROVIDER", "Provider"
|
|
203
|
+
|
|
204
|
+
staff = models.ForeignKey(Staff, on_delete=models.CASCADE, related_name="roles")
|
|
205
|
+
internal_code = models.CharField(max_length=10)
|
|
206
|
+
public_abbreviation = models.CharField(max_length=10, default="", blank=True)
|
|
207
|
+
domain = models.CharField(max_length=3, choices=RoleDomain.choices, db_index=True)
|
|
208
|
+
name = models.CharField(max_length=50)
|
|
209
|
+
domain_privilege_level = models.IntegerField(default=0)
|
|
210
|
+
permissions = models.JSONField(default=dict, blank=True, null=True)
|
|
211
|
+
role_type = models.CharField(max_length=50, choices=RoleType.choices, blank=True)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
__exports__ = ("Staff", "StaffContactPoint", "StaffAddress", "StaffPhoto", "StaffRole")
|
|
@@ -563,12 +563,16 @@
|
|
|
563
563
|
"Questionnaire",
|
|
564
564
|
"QuestionnaireQuestionMap",
|
|
565
565
|
"ReasonForVisitSettingCoding",
|
|
566
|
+
"Referral",
|
|
567
|
+
"ReferralReport",
|
|
566
568
|
"ResponseOption",
|
|
567
569
|
"ResponseOptionSet",
|
|
570
|
+
"ServiceProvider",
|
|
568
571
|
"Staff",
|
|
569
572
|
"StaffAddress",
|
|
570
573
|
"StaffContactPoint",
|
|
571
574
|
"StaffPhoto",
|
|
575
|
+
"StaffRole",
|
|
572
576
|
"Task",
|
|
573
577
|
"TaskComment",
|
|
574
578
|
"TaskLabel",
|
|
@@ -796,11 +800,19 @@
|
|
|
796
800
|
"canvas_sdk.v1.data.reason_for_visit": [
|
|
797
801
|
"ReasonForVisitSettingCoding"
|
|
798
802
|
],
|
|
803
|
+
"canvas_sdk.v1.data.referral": [
|
|
804
|
+
"Referral",
|
|
805
|
+
"ReferralReport"
|
|
806
|
+
],
|
|
807
|
+
"canvas_sdk.v1.data.service_provider": [
|
|
808
|
+
"ServiceProvider"
|
|
809
|
+
],
|
|
799
810
|
"canvas_sdk.v1.data.staff": [
|
|
800
811
|
"Staff",
|
|
801
812
|
"StaffAddress",
|
|
802
813
|
"StaffContactPoint",
|
|
803
|
-
"StaffPhoto"
|
|
814
|
+
"StaffPhoto",
|
|
815
|
+
"StaffRole"
|
|
804
816
|
],
|
|
805
817
|
"canvas_sdk.v1.data.task": [
|
|
806
818
|
"EventType",
|
plugin_runner/sandbox.py
CHANGED
|
@@ -4,6 +4,7 @@ import ast
|
|
|
4
4
|
import builtins
|
|
5
5
|
import importlib
|
|
6
6
|
import json
|
|
7
|
+
import operator
|
|
7
8
|
import sys
|
|
8
9
|
import types
|
|
9
10
|
from _ast import AnnAssign
|
|
@@ -196,6 +197,8 @@ THIRD_PARTY_MODULES = {
|
|
|
196
197
|
"BigIntegerField",
|
|
197
198
|
"Case",
|
|
198
199
|
"CharField",
|
|
200
|
+
"Count",
|
|
201
|
+
"F",
|
|
199
202
|
"IntegerField",
|
|
200
203
|
"Model", # remove when hyperscribe no longer needs it
|
|
201
204
|
"Q",
|
|
@@ -264,6 +267,34 @@ def _unrestricted(_ob: Any, *args: Any, **kwargs: Any) -> Any:
|
|
|
264
267
|
return _ob
|
|
265
268
|
|
|
266
269
|
|
|
270
|
+
def _inplacevar(op: Any, val: Any, expr: Any) -> Any:
|
|
271
|
+
"""
|
|
272
|
+
Apply the specified operation to the value and expression.
|
|
273
|
+
|
|
274
|
+
NOTE: Does not yet support += concatenation for sequences.
|
|
275
|
+
"""
|
|
276
|
+
ops = {
|
|
277
|
+
"-=": operator.isub,
|
|
278
|
+
"@=": operator.imatmul,
|
|
279
|
+
"**=": operator.ipow,
|
|
280
|
+
"*=": operator.imul,
|
|
281
|
+
"//=": operator.ifloordiv,
|
|
282
|
+
"/=": operator.itruediv,
|
|
283
|
+
"&=": operator.iand,
|
|
284
|
+
"%=": operator.imod,
|
|
285
|
+
"^=": operator.ixor,
|
|
286
|
+
"+=": operator.iadd,
|
|
287
|
+
"<<=": operator.ilshift,
|
|
288
|
+
">>=": operator.irshift,
|
|
289
|
+
"|=": operator.ior,
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if op not in ops:
|
|
293
|
+
raise ValueError(f"Invalid inplace operation: {op}")
|
|
294
|
+
|
|
295
|
+
return ops[op](val, expr)
|
|
296
|
+
|
|
297
|
+
|
|
267
298
|
def _apply(_ob: Any, *args: Any, **kwargs: Any) -> Any:
|
|
268
299
|
"""Call the bound method with args, support calling super().__init__()."""
|
|
269
300
|
return _ob(*args, **kwargs)
|
|
@@ -614,6 +645,7 @@ class Sandbox:
|
|
|
614
645
|
"dict": builtins.dict,
|
|
615
646
|
"enumerate": builtins.enumerate,
|
|
616
647
|
"filter": builtins.filter,
|
|
648
|
+
"getattr": self._safe_getattr,
|
|
617
649
|
"hasattr": builtins.hasattr,
|
|
618
650
|
"iter": builtins.iter,
|
|
619
651
|
"list": builtins.list,
|
|
@@ -634,7 +666,7 @@ class Sandbox:
|
|
|
634
666
|
"_getattr_": self._safe_getattr,
|
|
635
667
|
"_getitem_": self._safe_getitem,
|
|
636
668
|
"_getiter_": _unrestricted,
|
|
637
|
-
"_inplacevar_":
|
|
669
|
+
"_inplacevar_": _inplacevar,
|
|
638
670
|
"_iter_unpack_sequence_": guarded_iter_unpack_sequence,
|
|
639
671
|
"_print_": PrintCollector,
|
|
640
672
|
"_unpack_sequence_": guarded_unpack_sequence,
|
|
File without changes
|
|
File without changes
|