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.

@@ -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 ValueSetLookupQuerySet(CommittableQuerySet):
33
- """A QuerySet that can filter objects based on a ValueSet."""
34
+ class BaseQuerySet(models.QuerySet):
35
+ """A base QuerySet inherited from Django's model.Queryset."""
34
36
 
35
- def find(self, value_set: Type["ValueSet"]) -> "Self":
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 ValueSetLookupByNameQuerySet(ValueSetLookupQuerySet):
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)
@@ -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")
@@ -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)
@@ -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.DateField()
32
- end_date = models.DateField()
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()
@@ -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
+ )