canvas 0.34.1__py3-none-any.whl → 0.35.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.

Files changed (60) hide show
  1. {canvas-0.34.1.dist-info → canvas-0.35.1.dist-info}/METADATA +2 -2
  2. {canvas-0.34.1.dist-info → canvas-0.35.1.dist-info}/RECORD +59 -51
  3. canvas_generated/messages/effects_pb2.py +4 -4
  4. canvas_generated/messages/effects_pb2.pyi +22 -2
  5. canvas_generated/messages/events_pb2.py +2 -2
  6. canvas_generated/messages/events_pb2.pyi +30 -0
  7. canvas_sdk/base.py +56 -0
  8. canvas_sdk/commands/base.py +22 -46
  9. canvas_sdk/commands/commands/adjust_prescription.py +0 -10
  10. canvas_sdk/commands/commands/allergy.py +0 -1
  11. canvas_sdk/commands/commands/assess.py +2 -2
  12. canvas_sdk/commands/commands/change_medication.py +58 -0
  13. canvas_sdk/commands/commands/close_goal.py +0 -1
  14. canvas_sdk/commands/commands/diagnose.py +0 -1
  15. canvas_sdk/commands/commands/exam.py +0 -1
  16. canvas_sdk/commands/commands/family_history.py +0 -1
  17. canvas_sdk/commands/commands/follow_up.py +4 -2
  18. canvas_sdk/commands/commands/goal.py +8 -7
  19. canvas_sdk/commands/commands/history_present_illness.py +0 -1
  20. canvas_sdk/commands/commands/imaging_order.py +9 -8
  21. canvas_sdk/commands/commands/instruct.py +2 -2
  22. canvas_sdk/commands/commands/lab_order.py +10 -9
  23. canvas_sdk/commands/commands/medical_history.py +0 -1
  24. canvas_sdk/commands/commands/medication_statement.py +0 -1
  25. canvas_sdk/commands/commands/past_surgical_history.py +0 -1
  26. canvas_sdk/commands/commands/perform.py +3 -2
  27. canvas_sdk/commands/commands/plan.py +0 -1
  28. canvas_sdk/commands/commands/prescribe.py +0 -9
  29. canvas_sdk/commands/commands/refer.py +10 -10
  30. canvas_sdk/commands/commands/refill.py +0 -9
  31. canvas_sdk/commands/commands/remove_allergy.py +0 -1
  32. canvas_sdk/commands/commands/resolve_condition.py +3 -2
  33. canvas_sdk/commands/commands/review_of_systems.py +0 -1
  34. canvas_sdk/commands/commands/stop_medication.py +0 -1
  35. canvas_sdk/commands/commands/structured_assessment.py +0 -1
  36. canvas_sdk/commands/commands/task.py +0 -4
  37. canvas_sdk/commands/commands/update_diagnosis.py +8 -6
  38. canvas_sdk/commands/commands/update_goal.py +0 -1
  39. canvas_sdk/commands/commands/vitals.py +0 -1
  40. canvas_sdk/effects/note/__init__.py +10 -0
  41. canvas_sdk/effects/note/appointment.py +148 -0
  42. canvas_sdk/effects/note/base.py +129 -0
  43. canvas_sdk/effects/note/note.py +79 -0
  44. canvas_sdk/effects/patient/__init__.py +3 -0
  45. canvas_sdk/effects/patient/base.py +123 -0
  46. canvas_sdk/utils/http.py +7 -26
  47. canvas_sdk/utils/metrics.py +192 -0
  48. canvas_sdk/utils/plugins.py +24 -0
  49. canvas_sdk/v1/data/__init__.py +4 -0
  50. canvas_sdk/v1/data/message.py +82 -0
  51. plugin_runner/load_all_plugins.py +0 -3
  52. plugin_runner/plugin_runner.py +159 -198
  53. plugin_runner/sandbox.py +3 -0
  54. protobufs/canvas_generated/messages/effects.proto +13 -0
  55. protobufs/canvas_generated/messages/events.proto +16 -0
  56. pubsub/pubsub.py +10 -1
  57. settings.py +8 -0
  58. canvas_sdk/utils/stats.py +0 -74
  59. {canvas-0.34.1.dist-info → canvas-0.35.1.dist-info}/WHEEL +0 -0
  60. {canvas-0.34.1.dist-info → canvas-0.35.1.dist-info}/entry_points.txt +0 -0
@@ -770,6 +770,21 @@ class EventType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
770
770
  DEFER_CODING_GAP_COMMAND__PRE_EXECUTE_ACTION: _ClassVar[EventType]
771
771
  DEFER_CODING_GAP_COMMAND__POST_EXECUTE_ACTION: _ClassVar[EventType]
772
772
  DEFER_CODING_GAP_COMMAND__POST_INSERTED_INTO_NOTE: _ClassVar[EventType]
773
+ CHANGE_MEDICATION_COMMAND__PRE_ORIGINATE: _ClassVar[EventType]
774
+ CHANGE_MEDICATION_COMMAND__POST_ORIGINATE: _ClassVar[EventType]
775
+ CHANGE_MEDICATION_COMMAND__PRE_UPDATE: _ClassVar[EventType]
776
+ CHANGE_MEDICATION_COMMAND__POST_UPDATE: _ClassVar[EventType]
777
+ CHANGE_MEDICATION_COMMAND__PRE_COMMIT: _ClassVar[EventType]
778
+ CHANGE_MEDICATION_COMMAND__POST_COMMIT: _ClassVar[EventType]
779
+ CHANGE_MEDICATION_COMMAND__PRE_DELETE: _ClassVar[EventType]
780
+ CHANGE_MEDICATION_COMMAND__POST_DELETE: _ClassVar[EventType]
781
+ CHANGE_MEDICATION_COMMAND__PRE_ENTER_IN_ERROR: _ClassVar[EventType]
782
+ CHANGE_MEDICATION_COMMAND__POST_ENTER_IN_ERROR: _ClassVar[EventType]
783
+ CHANGE_MEDICATION_COMMAND__PRE_EXECUTE_ACTION: _ClassVar[EventType]
784
+ CHANGE_MEDICATION_COMMAND__POST_EXECUTE_ACTION: _ClassVar[EventType]
785
+ CHANGE_MEDICATION_COMMAND__POST_INSERTED_INTO_NOTE: _ClassVar[EventType]
786
+ CHANGE_MEDICATION__MEDICATION__PRE_SEARCH: _ClassVar[EventType]
787
+ CHANGE_MEDICATION__MEDICATION__POST_SEARCH: _ClassVar[EventType]
773
788
  SHOW_NOTE_HEADER_BUTTON: _ClassVar[EventType]
774
789
  SHOW_NOTE_FOOTER_BUTTON: _ClassVar[EventType]
775
790
  ACTION_BUTTON_CLICKED: _ClassVar[EventType]
@@ -1571,6 +1586,21 @@ DEFER_CODING_GAP_COMMAND__POST_ENTER_IN_ERROR: EventType
1571
1586
  DEFER_CODING_GAP_COMMAND__PRE_EXECUTE_ACTION: EventType
1572
1587
  DEFER_CODING_GAP_COMMAND__POST_EXECUTE_ACTION: EventType
1573
1588
  DEFER_CODING_GAP_COMMAND__POST_INSERTED_INTO_NOTE: EventType
1589
+ CHANGE_MEDICATION_COMMAND__PRE_ORIGINATE: EventType
1590
+ CHANGE_MEDICATION_COMMAND__POST_ORIGINATE: EventType
1591
+ CHANGE_MEDICATION_COMMAND__PRE_UPDATE: EventType
1592
+ CHANGE_MEDICATION_COMMAND__POST_UPDATE: EventType
1593
+ CHANGE_MEDICATION_COMMAND__PRE_COMMIT: EventType
1594
+ CHANGE_MEDICATION_COMMAND__POST_COMMIT: EventType
1595
+ CHANGE_MEDICATION_COMMAND__PRE_DELETE: EventType
1596
+ CHANGE_MEDICATION_COMMAND__POST_DELETE: EventType
1597
+ CHANGE_MEDICATION_COMMAND__PRE_ENTER_IN_ERROR: EventType
1598
+ CHANGE_MEDICATION_COMMAND__POST_ENTER_IN_ERROR: EventType
1599
+ CHANGE_MEDICATION_COMMAND__PRE_EXECUTE_ACTION: EventType
1600
+ CHANGE_MEDICATION_COMMAND__POST_EXECUTE_ACTION: EventType
1601
+ CHANGE_MEDICATION_COMMAND__POST_INSERTED_INTO_NOTE: EventType
1602
+ CHANGE_MEDICATION__MEDICATION__PRE_SEARCH: EventType
1603
+ CHANGE_MEDICATION__MEDICATION__POST_SEARCH: EventType
1574
1604
  SHOW_NOTE_HEADER_BUTTON: EventType
1575
1605
  SHOW_NOTE_FOOTER_BUTTON: EventType
1576
1606
  ACTION_BUTTON_CLICKED: EventType
canvas_sdk/base.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from datetime import date, datetime
2
2
  from enum import Enum
3
3
  from typing import Any
4
+ from uuid import UUID
4
5
 
5
6
  from pydantic import BaseModel, ConfigDict
6
7
  from pydantic_core import InitErrorDetails, PydanticCustomError, ValidationError
@@ -51,3 +52,58 @@ class Model(BaseModel):
51
52
  self.model_validate(self)
52
53
  if error_details := self._get_error_details(method):
53
54
  raise ValidationError.from_exception_data(self.__class__.__name__, error_details)
55
+
56
+
57
+ class TrackableFieldsModel(Model):
58
+ """
59
+ A base model with additional functionality for tracking modified fields.
60
+
61
+ Attributes:
62
+ _dirty_keys (set[str]): A set to track which fields have been modified.
63
+ """
64
+
65
+ _dirty_excluded_keys: list[str] = [
66
+ "note_uuid",
67
+ ]
68
+
69
+ _dirty_keys: set[str] = set()
70
+
71
+ def __init__(self, /, **data: Any) -> None:
72
+ """Initialize the command and mark all provided keys as dirty."""
73
+ super().__init__(**data)
74
+
75
+ # Initialize a set to track which fields have been modified.
76
+ self._dirty_keys = set()
77
+
78
+ # Explicitly mark all keys provided in the constructor as dirty.
79
+ self._dirty_keys.update(data.keys())
80
+
81
+ def __setattr__(self, name: str, value: Any) -> None:
82
+ """Set an attribute and mark it as dirty unless excluded."""
83
+ if not name.startswith("_") and name not in self._dirty_excluded_keys:
84
+ self._dirty_keys.add(name)
85
+ super().__setattr__(name, value)
86
+
87
+ def is_dirty(self, key: str) -> bool:
88
+ """Returns True if the given property has been modified (i.e. marked as dirty), False otherwise."""
89
+ return key in self._dirty_keys
90
+
91
+ @property
92
+ def values(self) -> dict:
93
+ """Return a dictionary of modified attributes with type-specific transformations."""
94
+ result = {}
95
+ for key in self._dirty_keys:
96
+ value = getattr(self, key)
97
+ if isinstance(value, Enum):
98
+ # If it's an enum, use its .value.
99
+ result[key] = value.value if value else None
100
+ elif isinstance(value, date | datetime):
101
+ # If it's a date/datetime, use isoformat().
102
+ result[key] = value.isoformat() if value else None
103
+ elif isinstance(value, UUID):
104
+ # If it's a UUID, use its string representation.
105
+ result[key] = str(value) if value else None
106
+ else:
107
+ # For strings, integers, or any other type, return as is.
108
+ result[key] = value
109
+ return result
@@ -1,18 +1,18 @@
1
- import datetime
2
1
  import json
3
2
  import re
4
- from enum import Enum, EnumType
3
+ from enum import EnumType
5
4
  from types import NoneType, UnionType
6
5
  from typing import Any, Union, get_args, get_origin
7
- from uuid import UUID
8
6
 
9
- from canvas_sdk.base import Model
7
+ from django.core.exceptions import ImproperlyConfigured
8
+
9
+ from canvas_sdk.base import TrackableFieldsModel
10
10
  from canvas_sdk.commands.constants import Coding
11
11
  from canvas_sdk.effects import Effect
12
12
  from canvas_sdk.effects.protocol_card import Recommendation
13
13
 
14
14
 
15
- class _BaseCommand(Model):
15
+ class _BaseCommand(TrackableFieldsModel):
16
16
  class Meta:
17
17
  key = ""
18
18
  originate_required_fields = ("note_uuid",)
@@ -21,34 +21,29 @@ class _BaseCommand(Model):
21
21
  commit_required_fields = ("command_uuid",)
22
22
  enter_in_error_required_fields = ("command_uuid",)
23
23
 
24
- # A set to track which fields have been modified.
25
- _dirty_keys: set[str] = set()
24
+ _dirty_excluded_keys = [
25
+ "note_uuid",
26
+ "command_uuid",
27
+ ]
26
28
 
27
29
  def __init__(self, /, **data: Any) -> None:
28
30
  """Initialize the command and mark all provided keys as dirty."""
29
31
  super().__init__(**data)
30
32
 
31
- # Initialize a set to track which fields have been modified.
32
- self._dirty_keys = set()
33
-
34
- # Explicitly mark all keys provided in the constructor as dirty.
35
- self._dirty_keys.update(data.keys())
33
+ def __init_subclass__(cls, **kwargs: Any) -> None:
34
+ """Validate that the command has a key and required fields."""
35
+ if not hasattr(cls.Meta, "key") or not cls.Meta.key:
36
+ raise ImproperlyConfigured(f"Command {cls.__name__!r} must specify Meta.key.")
36
37
 
37
- def __setattr__(self, name: str, value: Any) -> None:
38
- """Set an attribute and mark it as dirty unless excluded."""
39
- if not name.startswith("_") and name not in (
40
- "note_uuid",
41
- "command_uuid",
42
- ):
43
- self._dirty_keys.add(name)
44
- super().__setattr__(name, value)
38
+ if hasattr(cls.Meta, "commit_required_fields"):
39
+ command_fields = set(cls.__pydantic_fields__.keys() | cls.__annotations__.keys())
40
+ for field in cls.Meta.commit_required_fields:
41
+ if field not in command_fields:
42
+ raise ImproperlyConfigured(f"Command {cls.__name__!r} must specify {field}.")
45
43
 
46
- def is_dirty(self, key: str) -> bool:
47
- """Returns True if the given property has been modified (i.e. marked as dirty), False otherwise."""
48
- return key in self._dirty_keys
49
-
50
- def constantized_key(self) -> str:
51
- return re.sub(r"(?<!^)(?=[A-Z])", "_", self.Meta.key).upper()
44
+ @classmethod
45
+ def constantized_key(cls) -> str:
46
+ return re.sub(r"(?<!^)(?=[A-Z])", "_", cls.Meta.key).upper()
52
47
 
53
48
  note_uuid: str | None = None
54
49
  command_uuid: str | None = None
@@ -58,26 +53,6 @@ class _BaseCommand(Model):
58
53
  command_required_fields = super()._get_effect_method_required_fields(method)
59
54
  return tuple(set(base_required_fields) | set(command_required_fields))
60
55
 
61
- @property
62
- def values(self) -> dict:
63
- """Return a dictionary of modified attributes with type-specific transformations."""
64
- result = {}
65
- for key in self._dirty_keys:
66
- value = getattr(self, key)
67
- if isinstance(value, Enum):
68
- # If it's an enum, use its .value.
69
- result[key] = value.value if value else None
70
- elif isinstance(value, datetime.date | datetime.datetime):
71
- # If it's a date/datetime, use isoformat().
72
- result[key] = value.isoformat() if value else None
73
- elif isinstance(value, UUID):
74
- # If it's a UUID, use its string representation.
75
- result[key] = str(value) if value else None
76
- else:
77
- # For strings, integers, or any other type, return as is.
78
- result[key] = value
79
- return result
80
-
81
56
  @property
82
57
  def coding_filter(self) -> Coding | None:
83
58
  """The coding filter used for command insertion in protocol cards."""
@@ -115,6 +90,7 @@ class _BaseCommand(Model):
115
90
  base_properties = {"note_uuid", "command_uuid"}
116
91
  schema = cls.model_json_schema()
117
92
  required_fields: tuple = getattr(cls.Meta, "commit_required_fields", ())
93
+
118
94
  return {
119
95
  definition.get("commands_api_name", name): {
120
96
  "required": name in required_fields,
@@ -8,16 +8,6 @@ class AdjustPrescriptionCommand(RefillCommand):
8
8
 
9
9
  class Meta:
10
10
  key = "adjustPrescription"
11
- commit_required_fields = (
12
- "fdb_code",
13
- "sig",
14
- "quantity_to_dispense",
15
- "type_to_dispense",
16
- "refills",
17
- "substitutions",
18
- "prescriber_id",
19
- "new_fdb_code",
20
- )
21
11
 
22
12
  new_fdb_code: str | None = Field(
23
13
  default=None, json_schema_extra={"commands_api_name": "change_medication_to"}
@@ -26,7 +26,6 @@ class AllergyCommand(BaseCommand):
26
26
 
27
27
  class Meta:
28
28
  key = "allergy"
29
- commit_required_fields = ("allergy",)
30
29
 
31
30
  class Severity(Enum):
32
31
  MILD = "mild"
@@ -1,4 +1,5 @@
1
1
  from enum import Enum
2
+ from uuid import UUID
2
3
 
3
4
  from pydantic import Field
4
5
 
@@ -10,14 +11,13 @@ class AssessCommand(_BaseCommand):
10
11
 
11
12
  class Meta:
12
13
  key = "assess"
13
- commit_required_fields = ("condition_id",)
14
14
 
15
15
  class Status(Enum):
16
16
  IMPROVED = "improved"
17
17
  STABLE = "stable"
18
18
  DETERIORATED = "deteriorated"
19
19
 
20
- condition_id: str | None = Field(
20
+ condition_id: UUID | str | None = Field(
21
21
  default=None, json_schema_extra={"commands_api_name": "condition"}
22
22
  )
23
23
  background: str | None = None
@@ -0,0 +1,58 @@
1
+ from typing import Literal
2
+
3
+ from pydantic import Field
4
+ from pydantic_core import InitErrorDetails
5
+
6
+ from canvas_sdk.commands.base import _BaseCommand as BaseCommand
7
+ from canvas_sdk.v1.data import Medication, Note
8
+
9
+
10
+ class ChangeMedicationCommand(BaseCommand):
11
+ """A class for managing a ChangeMedication command within a specific note."""
12
+
13
+ class Meta:
14
+ key = "changeMedication"
15
+ commit_required_fields = ("medication_id",)
16
+
17
+ medication_id: str | None = Field(
18
+ default=None, json_schema_extra={"commands_api_name": "medication"}
19
+ )
20
+ sig: str | None = None
21
+
22
+ def _get_error_details(
23
+ self, method: Literal["originate", "edit", "delete", "commit", "enter_in_error"]
24
+ ) -> list[InitErrorDetails]:
25
+ errors = super()._get_error_details(method)
26
+
27
+ note = None
28
+
29
+ if self.note_uuid:
30
+ note = Note.objects.filter(id=self.note_uuid).first()
31
+
32
+ if not note:
33
+ errors.append(
34
+ self._create_error_detail(
35
+ "value",
36
+ f"note with id {self.note_uuid} not found.",
37
+ self.note_uuid,
38
+ )
39
+ )
40
+
41
+ if self.medication_id and note:
42
+ medication = Medication.objects.filter(
43
+ id=self.medication_id, patient=note.patient
44
+ ).first()
45
+
46
+ if not medication:
47
+ errors.append(
48
+ self._create_error_detail(
49
+ "value",
50
+ f"Medication with Id {self.medication_id} not found or not associated with the patient.",
51
+ self.medication_id,
52
+ )
53
+ )
54
+
55
+ return errors
56
+
57
+
58
+ __exports__ = ("ChangeMedicationCommand",)
@@ -7,7 +7,6 @@ class CloseGoalCommand(BaseCommand):
7
7
 
8
8
  class Meta:
9
9
  key = "closeGoal"
10
- commit_required_fields = ("goal_id",)
11
10
 
12
11
  goal_id: int | None = None
13
12
  achievement_status: GoalCommand.AchievementStatus | None = None
@@ -10,7 +10,6 @@ class DiagnoseCommand(_BaseCommand):
10
10
 
11
11
  class Meta:
12
12
  key = "diagnose"
13
- commit_required_fields = ("icd10_code",)
14
13
 
15
14
  icd10_code: str | None = Field(
16
15
  default=None, json_schema_extra={"commands_api_name": "diagnose"}
@@ -6,7 +6,6 @@ class PhysicalExamCommand(QuestionnaireCommand):
6
6
 
7
7
  class Meta:
8
8
  key = "exam"
9
- commit_required_fields = ("questionnaire_id",)
10
9
 
11
10
 
12
11
  __exports__ = ("PhysicalExamCommand",)
@@ -6,7 +6,6 @@ class FamilyHistoryCommand(BaseCommand):
6
6
 
7
7
  class Meta:
8
8
  key = "familyHistory"
9
- commit_required_fields = ("family_history",)
10
9
 
11
10
  family_history: str | None = None
12
11
  relative: str | None = None
@@ -2,6 +2,7 @@ from datetime import date
2
2
  from typing import Literal
3
3
  from uuid import UUID
4
4
 
5
+ from pydantic import Field
5
6
  from pydantic_core import InitErrorDetails
6
7
 
7
8
  from canvas_sdk.commands.base import _BaseCommand
@@ -14,11 +15,12 @@ class FollowUpCommand(_BaseCommand):
14
15
 
15
16
  class Meta:
16
17
  key = "followUp"
17
- commit_required_fields = ("requested_date", "note_type")
18
18
 
19
19
  structured: bool = False
20
20
  requested_date: date | None = None
21
- note_type_id: UUID | str | None = None
21
+ note_type_id: UUID | str | None = Field(
22
+ default=None, json_schema_extra={"commands_api_name": "note_type"}
23
+ )
22
24
  reason_for_visit: Coding | UUID | str | None = None
23
25
  comment: str | None = None
24
26
 
@@ -1,5 +1,7 @@
1
- from datetime import datetime
2
- from enum import Enum
1
+ from datetime import date, datetime
2
+ from enum import StrEnum
3
+
4
+ from pydantic import Field
3
5
 
4
6
  from canvas_sdk.commands.base import _BaseCommand
5
7
 
@@ -9,14 +11,13 @@ class GoalCommand(_BaseCommand):
9
11
 
10
12
  class Meta:
11
13
  key = "goal"
12
- commit_required_fields = ("goal_statement", "start_date")
13
14
 
14
- class Priority(Enum):
15
+ class Priority(StrEnum):
15
16
  HIGH = "high-priority"
16
17
  MEDIUM = "medium-priority"
17
18
  LOW = "low-priority"
18
19
 
19
- class AchievementStatus(Enum):
20
+ class AchievementStatus(StrEnum):
20
21
  IN_PROGRESS = "in-progress"
21
22
  IMPROVING = "improving"
22
23
  WORSENING = "worsening"
@@ -28,8 +29,8 @@ class GoalCommand(_BaseCommand):
28
29
  NOT_ATTAINABLE = "not-attainable"
29
30
 
30
31
  goal_statement: str = ""
31
- start_date: datetime = datetime.now()
32
- due_date: datetime | None = None
32
+ start_date: date = Field(default_factory=lambda: datetime.now().date())
33
+ due_date: date | None = None
33
34
  achievement_status: AchievementStatus | None = None
34
35
  priority: Priority | None = None
35
36
  progress: str | None = None
@@ -6,7 +6,6 @@ class HistoryOfPresentIllnessCommand(_BaseCommand):
6
6
 
7
7
  class Meta:
8
8
  key = "hpi"
9
- commit_required_fields = ("narrative",)
10
9
 
11
10
  narrative: str | None = None
12
11
 
@@ -1,5 +1,7 @@
1
1
  from enum import Enum
2
2
 
3
+ from pydantic import Field
4
+
3
5
  from canvas_sdk.commands.base import _BaseCommand as BaseCommand
4
6
  from canvas_sdk.commands.constants import ServiceProvider
5
7
 
@@ -9,21 +11,20 @@ class ImagingOrderCommand(BaseCommand):
9
11
 
10
12
  class Meta:
11
13
  key = "imagingOrder"
12
- commit_required_fields = (
13
- "image_code",
14
- "diagnosis_codes",
15
- "ordering_provider",
16
- )
17
14
 
18
15
  class Priority(Enum):
19
16
  ROUTINE = "Routine"
20
17
  URGENT = "Urgent"
21
18
 
22
- image_code: str | None = None
23
- diagnosis_codes: list[str] = []
19
+ image_code: str | None = Field(default=None, json_schema_extra={"commands_api_name": "image"})
20
+ diagnosis_codes: list[str] | None = Field(
21
+ default=None, json_schema_extra={"commands_api_name": "indications"}
22
+ )
24
23
  priority: Priority | None = None
25
24
  additional_details: str | None = None
26
- service_provider: ServiceProvider | None = None
25
+ service_provider: ServiceProvider | None = Field(
26
+ default=None, json_schema_extra={"commands_api_name": "ordering_provider"}
27
+ )
27
28
  comment: str | None = None
28
29
  ordering_provider_key: str | None = None
29
30
  linked_items_urns: list[str] | None = None
@@ -1,3 +1,4 @@
1
+ from pydantic import Field
1
2
  from pydantic_core import InitErrorDetails
2
3
 
3
4
  from canvas_sdk.commands.base import _BaseCommand as BaseCommand
@@ -9,9 +10,8 @@ class InstructCommand(BaseCommand):
9
10
 
10
11
  class Meta:
11
12
  key = "instruct"
12
- commit_required_fields = ("instruction",)
13
13
 
14
- coding: Coding | None = None
14
+ coding: Coding | None = Field(default=None, json_schema_extra={"commands_api_name": "instruct"})
15
15
  comment: str | None = None
16
16
 
17
17
  def _get_error_details(self, method: str) -> list[InitErrorDetails]:
@@ -2,6 +2,7 @@ from typing import Literal
2
2
  from uuid import UUID
3
3
 
4
4
  from django.db.models.query_utils import Q
5
+ from pydantic import Field
5
6
  from pydantic_core import InitErrorDetails
6
7
 
7
8
  from canvas_sdk.commands.base import _BaseCommand as BaseCommand
@@ -13,17 +14,17 @@ class LabOrderCommand(BaseCommand):
13
14
 
14
15
  class Meta:
15
16
  key = "labOrder"
16
- commit_required_fields = (
17
- "lab_partner",
18
- "tests_order_codes",
19
- "ordering_provider",
20
- "diagnosis_codes",
21
- )
22
17
 
23
18
  lab_partner: UUID | str | None = None
24
- tests_order_codes: list[str] = []
25
- ordering_provider_key: str | None = None
26
- diagnosis_codes: list[str] = []
19
+ tests_order_codes: list[str] = Field(
20
+ default=[], json_schema_extra={"commands_api_name": "tests"}
21
+ )
22
+ ordering_provider_key: str | None = Field(
23
+ default=None, json_schema_extra={"commands_api_name": "ordering_provider"}
24
+ )
25
+ diagnosis_codes: list[str] = Field(
26
+ default=[], json_schema_extra={"commands_api_name": "diagnosis"}
27
+ )
27
28
  fasting_required: bool = False
28
29
  comment: str | None = None
29
30
 
@@ -10,7 +10,6 @@ class MedicalHistoryCommand(BaseCommand):
10
10
 
11
11
  class Meta:
12
12
  key = "medicalHistory"
13
- commit_required_fields = ("past_medical_history",)
14
13
 
15
14
  past_medical_history: str | None = None
16
15
  approximate_start_date: date | None = None
@@ -8,7 +8,6 @@ class MedicationStatementCommand(_BaseCommand):
8
8
 
9
9
  class Meta:
10
10
  key = "medicationStatement"
11
- commit_required_fields = ("fdb_code",)
12
11
 
13
12
  fdb_code: str | None = Field(
14
13
  default=None, json_schema_extra={"commands_api_name": "medication"}
@@ -10,7 +10,6 @@ class PastSurgicalHistoryCommand(BaseCommand):
10
10
 
11
11
  class Meta:
12
12
  key = "surgicalHistory"
13
- commit_required_fields = ("past_surgical_history",)
14
13
 
15
14
  past_surgical_history: str | None = None
16
15
  approximate_date: date | None = None
@@ -1,3 +1,5 @@
1
+ from pydantic import Field
2
+
1
3
  from canvas_sdk.commands.base import _BaseCommand as BaseCommand
2
4
 
3
5
 
@@ -6,9 +8,8 @@ class PerformCommand(BaseCommand):
6
8
 
7
9
  class Meta:
8
10
  key = "perform"
9
- commit_required_fields = ("cpt_code",)
10
11
 
11
- cpt_code: str | None = None
12
+ cpt_code: str | None = Field(default=None, json_schema_extra={"commands_api_name": "perform"})
12
13
  notes: str | None = None
13
14
 
14
15
 
@@ -6,7 +6,6 @@ class PlanCommand(_BaseCommand):
6
6
 
7
7
  class Meta:
8
8
  key = "plan"
9
- commit_required_fields = ("narrative",)
10
9
 
11
10
  narrative: str = ""
12
11
 
@@ -12,15 +12,6 @@ class PrescribeCommand(_BaseCommand):
12
12
 
13
13
  class Meta:
14
14
  key = "prescribe"
15
- commit_required_fields = (
16
- "fdb_code",
17
- "sig",
18
- "quantity_to_dispense",
19
- "type_to_dispense",
20
- "refills",
21
- "substitutions",
22
- "prescriber_id",
23
- )
24
15
 
25
16
  class Substitutions(Enum):
26
17
  ALLOWED = "allowed"
@@ -1,5 +1,7 @@
1
1
  from enum import Enum
2
2
 
3
+ from pydantic import Field
4
+
3
5
  from canvas_sdk.commands.base import _BaseCommand as BaseCommand
4
6
  from canvas_sdk.commands.constants import ServiceProvider
5
7
 
@@ -9,12 +11,6 @@ class ReferCommand(BaseCommand):
9
11
 
10
12
  class Meta:
11
13
  key = "refer"
12
- commit_required_fields = (
13
- "service_provider",
14
- "diagnosis_codes",
15
- "clinical_question",
16
- "notes_to_specialist",
17
- )
18
14
 
19
15
  class ClinicalQuestion(Enum):
20
16
  """Clinical question choices."""
@@ -30,8 +26,12 @@ class ReferCommand(BaseCommand):
30
26
  ROUTINE = "Routine"
31
27
  URGENT = "Urgent"
32
28
 
33
- service_provider: ServiceProvider | None = None
34
- diagnosis_codes: list[str] = []
29
+ service_provider: ServiceProvider | None = Field(
30
+ default=None, json_schema_extra={"commands_api_name": "refer_to"}
31
+ )
32
+ diagnosis_codes: list[str] = Field(
33
+ default=[], json_schema_extra={"commands_api_name": "indications"}
34
+ )
35
35
  clinical_question: ClinicalQuestion | None = None
36
36
  priority: Priority | None = None
37
37
  notes_to_specialist: str | None = None
@@ -44,8 +44,8 @@ class ReferCommand(BaseCommand):
44
44
  """The Refer command's field values."""
45
45
  values = super().values
46
46
 
47
- if self.is_dirty("service_provider"):
48
- values["service_provider"] = self.service_provider.__dict__
47
+ if self.service_provider and self.is_dirty("service_provider"):
48
+ values["service_provider"] = self.service_provider.model_dump()
49
49
 
50
50
  return values
51
51
 
@@ -12,15 +12,6 @@ class RefillCommand(PrescribeCommand):
12
12
 
13
13
  class Meta:
14
14
  key = "refill"
15
- commit_required_fields = (
16
- "fdb_code",
17
- "sig",
18
- "quantity_to_dispense",
19
- "type_to_dispense",
20
- "refills",
21
- "substitutions",
22
- "prescriber_id",
23
- )
24
15
 
25
16
  def _get_error_details(
26
17
  self, method: Literal["originate", "edit", "delete", "commit", "enter_in_error"]
@@ -8,7 +8,6 @@ class RemoveAllergyCommand(BaseCommand):
8
8
 
9
9
  class Meta:
10
10
  key = "removeAllergy"
11
- commit_required_fields = ("allergy_id",)
12
11
 
13
12
  allergy_id: str | None = Field(
14
13
  description="The external ID of the allergy to remove.",