clinicedc 2.0.34__py3-none-any.whl → 2.0.35__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 clinicedc might be problematic. Click here for more details.

Files changed (165) hide show
  1. {clinicedc-2.0.34.dist-info → clinicedc-2.0.35.dist-info}/METADATA +1 -1
  2. {clinicedc-2.0.34.dist-info → clinicedc-2.0.35.dist-info}/RECORD +155 -165
  3. {clinicedc-2.0.34.dist-info → clinicedc-2.0.35.dist-info}/WHEEL +1 -1
  4. edc_action_item/templatetags/action_item_extras.py +1 -1
  5. edc_adverse_event/form_validator_mixins/death_report_form_validator.py +1 -1
  6. edc_adverse_event/form_validator_mixins/requires_death_report_form_validator_mixin.py +5 -3
  7. edc_adverse_event/form_validators/death_report_tmg.py +2 -2
  8. edc_adverse_event/modeladmin_mixins/ae_tmg_admin_mixin.py +1 -1
  9. edc_adverse_event/modeladmin_mixins/utils.py +1 -1
  10. edc_adverse_event/utils.py +1 -1
  11. edc_appointment/admin/appointment_admin.py +24 -24
  12. edc_appointment/admin/list_filters.py +5 -5
  13. edc_appointment/appointment_reason_updater.py +6 -5
  14. edc_appointment/appointment_status_updater.py +2 -2
  15. edc_appointment/context_processors.py +1 -2
  16. edc_appointment/creators/appointment_creator.py +9 -8
  17. edc_appointment/creators/unscheduled_appointment_creator.py +31 -24
  18. edc_appointment/creators/utils.py +35 -37
  19. edc_appointment/exceptions.py +3 -3
  20. edc_appointment/form_runners.py +2 -2
  21. edc_appointment/form_validator_mixins/next_appointment_crf_form_validator_mixin.py +5 -5
  22. edc_appointment/form_validator_mixins/window_period_form_validator_mixin.py +7 -7
  23. edc_appointment/form_validators/appointment_form_validator.py +16 -23
  24. edc_appointment/management/commands/close_appointments.py +3 -3
  25. edc_appointment/management/commands/reset_visit_code_sequences.py +1 -1
  26. edc_appointment/management/commands/update_appointment_status.py +2 -2
  27. edc_appointment/management/commands/update_skipped_appointments.py +7 -7
  28. edc_appointment/managers.py +6 -7
  29. edc_appointment/model_mixins/appointment_model_mixin.py +3 -4
  30. edc_appointment/modeladmin_mixins/next_appointment_crf_modeladmin_mixin.py +2 -2
  31. edc_appointment/modelform_mixins/next_appointment_crf_modelform_mixins.py +25 -24
  32. edc_appointment/models/signals.py +1 -1
  33. edc_appointment/skip_appointments.py +10 -29
  34. edc_appointment/utils.py +43 -47
  35. edc_appointment/view_mixins/appointment_view_mixin.py +11 -13
  36. edc_appointment/views/unscheduled_appointment_view.py +5 -6
  37. edc_consent/consent_definition.py +5 -13
  38. edc_consent/consent_definition_extension.py +0 -2
  39. edc_consent/form_validators/consent_definition_form_validator_mixin.py +26 -30
  40. edc_consent/form_validators/subject_consent_form_validator.py +3 -3
  41. edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_validation_mixin.py +1 -1
  42. edc_consent/modelform_mixins/requires_consent_modelform_mixin.py +4 -5
  43. edc_consent/site_consents.py +13 -14
  44. edc_crf/crf_form_validator.py +13 -19
  45. edc_crf/crf_form_validator_mixins.py +2 -17
  46. edc_data_manager/admin/actions.py +1 -1
  47. edc_data_manager/admin/data_query_admin.py +1 -1
  48. edc_dx/form_validators/result_form_validator_mixin.py +1 -1
  49. edc_dx_review/medical_date.py +1 -1
  50. edc_egfr/egfr.py +4 -8
  51. edc_egfr/form_validator_mixins/egfr_form_validator_mixins.py +2 -2
  52. edc_facility/facility.py +7 -11
  53. edc_facility/holidays.py +3 -3
  54. edc_facility/models/holiday.py +1 -1
  55. edc_form_runners/form_runner.py +15 -16
  56. edc_form_validators/base_form_validator.py +5 -1
  57. edc_form_validators/date_range_validator.py +49 -61
  58. edc_form_validators/date_validator.py +1 -1
  59. edc_form_validators/extra_mixins/study_day_form_validator.py +1 -1
  60. edc_lab/form_validators/crf_requisition_form_validator_mixin.py +21 -15
  61. edc_lab/form_validators/requisition_form_validator_mixin.py +3 -3
  62. edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py +1 -1
  63. edc_ltfu/modelform_mixins.py +1 -1
  64. edc_metadata/metadata/metadata.py +20 -7
  65. edc_metadata/metadata_rules/logic.py +5 -4
  66. edc_metadata/metadata_rules/predicate.py +22 -24
  67. edc_model_form/mixins/report_datetime_modelform_mixin.py +1 -5
  68. edc_offstudy/model_mixins/offstudy_model_mixin.py +1 -1
  69. edc_offstudy/modelform_mixins/crf/offstudy_crf_modelform_mixin.py +2 -2
  70. edc_offstudy/utils.py +4 -4
  71. edc_pdf_reports/crf_pdf_report.py +2 -1
  72. edc_pdutils/helper.py +3 -3
  73. edc_pdutils/utils/convert_dates_from_model.py +4 -3
  74. edc_pharmacy/admin/actions/confirm_stock.py +3 -3
  75. edc_pharmacy/admin/actions/delete_items_for_stock_request.py +4 -3
  76. edc_pharmacy/admin/actions/delete_order_items.py +7 -3
  77. edc_pharmacy/admin/actions/delete_receive_items.py +5 -3
  78. edc_pharmacy/admin/actions/process_repack_request.py +7 -11
  79. edc_pharmacy/admin/autocomplete_admin.py +1 -1
  80. edc_pharmacy/admin/list_filters.py +48 -46
  81. edc_pharmacy/admin/medication/assignment_admin.py +4 -4
  82. edc_pharmacy/admin/medication/dosage_guideline_admin.py +3 -3
  83. edc_pharmacy/admin/medication/formulation_admin.py +5 -5
  84. edc_pharmacy/admin/medication/medication_admin.py +3 -3
  85. edc_pharmacy/admin/prescription/rx_admin.py +2 -2
  86. edc_pharmacy/admin/prescription/rx_refill_admin.py +11 -17
  87. edc_pharmacy/admin/remove_fields_for_blinded_users.py +1 -1
  88. edc_pharmacy/admin/reports/stock_availability_admin.py +12 -8
  89. edc_pharmacy/admin/stock/allocation_admin.py +13 -17
  90. edc_pharmacy/admin/stock/allocation_proxy_admin.py +1 -2
  91. edc_pharmacy/admin/stock/confirmation_admin.py +4 -8
  92. edc_pharmacy/admin/stock/confirmation_at_site_item_admin.py +1 -1
  93. edc_pharmacy/admin/stock/container_admin.py +3 -3
  94. edc_pharmacy/admin/stock/location_admin.py +3 -6
  95. edc_pharmacy/admin/stock/lot_admin.py +9 -12
  96. edc_pharmacy/admin/stock/order_admin.py +2 -5
  97. edc_pharmacy/admin/stock/order_item_admin.py +14 -22
  98. edc_pharmacy/admin/stock/product_admin.py +6 -9
  99. edc_pharmacy/admin/stock/receive_admin.py +2 -2
  100. edc_pharmacy/admin/stock/receive_item_admin.py +3 -3
  101. edc_pharmacy/admin/stock/repack_request_admin.py +7 -10
  102. edc_pharmacy/admin/stock/stock_adjustment_admin.py +1 -1
  103. edc_pharmacy/admin/stock/stock_admin.py +17 -28
  104. edc_pharmacy/admin/stock/stock_proxy_admin.py +3 -4
  105. edc_pharmacy/admin/stock/stock_request_admin.py +6 -10
  106. edc_pharmacy/admin/stock/stock_request_item_admin.py +9 -18
  107. edc_pharmacy/admin/stock/stock_transfer_admin.py +5 -5
  108. edc_pharmacy/admin/stock/stock_transfer_item_admin.py +3 -3
  109. edc_pharmacy/admin/stock/storage_bin_admin.py +1 -1
  110. edc_pharmacy/admin/stock/storage_bin_item_admin.py +2 -2
  111. edc_pharmacy/form_validators/crf/study_medication_form_validator.py +27 -27
  112. edc_pharmacy/forms/stock/confirmation_form.py +2 -2
  113. edc_pharmacy/forms/stock/dispense_form.py +2 -2
  114. edc_pharmacy/forms/stock/lot_form.py +6 -6
  115. edc_pharmacy/forms/stock/order_form.py +4 -6
  116. edc_pharmacy/forms/stock/product_form.py +2 -3
  117. edc_pharmacy/forms/stock/receive_form.py +4 -4
  118. edc_pharmacy/forms/stock/receive_item_form.py +7 -5
  119. edc_pharmacy/forms/stock/repack_request_form.py +10 -8
  120. edc_pharmacy/forms/stock/stock_form.py +2 -3
  121. edc_pharmacy/forms/stock/stock_request_form.py +10 -8
  122. edc_pharmacy/forms/stock/stock_request_item_form.py +6 -5
  123. edc_pharmacy/forms/stock/stock_transfer_form.py +16 -14
  124. edc_pharmacy/forms/stock/supplier_form.py +2 -2
  125. edc_pharmacy/labels/label_data.py +2 -3
  126. edc_pharmacy/model_mixins/study_medication_crf_model_mixin.py +1 -1
  127. edc_pharmacy/models/medication/dosage_guideline.py +3 -3
  128. edc_pharmacy/models/model_mixins.py +12 -10
  129. edc_pharmacy/models/prescription/rx.py +1 -1
  130. edc_pharmacy/models/prescription/rx_refill.py +1 -1
  131. edc_pharmacy/models/signals.py +2 -3
  132. edc_pharmacy/models/storage/utils.py +4 -4
  133. edc_pharmacy/pdf_reports/manifest_pdf_report.py +3 -6
  134. edc_pharmacy/pdf_reports/stock_pdf_report.py +1 -3
  135. edc_pharmacy/refill/refill_creator.py +3 -4
  136. edc_protocol/validators.py +10 -16
  137. edc_reportable/forms/reportables_form_validator_mixin.py +1 -1
  138. edc_screening/form_validator_mixins.py +2 -1
  139. edc_transfer/form_validators.py +1 -1
  140. edc_transfer/model_mixins.py +1 -1
  141. edc_utils/age.py +17 -15
  142. edc_utils/date.py +7 -7
  143. edc_utils/text.py +7 -6
  144. edc_visit_schedule/exceptions.py +8 -0
  145. edc_visit_schedule/model_mixins/off_schedule_model_mixin.py +1 -1
  146. edc_visit_schedule/model_mixins/on_schedule_model_mixin.py +1 -1
  147. edc_visit_schedule/modelform_mixins/off_schedule_modelform_mixin.py +1 -3
  148. edc_visit_schedule/schedule/visit_collection.py +1 -1
  149. edc_visit_schedule/schedule/window.py +0 -2
  150. edc_visit_schedule/subject_schedule.py +1 -1
  151. edc_visit_schedule/utils.py +4 -4
  152. edc_visit_schedule/visit/visit.py +9 -3
  153. edc_visit_schedule/visit/window_period.py +1 -1
  154. edc_visit_tracking/form_validators/visit_form_validator.py +1 -1
  155. edc_metadata/metadata_wrappers/__init__.py +0 -5
  156. edc_metadata/metadata_wrappers/crf_metadata_wrapper.py +0 -5
  157. edc_metadata/metadata_wrappers/crf_metadata_wrappers.py +0 -8
  158. edc_metadata/metadata_wrappers/metadata_wrapper.py +0 -74
  159. edc_metadata/metadata_wrappers/metadata_wrappers.py +0 -33
  160. edc_metadata/metadata_wrappers/requisition_metadata_wrapper.py +0 -26
  161. edc_metadata/metadata_wrappers/requisition_metadata_wrappers.py +0 -10
  162. edc_pharmacy/management/__init__.py +0 -0
  163. edc_pharmacy/management/commands/__init__.py +0 -0
  164. edc_pharmacy/management/commands/update_initial_pharmacy_data.py +0 -10
  165. {clinicedc-2.0.34.dist-info → clinicedc-2.0.35.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,5 @@
1
+ import contextlib
2
+
1
3
  from django.contrib import admin
2
4
  from django.template.loader import render_to_string
3
5
  from django.urls import reverse
@@ -48,19 +50,19 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
48
50
 
49
51
  show_form_tools = True
50
52
  show_history_label = True
51
- autocomplete_fields = ["container"]
53
+ autocomplete_fields = ("container",)
52
54
 
53
55
  change_list_note = (
54
56
  "C=Confirmed, A=Allocated, T=Transferred to site, "
55
57
  "S=Confirmed at site, B=Stored at site, D=Dispensed"
56
58
  )
57
59
 
58
- actions = [
60
+ actions = (
59
61
  print_labels,
60
62
  confirm_stock_from_queryset,
61
63
  go_to_add_repack_request_action,
62
64
  print_stock_report_action,
63
- ]
65
+ )
64
66
 
65
67
  form = StockForm
66
68
 
@@ -184,29 +186,24 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
184
186
 
185
187
  def get_list_display(self, request):
186
188
  fields = super().get_list_display(request)
187
- fields = remove_fields_for_blinded_users(request, fields)
188
- return fields
189
+ return remove_fields_for_blinded_users(request, fields)
189
190
 
190
191
  def get_list_display_links(self, request, list_display):
191
192
  display_links = super().get_list_display_links(request, list_display)
192
193
  if not request.user.userprofile.roles.filter(
193
194
  name__in=[PHARMACIST_ROLE, PHARMACY_SUPER_ROLE]
194
195
  ).exists():
195
- try:
196
+ with contextlib.suppress(ValueError):
196
197
  display_links.remove("formatted_code")
197
- except ValueError:
198
- pass
199
198
  return display_links
200
199
 
201
200
  def get_list_filter(self, request):
202
201
  fields = super().get_list_filter(request)
203
- fields = remove_fields_for_blinded_users(request, fields)
204
- return fields
202
+ return remove_fields_for_blinded_users(request, fields)
205
203
 
206
204
  def get_search_fields(self, request):
207
205
  fields = super().get_search_fields(request)
208
- fields = remove_fields_for_blinded_users(request, fields)
209
- return fields
206
+ return remove_fields_for_blinded_users(request, fields)
210
207
 
211
208
  def get_fieldsets(self, request, obj=None):
212
209
  fieldsets = super().get_fieldsets(request, obj)
@@ -228,12 +225,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
228
225
  mark_safe('<div style="color:red;">ERROR!</div>'), # nosec B703, B308
229
226
  )
230
227
  except AllocationError:
231
- return format_html(
232
- "{}",
233
- mark_safe(
234
- '<div style="color:red;">Allocation<BR>ERROR!</div>'
235
- ), # nosec B703, B308
236
- )
228
+ return mark_safe('<div style="color:red;">Allocation<BR>ERROR!</div>')
237
229
  return obj.lot.assignment
238
230
 
239
231
  @admin.display(description="Lot #", ordering="lot__lot_no")
@@ -259,7 +251,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
259
251
  @admin.display(description="C", boolean=True)
260
252
  def formatted_confirmation(self, obj):
261
253
  if obj:
262
- return True if get_related_or_none(obj, "confirmation") else False
254
+ return bool(get_related_or_none(obj, "confirmation"))
263
255
  return None
264
256
 
265
257
  @admin.display(description="A", boolean=True)
@@ -269,7 +261,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
269
261
  and get_related_or_none(obj, "confirmation")
270
262
  and get_related_or_none(obj, "from_stock")
271
263
  ):
272
- return True if get_related_or_none(obj, "allocation") else False
264
+ return bool(get_related_or_none(obj, "allocation"))
273
265
  return None
274
266
 
275
267
  @admin.display(description="T", boolean=True)
@@ -279,7 +271,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
279
271
  and get_related_or_none(obj, "confirmation")
280
272
  and get_related_or_none(obj, "from_stock")
281
273
  ):
282
- return True if get_related_or_none(obj, "stocktransferitem") else False
274
+ return bool(get_related_or_none(obj, "stocktransferitem"))
283
275
  return None
284
276
 
285
277
  @admin.display(description="S", boolean=True)
@@ -289,7 +281,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
289
281
  and get_related_or_none(obj, "confirmation")
290
282
  and get_related_or_none(obj, "from_stock")
291
283
  ):
292
- return True if get_related_or_none(obj, "confirmationatsiteitem") else False
284
+ return bool(get_related_or_none(obj, "confirmationatsiteitem"))
293
285
  return None
294
286
 
295
287
  @admin.display(description="B", boolean=True)
@@ -301,7 +293,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
301
293
  and get_related_or_none(obj, "confirmationatsiteitem")
302
294
  and not get_related_or_none(obj, "dispenseitem")
303
295
  ):
304
- return True if get_related_or_none(obj, "storagebinitem") else False
296
+ return bool(get_related_or_none(obj, "storagebinitem"))
305
297
  return None
306
298
 
307
299
  @admin.display(description="D", boolean=True)
@@ -312,15 +304,12 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
312
304
  and get_related_or_none(obj, "from_stock")
313
305
  and get_related_or_none(obj, "confirmationatsiteitem")
314
306
  ):
315
- return True if get_related_or_none(obj, "dispenseitem") else False
307
+ return bool(get_related_or_none(obj, "dispenseitem"))
316
308
  return None
317
309
 
318
310
  @admin.display(description="Container", ordering="container__name")
319
311
  def container_str(self, obj):
320
- return format_html(
321
- "{}",
322
- mark_safe("<BR>".join(str(obj.container).split(" "))), # nosec B703, B308
323
- )
312
+ return mark_safe("<BR>".join(str(obj.container).split(" "))) # noqa: S308
324
313
 
325
314
  @admin.display(description="formulation", ordering="product__formulation__name")
326
315
  def formulation(self, obj):
@@ -1,3 +1,5 @@
1
+ import contextlib
2
+
1
3
  from django.contrib import admin
2
4
  from django_audit_fields import audit_fieldset_tuple
3
5
 
@@ -15,7 +17,6 @@ from .stock_admin import StockAdmin
15
17
 
16
18
  @admin.register(StockProxy, site=edc_pharmacy_admin)
17
19
  class StockProxyAdmin(StockAdmin):
18
-
19
20
  fieldsets = (
20
21
  (
21
22
  "Stock item",
@@ -106,10 +107,8 @@ class StockProxyAdmin(StockAdmin):
106
107
  if not request.user.userprofile.roles.filter(
107
108
  name__in=[PHARMACIST_ROLE, PHARMACY_SUPER_ROLE]
108
109
  ).exists():
109
- try:
110
+ with contextlib.suppress(ValueError):
110
111
  display_links.remove("formatted_code")
111
- except ValueError:
112
- pass
113
112
  return display_links
114
113
 
115
114
  def get_view_only_site_ids_for_user(self, request) -> list[int]:
@@ -3,7 +3,6 @@ from celery.states import SUCCESS
3
3
  from django.contrib import admin
4
4
  from django.template.loader import render_to_string
5
5
  from django.urls import reverse
6
- from django.utils.html import format_html
7
6
  from django.utils.safestring import mark_safe
8
7
  from django.utils.translation import gettext as _
9
8
  from django_audit_fields import audit_fieldset_tuple
@@ -26,7 +25,7 @@ class StatusListFilter(admin.SimpleListFilter):
26
25
  title = _("Status")
27
26
  parameter_name = "status"
28
27
 
29
- def lookups(self, request, model_admin):
28
+ def lookups(self, request, model_admin): # noqa: ARG002
30
29
  return (
31
30
  (PENDING, _("Pending")),
32
31
  (COMPLETE, _("Complete")),
@@ -34,7 +33,7 @@ class StatusListFilter(admin.SimpleListFilter):
34
33
  ("ZERO", _("Zero total")),
35
34
  )
36
35
 
37
- def queryset(self, request, queryset):
36
+ def queryset(self, request, queryset): # noqa: ARG002
38
37
  if self.value():
39
38
  if self.value() == PENDING:
40
39
  return (
@@ -67,15 +66,15 @@ class StockRequestAdmin(ModelAdminMixin, SimpleHistoryAdmin):
67
66
  list_per_page = 20
68
67
  ordering = ("-request_identifier",)
69
68
 
70
- autocomplete_fields = ["container", "formulation", "location"]
69
+ autocomplete_fields = ("container", "formulation", "location")
71
70
  form = StockRequestForm
72
71
 
73
- actions = [
72
+ actions = (
74
73
  prepare_stock_request_items_action,
75
74
  allocate_stock_to_subject,
76
75
  print_labels_from_stock_request_by_code,
77
76
  "delete_selected",
78
- ]
77
+ )
79
78
 
80
79
  fieldsets = (
81
80
  (
@@ -194,10 +193,7 @@ class StockRequestAdmin(ModelAdminMixin, SimpleHistoryAdmin):
194
193
 
195
194
  @admin.display(description="Container", ordering="container_name")
196
195
  def container_str(self, obj):
197
- return format_html(
198
- "{}",
199
- mark_safe("<BR>".join(str(obj.container).split(" "))), # nosec B703, B308
200
- )
196
+ return mark_safe("<BR>".join(str(obj.container).split(" "))) # noqa: S308
201
197
 
202
198
  @admin.display(description="Task")
203
199
  def task_status(self, obj):
@@ -1,7 +1,6 @@
1
1
  from django.contrib import admin
2
2
  from django.template.loader import render_to_string
3
3
  from django.urls import reverse
4
- from django.utils.html import format_html
5
4
  from django.utils.safestring import mark_safe
6
5
  from django_audit_fields import audit_fieldset_tuple
7
6
  from rangefilter.filters import DateRangeFilterBuilder
@@ -39,8 +38,8 @@ class StockRequestItemAdmin(ModelAdminMixin, SimpleHistoryAdmin):
39
38
  show_cancel = True
40
39
  list_per_page = 20
41
40
  form = StockRequestItemForm
42
- autocomplete_fields = ["rx"]
43
- actions = [print_labels_from_stock_request_item, "delete_selected"]
41
+ autocomplete_fields = ("rx",)
42
+ actions = (print_labels_from_stock_request_item, "delete_selected")
44
43
 
45
44
  fieldsets = (
46
45
  (
@@ -98,27 +97,19 @@ class StockRequestItemAdmin(ModelAdminMixin, SimpleHistoryAdmin):
98
97
 
99
98
  def get_list_display(self, request):
100
99
  fields = super().get_list_display(request)
101
- fields = remove_fields_for_blinded_users(request, fields)
102
- return fields
100
+ return remove_fields_for_blinded_users(request, fields)
103
101
 
104
102
  def get_list_filter(self, request):
105
103
  fields = super().get_list_filter(request)
106
- fields = remove_fields_for_blinded_users(request, fields)
107
- return fields
104
+ return remove_fields_for_blinded_users(request, fields)
108
105
 
109
106
  def get_search_fields(self, request):
110
107
  fields = super().get_search_fields(request)
111
- fields = remove_fields_for_blinded_users(request, fields)
112
- return fields
108
+ return remove_fields_for_blinded_users(request, fields)
113
109
 
114
110
  @admin.display(description="Product")
115
111
  def formulation(self, obj):
116
- return format_html(
117
- "{}",
118
- mark_safe(
119
- f"{obj.stock_request.formulation}<BR>{obj.stock_request.container}"
120
- ), # nosec B703, B308
121
- )
112
+ return mark_safe(f"{obj.stock_request.formulation}<BR>{obj.stock_request.container}") # noqa: S308
122
113
 
123
114
  @admin.display(description="Date", ordering="request_item_datetime")
124
115
  def item_date(self, obj):
@@ -147,13 +138,13 @@ class StockRequestItemAdmin(ModelAdminMixin, SimpleHistoryAdmin):
147
138
  @admin.display(description="A", boolean=True)
148
139
  def allocated(self, obj):
149
140
  if obj:
150
- return True if getattr(obj, "allocation", None) else False
141
+ return bool(getattr(obj, "allocation", None))
151
142
  return None
152
143
 
153
144
  @admin.display(description="T", boolean=True)
154
145
  def transferred(self, obj):
155
146
  if obj and getattr(obj, "allocation", None):
156
- return True if obj.allocation.stock.stocktransferitem else False
147
+ return bool(obj.allocation.stock.stocktransferitem)
157
148
  return False
158
149
 
159
150
  @admin.display(description="Subject", ordering="appt_datetime")
@@ -211,7 +202,7 @@ class StockRequestItemAdmin(ModelAdminMixin, SimpleHistoryAdmin):
211
202
 
212
203
  def get_readonly_fields(self, request, obj=None):
213
204
  if obj:
214
- return self.readonly_fields + ("stock_request",)
205
+ return tuple({*self.readonly_fields, "stock_request"})
215
206
  return self.readonly_fields
216
207
 
217
208
  def formfield_for_foreignkey(self, db_field, request, **kwargs):
@@ -21,10 +21,10 @@ class ConfirmedAtSiteListFilter(SimpleListFilter):
21
21
  title = "Confirmed at site"
22
22
  parameter_name = "confirmed_at_site"
23
23
 
24
- def lookups(self, request, model_admin):
24
+ def lookups(self, request, model_admin): # noqa: ARG002
25
25
  return (YES, YES), (PARTIAL, "Partial"), (NO, NO)
26
26
 
27
- def queryset(self, request, queryset):
27
+ def queryset(self, request, queryset): # noqa: ARG002
28
28
  qs = None
29
29
  if self.value():
30
30
  if self.value() == YES:
@@ -60,10 +60,10 @@ class StockTransferAdmin(ModelAdminMixin, SimpleHistoryAdmin):
60
60
  show_object_tools = True
61
61
  show_cancel = True
62
62
  list_per_page = 20
63
- ordering = ["-transfer_identifier"]
63
+ ordering = ("-transfer_identifier",)
64
64
 
65
- autocomplete_fields = ["from_location", "to_location"]
66
- actions = [transfer_stock_action, print_transfer_stock_manifest_action]
65
+ autocomplete_fields = ("from_location", "to_location")
66
+ actions = (transfer_stock_action, print_transfer_stock_manifest_action)
67
67
 
68
68
  form = StockTransferForm
69
69
 
@@ -19,10 +19,10 @@ class ConfirmedAtSiteFilter(SimpleListFilter):
19
19
  title = "Confirmed at site"
20
20
  parameter_name = "confirmed_at_site"
21
21
 
22
- def lookups(self, request, model_admin):
22
+ def lookups(self, request, model_admin): # noqa: ARG002
23
23
  return (YES, YES), (NO, NO)
24
24
 
25
- def queryset(self, request, queryset):
25
+ def queryset(self, request, queryset): # noqa: ARG002
26
26
  qs = None
27
27
  if self.value():
28
28
  opts = dict(
@@ -53,7 +53,7 @@ class StockTransferItemAdmin(ModelAdminMixin, SimpleHistoryAdmin):
53
53
  show_cancel = True
54
54
  list_per_page = 20
55
55
 
56
- autocomplete_fields = ["stock"]
56
+ autocomplete_fields = ("stock",)
57
57
 
58
58
  fieldsets = (
59
59
  (
@@ -27,7 +27,7 @@ class StorageBinAdmin(SiteModelAdminMixin, ModelAdminMixin, SimpleHistoryAdmin):
27
27
 
28
28
  form = StorageBinForm
29
29
 
30
- actions = [add_to_storage_bin_action, move_to_storage_bin_action]
30
+ actions = (add_to_storage_bin_action, move_to_storage_bin_action)
31
31
 
32
32
  fieldsets = (
33
33
  (
@@ -23,8 +23,8 @@ class StorageBinItemAdmin(SiteModelAdminMixin, ModelAdminMixin, SimpleHistoryAdm
23
23
  show_cancel = True
24
24
  list_per_page = 20
25
25
 
26
- autocomplete_fields = ["stock"]
27
- actions = ["delete_selected"]
26
+ autocomplete_fields = ("stock",)
27
+ actions = ("delete_selected",)
28
28
 
29
29
  change_list_note = "Once an item is dispensed it is automatically removed from storage."
30
30
 
@@ -2,14 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  from datetime import datetime
4
4
  from typing import TYPE_CHECKING
5
- from zoneinfo import ZoneInfo
6
5
 
7
6
  from django.core.exceptions import ObjectDoesNotExist
8
7
 
9
8
  from edc_constants.constants import NO, YES
10
9
  from edc_crf.crf_form_validator import CrfFormValidator
11
10
  from edc_form_validators import INVALID_ERROR
12
- from edc_utils import formatted_datetime
11
+ from edc_utils.text import formatted_datetime
13
12
 
14
13
  from ...utils import get_rx_model_cls, get_rxrefill_model_cls
15
14
 
@@ -28,20 +27,18 @@ class StudyMedicationFormValidator(CrfFormValidator):
28
27
  if self.related_visit.appointment.relative_next:
29
28
  next_appt_datetime = self.related_visit.appointment.relative_next.appt_datetime
30
29
 
31
- if next_appt_datetime:
32
- if (
33
- self.refill_start_datetime
34
- and self.refill_start_datetime.astimezone(ZoneInfo("UTC")) > next_appt_datetime
35
- ):
36
- local_dte = formatted_datetime(next_appt_datetime)
37
- error_msg = (
38
- "Refill start date cannot be after next appointmnent date. "
39
- f"Next appointment date is {local_dte}."
40
- )
30
+ if (
31
+ next_appt_datetime
32
+ and self.refill_start_datetime
33
+ and self.refill_start_datetime > next_appt_datetime
34
+ ):
35
+ local_dte = formatted_datetime(next_appt_datetime)
36
+ error_msg = (
37
+ "Refill start date cannot be after next appointmnent date. "
38
+ f"Next appointment date is {local_dte}."
39
+ )
41
40
 
42
- self.raise_validation_error(
43
- {"refill_start_datetime": error_msg}, INVALID_ERROR
44
- )
41
+ self.raise_validation_error({"refill_start_datetime": error_msg}, INVALID_ERROR)
45
42
 
46
43
  self.validate_refill_start_date_against_offschedule_date()
47
44
 
@@ -66,14 +63,14 @@ class StudyMedicationFormValidator(CrfFormValidator):
66
63
  @property
67
64
  def refill_start_datetime(self) -> datetime | None:
68
65
  if refill_start_datetime := self.cleaned_data.get("refill_start_datetime"):
69
- return refill_start_datetime.astimezone(ZoneInfo("UTC"))
70
- return refill_start_datetime
66
+ return refill_start_datetime
67
+ return None
71
68
 
72
69
  @property
73
70
  def refill_end_datetime(self) -> datetime | None:
74
71
  if refill_end_datetime := self.cleaned_data.get("refill_end_datetime"):
75
- return refill_end_datetime.astimezone(ZoneInfo("UTC"))
76
- return refill_end_datetime
72
+ return refill_end_datetime
73
+ return None
77
74
 
78
75
  @property
79
76
  def next_refill(self) -> RxRefill | None:
@@ -93,14 +90,15 @@ class StudyMedicationFormValidator(CrfFormValidator):
93
90
  @property
94
91
  def rx(self) -> Rx:
95
92
  try:
96
- return get_rx_model_cls().objects.get(
93
+ obj = get_rx_model_cls().objects.get(
97
94
  subject_identifier=self.subject_identifier,
98
95
  medications__in=[self.medication],
99
96
  )
100
- except ObjectDoesNotExist:
97
+ except ObjectDoesNotExist as e:
101
98
  self.raise_validation_error(
102
- {"__all__": "Prescription does not exist"}, INVALID_ERROR
99
+ {"__all__": "Prescription does not exist"}, INVALID_ERROR, exc=e
103
100
  )
101
+ return obj
104
102
 
105
103
  @property
106
104
  def formulation(self) -> Formulation | None:
@@ -109,11 +107,13 @@ class StudyMedicationFormValidator(CrfFormValidator):
109
107
  @property
110
108
  def medication(self) -> Medication:
111
109
  if self.formulation:
112
- return self.formulation.medication
113
- self.raise_validation_error(
114
- {"__all__": "Need the formulation to look up the prescription."},
115
- INVALID_ERROR,
116
- )
110
+ medication = self.formulation.medication
111
+ else:
112
+ self.raise_validation_error(
113
+ {"__all__": "Need the formulation to look up the prescription."},
114
+ INVALID_ERROR,
115
+ )
116
+ return medication
117
117
 
118
118
  @property
119
119
  def next_appointment(self) -> Appointment | None:
@@ -7,7 +7,7 @@ class ConfirmationForm(forms.ModelForm):
7
7
  class Meta:
8
8
  model = Confirmation
9
9
  fields = "__all__"
10
- help_text = {"confirmation_identifier": "(read-only)"}
11
- widgets = {
10
+ help_text = {"confirmation_identifier": "(read-only)"} # noqa: RUF012
11
+ widgets = { # noqa: RUF012
12
12
  "confirmation_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
13
13
  }
@@ -7,7 +7,7 @@ class DispenseForm(forms.ModelForm):
7
7
  class Meta:
8
8
  model = Dispense
9
9
  fields = "__all__"
10
- help_text = {"dispense_identifier": "(read-only)"}
11
- widgets = {
10
+ help_text = {"dispense_identifier": "(read-only)"} # noqa: RUF012
11
+ widgets = { # noqa: RUF012
12
12
  "dispense_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
13
13
  }
@@ -4,14 +4,14 @@ from ...models import Lot
4
4
 
5
5
 
6
6
  class LotForm(forms.ModelForm):
7
-
8
7
  def clean(self):
9
8
  cleaned_data = super().clean()
10
- if cleaned_data.get("product") and cleaned_data.get("assignment"):
11
- if cleaned_data.get("product").assignment != cleaned_data.get("assignment"):
12
- raise forms.ValidationError(
13
- {"assignment": "Assignment does not match product"}
14
- )
9
+ if (
10
+ cleaned_data.get("product")
11
+ and cleaned_data.get("assignment")
12
+ and cleaned_data.get("product").assignment != cleaned_data.get("assignment")
13
+ ):
14
+ raise forms.ValidationError({"assignment": "Assignment does not match product"})
15
15
 
16
16
  return cleaned_data
17
17
 
@@ -4,7 +4,6 @@ from ...models import Order, OrderItem
4
4
 
5
5
 
6
6
  class OrderForm(forms.ModelForm):
7
-
8
7
  def clean(self):
9
8
  cleaned_data = super().clean()
10
9
  if (
@@ -21,18 +20,17 @@ class OrderForm(forms.ModelForm):
21
20
  class Meta:
22
21
  model = Order
23
22
  fields = "__all__"
24
- help_text = {"order_identifier": "(read-only)"}
25
- widgets = {
23
+ help_text = {"order_identifier": "(read-only)"} # noqa: RUF012
24
+ widgets = { # noqa: RUF012
26
25
  "order_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
27
26
  }
28
27
 
29
28
 
30
29
  class OrderFormSuper(forms.ModelForm):
31
-
32
30
  class Meta:
33
31
  model = Order
34
32
  fields = "__all__"
35
- help_text = {"order_identifier": "(read-only)"}
36
- widgets = {
33
+ help_text = {"order_identifier": "(read-only)"} # noqa: RUF012
34
+ widgets = { # noqa: RUF012
37
35
  "order_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
38
36
  }
@@ -4,7 +4,6 @@ from ...models import Product, Stock
4
4
 
5
5
 
6
6
  class ProductForm(forms.ModelForm):
7
-
8
7
  def clean(self):
9
8
  cleaned_data = super().clean()
10
9
  if (
@@ -17,7 +16,7 @@ class ProductForm(forms.ModelForm):
17
16
  class Meta:
18
17
  model = Product
19
18
  fields = "__all__"
20
- help_text = {"product_identifier": "(read-only)"}
21
- widgets = {
19
+ help_text = {"product_identifier": "(read-only)"} # noqa: RUF012
20
+ widgets = { # noqa: RUF012
22
21
  "product_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
23
22
  }
@@ -16,8 +16,8 @@ class ReceiveForm(forms.ModelForm):
16
16
  class Meta:
17
17
  model = Receive
18
18
  fields = "__all__"
19
- help_text = {"receive_identifier": "(read-only)"}
20
- widgets = {
19
+ help_text = {"receive_identifier": "(read-only)"} # noqa: RUF012
20
+ widgets = { # noqa: RUF012
21
21
  "receive_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
22
22
  }
23
23
 
@@ -26,7 +26,7 @@ class ReceiveFormSuper(forms.ModelForm):
26
26
  class Meta:
27
27
  model = Receive
28
28
  fields = "__all__"
29
- help_text = {"receive_identifier": "(read-only)"}
30
- widgets = {
29
+ help_text = {"receive_identifier": "(read-only)"} # noqa: RUF012
30
+ widgets = { # noqa: RUF012
31
31
  "receive_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
32
32
  }
@@ -7,7 +7,6 @@ from ...models import ReceiveItem
7
7
 
8
8
 
9
9
  class ReceiveItemForm(forms.ModelForm):
10
-
11
10
  def clean(self):
12
11
  cleaned_data = super().clean()
13
12
  if not self.instance.id:
@@ -18,12 +17,15 @@ class ReceiveItemForm(forms.ModelForm):
18
17
  if not cleaned_data.get("container"):
19
18
  raise forms.ValidationError({"container": "This field is required"})
20
19
 
21
- if cleaned_data.get("order_item") and cleaned_data.get("lot"):
22
- if (
20
+ if (
21
+ cleaned_data.get("order_item")
22
+ and cleaned_data.get("lot")
23
+ and (
23
24
  cleaned_data.get("order_item").product.assignment
24
25
  != cleaned_data.get("lot").assignment
25
- ):
26
- raise forms.ValidationError({"lot": "Lot assignment does not match product"})
26
+ )
27
+ ):
28
+ raise forms.ValidationError({"lot": "Lot assignment does not match product"})
27
29
 
28
30
  # in unit_qty's
29
31
  if cleaned_data.get("qty"):
@@ -4,7 +4,6 @@ from ...models import RepackRequest
4
4
 
5
5
 
6
6
  class RepackRequestForm(forms.ModelForm):
7
-
8
7
  def clean(self):
9
8
  cleaned_data = super().clean()
10
9
  if cleaned_data.get("from_stock") and not getattr(
@@ -31,11 +30,14 @@ class RepackRequestForm(forms.ModelForm):
31
30
  > cleaned_data.get("from_stock").container.qty
32
31
  ):
33
32
  raise forms.ValidationError({"container": "Cannot pack into larger container."})
34
- if cleaned_data.get("requested_qty") and self.instance.processed_qty:
35
- if cleaned_data.get("requested_qty") < self.instance.processed_qty:
36
- raise forms.ValidationError(
37
- {"requested_qty": "Cannot be less than the number of containers processed"}
38
- )
33
+ if (
34
+ cleaned_data.get("requested_qty")
35
+ and self.instance.processed_qty
36
+ and cleaned_data.get("requested_qty") < self.instance.processed_qty
37
+ ):
38
+ raise forms.ValidationError(
39
+ {"requested_qty": "Cannot be less than the number of containers processed"}
40
+ )
39
41
  if (
40
42
  cleaned_data.get("requested_qty") * cleaned_data.get("container").qty
41
43
  > cleaned_data.get("from_stock").unit_qty
@@ -56,9 +58,9 @@ class RepackRequestForm(forms.ModelForm):
56
58
  class Meta:
57
59
  model = RepackRequest
58
60
  fields = "__all__"
59
- help_text = {
61
+ help_text = { # noqa: RUF012
60
62
  "repack_identifier": "(read-only)",
61
63
  }
62
- widgets = {
64
+ widgets = { # noqa: RUF012
63
65
  "repack_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
64
66
  }