canvas 0.5.0__py3-none-any.whl → 0.7.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.

Files changed (38) hide show
  1. {canvas-0.5.0.dist-info → canvas-0.7.0.dist-info}/METADATA +4 -3
  2. {canvas-0.5.0.dist-info → canvas-0.7.0.dist-info}/RECORD +32 -31
  3. canvas_generated/messages/events_pb2.py +2 -2
  4. canvas_generated/messages/events_pb2.pyi +4 -0
  5. canvas_sdk/base.py +12 -9
  6. canvas_sdk/commands/base.py +2 -1
  7. canvas_sdk/effects/banner_alert/add_banner_alert.py +13 -2
  8. canvas_sdk/effects/banner_alert/remove_banner_alert.py +2 -2
  9. canvas_sdk/effects/banner_alert/tests.py +20 -4
  10. canvas_sdk/effects/base.py +2 -0
  11. canvas_sdk/effects/protocol_card/protocol_card.py +9 -2
  12. canvas_sdk/effects/protocol_card/tests.py +3 -3
  13. canvas_sdk/effects/task/task.py +99 -0
  14. canvas_sdk/protocols/base.py +1 -11
  15. canvas_sdk/protocols/clinical_quality_measure.py +57 -1
  16. canvas_sdk/protocols/timeframe.py +39 -0
  17. canvas_sdk/v1/data/__init__.py +3 -0
  18. canvas_sdk/v1/data/base.py +95 -12
  19. canvas_sdk/v1/data/billing.py +59 -0
  20. canvas_sdk/v1/data/common.py +58 -0
  21. canvas_sdk/v1/data/condition.py +18 -2
  22. canvas_sdk/v1/data/medication.py +11 -3
  23. canvas_sdk/v1/data/note.py +161 -0
  24. canvas_sdk/v1/data/patient.py +19 -1
  25. canvas_sdk/v1/data/protocol_override.py +1 -1
  26. canvas_sdk/v1/data/staff.py +65 -0
  27. canvas_sdk/v1/data/task.py +112 -0
  28. canvas_sdk/value_set/custom.py +984 -0
  29. canvas_sdk/value_set/value_set.py +4 -1
  30. plugin_runner/sandbox.py +3 -0
  31. canvas_sdk/data/__init__.py +0 -1
  32. canvas_sdk/data/base.py +0 -26
  33. canvas_sdk/data/client.py +0 -82
  34. canvas_sdk/data/patient.py +0 -8
  35. canvas_sdk/data/staff.py +0 -8
  36. canvas_sdk/data/task.py +0 -67
  37. {canvas-0.5.0.dist-info → canvas-0.7.0.dist-info}/WHEEL +0 -0
  38. {canvas-0.5.0.dist-info → canvas-0.7.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,99 @@
1
+ from datetime import datetime
2
+ from enum import Enum
3
+ from typing import Any, cast
4
+
5
+ from canvas_sdk.effects.base import EffectType, _BaseEffect
6
+
7
+
8
+ class TaskStatus(Enum):
9
+ """TaskStatus."""
10
+
11
+ COMPLETED = "COMPLETED"
12
+ CLOSED = "CLOSED"
13
+ OPEN = "OPEN"
14
+
15
+
16
+ class AddTask(_BaseEffect):
17
+ """
18
+ An Effect that will create a Task in Canvas.
19
+ """
20
+
21
+ class Meta:
22
+ effect_type = EffectType.CREATE_TASK
23
+ apply_required_fields = ("title",)
24
+
25
+ assignee_id: str | None = None
26
+ patient_id: str | None = None
27
+ title: str | None = None
28
+ due: datetime | None = None
29
+ status: TaskStatus = TaskStatus.OPEN
30
+ labels: list[str] = []
31
+
32
+ @property
33
+ def values(self) -> dict[str, Any]:
34
+ """The values for Task addition."""
35
+ return {
36
+ "patient": {"id": self.patient_id},
37
+ "due": self.due.isoformat() if self.due else None,
38
+ "assignee": {"id": self.assignee_id},
39
+ "title": self.title,
40
+ "status": self.status.value,
41
+ "labels": self.labels,
42
+ }
43
+
44
+
45
+ class AddTaskComment(_BaseEffect):
46
+ """
47
+ An Effect that will create a Task Comment on a Task.
48
+ """
49
+
50
+ class Meta:
51
+ effect_type = EffectType.CREATE_TASK_COMMENT
52
+ apply_required_fields = (
53
+ "body",
54
+ "task_id",
55
+ )
56
+
57
+ body: str | None = None
58
+ task_id: str | None = None
59
+
60
+ @property
61
+ def values(self) -> dict[str, Any]:
62
+ """The values for adding a task comment."""
63
+ return {"task": {"id": self.task_id}, "body": self.body}
64
+
65
+
66
+ class UpdateTask(_BaseEffect):
67
+ """
68
+ An Effect that will update a Task in Canvas.
69
+ """
70
+
71
+ class Meta:
72
+ effect_type = EffectType.UPDATE_TASK
73
+ apply_required_fields = ("id",)
74
+
75
+ id: str | None = None
76
+ assignee_id: str | None = None
77
+ patient_id: str | None = None
78
+ title: str | None = None
79
+ due: datetime | None = None
80
+ status: TaskStatus = TaskStatus.OPEN
81
+ labels: list[str] = []
82
+
83
+ @property
84
+ def values(self) -> dict[str, Any]:
85
+ """The values for adding a task comment."""
86
+ value_dict: dict[str, Any] = {}
87
+ # Only add the fields that have been explicitly set on the model (exclude_unset=True).
88
+ # Oherwise, the effect interpreter will set values to null based on their defaults.
89
+ set_fields = self.model_dump(exclude_unset=True)
90
+ for field, val in set_fields.items():
91
+ if field.endswith("_id"):
92
+ value_dict[field.split("_")[0]] = {"id": val}
93
+ elif field == "due" and val is not None:
94
+ value_dict[field] = cast(datetime, val).isoformat()
95
+ elif field == "status":
96
+ value_dict[field] = cast(TaskStatus, val).value
97
+ else:
98
+ value_dict[field] = getattr(self, field)
99
+ return value_dict
@@ -1,6 +1,5 @@
1
1
  from typing import Any
2
2
 
3
- from canvas_sdk.data.client import GQL_CLIENT
4
3
  from canvas_sdk.handlers.base import BaseHandler
5
4
 
6
5
 
@@ -9,13 +8,4 @@ class BaseProtocol(BaseHandler):
9
8
  The class that protocols inherit from.
10
9
  """
11
10
 
12
- def _beta_run_gql_query(self, query: str, variables: dict | None = None) -> dict[str, Any]:
13
- return GQL_CLIENT.query(
14
- query,
15
- variables=variables,
16
- extra_args={
17
- "headers": {
18
- "Authorization": f'Bearer {self.secrets["graphql_jwt"]}',
19
- },
20
- },
21
- )
11
+ pass
@@ -1,6 +1,13 @@
1
- from typing import Any
1
+ from typing import Any, cast
2
2
 
3
+ import arrow
4
+ from django.db.models import Model
5
+
6
+ from canvas_sdk.events import EventType
3
7
  from canvas_sdk.protocols.base import BaseProtocol
8
+ from canvas_sdk.protocols.timeframe import Timeframe
9
+ from canvas_sdk.v1.data.condition import Condition
10
+ from canvas_sdk.v1.data.medication import Medication
4
11
 
5
12
 
6
13
  class ClinicalQualityMeasure(BaseProtocol):
@@ -23,6 +30,11 @@ class ClinicalQualityMeasure(BaseProtocol):
23
30
  is_abstract: bool = False
24
31
  is_predictive: bool = False
25
32
 
33
+ def __init__(self, *args: Any, **kwargs: Any):
34
+ self._patient_id: str | None = None
35
+ self.now = arrow.utcnow()
36
+ super().__init__(*args, **kwargs)
37
+
26
38
  @classmethod
27
39
  def _meta(cls) -> dict[str, Any]:
28
40
  """
@@ -41,3 +53,47 @@ class ClinicalQualityMeasure(BaseProtocol):
41
53
  External key used to identify the protocol.
42
54
  """
43
55
  return cls.__name__
56
+
57
+ @property
58
+ def timeframe(self) -> Timeframe:
59
+ """The default Timeframe (self.timeframe) for all protocols.
60
+ This defaults to have a start of 1 year ago and an end time of the current time.
61
+ Plugin authors can override this if a different timeframe is desired.
62
+ """
63
+ end = self.now
64
+ return Timeframe(start=end.shift(years=-1), end=end)
65
+
66
+ # TODO: This approach should be considered against the alternative of just including the patient
67
+ # ID in the event context, given that so many events will be patient-centric.
68
+ def patient_id_from_target(self) -> str:
69
+ """
70
+ Get and return the patient ID from an event target.
71
+
72
+ This method will attempt to obtain the patient ID from the event target for supported event
73
+ types. It stores the patient ID on a member variable so that it can be referenced without
74
+ incurring more SQL queries.
75
+ """
76
+
77
+ def patient_id(model: type[Model]) -> str:
78
+ return cast(
79
+ str,
80
+ model._default_manager.select_related("patient")
81
+ .values_list("patient__id")
82
+ .get(id=self.event.target)[0],
83
+ )
84
+
85
+ if not self._patient_id:
86
+ # TODO: Add cases for ProtocolOverride
87
+ match self.event.type:
88
+ case EventType.CONDITION_CREATED | EventType.CONDITION_UPDATED:
89
+ self._patient_id = patient_id(Condition)
90
+ case (
91
+ EventType.MEDICATION_LIST_ITEM_CREATED | EventType.MEDICATION_LIST_ITEM_UPDATED
92
+ ):
93
+ self._patient_id = patient_id(Medication)
94
+ case _:
95
+ raise AssertionError(
96
+ f"Event type {self.event.type} not supported by 'patient_id_from_event'"
97
+ )
98
+
99
+ return self._patient_id
@@ -0,0 +1,39 @@
1
+ import arrow
2
+
3
+
4
+ class Timeframe:
5
+ """A class representing a timeframe with a start and and end."""
6
+
7
+ def __init__(self, start: arrow.Arrow, end: arrow.Arrow):
8
+ self.start = start
9
+ self.end = end
10
+
11
+ def __str__(self) -> str:
12
+ return f"<Timeframe start={self.start}, end={self.end}>"
13
+
14
+ @property
15
+ def duration(self) -> int:
16
+ """Returns the number of days in the timeframe."""
17
+ return (self.end - self.start).days
18
+
19
+ def increased_by(self, years: int = 0, months: int = 0, days: int = 0) -> "Timeframe":
20
+ """Returns a new Timeframe object increased by the years, months, days in the arguments."""
21
+ start = self.start
22
+ end = self.end
23
+
24
+ if years > 0:
25
+ end = end.shift(years=years)
26
+ elif years < 0:
27
+ start = start.shift(years=years)
28
+
29
+ if months > 0:
30
+ end = end.shift(months=months)
31
+ elif months < 0:
32
+ start = start.shift(months=months)
33
+
34
+ if days > 0:
35
+ end = end.shift(days=days)
36
+ elif days < 0:
37
+ start = start.shift(days=days)
38
+
39
+ return Timeframe(start=start, end=end)
@@ -1,3 +1,6 @@
1
+ from .billing import BillingLineItem
1
2
  from .condition import Condition, ConditionCoding
2
3
  from .medication import Medication, MedicationCoding
3
4
  from .patient import Patient
5
+ from .staff import Staff
6
+ from .task import Task, TaskComment, TaskLabel
@@ -1,35 +1,70 @@
1
+ from abc import abstractmethod
1
2
  from collections.abc import Container
2
- from typing import TYPE_CHECKING, 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
 
11
13
  class CommittableModelManager(models.Manager):
12
14
  """A manager for commands that can be committed."""
13
15
 
14
- def get_queryset(self) -> "models.QuerySet":
16
+ def get_queryset(self) -> "CommittableQuerySet":
15
17
  """Return a queryset that filters out deleted objects."""
16
18
  # TODO: Should we just filter these out at the view level?
17
- return super().get_queryset().filter(deleted=False)
19
+ return CommittableQuerySet(self.model, using=self._db).filter(deleted=False)
18
20
 
19
- def committed(self) -> "models.QuerySet":
21
+
22
+ class CommittableQuerySet(models.QuerySet):
23
+ """A queryset for committable objects."""
24
+
25
+ def committed(self) -> "Self":
20
26
  """Return a queryset that filters for objects that have been committed."""
21
- # The committer_id IS set, and the entered_in_error_id IS NOT set
22
27
  return self.filter(committer_id__isnull=False, entered_in_error_id__isnull=True)
23
28
 
24
- def for_patient(self, patient_id: str) -> "models.QuerySet":
29
+ def for_patient(self, patient_id: str) -> "Self":
25
30
  """Return a queryset that filters objects for a specific patient."""
26
31
  return self.filter(patient__id=patient_id)
27
32
 
28
33
 
29
- class ValueSetLookupQuerySet(models.QuerySet):
30
- """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."""
36
+
37
+ pass
38
+
39
+
40
+ class QuerySetProtocol(Protocol):
41
+ """A typing protocol for use in mixins into models.QuerySet-inherited classes."""
31
42
 
32
- def find(self, value_set: Type["ValueSet"]) -> models.QuerySet:
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]:
33
68
  """
34
69
  Filters conditions, medications, etc. to those found in the inherited ValueSet class that is passed.
35
70
 
@@ -51,7 +86,7 @@ class ValueSetLookupQuerySet(models.QuerySet):
51
86
  @staticmethod
52
87
  def codings(value_set: Type["ValueSet"]) -> tuple[tuple[str, set[str]]]:
53
88
  """Provide a sequence of tuples where each tuple is a code system URL and a set of codes."""
54
- values_dict = value_set.values
89
+ values_dict = cast(dict, value_set.values)
55
90
  return cast(
56
91
  tuple[tuple[str, set[str]]],
57
92
  tuple(
@@ -69,7 +104,7 @@ class ValueSetLookupQuerySet(models.QuerySet):
69
104
  return Q(codings__system=system, codings__code__in=codes)
70
105
 
71
106
 
72
- class ValueSetLookupByNameQuerySet(ValueSetLookupQuerySet):
107
+ class ValueSetLookupByNameQuerySetMixin(ValueSetLookupQuerySetMixin):
73
108
  """
74
109
  QuerySet for ValueSet lookups using code system name rather than URL.
75
110
 
@@ -82,7 +117,7 @@ class ValueSetLookupByNameQuerySet(ValueSetLookupQuerySet):
82
117
  """
83
118
  Provide a sequence of tuples where each tuple is a code system name and a set of codes.
84
119
  """
85
- values_dict = value_set.values
120
+ values_dict = cast(dict, value_set.values)
86
121
  return cast(
87
122
  tuple[tuple[str, set[str]]],
88
123
  tuple(
@@ -91,3 +126,51 @@ class ValueSetLookupByNameQuerySet(ValueSetLookupQuerySet):
91
126
  if i[0] in values_dict
92
127
  ),
93
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(BaseQuerySet, ValueSetLookupQuerySetMixin):
162
+ """A class that includes methods for looking up value sets."""
163
+
164
+ pass
165
+
166
+
167
+ class ValueSetLookupByNameQuerySet(BaseQuerySet, 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,10 +1,25 @@
1
1
  from django.db import models
2
+ from django.db.models import TextChoices
2
3
 
3
- from canvas_sdk.v1.data.base import CommittableModelManager, ValueSetLookupQuerySet
4
+ from canvas_sdk.v1.data.base import (
5
+ CommittableModelManager,
6
+ CommittableQuerySet,
7
+ ValueSetLookupQuerySet,
8
+ )
4
9
  from canvas_sdk.v1.data.patient import Patient
5
10
  from canvas_sdk.v1.data.user import CanvasUser
6
11
 
7
12
 
13
+ class ClinicalStatus(TextChoices):
14
+ """Condition clinical status."""
15
+
16
+ ACTIVE = "active", "active"
17
+ RELAPSE = "relapse", "relapse"
18
+ REMISSION = "remission", "remission"
19
+ RESOLVED = "resolved", "resolved"
20
+ INVESTIGATIVE = "investigative", "investigative"
21
+
22
+
8
23
  class ConditionQuerySet(ValueSetLookupQuerySet):
9
24
  """ConditionQuerySet."""
10
25
 
@@ -19,12 +34,13 @@ class Condition(models.Model):
19
34
  app_label = "canvas_sdk"
20
35
  db_table = "canvas_sdk_data_api_condition_001"
21
36
 
22
- objects = CommittableModelManager.from_queryset(ConditionQuerySet)()
37
+ objects = ConditionQuerySet.as_manager()
23
38
 
24
39
  id = models.UUIDField()
25
40
  dbid = models.BigIntegerField(primary_key=True)
26
41
  onset_date = models.DateField()
27
42
  resolution_date = models.DateField()
43
+ clinical_status = models.CharField(choices=ClinicalStatus.choices)
28
44
  deleted = models.BooleanField()
29
45
  entered_in_error = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
30
46
  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()