canvas 0.63.0__py3-none-any.whl → 0.89.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.
Files changed (185) hide show
  1. {canvas-0.63.0.dist-info → canvas-0.89.0.dist-info}/METADATA +4 -1
  2. {canvas-0.63.0.dist-info → canvas-0.89.0.dist-info}/RECORD +184 -98
  3. {canvas-0.63.0.dist-info → canvas-0.89.0.dist-info}/WHEEL +1 -1
  4. canvas_cli/apps/emit/event_fixtures/UNKNOWN.ndjson +1 -0
  5. canvas_cli/apps/logs/logs.py +386 -22
  6. canvas_cli/main.py +3 -1
  7. canvas_cli/templates/plugins/default/{{ cookiecutter.__project_slug }}/tests/test_models.py +46 -4
  8. canvas_cli/utils/context/context.py +13 -13
  9. canvas_cli/utils/validators/manifest_schema.py +26 -1
  10. canvas_generated/messages/effects_pb2.py +5 -5
  11. canvas_generated/messages/effects_pb2.pyi +108 -2
  12. canvas_generated/messages/events_pb2.py +6 -6
  13. canvas_generated/messages/events_pb2.pyi +282 -2
  14. canvas_sdk/clients/__init__.py +1 -0
  15. canvas_sdk/clients/llms/__init__.py +17 -0
  16. canvas_sdk/clients/llms/libraries/__init__.py +11 -0
  17. canvas_sdk/clients/llms/libraries/llm_anthropic.py +87 -0
  18. canvas_sdk/clients/llms/libraries/llm_api.py +143 -0
  19. canvas_sdk/clients/llms/libraries/llm_google.py +92 -0
  20. canvas_sdk/clients/llms/libraries/llm_openai.py +98 -0
  21. canvas_sdk/clients/llms/structures/__init__.py +9 -0
  22. canvas_sdk/clients/llms/structures/llm_response.py +33 -0
  23. canvas_sdk/clients/llms/structures/llm_tokens.py +53 -0
  24. canvas_sdk/clients/llms/structures/llm_turn.py +47 -0
  25. canvas_sdk/clients/llms/structures/settings/__init__.py +13 -0
  26. canvas_sdk/clients/llms/structures/settings/llm_settings.py +27 -0
  27. canvas_sdk/clients/llms/structures/settings/llm_settings_anthropic.py +43 -0
  28. canvas_sdk/clients/llms/structures/settings/llm_settings_gemini.py +40 -0
  29. canvas_sdk/clients/llms/structures/settings/llm_settings_gpt4.py +40 -0
  30. canvas_sdk/clients/llms/structures/settings/llm_settings_gpt5.py +48 -0
  31. canvas_sdk/clients/third_party.py +3 -0
  32. canvas_sdk/commands/__init__.py +12 -0
  33. canvas_sdk/commands/base.py +33 -2
  34. canvas_sdk/commands/commands/adjust_prescription.py +4 -0
  35. canvas_sdk/commands/commands/custom_command.py +86 -0
  36. canvas_sdk/commands/commands/family_history.py +17 -1
  37. canvas_sdk/commands/commands/immunization_statement.py +42 -2
  38. canvas_sdk/commands/commands/medication_statement.py +16 -1
  39. canvas_sdk/commands/commands/past_surgical_history.py +16 -1
  40. canvas_sdk/commands/commands/perform.py +18 -1
  41. canvas_sdk/commands/commands/prescribe.py +8 -9
  42. canvas_sdk/commands/commands/refill.py +5 -5
  43. canvas_sdk/commands/commands/resolve_condition.py +5 -5
  44. canvas_sdk/commands/commands/review/__init__.py +3 -0
  45. canvas_sdk/commands/commands/review/base.py +72 -0
  46. canvas_sdk/commands/commands/review/imaging.py +13 -0
  47. canvas_sdk/commands/commands/review/lab.py +13 -0
  48. canvas_sdk/commands/commands/review/referral.py +13 -0
  49. canvas_sdk/commands/commands/review/uncategorized_document.py +13 -0
  50. canvas_sdk/commands/validation.py +43 -0
  51. canvas_sdk/effects/batch_originate.py +22 -0
  52. canvas_sdk/effects/calendar/__init__.py +13 -3
  53. canvas_sdk/effects/calendar/{create_calendar.py → calendar.py} +19 -5
  54. canvas_sdk/effects/calendar/event.py +172 -0
  55. canvas_sdk/effects/claim_label.py +93 -0
  56. canvas_sdk/effects/claim_line_item.py +47 -0
  57. canvas_sdk/effects/claim_queue.py +49 -0
  58. canvas_sdk/effects/fax/__init__.py +3 -0
  59. canvas_sdk/effects/fax/base.py +77 -0
  60. canvas_sdk/effects/fax/note.py +42 -0
  61. canvas_sdk/effects/metadata.py +15 -1
  62. canvas_sdk/effects/note/__init__.py +8 -1
  63. canvas_sdk/effects/note/appointment.py +135 -7
  64. canvas_sdk/effects/note/base.py +17 -0
  65. canvas_sdk/effects/note/message.py +22 -14
  66. canvas_sdk/effects/note/note.py +150 -1
  67. canvas_sdk/effects/observation/__init__.py +11 -0
  68. canvas_sdk/effects/observation/base.py +206 -0
  69. canvas_sdk/effects/patient/__init__.py +2 -0
  70. canvas_sdk/effects/patient/base.py +8 -0
  71. canvas_sdk/effects/payment/__init__.py +11 -0
  72. canvas_sdk/effects/payment/base.py +355 -0
  73. canvas_sdk/effects/payment/post_claim_payment.py +49 -0
  74. canvas_sdk/effects/send_contact_verification.py +42 -0
  75. canvas_sdk/effects/task/__init__.py +2 -1
  76. canvas_sdk/effects/task/task.py +30 -0
  77. canvas_sdk/effects/validation/__init__.py +3 -0
  78. canvas_sdk/effects/validation/base.py +92 -0
  79. canvas_sdk/events/base.py +15 -0
  80. canvas_sdk/handlers/application.py +7 -7
  81. canvas_sdk/handlers/simple_api/api.py +1 -4
  82. canvas_sdk/handlers/simple_api/websocket.py +1 -4
  83. canvas_sdk/handlers/utils.py +14 -0
  84. canvas_sdk/questionnaires/utils.py +1 -0
  85. canvas_sdk/templates/utils.py +17 -4
  86. canvas_sdk/test_utils/factories/FACTORY_GUIDE.md +362 -0
  87. canvas_sdk/test_utils/factories/__init__.py +115 -0
  88. canvas_sdk/test_utils/factories/calendar.py +24 -0
  89. canvas_sdk/test_utils/factories/claim.py +81 -0
  90. canvas_sdk/test_utils/factories/claim_diagnosis_code.py +16 -0
  91. canvas_sdk/test_utils/factories/coverage.py +17 -0
  92. canvas_sdk/test_utils/factories/imaging.py +74 -0
  93. canvas_sdk/test_utils/factories/lab.py +192 -0
  94. canvas_sdk/test_utils/factories/medication_history.py +75 -0
  95. canvas_sdk/test_utils/factories/note.py +52 -0
  96. canvas_sdk/test_utils/factories/organization.py +50 -0
  97. canvas_sdk/test_utils/factories/practicelocation.py +88 -0
  98. canvas_sdk/test_utils/factories/referral.py +81 -0
  99. canvas_sdk/test_utils/factories/staff.py +111 -0
  100. canvas_sdk/test_utils/factories/task.py +66 -0
  101. canvas_sdk/test_utils/factories/uncategorized_clinical_document.py +48 -0
  102. canvas_sdk/utils/metrics.py +4 -1
  103. canvas_sdk/v1/data/__init__.py +66 -7
  104. canvas_sdk/v1/data/allergy_intolerance.py +5 -11
  105. canvas_sdk/v1/data/appointment.py +18 -4
  106. canvas_sdk/v1/data/assessment.py +2 -12
  107. canvas_sdk/v1/data/banner_alert.py +2 -4
  108. canvas_sdk/v1/data/base.py +53 -14
  109. canvas_sdk/v1/data/billing.py +8 -11
  110. canvas_sdk/v1/data/calendar.py +64 -0
  111. canvas_sdk/v1/data/care_team.py +4 -10
  112. canvas_sdk/v1/data/claim.py +172 -66
  113. canvas_sdk/v1/data/claim_diagnosis_code.py +19 -0
  114. canvas_sdk/v1/data/claim_line_item.py +2 -5
  115. canvas_sdk/v1/data/coding.py +19 -0
  116. canvas_sdk/v1/data/command.py +2 -4
  117. canvas_sdk/v1/data/common.py +10 -0
  118. canvas_sdk/v1/data/compound_medication.py +3 -4
  119. canvas_sdk/v1/data/condition.py +4 -9
  120. canvas_sdk/v1/data/coverage.py +66 -26
  121. canvas_sdk/v1/data/detected_issue.py +20 -20
  122. canvas_sdk/v1/data/device.py +2 -14
  123. canvas_sdk/v1/data/discount.py +2 -5
  124. canvas_sdk/v1/data/encounter.py +44 -0
  125. canvas_sdk/v1/data/facility.py +1 -0
  126. canvas_sdk/v1/data/goal.py +2 -14
  127. canvas_sdk/v1/data/imaging.py +4 -30
  128. canvas_sdk/v1/data/immunization.py +7 -15
  129. canvas_sdk/v1/data/lab.py +12 -65
  130. canvas_sdk/v1/data/line_item_transaction.py +2 -5
  131. canvas_sdk/v1/data/medication.py +3 -8
  132. canvas_sdk/v1/data/medication_history.py +142 -0
  133. canvas_sdk/v1/data/medication_statement.py +41 -0
  134. canvas_sdk/v1/data/message.py +4 -8
  135. canvas_sdk/v1/data/note.py +37 -38
  136. canvas_sdk/v1/data/observation.py +9 -36
  137. canvas_sdk/v1/data/organization.py +70 -9
  138. canvas_sdk/v1/data/patient.py +8 -12
  139. canvas_sdk/v1/data/patient_consent.py +4 -14
  140. canvas_sdk/v1/data/payment_collection.py +2 -5
  141. canvas_sdk/v1/data/posting.py +3 -9
  142. canvas_sdk/v1/data/practicelocation.py +66 -7
  143. canvas_sdk/v1/data/protocol_override.py +3 -4
  144. canvas_sdk/v1/data/protocol_result.py +3 -3
  145. canvas_sdk/v1/data/questionnaire.py +10 -26
  146. canvas_sdk/v1/data/reason_for_visit.py +2 -6
  147. canvas_sdk/v1/data/referral.py +41 -17
  148. canvas_sdk/v1/data/staff.py +34 -26
  149. canvas_sdk/v1/data/stop_medication_event.py +27 -0
  150. canvas_sdk/v1/data/task.py +30 -11
  151. canvas_sdk/v1/data/team.py +2 -4
  152. canvas_sdk/v1/data/uncategorized_clinical_document.py +84 -0
  153. canvas_sdk/v1/data/user.py +14 -0
  154. canvas_sdk/v1/data/utils.py +5 -0
  155. canvas_sdk/value_set/v2026/__init__.py +1 -0
  156. canvas_sdk/value_set/v2026/adverse_event.py +157 -0
  157. canvas_sdk/value_set/v2026/allergy.py +116 -0
  158. canvas_sdk/value_set/v2026/assessment.py +466 -0
  159. canvas_sdk/value_set/v2026/communication.py +496 -0
  160. canvas_sdk/value_set/v2026/condition.py +52934 -0
  161. canvas_sdk/value_set/v2026/device.py +315 -0
  162. canvas_sdk/value_set/v2026/diagnostic_study.py +5243 -0
  163. canvas_sdk/value_set/v2026/encounter.py +2714 -0
  164. canvas_sdk/value_set/v2026/immunization.py +297 -0
  165. canvas_sdk/value_set/v2026/individual_characteristic.py +339 -0
  166. canvas_sdk/value_set/v2026/intervention.py +1703 -0
  167. canvas_sdk/value_set/v2026/laboratory_test.py +1831 -0
  168. canvas_sdk/value_set/v2026/medication.py +8218 -0
  169. canvas_sdk/value_set/v2026/no_qdm_category_assigned.py +26493 -0
  170. canvas_sdk/value_set/v2026/physical_exam.py +342 -0
  171. canvas_sdk/value_set/v2026/procedure.py +27869 -0
  172. canvas_sdk/value_set/v2026/symptom.py +625 -0
  173. logger/logger.py +30 -31
  174. logger/logstash.py +282 -0
  175. logger/pubsub.py +26 -0
  176. plugin_runner/allowed-module-imports.json +940 -9
  177. plugin_runner/generate_allowed_imports.py +1 -0
  178. plugin_runner/installation.py +2 -2
  179. plugin_runner/plugin_runner.py +21 -24
  180. plugin_runner/sandbox.py +34 -0
  181. protobufs/canvas_generated/messages/effects.proto +65 -0
  182. protobufs/canvas_generated/messages/events.proto +150 -51
  183. settings.py +27 -11
  184. canvas_sdk/effects/calendar/create_event.py +0 -43
  185. {canvas-0.63.0.dist-info → canvas-0.89.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,362 @@
1
+ # Canvas SDK Factory Guide
2
+
3
+ This guide explains how to use the existing Canvas SDK test factories and how to create your own custom factories.
4
+
5
+ ## Available Factories
6
+
7
+ The Canvas SDK provides the following built-in factories in `canvas_sdk.test_utils.factories`:
8
+
9
+ ### Core Entity Factories
10
+ - **PatientFactory** - Creates test patient records
11
+ - **StaffFactory** - Creates staff/provider records
12
+ - **CanvasUserFactory** (UserFactory) - Creates user accounts
13
+ - **OrganizationFactory** - Creates organization entities
14
+ - **FacilityFactory** - Creates healthcare facilities
15
+ - **PracticeLocationFactory** - Creates practice locations
16
+
17
+ ### Clinical Data Factories
18
+ - **NoteFactory** - Creates clinical notes (automatically creates initial state)
19
+ - **ClaimFactory** - Creates insurance claims
20
+ - **ClaimDiagnosisCodeFactory** - Creates diagnosis codes for claims
21
+ - **MedicationHistoryMedicationFactory** - Creates medication history records
22
+ - **ProtocolCurrentFactory** - Creates protocol instances
23
+
24
+ ### Related Factories
25
+ - **PatientAddressFactory** - Creates patient addresses
26
+ - **StaffAddressFactory** - Creates staff addresses
27
+ - **StaffContactPointFactory** - Creates staff contact info
28
+ - **StaffLicenseFactory** - Creates staff licenses
29
+ - **StaffRoleFactory** - Creates staff role assignments
30
+ - **NoteStateChangeEventFactory** - Creates note state change events
31
+
32
+ ## Using Existing Factories
33
+
34
+ ### Basic Usage
35
+
36
+ ```python
37
+ from canvas_sdk.test_utils.factories import PatientFactory, StaffFactory, NoteFactory
38
+
39
+ def test_example():
40
+ # Create a patient with random data
41
+ patient = PatientFactory.create()
42
+
43
+ # Access generated fields
44
+ assert patient.first_name is not None
45
+ assert patient.last_name is not None
46
+ assert patient.birth_date is not None
47
+
48
+ # Patient automatically has a user account
49
+ assert patient.user is not None
50
+ ```
51
+
52
+ ### Customizing Factory Data
53
+
54
+ ```python
55
+ def test_with_custom_data():
56
+ # Override specific fields
57
+ patient = PatientFactory.create(
58
+ first_name="John",
59
+ last_name="Doe",
60
+ birth_date=datetime.date(1980, 1, 1)
61
+ )
62
+
63
+ assert patient.first_name == "John"
64
+ assert patient.last_name == "Doe"
65
+ ```
66
+
67
+ ### Creating Multiple Instances
68
+
69
+ ```python
70
+ def test_multiple_patients():
71
+ # Create 5 patients at once
72
+ patients = PatientFactory.create_batch(5)
73
+
74
+ assert len(patients) == 5
75
+ assert all(p.id is not None for p in patients)
76
+ ```
77
+
78
+ ### Using Factories with Related Objects
79
+
80
+ ```python
81
+ def test_note_with_patient():
82
+ # Create a note (automatically creates patient, provider, and location)
83
+ note = NoteFactory.create()
84
+
85
+ assert note.patient is not None
86
+ assert note.provider is not None
87
+ assert note.location is not None
88
+
89
+ # Note automatically has initial state
90
+ assert note.state == NoteStates.NEW
91
+ ```
92
+
93
+ ### Creating Related Objects Together
94
+
95
+ ```python
96
+ def test_note_for_specific_patient():
97
+ # Create a patient first
98
+ patient = PatientFactory.create(first_name="Jane")
99
+
100
+ # Create a note for that specific patient
101
+ note = NoteFactory.create(patient=patient)
102
+
103
+ assert note.patient.first_name == "Jane"
104
+ ```
105
+
106
+ ## Creating Custom Factories
107
+
108
+ ### Basic Custom Factory
109
+
110
+ Here's how to create a custom factory for a model that doesn't have one yet:
111
+
112
+ ```python
113
+ import factory
114
+ from factory.fuzzy import FuzzyDate, FuzzyChoice
115
+ import datetime
116
+
117
+ from canvas_sdk.v1.data.appointment import Appointment as AppointmentData
118
+ from canvas_sdk.test_utils.factories import PatientFactory, StaffFactory, PracticeLocationFactory
119
+
120
+
121
+ class AppointmentFactory(factory.django.DjangoModelFactory):
122
+ """Factory for creating test Appointments."""
123
+
124
+ class Meta:
125
+ model = AppointmentData
126
+
127
+ # Required relationships
128
+ patient = factory.SubFactory(PatientFactory)
129
+ provider = factory.SubFactory(StaffFactory)
130
+ location = factory.SubFactory(PracticeLocationFactory)
131
+
132
+ # Date/time fields with reasonable defaults
133
+ start_time = factory.LazyFunction(
134
+ lambda: datetime.datetime.now() + datetime.timedelta(days=7)
135
+ )
136
+
137
+ # Use FuzzyChoice for fields with limited options
138
+ duration_minutes = FuzzyChoice([15, 30, 45, 60])
139
+
140
+ # Simple string fields
141
+ comment = factory.Faker("sentence")
142
+ description = factory.Faker("sentence")
143
+ ```
144
+
145
+ ### Usage of Custom Factory
146
+
147
+ ```python
148
+ def test_with_custom_appointment_factory():
149
+ """Example test using the custom AppointmentFactory."""
150
+ # Basic creation
151
+ appointment = AppointmentFactory.create()
152
+
153
+ assert appointment.id is not None
154
+ assert appointment.patient is not None
155
+ assert appointment.provider is not None
156
+
157
+ # With custom values
158
+ specific_patient = PatientFactory.create(first_name="Alice")
159
+ appointment = AppointmentFactory.create(
160
+ patient=specific_patient,
161
+ duration_minutes=60
162
+ )
163
+
164
+ assert appointment.patient.first_name == "Alice"
165
+ assert appointment.duration_minutes == 60
166
+ ```
167
+
168
+ ### Advanced Factory Features
169
+
170
+ #### 1. Post-Generation Hooks
171
+
172
+ Use `@factory.post_generation` to perform actions after object creation:
173
+
174
+ ```python
175
+ class TaskFactory(factory.django.DjangoModelFactory):
176
+ class Meta:
177
+ model = Task
178
+
179
+ title = factory.Faker("sentence")
180
+ patient = factory.SubFactory(PatientFactory)
181
+
182
+ @factory.post_generation
183
+ def add_labels(self, create, extracted, **kwargs):
184
+ """Add labels to the task after creation."""
185
+ if not create:
186
+ return
187
+
188
+ if extracted:
189
+ # Use provided labels
190
+ for label in extracted:
191
+ self.labels.add(label)
192
+ else:
193
+ # Add default labels
194
+ self.labels.add("test-label")
195
+
196
+ # Usage:
197
+ task = TaskFactory.create(add_labels=["urgent", "review"])
198
+ ```
199
+
200
+ #### 2. Lazy Attributes
201
+
202
+ Use `factory.LazyAttribute` for fields that depend on other fields:
203
+
204
+ ```python
205
+ class StaffFactory(factory.django.DjangoModelFactory):
206
+ class Meta:
207
+ model = Staff
208
+
209
+ first_name = factory.Faker("first_name")
210
+ last_name = factory.Faker("last_name")
211
+
212
+ # Email based on name
213
+ email = factory.LazyAttribute(
214
+ lambda obj: f"{obj.first_name.lower()}.{obj.last_name.lower()}@example.com"
215
+ )
216
+ ```
217
+
218
+ #### 3. Sequences
219
+
220
+ Use `factory.Sequence` for unique values:
221
+
222
+ ```python
223
+ class PatientFactory(factory.django.DjangoModelFactory):
224
+ class Meta:
225
+ model = Patient
226
+
227
+ # Creates patient-1@example.com, patient-2@example.com, etc.
228
+ email = factory.Sequence(lambda n: f"patient-{n}@example.com")
229
+ ```
230
+
231
+ #### 4. Conditional SubFactories
232
+
233
+ Use `factory.Maybe` for conditional relationships:
234
+
235
+ ```python
236
+ class NoteFactory(factory.django.DjangoModelFactory):
237
+ class Meta:
238
+ model = Note
239
+
240
+ is_signed = factory.Faker("boolean")
241
+
242
+ # Only create signing provider if note is signed
243
+ signing_provider = factory.Maybe(
244
+ "is_signed",
245
+ yes_declaration=factory.SubFactory(StaffFactory),
246
+ no_declaration=None
247
+ )
248
+ ```
249
+
250
+ ## Factory Best Practices
251
+
252
+ ### 1. Use Factories for All Test Data
253
+
254
+ ```python
255
+ # Good: Using factories
256
+ def test_patient_lookup():
257
+ patient = PatientFactory.create(first_name="John")
258
+ result = lookup_patient(patient.id)
259
+ assert result.first_name == "John"
260
+
261
+ # Avoid: Manual object creation
262
+ def test_patient_lookup_manual():
263
+ patient = Patient.objects.create(
264
+ first_name="John",
265
+ last_name="Doe",
266
+ birth_date=datetime.date(1980, 1, 1),
267
+ # ... many more required fields
268
+ )
269
+ ```
270
+
271
+ ### 2. Create Factories in a Shared Location
272
+
273
+ Place custom factories in a dedicated file:
274
+
275
+ ```
276
+ tests/
277
+ ├── __init__.py
278
+ ├── factories.py # Your custom factories
279
+ └── test_my_plugin.py # Your tests
280
+ ```
281
+
282
+ ### 3. Override Only What You Need
283
+
284
+ ```python
285
+ # Good: Only override relevant fields
286
+ patient = PatientFactory.create(first_name="Alice")
287
+
288
+ # Avoid: Overriding everything
289
+ patient = PatientFactory.create(
290
+ first_name="Alice",
291
+ last_name="Smith", # Could use default
292
+ birth_date=datetime.date(1990, 1, 1), # Could use default
293
+ )
294
+ ```
295
+
296
+ ### 4. Use build() Instead of create() When Database Isn't Needed
297
+
298
+ ```python
299
+ # Creates object in database
300
+ patient = PatientFactory.create()
301
+
302
+ # Creates object in memory only (faster for unit tests)
303
+ patient = PatientFactory.build()
304
+ ```
305
+
306
+ ## Common Patterns
307
+
308
+ ### Testing Protocols with Factories
309
+
310
+ ```python
311
+ from canvas_sdk.test_utils.factories import PatientFactory, NoteFactory
312
+
313
+ def test_my_protocol():
314
+ # Create test data
315
+ patient = PatientFactory.create(
316
+ first_name="Test",
317
+ birth_date=datetime.date(1980, 1, 1)
318
+ )
319
+
320
+ note = NoteFactory.create(patient=patient)
321
+
322
+ # Create a mock event
323
+ mock_event = Mock()
324
+ mock_event.context = {"patient": {"id": patient.id}}
325
+
326
+ # Test your protocol
327
+ protocol = MyProtocol(event=mock_event)
328
+ effects = protocol.compute()
329
+
330
+ assert len(effects) > 0
331
+ ```
332
+
333
+ ### Testing API Handlers with Factories
334
+
335
+ ```python
336
+ from canvas_sdk.test_utils.factories import PatientFactory, StaffFactory
337
+
338
+ def test_appointment_api():
339
+ # Create test data
340
+ patient = PatientFactory.create()
341
+ provider = StaffFactory.create()
342
+
343
+ # Create API request
344
+ api = AppointmentAPI(event=mock_event)
345
+ api.request = DummyRequest(
346
+ json_body={
347
+ "patient_id": str(patient.id),
348
+ "provider_id": str(provider.id),
349
+ "start_time": "2024-01-01T10:00:00"
350
+ }
351
+ )
352
+
353
+ # Test the API
354
+ result = api.post()
355
+ assert result[0].status_code == HTTPStatus.CREATED
356
+ ```
357
+
358
+ ## References
359
+
360
+ - [Factory Boy Documentation](https://factoryboy.readthedocs.io/)
361
+ - [Canvas SDK Testing Utils](https://docs.canvasmedical.com/sdk/testing-utils/)
362
+ - [Canvas SDK Factory Source](https://github.com/canvas-medical/canvas-plugins/tree/main/canvas_sdk/test_utils/factories)
@@ -1,13 +1,128 @@
1
+ from .calendar import CalendarFactory, EventFactory
2
+ from .claim import (
3
+ ClaimCommentFactory,
4
+ ClaimCoverageFactory,
5
+ ClaimFactory,
6
+ ClaimLabelFactory,
7
+ ClaimProviderFactory,
8
+ ClaimQueueFactory,
9
+ ClaimSubmissionFactory,
10
+ )
11
+ from .claim_diagnosis_code import ClaimDiagnosisCodeFactory
12
+ from .coverage import CoverageFactory
1
13
  from .facility import FacilityFactory
14
+ from .imaging import ImagingOrderFactory, ImagingReportFactory, ImagingReviewFactory
15
+ from .lab import (
16
+ LabOrderFactory,
17
+ LabOrderReasonConditionFactory,
18
+ LabOrderReasonFactory,
19
+ LabPartnerFactory,
20
+ LabPartnerTestFactory,
21
+ LabReportFactory,
22
+ LabReviewFactory,
23
+ LabTestFactory,
24
+ LabValueCodingFactory,
25
+ LabValueFactory,
26
+ )
27
+ from .medication_history import (
28
+ MedicationHistoryMedicationCodingFactory,
29
+ MedicationHistoryMedicationFactory,
30
+ MedicationHistoryResponseFactory,
31
+ )
32
+ from .note import NoteFactory, NoteStateChangeEventFactory, NoteTypeFactory
33
+ from .organization import (
34
+ OrganizationAddressFactory,
35
+ OrganizationContactPointFactory,
36
+ OrganizationFactory,
37
+ )
2
38
  from .patient import PatientAddressFactory, PatientFacilityAddressFactory, PatientFactory
39
+ from .practicelocation import (
40
+ PracticeLocationAddressFactory,
41
+ PracticeLocationContactPointFactory,
42
+ PracticeLocationFactory,
43
+ PracticeLocationSettingFactory,
44
+ )
3
45
  from .protocol_current import ProtocolCurrentFactory
46
+ from .referral import ReferralFactory, ReferralReportFactory, ReferralReviewFactory
47
+ from .staff import (
48
+ StaffAddressFactory,
49
+ StaffContactPointFactory,
50
+ StaffFactory,
51
+ StaffLicenseFactory,
52
+ StaffPhotoFactory,
53
+ StaffRoleFactory,
54
+ )
55
+ from .task import (
56
+ TaskCommentFactory,
57
+ TaskFactory,
58
+ TaskLabelFactory,
59
+ TaskMetadataFactory,
60
+ TaskTaskLabelFactory,
61
+ )
62
+ from .uncategorized_clinical_document import (
63
+ UncategorizedClinicalDocumentFactory,
64
+ UncategorizedClinicalDocumentReviewFactory,
65
+ )
4
66
  from .user import CanvasUserFactory
5
67
 
6
68
  __all__ = (
69
+ "CalendarFactory",
7
70
  "CanvasUserFactory",
71
+ "ClaimFactory",
72
+ "ClaimCommentFactory",
73
+ "ClaimCoverageFactory",
74
+ "ClaimDiagnosisCodeFactory",
75
+ "ClaimLabelFactory",
76
+ "ClaimProviderFactory",
77
+ "ClaimQueueFactory",
78
+ "ClaimSubmissionFactory",
79
+ "CoverageFactory",
80
+ "EventFactory",
8
81
  "FacilityFactory",
82
+ "ImagingOrderFactory",
83
+ "ImagingReportFactory",
84
+ "ImagingReviewFactory",
85
+ "LabOrderFactory",
86
+ "LabOrderReasonConditionFactory",
87
+ "LabOrderReasonFactory",
88
+ "LabPartnerFactory",
89
+ "LabPartnerTestFactory",
90
+ "LabReportFactory",
91
+ "LabReviewFactory",
92
+ "LabTestFactory",
93
+ "LabValueCodingFactory",
94
+ "LabValueFactory",
95
+ "MedicationHistoryMedicationFactory",
96
+ "MedicationHistoryMedicationCodingFactory",
97
+ "MedicationHistoryResponseFactory",
98
+ "NoteFactory",
99
+ "NoteStateChangeEventFactory",
100
+ "NoteTypeFactory",
101
+ "OrganizationAddressFactory",
102
+ "OrganizationContactPointFactory",
103
+ "OrganizationFactory",
9
104
  "PatientAddressFactory",
10
105
  "PatientFacilityAddressFactory",
11
106
  "PatientFactory",
107
+ "PracticeLocationFactory",
108
+ "PracticeLocationAddressFactory",
109
+ "PracticeLocationContactPointFactory",
110
+ "PracticeLocationSettingFactory",
12
111
  "ProtocolCurrentFactory",
112
+ "ReferralFactory",
113
+ "ReferralReportFactory",
114
+ "ReferralReviewFactory",
115
+ "StaffFactory",
116
+ "StaffPhotoFactory",
117
+ "StaffRoleFactory",
118
+ "StaffLicenseFactory",
119
+ "StaffContactPointFactory",
120
+ "StaffAddressFactory",
121
+ "TaskCommentFactory",
122
+ "TaskFactory",
123
+ "TaskLabelFactory",
124
+ "TaskMetadataFactory",
125
+ "TaskTaskLabelFactory",
126
+ "UncategorizedClinicalDocumentFactory",
127
+ "UncategorizedClinicalDocumentReviewFactory",
13
128
  )
@@ -0,0 +1,24 @@
1
+ import factory
2
+
3
+ from canvas_sdk.v1.data import Calendar, Event
4
+
5
+
6
+ class EventFactory(factory.django.DjangoModelFactory[Event]):
7
+ """Factory for creating an Event."""
8
+
9
+ class Meta:
10
+ model = Event
11
+
12
+ title = "Calendar Event"
13
+ calendar = factory.SubFactory("canvas_sdk.test_utils.factories.CalendarFactory")
14
+ starts_at = factory.Faker("date_time")
15
+ ends_at = factory.Faker("date_time")
16
+
17
+
18
+ class CalendarFactory(factory.django.DjangoModelFactory[Calendar]):
19
+ """Factory for creating Calendar."""
20
+
21
+ class Meta:
22
+ model = Calendar
23
+
24
+ title = "Calendar"
@@ -0,0 +1,81 @@
1
+ import factory
2
+
3
+ from canvas_sdk.v1.data import (
4
+ Claim,
5
+ ClaimComment,
6
+ ClaimCoverage,
7
+ ClaimLabel,
8
+ ClaimProvider,
9
+ ClaimQueue,
10
+ ClaimSubmission,
11
+ )
12
+
13
+
14
+ class ClaimCoverageFactory(factory.django.DjangoModelFactory[ClaimCoverage]):
15
+ """Factory for creating ClaimCoverage."""
16
+
17
+ class Meta:
18
+ model = ClaimCoverage
19
+
20
+ claim = factory.SubFactory("canvas_sdk.test_utils.factories.claim.ClaimFactory")
21
+ coverage = factory.SubFactory("canvas_sdk.test_utils.factories.CoverageFactory")
22
+
23
+
24
+ class ClaimQueueFactory(factory.django.DjangoModelFactory[ClaimQueue]):
25
+ """Factory for creating ClaimQueue."""
26
+
27
+ class Meta:
28
+ model = ClaimQueue
29
+ django_get_or_create = ("queue_sort_ordering",)
30
+
31
+ queue_sort_ordering = 5
32
+ name = "Claim: Filed"
33
+
34
+
35
+ class ClaimFactory(factory.django.DjangoModelFactory[Claim]):
36
+ """Factory for creating Claim."""
37
+
38
+ class Meta:
39
+ model = Claim
40
+
41
+ note = factory.SubFactory("canvas_sdk.test_utils.factories.NoteFactory")
42
+ current_queue = factory.SubFactory(ClaimQueueFactory)
43
+
44
+
45
+ class ClaimCommentFactory(factory.django.DjangoModelFactory[ClaimComment]):
46
+ """Factory for creating ClaimComment."""
47
+
48
+ class Meta:
49
+ model = ClaimComment
50
+
51
+ claim = factory.SubFactory(ClaimFactory)
52
+ comment = "Need to message Dr. House"
53
+
54
+
55
+ class ClaimProviderFactory(factory.django.DjangoModelFactory[ClaimProvider]):
56
+ """Factory for creating ClaimProvider."""
57
+
58
+ class Meta:
59
+ model = ClaimProvider
60
+
61
+ claim = factory.SubFactory(ClaimFactory)
62
+
63
+
64
+ class ClaimLabelFactory(factory.django.DjangoModelFactory[ClaimLabel]):
65
+ """Factory for creating ClaimLabel."""
66
+
67
+ class Meta:
68
+ model = ClaimLabel
69
+
70
+ claim = factory.SubFactory(ClaimFactory)
71
+ label = factory.SubFactory("canvas_sdk.test_utils.factories.TaskLabelFactory")
72
+
73
+
74
+ class ClaimSubmissionFactory(factory.django.DjangoModelFactory[ClaimSubmission]):
75
+ """Factory for creating ClaimSubmission."""
76
+
77
+ class Meta:
78
+ model = ClaimSubmission
79
+
80
+ claim = factory.SubFactory(ClaimFactory)
81
+ clearinghouse_claim_id = "123456"
@@ -0,0 +1,16 @@
1
+ import factory
2
+ from factory.fuzzy import FuzzyInteger
3
+
4
+ from canvas_sdk.v1.data import ClaimDiagnosisCode
5
+
6
+
7
+ class ClaimDiagnosisCodeFactory(factory.django.DjangoModelFactory[ClaimDiagnosisCode]):
8
+ """Factory for creating a ClaimDiagnosisCode."""
9
+
10
+ class Meta:
11
+ model = ClaimDiagnosisCode
12
+
13
+ rank = FuzzyInteger(1, 12) # ICD-10 allows up to 12 diagnosis codes on a claim
14
+ code = factory.Faker("bothify", text="?##.##") # ICD-10 format like A01.23
15
+ display = factory.Faker("sentence", nb_words=4) # Human readable diagnosis description
16
+ claim = factory.SubFactory("canvas_sdk.test_utils.factories.ClaimFactory")
@@ -0,0 +1,17 @@
1
+ import factory
2
+
3
+ from canvas_sdk.v1.data import Coverage
4
+
5
+
6
+ class CoverageFactory(factory.django.DjangoModelFactory[Coverage]):
7
+ """Factory for creating a Coverage."""
8
+
9
+ class Meta:
10
+ model = Coverage
11
+
12
+ patient = factory.SubFactory("canvas_sdk.test_utils.factories.PatientFactory")
13
+ plan = "Healthy Families of California"
14
+ group = "San Francisco Mail Carriers"
15
+ coverage_rank = 1
16
+ id_number = "1098659867"
17
+ coverage_start_date = factory.Faker("date")
@@ -0,0 +1,74 @@
1
+ import datetime
2
+ import json
3
+
4
+ import factory
5
+ from django.utils import timezone
6
+ from factory.fuzzy import FuzzyDate
7
+
8
+ from canvas_sdk.v1.data import ImagingOrder, ImagingReport, ImagingReview
9
+ from canvas_sdk.v1.data.common import (
10
+ DocumentReviewMode,
11
+ OrderStatus,
12
+ ReviewPatientCommunicationMethod,
13
+ ReviewStatus,
14
+ )
15
+ from canvas_sdk.v1.data.imaging import ImagingReport as ImagingReportModel
16
+
17
+
18
+ class ImagingOrderFactory(factory.django.DjangoModelFactory[ImagingOrder]):
19
+ """Factory for creating an ImagingOrder."""
20
+
21
+ class Meta:
22
+ model = ImagingOrder
23
+
24
+ patient = factory.SubFactory("canvas_sdk.test_utils.factories.PatientFactory")
25
+ note = factory.SubFactory("canvas_sdk.test_utils.factories.NoteFactory")
26
+ imaging = factory.Faker("text", max_nb_chars=1024)
27
+ imaging_center = None # Optional field
28
+ note_to_radiologist = factory.Faker("text", max_nb_chars=1024)
29
+ internal_comment = factory.Faker("text", max_nb_chars=1024)
30
+ status = OrderStatus.REQUESTED
31
+ date_time_ordered = factory.LazyFunction(lambda: timezone.now())
32
+ priority = factory.Faker("random_element", elements=["routine", "urgent", "stat"])
33
+ ordering_provider = factory.SubFactory("canvas_sdk.test_utils.factories.StaffFactory")
34
+ delegated = False
35
+ task_ids = factory.LazyFunction(lambda: json.dumps([]))
36
+
37
+
38
+ class ImagingReviewFactory(factory.django.DjangoModelFactory[ImagingReview]):
39
+ """Factory for creating an ImagingReview."""
40
+
41
+ class Meta:
42
+ model = ImagingReview
43
+
44
+ patient_communication_method = ReviewPatientCommunicationMethod.DELEGATED_CALL_CAN_LEAVE_MESSAGE
45
+ internal_comment = factory.Faker("text", max_nb_chars=2048)
46
+ message_to_patient = factory.Faker("text", max_nb_chars=2048)
47
+ is_released_to_patient = False
48
+ status = ReviewStatus.STATUS_REVIEWING
49
+ patient = factory.SubFactory("canvas_sdk.test_utils.factories.PatientFactory")
50
+
51
+
52
+ class ImagingReportFactory(factory.django.DjangoModelFactory[ImagingReport]):
53
+ """Factory for creating an ImagingReport."""
54
+
55
+ class Meta:
56
+ model = ImagingReport
57
+
58
+ review_mode = DocumentReviewMode.REVIEW_REQUIRED
59
+ junked = False
60
+ requires_signature = False
61
+ assigned_date = factory.LazyFunction(lambda: timezone.now())
62
+ patient = factory.SubFactory("canvas_sdk.test_utils.factories.PatientFactory")
63
+ order = None # Optional field
64
+ source = ImagingReportModel.ImagingReportSource.DIRECTLY_REPORT
65
+ name = factory.Faker("text", max_nb_chars=255)
66
+ result_date = FuzzyDate(
67
+ start_date=datetime.date.today() - datetime.timedelta(days=365),
68
+ end_date=datetime.date.today(),
69
+ )
70
+ original_date = FuzzyDate(
71
+ start_date=datetime.date.today() - datetime.timedelta(days=365),
72
+ end_date=datetime.date.today(),
73
+ )
74
+ review = None # Optional field