clinicedc 2.0.6__py3-none-any.whl → 2.0.8__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 (495) hide show
  1. {clinicedc-2.0.6.dist-info → clinicedc-2.0.8.dist-info}/METADATA +2 -2
  2. {clinicedc-2.0.6.dist-info → clinicedc-2.0.8.dist-info}/RECORD +489 -441
  3. {clinicedc-2.0.6.dist-info → clinicedc-2.0.8.dist-info}/WHEEL +1 -1
  4. edc_action_item/action.py +1 -1
  5. edc_action_item/action_item_notification.py +2 -2
  6. edc_action_item/auths.py +37 -32
  7. edc_action_item/create_action_item.py +1 -1
  8. edc_action_item/decorators.py +1 -1
  9. edc_action_item/migrations/0001_initial.py +21 -22
  10. edc_action_item/migrations/0006_auto_20180707_1659.py +4 -4
  11. edc_action_item/migrations/0008_auto_20180809_0303.py +4 -4
  12. edc_action_item/migrations/0015_auto_20190114_0250.py +4 -4
  13. edc_action_item/migrations/0017_auto_20190305_0123.py +11 -30
  14. edc_action_item/migrations/0030_edcpermissions.py +3 -2
  15. edc_action_item/migrations/0039_alter_actionitem_auto_created_comment_and_more.py +398 -0
  16. edc_action_item/migrations/0040_alter_actionitem_report_datetime_and_more.py +34 -0
  17. edc_action_item/models/action_item.py +9 -4
  18. edc_action_item/models/action_model_mixin.py +5 -6
  19. edc_action_item/models/reference.py +2 -2
  20. edc_action_item/models/signals.py +22 -23
  21. edc_action_item/post_migrate_signals.py +1 -1
  22. edc_action_item/send_email.py +0 -85
  23. edc_action_item/site_action_items.py +5 -9
  24. edc_action_item/utils.py +3 -3
  25. edc_adherence/migrations/0005_alter_nonadherencereasons_extra_value_and_more.py +36 -0
  26. edc_adherence/model_mixins.py +1 -1
  27. edc_adverse_event/auths.py +55 -51
  28. edc_adverse_event/migrations/0001_initial.py +5 -4
  29. edc_adverse_event/migrations/0002_auto_20190802_0059.py +3 -2
  30. edc_adverse_event/migrations/0008_auto_20220825_0451.py +3 -2
  31. edc_adverse_event/migrations/0009_auto_20220907_0157.py +3 -2
  32. edc_adverse_event/migrations/0016_alter_aeactionclassification_device_created_and_more.py +285 -0
  33. edc_adverse_event/model_mixins/ae_followup/ae_followup_fields_model_mixin.py +2 -2
  34. edc_adverse_event/model_mixins/ae_initial/ae_initial_fields_model_mixin.py +6 -5
  35. edc_adverse_event/model_mixins/ae_special_interest/aesi_fields_model_mixin.py +2 -2
  36. edc_adverse_event/model_mixins/ae_susar/ae_susar_fields_model_mixin.py +2 -2
  37. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_fields_model_mixin.py +15 -11
  38. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_methods_model_mixin.py +2 -4
  39. edc_adverse_event/model_mixins/death_report/death_report_model_mixin.py +5 -4
  40. edc_adverse_event/model_mixins/death_report/death_report_tmg_model_mixin.py +6 -3
  41. edc_adverse_event/model_mixins/death_report/simple_death_report_model_mixin.py +2 -2
  42. edc_adverse_event/model_mixins/hospitaization/hospitalization_model_mixin.py +4 -3
  43. edc_adverse_event/modeladmin_mixins/death_report_admin_mixin.py +3 -3
  44. edc_adverse_event/models/signals.py +7 -7
  45. edc_adverse_event/templatetags/edc_adverse_event_extras.py +5 -4
  46. edc_adverse_event/view_mixins/ae/ae_listboard_view_mixin.py +3 -5
  47. edc_adverse_event/view_mixins/ae/death_report_listboard_view_mixin.py +6 -8
  48. edc_adverse_event/view_mixins/tmg/tmg_ae_listboard_view_mixin.py +2 -2
  49. edc_adverse_event/views/tmg/summary_listboard_view.py +3 -2
  50. edc_appointment/admin/appointment_admin.py +2 -2
  51. edc_appointment/admin/list_filters.py +2 -2
  52. edc_appointment/auths.py +14 -10
  53. edc_appointment/creators/appointment_creator.py +1 -1
  54. edc_appointment/creators/appointments_creator.py +1 -1
  55. edc_appointment/form_validators/appointment_form_validator.py +108 -82
  56. edc_appointment/migrations/0003_auto_20161127_2226.py +5 -6
  57. edc_appointment/migrations/0006_auto_20170106_2118.py +5 -5
  58. edc_appointment/migrations/0018_auto_20190305_0123.py +5 -12
  59. edc_appointment/migrations/0050_alter_appointment_appt_type_and_more.py +220 -0
  60. edc_appointment/model_mixins/appointment_fields_model_mixin.py +1 -2
  61. edc_appointment/model_mixins/appointment_methods_model_mixin.py +2 -3
  62. edc_appointment/model_mixins/appointment_model_mixin.py +31 -28
  63. edc_appointment/model_mixins/window_period_model_mixin.py +6 -7
  64. edc_appointment/models/appointment.py +1 -1
  65. edc_appointment/models/signals.py +44 -35
  66. edc_appointment/utils.py +19 -24
  67. edc_appointment/view_utils/appointment_button.py +2 -2
  68. edc_auth/admin/user_admin.py +1 -1
  69. edc_auth/auth_objects/__init__.py +2 -20
  70. edc_auth/auth_objects/default_groups.py +13 -11
  71. edc_auth/auth_objects/default_roles.py +26 -24
  72. edc_auth/auth_updater/auth_updater.py +13 -2
  73. edc_auth/auth_updater/group_updater.py +12 -10
  74. edc_auth/auth_updater/role_updater.py +2 -2
  75. edc_auth/constants.py +10 -0
  76. edc_auth/import_users.py +3 -3
  77. edc_auth/migrations/0001_squashed_0033_alter_userprofile_is_multisite_viewer.py +5 -5
  78. edc_auth/migrations/0012_auto_20191026_0034.py +3 -2
  79. edc_auth/migrations/0013_auto_20191026_0055.py +3 -2
  80. edc_auth/migrations/0025_permissions.py +3 -2
  81. edc_auth/migrations/0035_alter_edcpermissions_device_created_and_more.py +85 -0
  82. edc_auth/models/user_profile.py +14 -11
  83. edc_auth/post_migrate_signals.py +1 -1
  84. edc_auth/site_auths.py +80 -67
  85. edc_consent/actions.py +4 -5
  86. edc_consent/auths.py +18 -12
  87. edc_consent/migrations/0001_initial.py +3 -2
  88. edc_consent/migrations/0006_alter_edcpermissions_device_created_and_more.py +49 -0
  89. edc_consent/model_mixins/consent_extension_model_mixin.py +4 -5
  90. edc_consent/model_mixins/requires_consent_fields_model_mixin.py +2 -2
  91. edc_consent/utils.py +1 -1
  92. edc_constants/constants.py +1 -0
  93. edc_crf/auths.py +5 -0
  94. edc_crf/migrations/0001_initial.py +3 -2
  95. edc_crf/migrations/0009_alter_crfstatus_device_created_and_more.py +54 -0
  96. edc_dashboard/auths.py +10 -6
  97. edc_dashboard/migrations/0001_initial.py +3 -2
  98. edc_dashboard/migrations/0006_alter_edcpermissions_device_created_and_more.py +49 -0
  99. edc_dashboard/templatetags/edc_dashboard_extras.py +3 -2
  100. edc_dashboard/url_config.py +92 -83
  101. edc_dashboard/url_names.py +4 -4
  102. edc_dashboard/view_mixins/url_request_context_mixin.py +6 -5
  103. edc_data_manager/action_items.py +4 -6
  104. edc_data_manager/admin/actions.py +5 -5
  105. edc_data_manager/admin/data_query_admin.py +12 -11
  106. edc_data_manager/auths.py +37 -34
  107. edc_data_manager/handlers/handlers.py +6 -7
  108. edc_data_manager/migrations/0001_initial.py +19 -19
  109. edc_data_manager/migrations/0025_edcpermissions.py +3 -2
  110. edc_data_manager/migrations/0040_alter_datadictionary_device_created_and_more.py +327 -0
  111. edc_data_manager/migrations/0041_alter_dataquery_dm_user_and_more.py +164 -0
  112. edc_data_manager/models/data_query.py +5 -5
  113. edc_data_manager/models/model_mixins.py +8 -8
  114. edc_data_manager/rule/query_rule_wrapper.py +7 -7
  115. edc_data_manager/rule/rule_runner.py +4 -3
  116. edc_document_status/model_mixins.py +1 -1
  117. edc_egfr/egfr.py +11 -7
  118. edc_egfr/model_mixins/egfr_model_mixin.py +3 -7
  119. edc_export/admin/data_request_admin.py +2 -2
  120. edc_export/archive_exporter.py +7 -5
  121. edc_export/auths.py +32 -28
  122. edc_export/files_archiver.py +3 -2
  123. edc_export/migrations/0001_initial.py +26 -26
  124. edc_export/migrations/0004_auto_20190305_0123.py +25 -72
  125. edc_export/migrations/0013_edcpermissions.py +3 -2
  126. edc_export/migrations/0022_alter_datarequest_description_and_more.py +611 -0
  127. edc_export/migrations/0023_alter_datarequesthistory_archive_filename_and_more.py +29 -0
  128. edc_export/model_exporter/file_history_updater.py +10 -10
  129. edc_export/model_exporter/model_exporter.py +17 -15
  130. edc_export/model_exporter/object_history_helpers.py +2 -2
  131. edc_export/models/data_request_history.py +4 -4
  132. edc_export/utils.py +10 -15
  133. edc_facility/auths.py +8 -3
  134. edc_facility/facility.py +16 -22
  135. edc_facility/import_holidays.py +13 -13
  136. edc_facility/migrations/0005_healthfacility_healthfacilitytypes_and_more.py +8 -7
  137. edc_facility/migrations/0016_alter_healthfacility_device_created_and_more.py +139 -0
  138. edc_facility/migrations/0017_alter_healthfacility_gps_and_more.py +54 -0
  139. edc_facility/model_mixins.py +5 -5
  140. edc_form_label/custom_label_condition.py +11 -8
  141. edc_form_label/form_label.py +1 -1
  142. edc_form_runners/auths.py +11 -6
  143. edc_form_runners/form_runner.py +3 -5
  144. edc_form_runners/migrations/0001_initial.py +3 -2
  145. edc_form_runners/migrations/0005_alter_issue_device_created_and_more.py +49 -0
  146. edc_form_validators/applicable_field_validator.py +7 -6
  147. edc_form_validators/base_form_validator.py +8 -9
  148. edc_form_validators/other_specify_field_validator.py +2 -8
  149. edc_form_validators/required_field_validator.py +19 -16
  150. edc_identifier/migrations/0001_squashed_0018_auto_20180128_1054.py +3 -3
  151. edc_identifier/migrations/0002_auto_20190305_0123.py +3 -6
  152. edc_identifier/migrations/0006_auto_20161127_2226.py +15 -15
  153. edc_identifier/migrations/0007_auto_20161204_2227.py +3 -3
  154. edc_identifier/migrations/0009_auto_20161221_2323.py +3 -5
  155. edc_identifier/migrations/0010_auto_20170112_0602.py +17 -17
  156. edc_identifier/migrations/0011_alter_identifiermodel_device_created_and_more.py +79 -0
  157. edc_identifier/model_mixins.py +1 -1
  158. edc_identifier/models.py +9 -9
  159. edc_identifier/research_identifier.py +11 -10
  160. edc_identifier/simple_identifier.py +9 -5
  161. edc_identifier/subject_identifier.py +3 -2
  162. edc_lab/auths.py +26 -23
  163. edc_lab/lab/aliquot_creator.py +5 -8
  164. edc_lab/lab/primary_aliquot.py +14 -5
  165. edc_lab/migrations/0001_initial.py +29 -29
  166. edc_lab/migrations/0008_auto_20170921_0719.py +3 -3
  167. edc_lab/migrations/0010_auto_20171127_1541.py +15 -15
  168. edc_lab/migrations/0012_auto_20180114_1438.py +3 -3
  169. edc_lab/migrations/0019_auto_20190305_0123.py +43 -127
  170. edc_lab/migrations/0036_alter_aliquot_comment_alter_aliquot_device_created_and_more.py +1139 -0
  171. edc_lab/migrations/0037_alter_historicalorder_order_datetime_and_more.py +31 -0
  172. edc_lab/model_mixins/aliquot/aliquot_identifier_model_mixin.py +2 -2
  173. edc_lab/model_mixins/aliquot/aliquot_model_mixin.py +2 -2
  174. edc_lab/model_mixins/panel_model_mixin.py +18 -7
  175. edc_lab/model_mixins/requisition/crf_with_requisition_model_mixin.py +2 -4
  176. edc_lab/model_mixins/requisition/requisition_model_mixin.py +13 -16
  177. edc_lab/model_mixins/requisition/requisition_verify_model_mixin.py +1 -1
  178. edc_lab/model_mixins/result/result_item_model_mixin.py +4 -4
  179. edc_lab/model_mixins/shipping/manifest_model_mixin.py +4 -5
  180. edc_lab/model_mixins/shipping/verify_model_mixin.py +4 -6
  181. edc_lab/models/aliquot.py +2 -2
  182. edc_lab/models/box.py +1 -1
  183. edc_lab/models/box_item.py +6 -7
  184. edc_lab/models/manifest/manifest.py +3 -3
  185. edc_lab/models/manifest/manifest_item.py +7 -7
  186. edc_lab/models/manifest/shipper.py +3 -4
  187. edc_lab/models/order.py +6 -4
  188. edc_lab/models/panel.py +4 -4
  189. edc_lab/models/result.py +2 -2
  190. edc_lab/models/result_item.py +2 -2
  191. edc_lab_dashboard/auths.py +16 -11
  192. edc_lab_dashboard/migrations/0001_initial.py +3 -2
  193. edc_lab_dashboard/migrations/0005_alter_edcpermissions_device_created_and_more.py +49 -0
  194. edc_lab_dashboard/views/action_views/receive_view.py +2 -2
  195. edc_lab_dashboard/views/action_views/verify_box_item_view.py +2 -2
  196. edc_lab_results/calculate_missing.py +8 -8
  197. edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py +2 -2
  198. edc_lab_results/get_summary.py +26 -25
  199. edc_lab_results/model_mixins/blood_result_model_mixin.py +9 -7
  200. edc_lab_results/model_mixins/fbg_model_mixin.py +3 -3
  201. edc_lab_results/model_mixins/glucose_model_mixin.py +3 -3
  202. edc_lab_results/model_mixins/hba1c_model_mixin.py +3 -3
  203. edc_label/auths.py +6 -1
  204. edc_label/label_template.py +8 -8
  205. edc_label/migrations/0001_initial.py +3 -2
  206. edc_label/migrations/0007_alter_zpllabeltemplates_device_created_and_more.py +49 -0
  207. edc_list_data/list_model_maker.py +2 -2
  208. edc_list_data/load_list_data.py +11 -14
  209. edc_list_data/load_model_data.py +3 -3
  210. edc_list_data/model_mixins.py +12 -4
  211. edc_list_data/post_migrate_signals.py +1 -1
  212. edc_list_data/preload_data.py +4 -4
  213. edc_list_data/row.py +1 -1
  214. edc_list_data/site_list_data.py +6 -5
  215. edc_listboard/migrations/0001_initial.py +3 -2
  216. edc_listboard/migrations/0007_alter_listboard_device_created_and_more.py +49 -0
  217. edc_locator/auths.py +18 -13
  218. edc_locator/migrations/0001_initial.py +7 -7
  219. edc_locator/migrations/0018_auto_20190305_0123.py +5 -12
  220. edc_locator/migrations/0040_alter_historicalsubjectlocator_action_identifier_and_more.py +187 -0
  221. edc_locator/migrations/0041_alter_historicalsubjectlocator_report_datetime_and_more.py +27 -0
  222. edc_locator/model_mixins/locator_model_mixin.py +2 -2
  223. edc_locator/model_mixins/subject_contact_fields_mixin.py +1 -1
  224. edc_locator/modeladmin_mixins.py +5 -4
  225. edc_ltfu/model_mixins.py +7 -5
  226. edc_metadata/auths.py +11 -7
  227. edc_metadata/metadata/metadata.py +1 -1
  228. edc_metadata/metadata_rules/crf/crf_rule.py +1 -1
  229. edc_metadata/metadata_rules/decorators.py +1 -1
  230. edc_metadata/metadata_rules/metadata_rule_evaluator.py +5 -3
  231. edc_metadata/metadata_rules/rule.py +2 -3
  232. edc_metadata/metadata_rules/rule_evaluator.py +1 -1
  233. edc_metadata/migrations/0002_auto_20161127_2226.py +5 -5
  234. edc_metadata/migrations/0005_auto_20170112_0602.py +5 -5
  235. edc_metadata/migrations/0011_auto_20190305_0123.py +5 -12
  236. edc_metadata/migrations/0031_alter_crfmetadata_device_created_and_more.py +120 -0
  237. edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +7 -4
  238. edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +5 -2
  239. edc_metadata/models/crf_metadata.py +5 -5
  240. edc_metadata/models/crf_metadata_model_mixin.py +4 -5
  241. edc_metadata/models/requisition_metadata.py +6 -6
  242. edc_metadata/models/signals.py +10 -11
  243. edc_metadata/views/refresh_metadata_actions_view.py +3 -3
  244. edc_model/models/address_mixin.py +6 -6
  245. edc_model/models/fields/initials_field.py +2 -1
  246. edc_model/models/historical_records.py +2 -9
  247. edc_model/models/url_model_mixin.py +2 -2
  248. edc_model/validators/date.py +7 -8
  249. edc_model_admin/list_filters/future_date_list_filter.py +3 -3
  250. edc_model_admin/list_filters/past_date_list_filter.py +3 -3
  251. edc_model_admin/mixins/model_admin_protect_pii_mixin.py +4 -5
  252. edc_model_fields/fields/other_charfield.py +2 -2
  253. edc_navbar/auths.py +18 -13
  254. edc_navbar/migrations/0004_auto_20220825_0451.py +3 -2
  255. edc_navbar/migrations/0009_alter_edcpermissions_device_created_and_more.py +49 -0
  256. edc_notification/auths.py +9 -4
  257. edc_notification/migrations/0001_initial.py +3 -3
  258. edc_notification/migrations/0004_auto_20190305_0123.py +3 -6
  259. edc_notification/migrations/0011_alter_notification_device_created_and_more.py +59 -0
  260. edc_notification/models/notification.py +1 -1
  261. edc_notification/notification/graded_event_notification.py +2 -2
  262. edc_notification/notification/model_notification.py +3 -30
  263. edc_notification/notification/new_model_notification.py +1 -1
  264. edc_notification/notification/notification.py +12 -12
  265. edc_notification/notification/updated_model_notification.py +2 -2
  266. edc_offstudy/auths.py +12 -7
  267. edc_offstudy/migrations/0001_initial.py +4 -4
  268. edc_offstudy/migrations/0002_auto_20180921_0434.py +4 -4
  269. edc_offstudy/migrations/0005_auto_20190305_0123.py +5 -12
  270. edc_offstudy/migrations/0023_alter_historicalsubjectoffstudy_action_identifier_and_more.py +188 -0
  271. edc_offstudy/migrations/0024_alter_historicalsubjectoffstudy_offstudy_datetime_and_more.py +43 -0
  272. edc_offstudy/model_mixins/offstudy_model_mixin.py +3 -2
  273. edc_pdutils/df_exporters/csv_exporter.py +33 -37
  274. edc_pdutils/df_exporters/csv_model_exporter.py +7 -3
  275. edc_pdutils/migrations/0001_initial.py +8 -8
  276. edc_pharmacy/admin/prescription/rx_refill_admin.py +4 -3
  277. edc_pharmacy/admin/reports/stock_availability_admin.py +5 -6
  278. edc_pharmacy/auths.py +19 -15
  279. edc_pharmacy/migrations/0001_initial.py +64 -63
  280. edc_pharmacy/migrations/0015_auto_20220913_2139.py +26 -26
  281. edc_pharmacy/migrations/0024_allocation_assignment_containerunits_dispense_and_more.py +81 -86
  282. edc_pharmacy/migrations/0037_remove_historicalstock_confirmed_at_site_by_and_more.py +5 -5
  283. edc_pharmacy/migrations/0039_remove_dispense_registered_subject_and_more.py +7 -8
  284. edc_pharmacy/migrations/0049_remove_stocktransferconfirmation_stock_and_more.py +12 -12
  285. edc_pharmacy/migrations/0050_remove_stocktransferconfirmation2_location_and_more.py +5 -5
  286. edc_pharmacy/migrations/0051_alter_historicalstocktransferconfirmationitem_options_and_more.py +3 -4
  287. edc_pharmacy/migrations/0053_alter_location_managers_alter_historicalstock_lot_and_more.py +3 -3
  288. edc_pharmacy/migrations/0057_scanduplicates.py +3 -3
  289. edc_pharmacy/migrations/0060_alter_container_max_per_subject_and_more.py +13 -15
  290. edc_pharmacy/migrations/0068_stockout.py +2 -3
  291. edc_pharmacy/migrations/0076_historicalstockadjustment_stockadjustment.py +5 -5
  292. edc_pharmacy/migrations/0077_historicalstockadjustment_adjustment_datetime_and_more.py +3 -4
  293. edc_pharmacy/migrations/0081_historicalconfirmation_confirmation.py +8 -8
  294. edc_pharmacy/migrations/0084_confirmationatsiteitem_and_more.py +0 -2
  295. edc_pharmacy/migrations/0089_alter_allocation_allocated_by_and_more.py +3284 -0
  296. edc_pharmacy/migrations/0090_alter_allocation_allocation_datetime_and_more.py +227 -0
  297. edc_pharmacy/model_mixins/study_medication_refill_model_mixin.py +2 -2
  298. edc_pharmacy/models/medication/formulation.py +5 -7
  299. edc_pharmacy/models/medication/medication.py +3 -3
  300. edc_pharmacy/models/model_mixins.py +10 -11
  301. edc_pharmacy/models/prescription/rx.py +13 -12
  302. edc_pharmacy/models/prescription/rx_refill.py +0 -1
  303. edc_pharmacy/models/stock/allocation.py +3 -3
  304. edc_pharmacy/models/stock/confirmation.py +3 -3
  305. edc_pharmacy/models/stock/confirmation_at_site.py +3 -2
  306. edc_pharmacy/models/stock/confirmation_at_site_item.py +3 -4
  307. edc_pharmacy/models/stock/dispense.py +5 -6
  308. edc_pharmacy/models/stock/dispense_item.py +2 -2
  309. edc_pharmacy/models/stock/location.py +3 -4
  310. edc_pharmacy/models/stock/lot.py +6 -7
  311. edc_pharmacy/models/stock/order.py +1 -1
  312. edc_pharmacy/models/stock/receive.py +3 -3
  313. edc_pharmacy/models/stock/receive_item.py +4 -4
  314. edc_pharmacy/models/stock/repack_request.py +2 -2
  315. edc_pharmacy/models/stock/stock.py +12 -18
  316. edc_pharmacy/models/stock/stock_adjustment.py +4 -4
  317. edc_pharmacy/models/stock/stock_request.py +2 -2
  318. edc_pharmacy/models/stock/stock_request_item.py +2 -2
  319. edc_pharmacy/models/stock/stock_transfer.py +3 -3
  320. edc_pharmacy/models/stock/stock_transfer_item.py +2 -2
  321. edc_pharmacy/models/stock/storage_bin.py +3 -4
  322. edc_pharmacy/models/stock/storage_bin_item.py +2 -2
  323. edc_pharmacy/models/stock/supplier.py +3 -4
  324. edc_pharmacy/models/storage/box.py +2 -2
  325. edc_pharmacy/models/storage/items/container_model_mixin.py +3 -4
  326. edc_pharmacy/models/storage/items/pill_bottle_model_mixin.py +2 -2
  327. edc_pharmacy/models/storage/items/subject_pill_bottle.py +1 -1
  328. edc_pharmacy/models/storage/room.py +2 -2
  329. edc_pharmacy/models/storage/shelf.py +2 -2
  330. edc_pharmacy/models/storage/utils.py +1 -1
  331. edc_pharmacy/prescribe/create_prescription.py +3 -3
  332. edc_pharmacy/utils/allocate_stock.py +2 -3
  333. edc_pharmacy/utils/confirm_stock.py +3 -4
  334. edc_pharmacy/utils/confirm_stock_at_site.py +4 -5
  335. edc_pharmacy/utils/dispense.py +3 -4
  336. edc_pharmacy/utils/process_repack_request.py +4 -5
  337. edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py +2 -3
  338. edc_pharmacy/utils/transfer_stock.py +2 -3
  339. edc_pharmacy/views/add_to_storage_bin_view.py +2 -2
  340. edc_pharmacy/views/allocate_to_subject_view.py +2 -2
  341. edc_pharmacy/views/confirmation_at_site_view.py +6 -9
  342. edc_pharmacy/views/dispense_view.py +2 -2
  343. edc_pharmacy/views/move_to_storage_bin_view.py +2 -3
  344. edc_pharmacy/views/print_labels_view.py +2 -2
  345. edc_prn/admin_site.py +5 -0
  346. edc_prn/prn.py +10 -11
  347. edc_prn/urls.py +11 -0
  348. edc_protocol/research_protocol_config.py +2 -3
  349. edc_protocol_incident/action_items.py +4 -4
  350. edc_protocol_incident/auths.py +27 -20
  351. edc_protocol_incident/migrations/0001_initial.py +7 -7
  352. edc_protocol_incident/migrations/0001_squashed_0015_auto_20220927_0401.py +13 -13
  353. edc_protocol_incident/migrations/0005_protocolincident_historicalprotocolincident_and_more.py +7 -7
  354. edc_protocol_incident/migrations/0024_alter_actionsrequired_extra_value_and_more.py +625 -0
  355. edc_protocol_incident/migrations/0025_alter_historicalprotocoldeviationviolation_report_datetime_and_more.py +42 -0
  356. edc_protocol_incident/model_mixins/protocol_deviation_violation_model_mixin.py +18 -11
  357. edc_protocol_incident/model_mixins/protocol_incident_model_mixin.py +26 -11
  358. edc_pylabels/actions.py +3 -5
  359. edc_pylabels/auths.py +6 -1
  360. edc_pylabels/migrations/0002_alter_label_options_label_created_and_more.py +3 -7
  361. edc_pylabels/migrations/0005_labelconfiguration_delete_label_and_more.py +3 -3
  362. edc_pylabels/migrations/0013_alter_labelconfiguration_device_created_and_more.py +49 -0
  363. edc_qareports/auths.py +11 -7
  364. edc_qareports/migrations/0001_initial.py +4 -5
  365. edc_qareports/migrations/0005_edcpermissions.py +3 -3
  366. edc_qareports/migrations/0006_qareportlog.py +2 -3
  367. edc_qareports/migrations/0019_alter_edcpermissions_device_created_and_more.py +102 -0
  368. edc_qareports/migrations/0020_alter_note_report_datetime_and_more.py +24 -0
  369. edc_qareports/model_mixins/note_model_mixin.py +7 -4
  370. edc_qareports/model_mixins/on_study_missing_values_model_mixin.py +4 -4
  371. edc_qareports/model_mixins/qa_report_model_mixin.py +2 -3
  372. edc_qareports/models/note.py +4 -4
  373. edc_qareports/models/qa_reports_log.py +2 -3
  374. edc_randomization/admin.py +30 -24
  375. edc_randomization/auths.py +12 -7
  376. edc_randomization/decorators.py +1 -1
  377. edc_randomization/migrations/0001_initial.py +3 -2
  378. edc_randomization/migrations/0002_historicalrandomizationlist.py +3 -2
  379. edc_randomization/migrations/0009_edcpermissions.py +3 -2
  380. edc_randomization/migrations/0014_alter_edcpermissions_device_created_and_more.py +141 -0
  381. edc_randomization/model_mixins.py +9 -9
  382. edc_randomization/randomizer.py +22 -20
  383. edc_randomization/utils.py +19 -18
  384. edc_refusal/auths.py +7 -2
  385. edc_refusal/migrations/0001_initial.py +4 -4
  386. edc_refusal/migrations/0002_historicalsubjectrefusal.py +4 -4
  387. edc_refusal/migrations/0012_alter_historicalsubjectrefusal_comment_and_more.py +122 -0
  388. edc_refusal/migrations/0013_alter_historicalsubjectrefusal_report_datetime_and_more.py +28 -0
  389. edc_refusal/model_mixins.py +4 -4
  390. edc_registration/auths.py +28 -23
  391. edc_registration/migrations/0002_auto_20161127_2226.py +3 -4
  392. edc_registration/migrations/0005_auto_20170111_1809.py +4 -2
  393. edc_registration/migrations/0016_historicalregisteredsubject.py +3 -2
  394. edc_registration/migrations/0017_auto_20190305_0123.py +5 -12
  395. edc_registration/migrations/0033_alter_historicalregisteredsubject_additional_key_and_more.py +302 -0
  396. edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +15 -7
  397. edc_registration/models/registered_subject.py +28 -27
  398. edc_reportable/age_evaluator.py +4 -2
  399. edc_reportable/formula.py +5 -5
  400. edc_reportable/migrations/0001_initial.py +7 -7
  401. edc_reportable/migrations/0003_referencerangecollection_grade1_and_more.py +3 -3
  402. edc_reportable/migrations/0004_alter_referencerangecollection_grade3_and_more.py +5 -5
  403. edc_reportable/migrations/0006_alter_gradingdata_revision_and_more.py +5 -6
  404. edc_reportable/migrations/0007_alter_gradingdata_age_phrase_and_more.py +509 -0
  405. edc_reportable/models/normal_data.py +4 -5
  406. edc_reportable/models/reference_model_mixins.py +10 -11
  407. edc_reportable/models/reference_range_collection.py +12 -13
  408. edc_reportable/post_migrate_signals.py +1 -1
  409. edc_reportable/utils/convert_units.py +11 -45
  410. edc_reportable/utils/get_normal_data_or_raise.py +2 -2
  411. edc_reportable/utils/get_reference_range_collection.py +2 -3
  412. edc_reportable/utils/load_data.py +4 -4
  413. edc_reportable/utils/update_grading_data.py +5 -5
  414. edc_review_dashboard/auths.py +23 -18
  415. edc_review_dashboard/migrations/0001_initial.py +3 -2
  416. edc_review_dashboard/migrations/0006_alter_edcpermissions_device_created_and_more.py +49 -0
  417. edc_screening/age_evaluator.py +6 -6
  418. edc_screening/auths.py +35 -30
  419. edc_screening/eligibility.py +1 -1
  420. edc_screening/gender_evaluator.py +1 -1
  421. edc_screening/migrations/0001_initial.py +3 -2
  422. edc_screening/migrations/0005_alter_edcpermissions_device_created_and_more.py +49 -0
  423. edc_screening/model_mixins/eligibility_model_mixin.py +3 -6
  424. edc_screening/model_mixins/screening_fields_model_mixin.py +6 -8
  425. edc_screening/model_mixins/screening_methods_model_mixin.py +1 -1
  426. edc_screening/screening_eligibility.py +2 -4
  427. edc_screening/utils.py +9 -9
  428. edc_search/generate_slug.py +26 -0
  429. edc_search/model_mixins.py +10 -22
  430. edc_sites/auths.py +8 -3
  431. edc_sites/migrations/0007_edcpermissions.py +3 -2
  432. edc_sites/migrations/0010_alter_edcpermissions_device_created_and_more.py +69 -0
  433. edc_sites/model_mixins/site_model_mixin.py +2 -2
  434. edc_sites/site.py +5 -5
  435. edc_sites/system_checks.py +1 -1
  436. edc_subject_dashboard/auths.py +27 -22
  437. edc_subject_dashboard/migrations/0001_initial.py +3 -2
  438. edc_subject_dashboard/migrations/0005_alter_edcpermissions_device_created_and_more.py +49 -0
  439. edc_subject_dashboard/requisition_report.py +6 -4
  440. edc_subject_dashboard/requisition_verifier.py +6 -6
  441. edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +2 -2
  442. edc_subject_dashboard/view_mixins/subject_visit_view_mixin.py +7 -8
  443. edc_subject_dashboard/view_utils/subject_consent_listboard_button.py +3 -2
  444. edc_subject_dashboard/view_utils/timepoint_status_button.py +2 -2
  445. edc_timepoint/apps.py +0 -21
  446. edc_timepoint/model_mixins.py +22 -20
  447. edc_transfer/model_mixins.py +4 -4
  448. edc_transfer/modeladmin_mixins.py +1 -1
  449. edc_unblinding/auths.py +9 -4
  450. edc_unblinding/migrations/0001_initial.py +13 -13
  451. edc_unblinding/migrations/0014_alter_historicalunblindingrequest_action_identifier_and_more.py +291 -0
  452. edc_unblinding/migrations/0015_alter_historicalunblindingrequest_report_datetime_and_more.py +45 -0
  453. edc_unblinding/models/unblinding_request.py +2 -2
  454. edc_unblinding/models/unblinding_review.py +3 -3
  455. edc_utils/__init__.py +3 -1
  456. edc_utils/age.py +10 -16
  457. edc_utils/show_urls.py +29 -2
  458. edc_visit_schedule/auths.py +6 -1
  459. edc_visit_schedule/migrations/0001_initial.py +3 -3
  460. edc_visit_schedule/migrations/0002_auto_20190305_0123.py +3 -6
  461. edc_visit_schedule/migrations/0003_historicalvisitschedule_visitschedule.py +5 -4
  462. edc_visit_schedule/migrations/0015_historicalonschedule_offschedule_onschedule.py +10 -10
  463. edc_visit_schedule/migrations/0019_alter_historicalonschedule_device_created_and_more.py +229 -0
  464. edc_visit_schedule/migrations/0020_alter_historicalonschedule_onschedule_datetime_and_more.py +65 -0
  465. edc_visit_schedule/model_mixins/off_schedule_model_mixin.py +5 -4
  466. edc_visit_schedule/model_mixins/on_schedule_model_mixin.py +5 -4
  467. edc_visit_schedule/model_mixins/visit_schedule/visit_code_fields_model_mixin.py +1 -1
  468. edc_visit_schedule/models/subject_schedule_history.py +6 -6
  469. edc_visit_schedule/schedule/visit_collection.py +1 -1
  470. edc_visit_schedule/site_visit_schedules.py +2 -2
  471. edc_visit_schedule/subject_schedule.py +5 -4
  472. edc_visit_schedule/view_mixins.py +2 -3
  473. edc_visit_schedule/visit/visit.py +3 -2
  474. edc_visit_tracking/migrations/0004_subjectvisit_subjectvisitmissedreasons_extra_value_and_more.py +13 -13
  475. edc_visit_tracking/migrations/0009_alter_historicalsubjectvisit_comments_and_more.py +450 -0
  476. edc_visit_tracking/migrations/0010_alter_historicalsubjectvisit_report_datetime_and_more.py +68 -0
  477. edc_visit_tracking/model_mixins/base/visit_methods_model_mixin.py +3 -3
  478. edc_visit_tracking/model_mixins/crfs/visit_tracking_crf_model_mixin.py +2 -2
  479. edc_visit_tracking/model_mixins/requisitions/visit_tracking_requisition_model_mixin.py +2 -2
  480. edc_visit_tracking/model_mixins/subject_visit_missed_model_mixin.py +2 -2
  481. edc_visit_tracking/model_mixins/visit_model_mixin/caretaker_fields_mixin.py +2 -3
  482. edc_visit_tracking/model_mixins/visit_model_mixin/previous_visit_model_mixin.py +1 -1
  483. edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_fields_mixin.py +8 -11
  484. edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_mixin.py +1 -1
  485. edc_visit_tracking/models/signals.py +3 -3
  486. edc_visit_tracking/models/subject_visit.py +1 -1
  487. edc_vitals/calculators/bmi.py +7 -5
  488. edc_vitals/utils.py +1 -1
  489. edc_form_label/models.py +0 -0
  490. edc_search/constants.py +0 -1
  491. edc_search/models.py +0 -0
  492. edc_search/search_slug.py +0 -50
  493. edc_search/updater.py +0 -30
  494. edc_search/wsgi.py +0 -7
  495. {clinicedc-2.0.6.dist-info → clinicedc-2.0.8.dist-info}/licenses/LICENSE +0 -0
@@ -8,6 +8,7 @@ from django.core.validators import (
8
8
  RegexValidator,
9
9
  )
10
10
  from django.db import models
11
+ from django.utils import timezone
11
12
  from django_crypto_fields.fields import EncryptedCharField
12
13
 
13
14
  from edc_constants.choices import GENDER, YES_NO, YES_NO_NA
@@ -15,7 +16,6 @@ from edc_constants.constants import NO, NOT_APPLICABLE
15
16
  from edc_model.validators import datetime_not_future
16
17
  from edc_protocol.validators import datetime_not_before_study_start
17
18
  from edc_sites.model_mixins import SiteModelMixin
18
- from edc_utils.date import get_utcnow
19
19
 
20
20
 
21
21
  class ScreeningFieldsModeMixin(SiteModelMixin, models.Model):
@@ -34,7 +34,7 @@ class ScreeningFieldsModeMixin(SiteModelMixin, models.Model):
34
34
  report_datetime = models.DateTimeField(
35
35
  verbose_name="Report Date and Time",
36
36
  validators=[datetime_not_before_study_start, datetime_not_future],
37
- default=get_utcnow,
37
+ default=timezone.now,
38
38
  help_text="Date and time of report.",
39
39
  )
40
40
 
@@ -54,7 +54,7 @@ class ScreeningFieldsModeMixin(SiteModelMixin, models.Model):
54
54
  validators=[MinValueValidator(0), MaxValueValidator(110)]
55
55
  )
56
56
 
57
- ethnicity = models.CharField(max_length=25, null=True, blank=True)
57
+ ethnicity = models.CharField(max_length=25, default="", blank=True)
58
58
 
59
59
  consent_ability = models.CharField(
60
60
  verbose_name="Participant or legal guardian/representative able and "
@@ -65,8 +65,7 @@ class ScreeningFieldsModeMixin(SiteModelMixin, models.Model):
65
65
 
66
66
  unsuitable_for_study = models.CharField(
67
67
  verbose_name=(
68
- "Is there any other reason the patient is "
69
- "deemed to not be suitable for the study?"
68
+ "Is there any other reason the patient is deemed to not be suitable for the study?"
70
69
  ),
71
70
  max_length=5,
72
71
  choices=YES_NO,
@@ -77,14 +76,13 @@ class ScreeningFieldsModeMixin(SiteModelMixin, models.Model):
77
76
  reasons_unsuitable = models.TextField(
78
77
  verbose_name="Reason not suitable for the study",
79
78
  max_length=150,
80
- null=True,
79
+ default="",
81
80
  blank=True,
82
81
  )
83
82
 
84
83
  unsuitable_agreed = models.CharField(
85
84
  verbose_name=(
86
- "Does the study coordinator agree that the patient "
87
- "is not suitable for the study?"
85
+ "Does the study coordinator agree that the patient is not suitable for the study?"
88
86
  ),
89
87
  max_length=5,
90
88
  choices=YES_NO_NA,
@@ -16,7 +16,7 @@ class ScreeningMethodsModeMixin(models.Model):
16
16
 
17
17
  @staticmethod
18
18
  def get_search_slug_fields():
19
- return ["screening_identifier", "subject_identifier", "reference"]
19
+ return "screening_identifier", "subject_identifier", "reference"
20
20
 
21
21
  @property
22
22
  def estimated_dob(self: SubjectScreeningModelStub) -> date:
@@ -174,7 +174,7 @@ class ScreeningEligibility:
174
174
  setattr(
175
175
  self.model_obj,
176
176
  self.reasons_ineligible_fld_name,
177
- "|".join(self.reasons_ineligible.values()) or None,
177
+ "|".join(self.reasons_ineligible.values()) or "",
178
178
  )
179
179
  self.set_eligible_model_field()
180
180
  self.set_fld_attrs_on_model()
@@ -229,9 +229,7 @@ class ScreeningEligibility:
229
229
  }
230
230
 
231
231
  def formatted_reasons_ineligible(self) -> str:
232
- str_values = "<BR>".join(
233
- [x for x in self.reasons_ineligible.values() if x is not None]
234
- )
232
+ str_values = "<BR>".join([x for x in self.reasons_ineligible.values() if x])
235
233
  return format_html(
236
234
  "{}",
237
235
  mark_safe(str_values), # nosec B703 B308
edc_screening/utils.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  import re
4
5
  from typing import TYPE_CHECKING, Any
5
6
 
@@ -33,14 +34,15 @@ def get_subject_screening_model_cls() -> Any:
33
34
  return django_apps.get_model(get_subject_screening_model())
34
35
 
35
36
 
36
- def format_reasons_ineligible(*str_values: str, delimiter=None) -> str:
37
+ def format_reasons_ineligible(*str_values: str | None, delimiter: str | None = None) -> str:
37
38
  reasons = None
38
39
  delimiter = delimiter or "|"
39
- str_values = tuple(x for x in str_values if x is not None)
40
+ str_values = str_values or []
41
+ str_values = tuple(x for x in str_values if x)
40
42
  if str_values:
41
43
  reasons = format_html(
42
44
  "{}",
43
- mark_safe(delimiter.join(str_values)), # nosec B703 B308
45
+ mark_safe(delimiter.join(str_values)), # noqa: S308
44
46
  )
45
47
  return reasons
46
48
 
@@ -73,10 +75,10 @@ def get_subject_screening_or_raise(
73
75
  )
74
76
  except ObjectDoesNotExist as e:
75
77
  if is_modelform:
76
- raise forms.ValidationError("Not allowed. Screening form not found.")
78
+ raise forms.ValidationError("Not allowed. Screening form not found.") from e
77
79
  raise ObjectDoesNotExist(
78
80
  f"{e} screening_identifier={screening_identifier}. Perhaps catch this in the form."
79
- )
81
+ ) from e
80
82
  return subject_screening
81
83
 
82
84
 
@@ -101,10 +103,8 @@ def is_eligible_or_raise(
101
103
  )
102
104
 
103
105
  url_name = url_name or "screening_listboard_url"
104
- try:
106
+ with contextlib.suppress(InvalidDashboardUrlName):
105
107
  url_name = url_names.get(url_name)
106
- except InvalidDashboardUrlName:
107
- pass
108
108
 
109
109
  if not subject_screening.eligible:
110
110
  try:
@@ -128,7 +128,7 @@ def is_eligible_or_raise(
128
128
  else:
129
129
  msg = format_html(
130
130
  'Not allowed. Subject is not eligible. See subject <A href="{}">{}</A>',
131
- mark_safe(url), # nosec B308 B703
131
+ mark_safe(url), # noqa: S308
132
132
  subject_screening.screening_identifier,
133
133
  )
134
134
  raise forms.ValidationError(msg)
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from django.utils.text import slugify
4
+ from django_crypto_fields.utils import get_encrypted_field_names
5
+
6
+ SLUG_SEP = "|"
7
+
8
+
9
+ def generate_slug(obj, fields) -> str | None:
10
+ """Returns a slug of the char values of the fields
11
+ listed on the models get_search_slug_fields() method.
12
+
13
+ Excludes any encrypted fields if listed.
14
+ """
15
+ slug = None
16
+ if obj and fields:
17
+ values = []
18
+ for field in (f for f in fields if f not in get_encrypted_field_names(obj)):
19
+ v = obj
20
+ for f in field.split("."):
21
+ v = getattr(v, f)
22
+ if isinstance(v, str):
23
+ values.append(v[:50]) # truncate value
24
+ slugs = [slugify(v) for v in list(set(values)) if v]
25
+ slug = SLUG_SEP.join(slugs)[:250]
26
+ return slug
@@ -1,43 +1,31 @@
1
1
  from django.db import models
2
+ from tqdm import tqdm
2
3
 
3
- from .updater import SearchSlugUpdater
4
+ from .generate_slug import generate_slug
4
5
 
5
6
 
6
7
  class SearchSlugManager(models.Manager):
7
- search_slug_updater_cls = SearchSlugUpdater
8
- search_slug_field_name = "slug"
9
-
10
8
  def update_search_slugs(self) -> None:
11
- for obj in self.all():
12
- updater = self.search_slug_updater_cls(
13
- fields=obj.get_search_slug_fields(), model_obj=obj
14
- )
15
- setattr(obj, self.search_slug_field_name, updater.slug)
16
- obj.save_base(update_fields=[self.search_slug_field_name])
9
+ qs = self.all()
10
+ for obj in tqdm(qs, total=qs.count()):
11
+ obj.slug = generate_slug(obj, obj.get_search_slug_fields()) or ""
12
+ obj.save_base(update_fields=["slug"])
17
13
 
18
14
 
19
15
  class SearchSlugModelMixin(models.Model):
20
- search_slug_warning = None
21
- search_slug_updater_cls = SearchSlugUpdater
22
-
23
- def get_search_slug_fields(self) -> list[str]:
24
- return []
16
+ def get_search_slug_fields(self) -> tuple[str, ...]:
17
+ return ()
25
18
 
26
19
  slug = models.CharField(
27
20
  max_length=250,
28
21
  default="",
29
- null=True,
30
22
  editable=False,
31
23
  db_index=True,
32
- help_text="a field used for quick search",
24
+ help_text="Hold slug field values for quick search. Excludes encrypted fields",
33
25
  )
34
26
 
35
27
  def save(self, *args, **kwargs):
36
- updater = self.search_slug_updater_cls(
37
- fields=self.get_search_slug_fields(), model_obj=self
38
- )
39
- self.search_slug_warning = updater.warning
40
- self.slug = updater.slug
28
+ self.slug = generate_slug(self, self.get_search_slug_fields()) or ""
41
29
  super().save(*args, **kwargs)
42
30
 
43
31
  class Meta:
edc_sites/auths.py CHANGED
@@ -1,6 +1,11 @@
1
1
  from edc_auth.site_auths import site_auths
2
2
  from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
3
3
 
4
- site_auths.add_post_update_func(
5
- "edc_sites", remove_default_model_permissions_from_edc_permissions
6
- )
4
+
5
+ def update_site_auths() -> None:
6
+ site_auths.add_post_update_func(
7
+ "edc_sites", remove_default_model_permissions_from_edc_permissions
8
+ )
9
+
10
+
11
+ update_site_auths()
@@ -7,6 +7,7 @@ import django_audit_fields.fields.uuid_auto_field
7
7
  import django_audit_fields.models.audit_model_mixin
8
8
  import django_revision.revision_field
9
9
  from django.db import migrations, models
10
+ import django.utils.timezone
10
11
 
11
12
 
12
13
  class Migration(migrations.Migration):
@@ -33,14 +34,14 @@ class Migration(migrations.Migration):
33
34
  "created",
34
35
  models.DateTimeField(
35
36
  blank=True,
36
- default=django_audit_fields.models.audit_model_mixin.utcnow,
37
+ default=django.utils.timezone.now,
37
38
  ),
38
39
  ),
39
40
  (
40
41
  "modified",
41
42
  models.DateTimeField(
42
43
  blank=True,
43
- default=django_audit_fields.models.audit_model_mixin.utcnow,
44
+ default=django.utils.timezone.now,
44
45
  ),
45
46
  ),
46
47
  (
@@ -0,0 +1,69 @@
1
+ # Generated by Django 5.2.6 on 2025-09-17 16:53
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("edc_sites", "0009_alter_edcpermissions_revision"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="edcpermissions",
15
+ name="device_created",
16
+ field=models.CharField(
17
+ blank=True, default="", max_length=10, verbose_name="Device created"
18
+ ),
19
+ ),
20
+ migrations.AlterField(
21
+ model_name="edcpermissions",
22
+ name="device_modified",
23
+ field=models.CharField(
24
+ blank=True, default="", max_length=10, verbose_name="Device modified"
25
+ ),
26
+ ),
27
+ migrations.AlterField(
28
+ model_name="edcpermissions",
29
+ name="locale_created",
30
+ field=models.CharField(
31
+ blank=True,
32
+ default="",
33
+ help_text="Auto-updated by Modeladmin",
34
+ max_length=10,
35
+ verbose_name="Locale created",
36
+ ),
37
+ ),
38
+ migrations.AlterField(
39
+ model_name="edcpermissions",
40
+ name="locale_modified",
41
+ field=models.CharField(
42
+ blank=True,
43
+ default="",
44
+ help_text="Auto-updated by Modeladmin",
45
+ max_length=10,
46
+ verbose_name="Locale modified",
47
+ ),
48
+ ),
49
+ migrations.AlterField(
50
+ model_name="siteprofile",
51
+ name="country",
52
+ field=models.CharField(default="", max_length=250),
53
+ ),
54
+ migrations.AlterField(
55
+ model_name="siteprofile",
56
+ name="country_code",
57
+ field=models.CharField(default="", max_length=15),
58
+ ),
59
+ migrations.AlterField(
60
+ model_name="siteprofile",
61
+ name="languages",
62
+ field=models.TextField(default=""),
63
+ ),
64
+ migrations.AlterField(
65
+ model_name="siteprofile",
66
+ name="title",
67
+ field=models.CharField(default="", max_length=250),
68
+ ),
69
+ ]
@@ -28,7 +28,7 @@ class SiteModelMixin(models.Model):
28
28
  self.update_site_on_save(*args, **kwargs)
29
29
  super().save(*args, **kwargs)
30
30
 
31
- def update_site_on_save(self, *args, **kwargs) -> None:
31
+ def update_site_on_save(self, *args, **kwargs) -> None: # noqa: ARG002
32
32
  if not self.id:
33
33
  if not self.site_id and not self.site:
34
34
  self.site = self.get_site_on_create()
@@ -59,7 +59,7 @@ class SiteModelMixin(models.Model):
59
59
  "Exception raised when trying manager method `get_current()`. "
60
60
  f"Sites registered with `sites` global are {site_ids}. "
61
61
  f"settings.SITE_ID={settings.SITE_ID}. Got {e}."
62
- )
62
+ ) from e
63
63
  return site_obj
64
64
 
65
65
  def validate_site_against_current(self) -> None:
edc_sites/site.py CHANGED
@@ -160,13 +160,13 @@ class Sites:
160
160
  single_site = dataclasses.replace(single_site, domain=domain)
161
161
 
162
162
  if single_site.site_id in self._registry:
163
- raise AlreadyRegistered(f"Site already registered. Got `{single_site}`.") # noqa: TRY003
163
+ raise AlreadyRegistered(f"Site already registered. Got `{single_site}`.")
164
164
  if single_site.name in [s.name for s in self._registry.values()]:
165
- raise AlreadyRegisteredName( # noqa: TRY003
165
+ raise AlreadyRegisteredName(
166
166
  f"Site with this name is already registered. Got `{single_site}`."
167
167
  )
168
168
  if single_site.domain in [s.domain for s in self._registry.values()]:
169
- raise AlreadyRegisteredDomain( # noqa: TRY003
169
+ raise AlreadyRegisteredDomain(
170
170
  f"Site with this domain is already registered. Got `{single_site}`."
171
171
  )
172
172
  self._registry.update({single_site.site_id: single_site})
@@ -187,7 +187,7 @@ class Sites:
187
187
  msg = "In fact, no sites have been registered!"
188
188
  else:
189
189
  msg = f"Expected one of {[s.site_id for s in self.all(aslist=True)]}."
190
- raise SiteNotRegistered( # noqa: TRY003
190
+ raise SiteNotRegistered(
191
191
  f"Site not registered. {msg} See {self!r}. Got `{site_id}`."
192
192
  )
193
193
  return self._registry.get(site_id)
@@ -196,7 +196,7 @@ class Sites:
196
196
  for single_site in self._registry.values():
197
197
  if getattr(single_site, attrname) == value:
198
198
  return single_site
199
- raise SiteDoesNotExist(f"No site exists with `{attrname}`==`{value}`.") # noqa: TRY003
199
+ raise SiteDoesNotExist(f"No site exists with `{attrname}`==`{value}`.")
200
200
 
201
201
  def all(self, aslist: bool | None = None) -> dict[int, SingleSite] | list[SingleSite]:
202
202
  if aslist:
@@ -10,7 +10,7 @@ from edc_sites.site import sites as site_sites
10
10
  from edc_sites.utils import get_site_model_cls
11
11
 
12
12
 
13
- def sites_check(app_configs, **kwargs): # noqa
13
+ def sites_check(app_configs, **kwargs):
14
14
  errors = []
15
15
  if "migrate" not in sys.argv and "makemigrations" not in sys.argv:
16
16
  try:
@@ -7,30 +7,35 @@ from edc_auth.constants import (
7
7
  from edc_auth.site_auths import site_auths
8
8
  from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
9
9
 
10
- site_auths.add_post_update_func(
11
- "edc_subject_dashboard", remove_default_model_permissions_from_edc_permissions
12
- )
13
-
14
10
  SUBJECT_VIEW = "SUBJECT_VIEW"
15
11
 
16
- site_auths.add_custom_permissions_tuples(
17
- model="edc_subject_dashboard.edcpermissions",
18
- codename_tuples=(
19
- (
20
- "edc_subject_dashboard.view_subject_listboard",
21
- "Can access subject listboard",
12
+
13
+ def update_site_auths() -> None:
14
+ site_auths.add_post_update_func(
15
+ "edc_subject_dashboard", remove_default_model_permissions_from_edc_permissions
16
+ )
17
+
18
+ site_auths.add_custom_permissions_tuples(
19
+ model="edc_subject_dashboard.edcpermissions",
20
+ codename_tuples=(
21
+ (
22
+ "edc_subject_dashboard.view_subject_listboard",
23
+ "Can access subject listboard",
24
+ ),
25
+ ("edc_subject_dashboard.nav_subject_section", "Can access nav_subject_section"),
22
26
  ),
23
- ("edc_subject_dashboard.nav_subject_section", "Can access nav_subject_section"),
24
- ),
25
- )
27
+ )
28
+
29
+ site_auths.add_group(
30
+ "edc_subject_dashboard.view_subject_listboard",
31
+ "edc_subject_dashboard.nav_subject_section",
32
+ name=SUBJECT_VIEW,
33
+ )
34
+
35
+ site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_ROLE)
36
+ site_auths.update_role(SUBJECT_VIEW, name=NURSE_ROLE)
37
+ site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_SUPER_ROLE)
38
+ site_auths.update_role(SUBJECT_VIEW, name=AUDITOR_ROLE)
26
39
 
27
- site_auths.add_group(
28
- "edc_subject_dashboard.view_subject_listboard",
29
- "edc_subject_dashboard.nav_subject_section",
30
- name=SUBJECT_VIEW,
31
- )
32
40
 
33
- site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_ROLE)
34
- site_auths.update_role(SUBJECT_VIEW, name=NURSE_ROLE)
35
- site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_SUPER_ROLE)
36
- site_auths.update_role(SUBJECT_VIEW, name=AUDITOR_ROLE)
41
+ update_site_auths()
@@ -7,6 +7,7 @@ import django_audit_fields.fields.uuid_auto_field
7
7
  import django_audit_fields.models.audit_model_mixin
8
8
  import django_revision.revision_field
9
9
  from django.db import migrations, models
10
+ import django.utils.timezone
10
11
 
11
12
 
12
13
  class Migration(migrations.Migration):
@@ -33,14 +34,14 @@ class Migration(migrations.Migration):
33
34
  "created",
34
35
  models.DateTimeField(
35
36
  blank=True,
36
- default=django_audit_fields.models.audit_model_mixin.utcnow,
37
+ default=django.utils.timezone.now,
37
38
  ),
38
39
  ),
39
40
  (
40
41
  "modified",
41
42
  models.DateTimeField(
42
43
  blank=True,
43
- default=django_audit_fields.models.audit_model_mixin.utcnow,
44
+ default=django.utils.timezone.now,
44
45
  ),
45
46
  ),
46
47
  (
@@ -0,0 +1,49 @@
1
+ # Generated by Django 5.2.6 on 2025-09-17 16:53
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("edc_subject_dashboard", "0004_alter_edcpermissions_revision"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="edcpermissions",
15
+ name="device_created",
16
+ field=models.CharField(
17
+ blank=True, default="", max_length=10, verbose_name="Device created"
18
+ ),
19
+ ),
20
+ migrations.AlterField(
21
+ model_name="edcpermissions",
22
+ name="device_modified",
23
+ field=models.CharField(
24
+ blank=True, default="", max_length=10, verbose_name="Device modified"
25
+ ),
26
+ ),
27
+ migrations.AlterField(
28
+ model_name="edcpermissions",
29
+ name="locale_created",
30
+ field=models.CharField(
31
+ blank=True,
32
+ default="",
33
+ help_text="Auto-updated by Modeladmin",
34
+ max_length=10,
35
+ verbose_name="Locale created",
36
+ ),
37
+ ),
38
+ migrations.AlterField(
39
+ model_name="edcpermissions",
40
+ name="locale_modified",
41
+ field=models.CharField(
42
+ blank=True,
43
+ default="",
44
+ help_text="Auto-updated by Modeladmin",
45
+ max_length=10,
46
+ verbose_name="Locale modified",
47
+ ),
48
+ ),
49
+ ]
@@ -1,6 +1,7 @@
1
1
  from tempfile import mkdtemp
2
2
 
3
3
  from django.contrib import messages
4
+ from django.utils import timezone
4
5
  from reportlab.graphics.barcode import code39
5
6
  from reportlab.lib import colors
6
7
  from reportlab.lib.units import cm, mm
@@ -11,7 +12,6 @@ from edc_identifier.utils import convert_to_human_readable
11
12
  from edc_lab.model_mixins import RequisitionModelMixin
12
13
  from edc_lab.models.manifest.shipper import Shipper
13
14
  from edc_pdf_reports import Report
14
- from edc_utils import get_utcnow
15
15
 
16
16
 
17
17
  class RequisitionReport(Report):
@@ -33,7 +33,7 @@ class RequisitionReport(Report):
33
33
  self.consignee = consignee
34
34
  self.contact_name = f"{self.user.first_name} {self.user.last_name}"
35
35
  self.image_folder = mkdtemp()
36
- self.timestamp = get_utcnow().strftime("%Y%m%d%H%M%S")
36
+ self.timestamp = timezone.now().strftime("%Y%m%d%H%M%S")
37
37
  self.report_filename = f"requisition_{self.timestamp}.pdf"
38
38
 
39
39
  @property
@@ -60,7 +60,9 @@ class RequisitionReport(Report):
60
60
  Paragraph("REFERENCE", self.styles["line_label"]),
61
61
  ],
62
62
  [
63
- Paragraph(get_utcnow().strftime("%Y-%m-%d"), self.styles["line_data_largest"]),
63
+ Paragraph(
64
+ timezone.now().strftime("%Y-%m-%d"), self.styles["line_data_largest"]
65
+ ),
64
66
  Paragraph(
65
67
  convert_to_human_readable(self.timestamp),
66
68
  self.styles["line_data_largest"],
@@ -173,7 +175,7 @@ class RequisitionReport(Report):
173
175
  Paragraph(self.contact_name, self.styles["line_data_large"]),
174
176
  "",
175
177
  Paragraph(
176
- get_utcnow().strftime("%Y-%m-%d %H:%M"),
178
+ timezone.now().strftime("%Y-%m-%d %H:%M"),
177
179
  self.styles["line_data_large"],
178
180
  ),
179
181
  ],
@@ -1,10 +1,12 @@
1
+ import contextlib
2
+
1
3
  from django.core.exceptions import ObjectDoesNotExist
4
+ from django.utils import timezone
2
5
 
3
6
  from edc_appointment.models import Appointment
4
7
  from edc_constants.constants import NO, YES
5
8
  from edc_lab.constants import TUBE
6
9
  from edc_lab.model_mixins import RequisitionModelMixin
7
- from edc_utils import get_utcnow
8
10
 
9
11
 
10
12
  class RequisitionVerifier:
@@ -22,12 +24,12 @@ class RequisitionVerifier:
22
24
  if self.requisition and self.requisition.is_drawn != NO:
23
25
  # verification fields
24
26
  self.requisition.clinic_verified = YES
25
- self.requisition.clinic_verified_datetime = get_utcnow()
27
+ self.requisition.clinic_verified_datetime = timezone.now()
26
28
  # other fields for label printing
27
29
  self.requisition.is_drawn = self.requisition.is_drawn or YES
28
30
  self.requisition.item_count = self.requisition.item_count or 1
29
31
  self.requisition.item_type = self.requisition.item_type or TUBE
30
- self.requisition.drawn_datetime = self.requisition.drawn_datetime or get_utcnow()
32
+ self.requisition.drawn_datetime = self.requisition.drawn_datetime or timezone.now()
31
33
  self.requisition.save()
32
34
  self.verified = self.requisition.clinic_verified
33
35
 
@@ -43,12 +45,10 @@ class RequisitionVerifier:
43
45
  def requisition(self):
44
46
  """Returns a requisition model instance."""
45
47
  if not self._requisition:
46
- try:
48
+ with contextlib.suppress(ObjectDoesNotExist):
47
49
  self._requisition = self.requisition_model_cls.objects.get(
48
50
  requisition_identifier=self.requisition_identifier.strip()
49
51
  )
50
- except ObjectDoesNotExist:
51
- pass
52
52
  return self._requisition
53
53
 
54
54
  @property
@@ -8,6 +8,7 @@ from django import template
8
8
  from django.apps import apps as django_apps
9
9
  from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
10
10
  from django.urls import reverse
11
+ from django.utils import timezone
11
12
 
12
13
  from edc_appointment.constants import (
13
14
  CANCELLED_APPT,
@@ -26,7 +27,6 @@ from edc_appointment.view_utils import AppointmentButton
26
27
  from edc_auth.constants import AUDITOR_ROLE
27
28
  from edc_metadata import KEYED, REQUIRED
28
29
  from edc_metadata.metadata_helper import MetadataHelper
29
- from edc_utils import get_utcnow
30
30
  from edc_view_utils import PrnButton, render_history_and_query_buttons
31
31
  from edc_visit_tracking.view_utils import RelatedVisitButton
32
32
 
@@ -171,7 +171,7 @@ def render_crf_totals(appointment: Appointment = None) -> dict[str, bool | int]:
171
171
  skipped = True
172
172
  elif (
173
173
  appointment.appt_status == NEW_APPT
174
- and appointment.appt_datetime.date() < get_utcnow().date()
174
+ and appointment.appt_datetime.date() < timezone.now().date()
175
175
  ):
176
176
  overdue = True
177
177
  else: