canvas 0.6.0__py3-none-any.whl → 0.7.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.
- {canvas-0.6.0.dist-info → canvas-0.7.1.dist-info}/METADATA +3 -3
- {canvas-0.6.0.dist-info → canvas-0.7.1.dist-info}/RECORD +25 -24
- canvas_generated/messages/events_pb2.py +2 -2
- canvas_generated/messages/events_pb2.pyi +4 -0
- canvas_sdk/effects/protocol_card/protocol_card.py +2 -0
- canvas_sdk/effects/protocol_card/tests.py +2 -2
- canvas_sdk/effects/task/task.py +99 -0
- canvas_sdk/protocols/base.py +1 -11
- canvas_sdk/protocols/clinical_quality_measure.py +57 -1
- canvas_sdk/protocols/timeframe.py +39 -0
- canvas_sdk/v1/data/__init__.py +3 -0
- canvas_sdk/v1/data/base.py +87 -7
- canvas_sdk/v1/data/billing.py +59 -0
- canvas_sdk/v1/data/common.py +58 -0
- canvas_sdk/v1/data/condition.py +13 -5
- canvas_sdk/v1/data/medication.py +11 -3
- canvas_sdk/v1/data/note.py +161 -0
- canvas_sdk/v1/data/patient.py +18 -0
- canvas_sdk/v1/data/staff.py +65 -0
- canvas_sdk/v1/data/task.py +112 -0
- canvas_sdk/value_set/custom.py +984 -0
- canvas_sdk/value_set/value_set.py +4 -1
- plugin_runner/sandbox.py +2 -0
- canvas_sdk/data/__init__.py +0 -1
- canvas_sdk/data/base.py +0 -26
- canvas_sdk/data/client.py +0 -82
- canvas_sdk/data/patient.py +0 -8
- canvas_sdk/data/staff.py +0 -8
- canvas_sdk/data/task.py +0 -67
- {canvas-0.6.0.dist-info → canvas-0.7.1.dist-info}/WHEEL +0 -0
- {canvas-0.6.0.dist-info → canvas-0.7.1.dist-info}/entry_points.txt +0 -0
canvas_sdk/v1/data/base.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
1
2
|
from collections.abc import Container
|
|
2
|
-
from typing import TYPE_CHECKING, Self, Type, cast
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Protocol, Self, Type, cast
|
|
3
4
|
|
|
4
5
|
from django.db import models
|
|
5
6
|
from django.db.models import Q
|
|
6
7
|
|
|
7
8
|
if TYPE_CHECKING:
|
|
9
|
+
from canvas_sdk.protocols.timeframe import Timeframe
|
|
8
10
|
from canvas_sdk.value_set.value_set import ValueSet
|
|
9
11
|
|
|
10
12
|
|
|
@@ -29,10 +31,40 @@ class CommittableQuerySet(models.QuerySet):
|
|
|
29
31
|
return self.filter(patient__id=patient_id)
|
|
30
32
|
|
|
31
33
|
|
|
32
|
-
class
|
|
33
|
-
"""A QuerySet
|
|
34
|
+
class BaseQuerySet(models.QuerySet):
|
|
35
|
+
"""A base QuerySet inherited from Django's model.Queryset."""
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class QuerySetProtocol(Protocol):
|
|
41
|
+
"""A typing protocol for use in mixins into models.QuerySet-inherited classes."""
|
|
42
|
+
|
|
43
|
+
def filter(self, *args: Any, **kwargs: Any) -> models.QuerySet[Any]:
|
|
44
|
+
"""Django's models.QuerySet filter method."""
|
|
45
|
+
...
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ValueSetLookupQuerySetProtocol(QuerySetProtocol):
|
|
49
|
+
"""A typing protocol for use in mixins using value set lookup methods."""
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def codings(value_set: Type["ValueSet"]) -> tuple[tuple[str, set[str]]]:
|
|
54
|
+
"""A protocol method for defining codings."""
|
|
55
|
+
raise NotImplementedError
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def q_object(system: str, codes: Container[str]) -> Q:
|
|
60
|
+
"""A protocol method for defining Q objects for value set lookups."""
|
|
61
|
+
raise NotImplementedError
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ValueSetLookupQuerySetMixin(ValueSetLookupQuerySetProtocol):
|
|
65
|
+
"""A QuerySet mixin that can filter objects based on a ValueSet."""
|
|
66
|
+
|
|
67
|
+
def find(self, value_set: Type["ValueSet"]) -> models.QuerySet[Any]:
|
|
36
68
|
"""
|
|
37
69
|
Filters conditions, medications, etc. to those found in the inherited ValueSet class that is passed.
|
|
38
70
|
|
|
@@ -54,7 +86,7 @@ class ValueSetLookupQuerySet(CommittableQuerySet):
|
|
|
54
86
|
@staticmethod
|
|
55
87
|
def codings(value_set: Type["ValueSet"]) -> tuple[tuple[str, set[str]]]:
|
|
56
88
|
"""Provide a sequence of tuples where each tuple is a code system URL and a set of codes."""
|
|
57
|
-
values_dict = value_set.values
|
|
89
|
+
values_dict = cast(dict, value_set.values)
|
|
58
90
|
return cast(
|
|
59
91
|
tuple[tuple[str, set[str]]],
|
|
60
92
|
tuple(
|
|
@@ -72,7 +104,7 @@ class ValueSetLookupQuerySet(CommittableQuerySet):
|
|
|
72
104
|
return Q(codings__system=system, codings__code__in=codes)
|
|
73
105
|
|
|
74
106
|
|
|
75
|
-
class
|
|
107
|
+
class ValueSetLookupByNameQuerySetMixin(ValueSetLookupQuerySetMixin):
|
|
76
108
|
"""
|
|
77
109
|
QuerySet for ValueSet lookups using code system name rather than URL.
|
|
78
110
|
|
|
@@ -85,7 +117,7 @@ class ValueSetLookupByNameQuerySet(ValueSetLookupQuerySet):
|
|
|
85
117
|
"""
|
|
86
118
|
Provide a sequence of tuples where each tuple is a code system name and a set of codes.
|
|
87
119
|
"""
|
|
88
|
-
values_dict = value_set.values
|
|
120
|
+
values_dict = cast(dict, value_set.values)
|
|
89
121
|
return cast(
|
|
90
122
|
tuple[tuple[str, set[str]]],
|
|
91
123
|
tuple(
|
|
@@ -94,3 +126,51 @@ class ValueSetLookupByNameQuerySet(ValueSetLookupQuerySet):
|
|
|
94
126
|
if i[0] in values_dict
|
|
95
127
|
),
|
|
96
128
|
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class TimeframeLookupQuerySetProtocol(QuerySetProtocol):
|
|
132
|
+
"""A typing protocol for use in TimeframeLookupQuerySetMixin."""
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
@abstractmethod
|
|
136
|
+
def timeframe_filter_field(self) -> str:
|
|
137
|
+
"""A protocol method for timeframe_filter_field."""
|
|
138
|
+
raise NotImplementedError
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class TimeframeLookupQuerySetMixin(TimeframeLookupQuerySetProtocol):
|
|
142
|
+
"""A class that adds queryset functionality to filter using timeframes."""
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def timeframe_filter_field(self) -> str:
|
|
146
|
+
"""Returns the field that should be filtered on. Can be overridden for different models."""
|
|
147
|
+
return "note__datetime_of_service"
|
|
148
|
+
|
|
149
|
+
def within(self, timeframe: "Timeframe") -> models.QuerySet:
|
|
150
|
+
"""A method to filter a queryset for datetimes within a timeframe."""
|
|
151
|
+
return self.filter(
|
|
152
|
+
**{
|
|
153
|
+
f"{self.timeframe_filter_field}__range": (
|
|
154
|
+
timeframe.start.datetime,
|
|
155
|
+
timeframe.end.datetime,
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class ValueSetLookupQuerySet(CommittableQuerySet, ValueSetLookupQuerySetMixin):
|
|
162
|
+
"""A class that includes methods for looking up value sets."""
|
|
163
|
+
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class ValueSetLookupByNameQuerySet(CommittableQuerySet, ValueSetLookupByNameQuerySetMixin):
|
|
168
|
+
"""A class that includes methods for looking up value sets by name."""
|
|
169
|
+
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class ValueSetTimeframeLookupQuerySet(ValueSetLookupQuerySet, TimeframeLookupQuerySetMixin):
|
|
174
|
+
"""A class that includes methods for looking up value sets and using timeframes."""
|
|
175
|
+
|
|
176
|
+
pass
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Type
|
|
2
|
+
|
|
3
|
+
from django.db import models
|
|
4
|
+
|
|
5
|
+
from canvas_sdk.v1.data.base import ValueSetTimeframeLookupQuerySet
|
|
6
|
+
from canvas_sdk.v1.data.note import Note
|
|
7
|
+
from canvas_sdk.v1.data.patient import Patient
|
|
8
|
+
from canvas_sdk.value_set.value_set import CodeConstants
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from canvas_sdk.value_set.value_set import ValueSet
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BillingLineItemQuerySet(ValueSetTimeframeLookupQuerySet):
|
|
15
|
+
"""A class that adds functionality to filter BillingLineItem objects."""
|
|
16
|
+
|
|
17
|
+
def find(self, value_set: Type["ValueSet"]) -> models.QuerySet:
|
|
18
|
+
"""
|
|
19
|
+
This method is overridden to use for BillingLineItem CPT codes.
|
|
20
|
+
The codes are saved as string values in the BillingLineItem.cpt field,
|
|
21
|
+
which differs from other coding models.
|
|
22
|
+
"""
|
|
23
|
+
values_dict = value_set.values
|
|
24
|
+
return self.filter(cpt__in=values_dict.get(CodeConstants.HCPCS, []))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BillingLineItemStatus(models.TextChoices):
|
|
28
|
+
"""Billing line item status."""
|
|
29
|
+
|
|
30
|
+
ACTIVE = "active", "Active"
|
|
31
|
+
REMOVED = "removed", "Removed"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class BillingLineItem(models.Model):
|
|
35
|
+
"""BillingLineItem."""
|
|
36
|
+
|
|
37
|
+
class Meta:
|
|
38
|
+
managed = False
|
|
39
|
+
app_label = "canvas_sdk"
|
|
40
|
+
db_table = "canvas_sdk_data_api_billinglineitem_001"
|
|
41
|
+
|
|
42
|
+
# objects = BillingLineItemQuerySet.as_manager()
|
|
43
|
+
objects = models.Manager().from_queryset(BillingLineItemQuerySet)()
|
|
44
|
+
|
|
45
|
+
id = models.UUIDField()
|
|
46
|
+
dbid = models.BigIntegerField(primary_key=True)
|
|
47
|
+
created = models.DateTimeField()
|
|
48
|
+
modified = models.DateTimeField()
|
|
49
|
+
note = models.ForeignKey(Note, on_delete=models.DO_NOTHING, related_name="billing_line_items")
|
|
50
|
+
patient = models.ForeignKey(
|
|
51
|
+
Patient, on_delete=models.DO_NOTHING, related_name="billing_line_items"
|
|
52
|
+
)
|
|
53
|
+
cpt = models.CharField()
|
|
54
|
+
charge = models.DecimalField()
|
|
55
|
+
description = models.CharField()
|
|
56
|
+
units = models.IntegerField()
|
|
57
|
+
command_type = models.CharField()
|
|
58
|
+
command_id = models.IntegerField()
|
|
59
|
+
status = models.CharField(choices=BillingLineItemStatus.choices)
|
canvas_sdk/v1/data/common.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from django.contrib.postgres.fields import ArrayField
|
|
1
2
|
from django.db import models
|
|
2
3
|
|
|
3
4
|
|
|
@@ -44,3 +45,60 @@ class ReviewStatus(models.TextChoices):
|
|
|
44
45
|
|
|
45
46
|
STATUS_REVIEWING = "reviewing", "reviewing"
|
|
46
47
|
STATUS_REVIEWED = "reviewed", "reviewed"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class PersonSex(models.TextChoices):
|
|
51
|
+
"""Status choices for individual sex."""
|
|
52
|
+
|
|
53
|
+
SEX_FEMALE = "F", "female"
|
|
54
|
+
SEX_MALE = "M", "male"
|
|
55
|
+
SEX_OTHER = "O", "other"
|
|
56
|
+
SEX_UNKNOWN = "UNK", "unknown"
|
|
57
|
+
SEX_BLANK = "", ""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TaxIDType(models.TextChoices):
|
|
61
|
+
"""Choices for Tax IDs."""
|
|
62
|
+
|
|
63
|
+
EIN = "E", "EIN text"
|
|
64
|
+
SSN = "S", "SSN"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ColorEnum(models.TextChoices):
|
|
68
|
+
"""Choices for colors."""
|
|
69
|
+
|
|
70
|
+
RED = "red", "Red"
|
|
71
|
+
ORANGE = "orange", "Orange"
|
|
72
|
+
YELLOW = "yellow", "Yellow"
|
|
73
|
+
OLIVE = "olive", "Olive"
|
|
74
|
+
GREEN = "green", "Green"
|
|
75
|
+
TEAL = "teal", "Teal"
|
|
76
|
+
BLUE = "blue", "Blue"
|
|
77
|
+
VIOLET = "violet", "Violet"
|
|
78
|
+
PURPLE = "purple", "Purple"
|
|
79
|
+
PINK = "pink", "Pink"
|
|
80
|
+
BROWN = "brown", "Brown"
|
|
81
|
+
GREY = "grey", "Grey"
|
|
82
|
+
BLACK = "black", "Black"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class Origin(models.TextChoices):
|
|
86
|
+
"""Choices for origins."""
|
|
87
|
+
|
|
88
|
+
REFERAL = ("REF_CMD", "Referral command")
|
|
89
|
+
COMPLETING_IMAGE_ORDERS = ("CMP_IMG_ORD", "Completing image orders")
|
|
90
|
+
IMAGING_REPORT_REVIEW = ("IMG_REP_REV", "Imaging report review")
|
|
91
|
+
LAB_RESULTS_REVIEW = ("LAB_RES_REV", "Lab results review")
|
|
92
|
+
CONSULT_REPORT_REVIEW = ("CON_REP_REV", "Consult report review")
|
|
93
|
+
UNCATEGORIZED_DOCUMENT_REPORT_REVIEW = (
|
|
94
|
+
"UNC_DOC_REP_REV",
|
|
95
|
+
"Uncategorized document report review",
|
|
96
|
+
)
|
|
97
|
+
ASSIGNED_NOTE_PHONE_CALL_FOR_REVIEW = ("ASN_NOT_PHN_REV", "Assigned note/phone call for review")
|
|
98
|
+
POPULATION_HEALTH_OUTREACH = ("POP_HLT_OUT", "Population health outreach")
|
|
99
|
+
COMPLETING_LAB_ORDERS = ("CMP_LAB_ORD", "Completing lab orders")
|
|
100
|
+
CHART_PDF = ("CHT_PDF", "Chart PDF")
|
|
101
|
+
EXPIRED_CLAIM_SNOOZED = ("EXP_CLM_SNO", "Expired claim snoozed")
|
|
102
|
+
FLAGGED_POSTING_REVIEW = ("FLG_PST_REV", "Flagged posting review")
|
|
103
|
+
BATCH_PATIENT_STATEMENTS = ("BAT_PTN_STA", "Batch patient statements")
|
|
104
|
+
INCOMPLETE_COVERAGE = ("INC_COV", "Incomplete Coverage")
|
canvas_sdk/v1/data/condition.py
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
+
from django.db.models import TextChoices
|
|
2
3
|
|
|
3
|
-
from canvas_sdk.v1.data.base import
|
|
4
|
-
CommittableModelManager,
|
|
5
|
-
CommittableQuerySet,
|
|
6
|
-
ValueSetLookupQuerySet,
|
|
7
|
-
)
|
|
4
|
+
from canvas_sdk.v1.data.base import ValueSetLookupQuerySet
|
|
8
5
|
from canvas_sdk.v1.data.patient import Patient
|
|
9
6
|
from canvas_sdk.v1.data.user import CanvasUser
|
|
10
7
|
|
|
11
8
|
|
|
9
|
+
class ClinicalStatus(TextChoices):
|
|
10
|
+
"""Condition clinical status."""
|
|
11
|
+
|
|
12
|
+
ACTIVE = "active", "active"
|
|
13
|
+
RELAPSE = "relapse", "relapse"
|
|
14
|
+
REMISSION = "remission", "remission"
|
|
15
|
+
RESOLVED = "resolved", "resolved"
|
|
16
|
+
INVESTIGATIVE = "investigative", "investigative"
|
|
17
|
+
|
|
18
|
+
|
|
12
19
|
class ConditionQuerySet(ValueSetLookupQuerySet):
|
|
13
20
|
"""ConditionQuerySet."""
|
|
14
21
|
|
|
@@ -29,6 +36,7 @@ class Condition(models.Model):
|
|
|
29
36
|
dbid = models.BigIntegerField(primary_key=True)
|
|
30
37
|
onset_date = models.DateField()
|
|
31
38
|
resolution_date = models.DateField()
|
|
39
|
+
clinical_status = models.CharField(choices=ClinicalStatus.choices)
|
|
32
40
|
deleted = models.BooleanField()
|
|
33
41
|
entered_in_error = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
|
|
34
42
|
committer = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
|
canvas_sdk/v1/data/medication.py
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
+
from django.db.models import TextChoices
|
|
2
3
|
|
|
3
4
|
from canvas_sdk.v1.data.base import CommittableModelManager, ValueSetLookupQuerySet
|
|
4
5
|
from canvas_sdk.v1.data.patient import Patient
|
|
5
6
|
from canvas_sdk.v1.data.user import CanvasUser
|
|
6
7
|
|
|
7
8
|
|
|
9
|
+
class Status(TextChoices):
|
|
10
|
+
"""Medication status."""
|
|
11
|
+
|
|
12
|
+
ACTIVE = "active", "active"
|
|
13
|
+
INACTIVE = "inactive", "inactive"
|
|
14
|
+
|
|
15
|
+
|
|
8
16
|
class MedicationQuerySet(ValueSetLookupQuerySet):
|
|
9
17
|
"""MedicationQuerySet."""
|
|
10
18
|
|
|
@@ -27,9 +35,9 @@ class Medication(models.Model):
|
|
|
27
35
|
deleted = models.BooleanField()
|
|
28
36
|
entered_in_error = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
|
|
29
37
|
committer = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
|
|
30
|
-
status = models.CharField()
|
|
31
|
-
start_date = models.
|
|
32
|
-
end_date = models.
|
|
38
|
+
status = models.CharField(choices=Status.choices)
|
|
39
|
+
start_date = models.DateTimeField()
|
|
40
|
+
end_date = models.DateTimeField()
|
|
33
41
|
quantity_qualifier_description = models.CharField()
|
|
34
42
|
clinical_quantity_description = models.CharField()
|
|
35
43
|
potency_unit_code = models.CharField()
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from django.contrib.postgres.fields import ArrayField
|
|
2
|
+
from django.db import models
|
|
3
|
+
|
|
4
|
+
from canvas_sdk.v1.data.patient import Patient
|
|
5
|
+
|
|
6
|
+
# from canvas_sdk.v1.data.staff import Staff
|
|
7
|
+
from canvas_sdk.v1.data.user import CanvasUser
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NoteTypeCategories(models.TextChoices):
|
|
11
|
+
"""Note type categories."""
|
|
12
|
+
|
|
13
|
+
MESSAGE = "message", "Message"
|
|
14
|
+
LETTER = "letter", "Letter"
|
|
15
|
+
INPATIENT = "inpatient", "Inpatient Visit Note"
|
|
16
|
+
REVIEW = "review", "Chart Review Note"
|
|
17
|
+
ENCOUNTER = "encounter", "Encounter Note"
|
|
18
|
+
APPOINTMENT = "appointment", "Appointment Note"
|
|
19
|
+
TASK = "task", "Task"
|
|
20
|
+
DATA = "data", "Data"
|
|
21
|
+
CCDA = "ccda", "C-CDA"
|
|
22
|
+
SCHEDULE_EVENT = "schedule_event", "Schedule Event"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PracticeLocationPOS(models.TextChoices):
|
|
26
|
+
"""Practice Location POS."""
|
|
27
|
+
|
|
28
|
+
PHARMACY = "01", "Pharmacy"
|
|
29
|
+
TELEHEALTH = "02", "Telehealth"
|
|
30
|
+
SCHOOL = "03", "Education Facility"
|
|
31
|
+
HOMELESS_SHELTER = "04", "Homeless Shelter"
|
|
32
|
+
PRISON = "09", "Prison"
|
|
33
|
+
TELEHEALTH_IN_PATIENT_HOME = "10", "Telehealth in Patient's Home"
|
|
34
|
+
OFFICE = "11", "Office"
|
|
35
|
+
HOME = "12", "Home"
|
|
36
|
+
ASSISTED_LIVING = "13", "Asssisted Living Facility"
|
|
37
|
+
GROUP_HOME = "14", "Group Home"
|
|
38
|
+
MOBILE = "15", "Mobile Unit"
|
|
39
|
+
WALK_IN_RETAIL = "17", "Walk-In Retail Health Clinic"
|
|
40
|
+
OFF_CAMPUS_OUTPATIENT_HOSPITAL = "19", "Off-Campus Outpatient Hospital"
|
|
41
|
+
URGENT_CARE = "20", "Urgent Care Facility"
|
|
42
|
+
INPATIENT_HOSPITAL = "21", "Inpatient Hospital"
|
|
43
|
+
ON_CAMPUS_OUTPATIENT_HOSPITAL = "22", "On-Campus Outpatient Hospital"
|
|
44
|
+
ER_HOSPITAL = "23", "Emergency Room Hospital"
|
|
45
|
+
AMBULATORY_SURGERY_CENTER = "24", "Ambulatory Surgery Center"
|
|
46
|
+
BIRTHING_CENTER = "25", "Birthing Center"
|
|
47
|
+
MILITARY_FACILITY = "26", "Military Treatment Facility"
|
|
48
|
+
STREET = "27", "Outreach Site / Street"
|
|
49
|
+
SNF = "31", "Skilled Nursing Facility"
|
|
50
|
+
NURSING = "32", "Nursing Facility"
|
|
51
|
+
CUSTODIAL = "33", "Custodial Care Facility"
|
|
52
|
+
HOSPICE = "34", "Hospice"
|
|
53
|
+
AMBULANCE_LAND = "41", "Ambulance Land"
|
|
54
|
+
AMBULANCE_AIR_WATER = "42", "Ambulance Air or Water"
|
|
55
|
+
INDEPENDENT_CLINIC = "49", "Independent Clinic"
|
|
56
|
+
FQHC = "50", "Federally Qualified Health Center"
|
|
57
|
+
PSYCH = "51", "Inpatient Psychiatric Facility"
|
|
58
|
+
PSYCH_PARTIAL = "52", "Inpatient Psychiatric Facility - Partial Hospitalization"
|
|
59
|
+
MENTAL_HEALTH_CENTER = "53", "Community Mental Health Center"
|
|
60
|
+
INTERMEDIATE_MENTAL = "54", "Intermediate Care Facility for Mentally Retarded"
|
|
61
|
+
SUBSTANCE_RESIDENTIAL = "55", "Residential Substance Abuse Treatment Facility"
|
|
62
|
+
PSYCH_RESIDENTIAL = "56", "Psychiatric Residential Treatment Center"
|
|
63
|
+
SUBSTANCE_NON_RESIDENTIAL = "57", "Non-Residential Substance Abuse Treatment Facility"
|
|
64
|
+
MASS_IMMUNIZATION = "60", "Mass Immunization Center"
|
|
65
|
+
INPATIENT_REHAB = "61", "Inpatient Rehabilitation Facility"
|
|
66
|
+
OUTPATIENT_REHAB = "62", "Outpatient Rehabilitation Facility"
|
|
67
|
+
ESRD = "65", "End-Stage Renal Disease Treatment Facility"
|
|
68
|
+
PUBLIC_CLINIC = "71", "State or Local Public Health Clinic"
|
|
69
|
+
RURAL_CLINIC = "72", "Rural Health Clinic"
|
|
70
|
+
INDEPENDENT_LAB = "81", "Independent Laboratory"
|
|
71
|
+
OTHER = "99", "Other Place of Service"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class NoteTypes(models.TextChoices):
|
|
75
|
+
"""Note types."""
|
|
76
|
+
|
|
77
|
+
MESSAGE = "message", "Message"
|
|
78
|
+
LETTER = "letter", "Letter"
|
|
79
|
+
INPATIENT = "inpatient", "Inpatient Visit Note"
|
|
80
|
+
REVIEW = "review", "Chart Review Note"
|
|
81
|
+
VOICE = "voice", "Phone Call Note"
|
|
82
|
+
VIDEO = "video", "Video Call Note"
|
|
83
|
+
OFFICE = "office", "Office Visit Note"
|
|
84
|
+
LAB = "lab", "Lab Visit Note"
|
|
85
|
+
HOME = "home", "Home Visit Note"
|
|
86
|
+
GROUP = "group", "Group Visit Note"
|
|
87
|
+
APPOINTMENT = "appointment", "Appointment Note"
|
|
88
|
+
OFFSITE = "offsite", "Other Offsite Visit Note"
|
|
89
|
+
SEARCH = "search", "Search"
|
|
90
|
+
TASK = "task", "Task"
|
|
91
|
+
DATA = "data", "Data"
|
|
92
|
+
CCDA = "ccda", "C-CDA Import"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class NoteType(models.Model):
|
|
96
|
+
"""NoteType."""
|
|
97
|
+
|
|
98
|
+
class Meta:
|
|
99
|
+
managed = False
|
|
100
|
+
app_label = "canvas_sdk"
|
|
101
|
+
db_table = "canvas_sdk_data_api_notetype_001"
|
|
102
|
+
|
|
103
|
+
dbid = models.BigIntegerField(primary_key=True)
|
|
104
|
+
created = models.DateTimeField()
|
|
105
|
+
modified = models.DateTimeField()
|
|
106
|
+
system = models.CharField()
|
|
107
|
+
version = models.CharField()
|
|
108
|
+
code = models.CharField()
|
|
109
|
+
display = models.CharField()
|
|
110
|
+
user_selected = models.BooleanField()
|
|
111
|
+
name = models.CharField()
|
|
112
|
+
icon = models.CharField()
|
|
113
|
+
category = models.CharField(choices=NoteTypeCategories.choices)
|
|
114
|
+
rank = models.PositiveIntegerField()
|
|
115
|
+
is_default_appointment_type = models.BooleanField()
|
|
116
|
+
is_scheduleable = models.BooleanField()
|
|
117
|
+
is_telehealth = models.BooleanField()
|
|
118
|
+
is_billable = models.BooleanField()
|
|
119
|
+
defer_place_of_service_to_practice_location = models.BooleanField()
|
|
120
|
+
available_places_of_service = ArrayField(models.CharField(choices=PracticeLocationPOS.choices))
|
|
121
|
+
default_place_of_service = models.CharField(choices=PracticeLocationPOS.choices)
|
|
122
|
+
is_system_managed = models.BooleanField()
|
|
123
|
+
is_visible = models.BooleanField()
|
|
124
|
+
is_active = models.BooleanField()
|
|
125
|
+
unique_identifier = models.UUIDField()
|
|
126
|
+
deprecated_at = models.DateTimeField()
|
|
127
|
+
is_patient_required = models.BooleanField()
|
|
128
|
+
allow_custom_title = models.BooleanField()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class Note(models.Model):
|
|
132
|
+
"""Note."""
|
|
133
|
+
|
|
134
|
+
class Meta:
|
|
135
|
+
managed = False
|
|
136
|
+
app_label = "canvas_sdk"
|
|
137
|
+
db_table = "canvas_sdk_data_api_note_001"
|
|
138
|
+
|
|
139
|
+
id = models.CharField(max_length=32)
|
|
140
|
+
dbid = models.BigIntegerField(db_column="dbid", primary_key=True)
|
|
141
|
+
created = models.DateTimeField()
|
|
142
|
+
modified = models.DateTimeField()
|
|
143
|
+
patient = models.ForeignKey(Patient, on_delete=models.DO_NOTHING)
|
|
144
|
+
# provider = models.ForeignKey(Staff, on_delete=models.DO_NOTHING, related_name="notes")
|
|
145
|
+
note_type = models.CharField(choices=NoteTypes.choices, null=True)
|
|
146
|
+
note_type_version = models.ForeignKey(
|
|
147
|
+
"NoteType", on_delete=models.DO_NOTHING, related_name="notes"
|
|
148
|
+
)
|
|
149
|
+
title = models.TextField()
|
|
150
|
+
body = models.JSONField()
|
|
151
|
+
originator = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
|
|
152
|
+
# last_modified_by_staff = models.ForeignKey(Staff, on_delete=models.DO_NOTHING, null=True)
|
|
153
|
+
checksum = models.CharField()
|
|
154
|
+
billing_note = models.TextField()
|
|
155
|
+
# TODO -implement InpatientStay model
|
|
156
|
+
# inpatient_stay = models.ForeignKey("InpatientStay", on_delete=models.DO_NOTHING)
|
|
157
|
+
related_data = models.JSONField()
|
|
158
|
+
# TODO -implement PracticeLocation model
|
|
159
|
+
# location = models.ForeignKey(PracticeLocation, on_delete=models.DO_NOTHING)
|
|
160
|
+
datetime_of_service = models.DateTimeField()
|
|
161
|
+
place_of_service = models.CharField()
|
canvas_sdk/v1/data/patient.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, Self
|
|
2
2
|
|
|
3
|
+
import arrow
|
|
3
4
|
from django.db import models
|
|
4
5
|
|
|
5
6
|
|
|
@@ -53,3 +54,20 @@ class Patient(models.Model):
|
|
|
53
54
|
|
|
54
55
|
def __str__(self) -> str:
|
|
55
56
|
return f"{self.first_name} {self.last_name}"
|
|
57
|
+
|
|
58
|
+
def age_at(self, time: arrow.Arrow) -> float:
|
|
59
|
+
"""Given a datetime, returns what the patient's age would be at that datetime."""
|
|
60
|
+
age = float(0)
|
|
61
|
+
birth_date = arrow.get(self.birth_date)
|
|
62
|
+
if birth_date.date() < time.date():
|
|
63
|
+
age = time.datetime.year - birth_date.datetime.year
|
|
64
|
+
if time.datetime.month < birth_date.datetime.month or (
|
|
65
|
+
time.datetime.month == birth_date.datetime.month
|
|
66
|
+
and time.datetime.day < birth_date.datetime.day
|
|
67
|
+
):
|
|
68
|
+
age -= 1
|
|
69
|
+
|
|
70
|
+
current_year = birth_date.shift(years=age)
|
|
71
|
+
next_year = birth_date.shift(years=age + 1)
|
|
72
|
+
age += (time.date() - current_year.date()) / (next_year.date() - current_year.date())
|
|
73
|
+
return age
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from django.contrib.postgres.fields import ArrayField
|
|
2
|
+
from django.db import models
|
|
3
|
+
from timezone_utils.fields import TimeZoneField
|
|
4
|
+
|
|
5
|
+
from canvas_sdk.v1.data.common import PersonSex, TaxIDType
|
|
6
|
+
from canvas_sdk.v1.data.user import CanvasUser
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Staff(models.Model):
|
|
10
|
+
"""Staff."""
|
|
11
|
+
|
|
12
|
+
class Meta:
|
|
13
|
+
managed = False
|
|
14
|
+
app_label = "canvas_sdk"
|
|
15
|
+
db_table = "canvas_sdk_data_api_staff_001"
|
|
16
|
+
|
|
17
|
+
def __str__(self) -> str:
|
|
18
|
+
return f"{self.first_name} {self.last_name}"
|
|
19
|
+
|
|
20
|
+
id = models.CharField(max_length=32, db_column="key")
|
|
21
|
+
dbid = models.BigIntegerField(db_column="dbid", primary_key=True)
|
|
22
|
+
created = models.DateTimeField()
|
|
23
|
+
modified = models.DateTimeField()
|
|
24
|
+
prefix = models.CharField()
|
|
25
|
+
suffix = models.CharField()
|
|
26
|
+
first_name = models.CharField()
|
|
27
|
+
middle_name = models.CharField()
|
|
28
|
+
last_name = models.CharField()
|
|
29
|
+
maiden_name = models.CharField()
|
|
30
|
+
nickname = models.CharField()
|
|
31
|
+
previous_names = models.JSONField()
|
|
32
|
+
birth_date = models.DateField(null=True)
|
|
33
|
+
sex_at_birth = models.CharField(choices=PersonSex.choices)
|
|
34
|
+
sexual_orientation_term = models.CharField()
|
|
35
|
+
sexual_orientation_code = models.CharField()
|
|
36
|
+
gender_identity_term = models.CharField()
|
|
37
|
+
gender_identity_code = models.CharField()
|
|
38
|
+
preferred_pronouns = models.CharField()
|
|
39
|
+
biological_race_codes = ArrayField(models.CharField())
|
|
40
|
+
biological_race_terms = ArrayField(models.CharField())
|
|
41
|
+
cultural_ethnicity_codes = ArrayField(models.CharField())
|
|
42
|
+
cultural_ethnicity_terms = ArrayField(models.CharField())
|
|
43
|
+
last_known_timezone = TimeZoneField(null=True)
|
|
44
|
+
active = models.BooleanField()
|
|
45
|
+
# TODO - uncomment when PracticeLocation field is developed
|
|
46
|
+
# primary_practice_location = models.ForeignKey(
|
|
47
|
+
# PracticeLocation, on_delete=models.DO_NOTHING, null=True
|
|
48
|
+
# )
|
|
49
|
+
npi_number = models.CharField()
|
|
50
|
+
nadean_number = models.CharField()
|
|
51
|
+
group_npi_number = models.CharField()
|
|
52
|
+
bill_through_organization = models.BooleanField()
|
|
53
|
+
tax_id = models.CharField()
|
|
54
|
+
tax_id_type = models.CharField(choices=TaxIDType.choices)
|
|
55
|
+
spi_number = models.CharField()
|
|
56
|
+
# TODO - uncomment when Language is developed
|
|
57
|
+
# language = models.ForeignKey(Language, on_delete=models.DO_NOTHING, related_name="staff_speakers")
|
|
58
|
+
# language_secondary = models.ForeignKey(Language, on_delete=models.DO_NOTHING, related_name="staff_secondary_speakers")
|
|
59
|
+
personal_meeting_room_link = models.URLField(null=True)
|
|
60
|
+
state = models.JSONField()
|
|
61
|
+
user = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
|
|
62
|
+
schedule_column_ordering = models.IntegerField()
|
|
63
|
+
default_supervising_provider = models.ForeignKey(
|
|
64
|
+
"Staff", on_delete=models.DO_NOTHING, null=True, related_name="supervising_team"
|
|
65
|
+
)
|