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
@@ -6,7 +6,7 @@ from django.db import migrations, models
6
6
 
7
7
  import edc_model_fields.fields.hostname_modification_field
8
8
  import edc_model_fields.fields.userfield
9
- import edc_utils
9
+ import django.utils.timezone
10
10
 
11
11
 
12
12
  class Migration(migrations.Migration):
@@ -16,7 +16,7 @@ class Migration(migrations.Migration):
16
16
  migrations.AlterField(
17
17
  model_name="crfmetadata",
18
18
  name="created",
19
- field=models.DateTimeField(blank=True, default=edc_utils.date.get_utcnow),
19
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
20
20
  ),
21
21
  migrations.AlterField(
22
22
  model_name="crfmetadata",
@@ -40,7 +40,7 @@ class Migration(migrations.Migration):
40
40
  migrations.AlterField(
41
41
  model_name="crfmetadata",
42
42
  name="modified",
43
- field=models.DateTimeField(blank=True, default=edc_utils.date.get_utcnow),
43
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
44
44
  ),
45
45
  migrations.AlterField(
46
46
  model_name="crfmetadata",
@@ -59,7 +59,7 @@ class Migration(migrations.Migration):
59
59
  migrations.AlterField(
60
60
  model_name="requisitionmetadata",
61
61
  name="created",
62
- field=models.DateTimeField(blank=True, default=edc_utils.date.get_utcnow),
62
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
63
63
  ),
64
64
  migrations.AlterField(
65
65
  model_name="requisitionmetadata",
@@ -83,7 +83,7 @@ class Migration(migrations.Migration):
83
83
  migrations.AlterField(
84
84
  model_name="requisitionmetadata",
85
85
  name="modified",
86
- field=models.DateTimeField(blank=True, default=edc_utils.date.get_utcnow),
86
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
87
87
  ),
88
88
  migrations.AlterField(
89
89
  model_name="requisitionmetadata",
@@ -4,6 +4,7 @@ import django_audit_fields.fields.hostname_modification_field
4
4
  import django_audit_fields.fields.userfield
5
5
  import django_audit_fields.models.audit_model_mixin
6
6
  from django.db import migrations, models
7
+ import django.utils.timezone
7
8
 
8
9
 
9
10
  class Migration(migrations.Migration):
@@ -13,9 +14,7 @@ class Migration(migrations.Migration):
13
14
  migrations.AlterField(
14
15
  model_name="crfmetadata",
15
16
  name="created",
16
- field=models.DateTimeField(
17
- blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
18
- ),
17
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
19
18
  ),
20
19
  migrations.AlterField(
21
20
  model_name="crfmetadata",
@@ -29,9 +28,7 @@ class Migration(migrations.Migration):
29
28
  migrations.AlterField(
30
29
  model_name="crfmetadata",
31
30
  name="modified",
32
- field=models.DateTimeField(
33
- blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
34
- ),
31
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
35
32
  ),
36
33
  migrations.AlterField(
37
34
  model_name="crfmetadata",
@@ -56,9 +53,7 @@ class Migration(migrations.Migration):
56
53
  migrations.AlterField(
57
54
  model_name="requisitionmetadata",
58
55
  name="created",
59
- field=models.DateTimeField(
60
- blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
61
- ),
56
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
62
57
  ),
63
58
  migrations.AlterField(
64
59
  model_name="requisitionmetadata",
@@ -72,9 +67,7 @@ class Migration(migrations.Migration):
72
67
  migrations.AlterField(
73
68
  model_name="requisitionmetadata",
74
69
  name="modified",
75
- field=models.DateTimeField(
76
- blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
77
- ),
70
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
78
71
  ),
79
72
  migrations.AlterField(
80
73
  model_name="requisitionmetadata",
@@ -0,0 +1,120 @@
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_metadata", "0030_alter_crfmetadata_revision_and_more"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="crfmetadata",
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="crfmetadata",
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="crfmetadata",
29
+ name="document_name",
30
+ field=models.CharField(default="", max_length=250, verbose_name="Document"),
31
+ ),
32
+ migrations.AlterField(
33
+ model_name="crfmetadata",
34
+ name="document_user",
35
+ field=models.CharField(default="", max_length=50, verbose_name="User"),
36
+ ),
37
+ migrations.AlterField(
38
+ model_name="crfmetadata",
39
+ name="entry_comment",
40
+ field=models.TextField(blank=True, default="", max_length=250),
41
+ ),
42
+ migrations.AlterField(
43
+ model_name="crfmetadata",
44
+ name="locale_created",
45
+ field=models.CharField(
46
+ blank=True,
47
+ default="",
48
+ help_text="Auto-updated by Modeladmin",
49
+ max_length=10,
50
+ verbose_name="Locale created",
51
+ ),
52
+ ),
53
+ migrations.AlterField(
54
+ model_name="crfmetadata",
55
+ name="locale_modified",
56
+ field=models.CharField(
57
+ blank=True,
58
+ default="",
59
+ help_text="Auto-updated by Modeladmin",
60
+ max_length=10,
61
+ verbose_name="Locale modified",
62
+ ),
63
+ ),
64
+ migrations.AlterField(
65
+ model_name="requisitionmetadata",
66
+ name="device_created",
67
+ field=models.CharField(
68
+ blank=True, default="", max_length=10, verbose_name="Device created"
69
+ ),
70
+ ),
71
+ migrations.AlterField(
72
+ model_name="requisitionmetadata",
73
+ name="device_modified",
74
+ field=models.CharField(
75
+ blank=True, default="", max_length=10, verbose_name="Device modified"
76
+ ),
77
+ ),
78
+ migrations.AlterField(
79
+ model_name="requisitionmetadata",
80
+ name="document_name",
81
+ field=models.CharField(default="", max_length=250, verbose_name="Document"),
82
+ ),
83
+ migrations.AlterField(
84
+ model_name="requisitionmetadata",
85
+ name="document_user",
86
+ field=models.CharField(default="", max_length=50, verbose_name="User"),
87
+ ),
88
+ migrations.AlterField(
89
+ model_name="requisitionmetadata",
90
+ name="entry_comment",
91
+ field=models.TextField(blank=True, default="", max_length=250),
92
+ ),
93
+ migrations.AlterField(
94
+ model_name="requisitionmetadata",
95
+ name="locale_created",
96
+ field=models.CharField(
97
+ blank=True,
98
+ default="",
99
+ help_text="Auto-updated by Modeladmin",
100
+ max_length=10,
101
+ verbose_name="Locale created",
102
+ ),
103
+ ),
104
+ migrations.AlterField(
105
+ model_name="requisitionmetadata",
106
+ name="locale_modified",
107
+ field=models.CharField(
108
+ blank=True,
109
+ default="",
110
+ help_text="Auto-updated by Modeladmin",
111
+ max_length=10,
112
+ verbose_name="Locale modified",
113
+ ),
114
+ ),
115
+ migrations.AlterField(
116
+ model_name="requisitionmetadata",
117
+ name="panel_name",
118
+ field=models.CharField(default="", max_length=50),
119
+ ),
120
+ ]
@@ -50,16 +50,19 @@ class UpdatesCrfMetadataModelMixin(UpdatesMetadataModelMixin):
50
50
  return django_apps.get_model(metadata_model)
51
51
 
52
52
  @property
53
- def metadata_default_entry_status(self: CrfModel) -> str:
53
+ def metadata_default_entry_status(self: CrfModel) -> str | None:
54
54
  """Returns a string that represents the default entry status
55
55
  of the CRF in the visit schedule.
56
56
  """
57
57
  crfs_prn = self.metadata_visit_object.crfs_prn
58
58
  if self.related_visit.visit_code_sequence != 0:
59
- crfs = self.metadata_visit_object.crfs_unscheduled.forms + crfs_prn.forms
59
+ crfs = (*self.metadata_visit_object.crfs_unscheduled.forms, *crfs_prn.forms)
60
60
  else:
61
- crfs = self.metadata_visit_object.crfs.forms + crfs_prn.forms
62
- crf = [c for c in crfs if c.model == self._meta.label_lower][0]
61
+ crfs = (*self.metadata_visit_object.crfs.forms, *crfs_prn.forms)
62
+ try:
63
+ crf = next(c for c in crfs if c.model == self._meta.label_lower)
64
+ except StopIteration:
65
+ return None
63
66
  return REQUIRED if crf.required else NOT_REQUIRED
64
67
 
65
68
  class Meta:
@@ -50,7 +50,7 @@ class UpdatesRequisitionMetadataModelMixin(UpdatesMetadataModelMixin):
50
50
  return options
51
51
 
52
52
  @property
53
- def metadata_default_entry_status(self: RequisitionModel) -> str:
53
+ def metadata_default_entry_status(self: RequisitionModel) -> str | None:
54
54
  """Returns a string that represents the configured
55
55
  entry status of the requisition in the visit schedule.
56
56
  """
@@ -64,7 +64,10 @@ class UpdatesRequisitionMetadataModelMixin(UpdatesMetadataModelMixin):
64
64
  requisitions = (
65
65
  self.metadata_visit_object.requisitions.forms + requisitions_prn.forms
66
66
  )
67
- requisition = [r for r in requisitions if r.panel.name == self.panel.name][0]
67
+ try:
68
+ requisition = next(r for r in requisitions if r.panel.name == self.panel.name)
69
+ except StopIteration:
70
+ return None
68
71
  return REQUIRED if requisition.required else NOT_REQUIRED
69
72
 
70
73
  @property
@@ -29,7 +29,7 @@ class CrfMetadata(CrfMetadataModelMixin, BaseUuidModel):
29
29
  )
30
30
 
31
31
  # noinspection PyTypeHints
32
- natural_key.dependencies = ["sites.Site"] # type: ignore
32
+ natural_key.dependencies = ("sites.Site",)
33
33
 
34
34
  @property
35
35
  def verbose_name(self) -> str:
@@ -42,8 +42,8 @@ class CrfMetadata(CrfMetadataModelMixin, BaseUuidModel):
42
42
  class Meta(CrfMetadataModelMixin.Meta, BaseUuidModel.Meta):
43
43
  verbose_name = "Crf collection status"
44
44
  verbose_name_plural = "Crf collection status"
45
- unique_together = []
46
- constraints = [
45
+ unique_together = ()
46
+ constraints = (
47
47
  UniqueConstraint(
48
48
  fields=[
49
49
  "subject_identifier",
@@ -54,8 +54,8 @@ class CrfMetadata(CrfMetadataModelMixin, BaseUuidModel):
54
54
  "model",
55
55
  ],
56
56
  name="%(app_label)s_%(class)s_subject_iden_visit_uniq",
57
- )
58
- ]
57
+ ),
58
+ )
59
59
  indexes = (
60
60
  *CrfMetadataModelMixin.Meta.indexes,
61
61
  *BaseUuidModel.Meta.indexes,
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  from typing import TYPE_CHECKING, Any
4
5
 
5
6
  from django.apps import apps as django_apps
@@ -38,7 +39,7 @@ class CrfMetadataModelMixin(
38
39
 
39
40
  model = models.CharField(max_length=50)
40
41
 
41
- document_name = models.CharField(verbose_name=_("Document"), max_length=250, null=True)
42
+ document_name = models.CharField(verbose_name=_("Document"), max_length=250, default="")
42
43
 
43
44
  show_order = models.IntegerField() # must always be provided!
44
45
 
@@ -54,7 +55,7 @@ class CrfMetadataModelMixin(
54
55
 
55
56
  fill_datetime = models.DateTimeField(null=True, blank=True)
56
57
 
57
- document_user = models.CharField(verbose_name=_("User"), max_length=50, null=True)
58
+ document_user = models.CharField(verbose_name=_("User"), max_length=50, default="")
58
59
 
59
60
  def natural_key(self):
60
61
  return (
@@ -92,10 +93,8 @@ class CrfMetadataModelMixin(
92
93
  def model_instance(self: Any) -> Any:
93
94
  """Returns the CRF/Requisition model instance or None"""
94
95
  instance = None
95
- try:
96
+ with contextlib.suppress(ObjectDoesNotExist):
96
97
  instance = self.model_cls.objects.get(**self.model_instance_query_opts())
97
- except ObjectDoesNotExist:
98
- pass
99
98
  return instance
100
99
 
101
100
  def refresh_entry_status(self) -> str:
@@ -10,7 +10,7 @@ from .crf_metadata_model_mixin import CrfMetadataModelMixin
10
10
 
11
11
 
12
12
  class RequisitionMetadata(CrfMetadataModelMixin, BaseUuidModel):
13
- panel_name = models.CharField(max_length=50, null=True)
13
+ panel_name = models.CharField(max_length=50, default="")
14
14
 
15
15
  objects = RequisitionMetadataManager()
16
16
 
@@ -26,7 +26,7 @@ class RequisitionMetadata(CrfMetadataModelMixin, BaseUuidModel):
26
26
 
27
27
  @property
28
28
  def verbose_name(self) -> str:
29
- from edc_lab.site_labs import site_labs
29
+ from edc_lab.site_labs import site_labs # noqa: PLC0415
30
30
 
31
31
  return site_labs.panel_names.get(self.panel_name) or self.panel_name
32
32
 
@@ -42,7 +42,7 @@ class RequisitionMetadata(CrfMetadataModelMixin, BaseUuidModel):
42
42
  )
43
43
 
44
44
  # noinspection PyTypeHints
45
- natural_key.dependencies = ["sites.Site"] # type: ignore
45
+ natural_key.dependencies = ("sites.Site",) # type: ignore
46
46
 
47
47
  def model_instance_query_opts(self) -> dict:
48
48
  opts = super().model_instance_query_opts()
@@ -71,7 +71,7 @@ class RequisitionMetadata(CrfMetadataModelMixin, BaseUuidModel):
71
71
  app_label = "edc_metadata"
72
72
  verbose_name = "Requisition collection status"
73
73
  verbose_name_plural = "Requisition collection status"
74
- constraints = [
74
+ constraints = (
75
75
  UniqueConstraint(
76
76
  fields=[
77
77
  "subject_identifier",
@@ -83,8 +83,8 @@ class RequisitionMetadata(CrfMetadataModelMixin, BaseUuidModel):
83
83
  "panel_name",
84
84
  ],
85
85
  name="%(app_label)s_%(class)s_subject_iden_visit_uniq",
86
- )
87
- ]
86
+ ),
87
+ )
88
88
  indexes = (
89
89
  *CrfMetadataModelMixin.Meta.indexes,
90
90
  *BaseUuidModel.Meta.indexes,
@@ -94,14 +94,13 @@ def metadata_reset_on_post_delete(sender, instance, using, **kwargs) -> None:
94
94
  def metadata_update_previous_timepoints_for_singleton_on_post_save(
95
95
  sender, instance, raw, created, using, **kwargs
96
96
  ):
97
- if not raw and not kwargs.get("update_fields"):
98
- if isinstance(instance, (SingletonCrfModelMixin,)):
99
- appointment = (
100
- instance.related_visit.appointment.relative_previous_with_related_visit
101
- )
102
- while appointment:
103
- if appointment.related_visit:
104
- refresh_metadata_for_timepoint(
105
- appointment.related_visit, allow_create=False
106
- )
107
- appointment = appointment.relative_previous_with_related_visit
97
+ if (
98
+ not raw
99
+ and not kwargs.get("update_fields")
100
+ and isinstance(instance, (SingletonCrfModelMixin,))
101
+ ):
102
+ appointment = instance.related_visit.appointment.relative_previous_with_related_visit
103
+ while appointment:
104
+ if appointment.related_visit:
105
+ refresh_metadata_for_timepoint(appointment.related_visit, allow_create=False)
106
+ appointment = appointment.relative_previous_with_related_visit
@@ -3,11 +3,11 @@ from django.contrib.auth.mixins import LoginRequiredMixin
3
3
  from django.contrib.messages import SUCCESS
4
4
  from django.http.response import HttpResponseRedirect
5
5
  from django.urls import reverse
6
+ from django.utils import timezone
6
7
  from django.views import View
7
8
 
8
9
  from edc_appointment.utils import update_appt_status_for_timepoint
9
10
  from edc_dashboard.url_names import url_names
10
- from edc_utils import get_utcnow
11
11
  from edc_utils.round_up import round_half_away_from_zero
12
12
  from edc_visit_tracking.utils import get_related_visit_model_cls
13
13
 
@@ -34,7 +34,7 @@ class RefreshMetadataActionsView(LoginRequiredMixin, View):
34
34
  return related_visit
35
35
 
36
36
  def get(self, request, *args, **kwargs):
37
- dte1 = get_utcnow()
37
+ dte1 = timezone.now()
38
38
  related_visit = self.refresh_metadata_for_timepoint(**kwargs)
39
39
  url_name = url_names.get("subject_dashboard_url")
40
40
  args = (
@@ -47,7 +47,7 @@ class RefreshMetadataActionsView(LoginRequiredMixin, View):
47
47
  SUCCESS,
48
48
  f"The data collection schedule for {related_visit.visit_code}."
49
49
  f"{related_visit.visit_code_sequence} has been refreshed "
50
- f"({round_half_away_from_zero((get_utcnow()-dte1).microseconds/1000000, 2)} "
50
+ f"({round_half_away_from_zero((timezone.now() - dte1).microseconds / 1000000, 2)} "
51
51
  "seconds)",
52
52
  )
53
53
  return HttpResponseRedirect(url)
@@ -2,7 +2,7 @@ from django.db import models
2
2
 
3
3
 
4
4
  class AddressMixin(models.Model):
5
- contact_name = models.CharField(max_length=50, null=True, blank=True)
5
+ contact_name = models.CharField(max_length=50, default="", blank=True)
6
6
 
7
7
  address = models.CharField(verbose_name="Address", max_length=50)
8
8
 
@@ -11,18 +11,18 @@ class AddressMixin(models.Model):
11
11
  city = models.CharField(max_length=50)
12
12
 
13
13
  state = models.CharField(
14
- verbose_name="State or Province", null=True, blank=True, max_length=50
14
+ verbose_name="State or Province", default="", blank=True, max_length=50
15
15
  )
16
16
 
17
17
  country = models.CharField(max_length=50)
18
18
 
19
- telephone = models.CharField(null=True, blank=True, max_length=50)
19
+ telephone = models.CharField(default="", blank=True, max_length=50)
20
20
 
21
- mobile = models.CharField(null=True, blank=True, max_length=50)
21
+ mobile = models.CharField(default="", blank=True, max_length=50)
22
22
 
23
- fax = models.CharField(null=True, blank=True, max_length=50)
23
+ fax = models.CharField(default="", blank=True, max_length=50)
24
24
 
25
- email = models.EmailField(null=True, blank=True, max_length=50)
25
+ email = models.EmailField(default="", blank=True, max_length=50)
26
26
 
27
27
  class Meta:
28
28
  abstract = True
@@ -12,6 +12,7 @@ class InitialsField(CharField):
12
12
  kwargs.setdefault("editable", True)
13
13
  kwargs.setdefault("verbose_name", _("Initials"))
14
14
  kwargs.setdefault("max_length", 3)
15
+ kwargs.setdefault("default", "")
15
16
  kwargs.setdefault("help_text", _("Type 2-3 letters, all in uppercase and no spaces"))
16
17
  CharField.__init__(self, *args, **kwargs)
17
18
 
@@ -31,4 +32,4 @@ class InitialsField(CharField):
31
32
  },
32
33
  }
33
34
  defaults.update(kwargs)
34
- return super(InitialsField, self).formfield(**defaults)
35
+ return super().formfield(**defaults)
@@ -16,10 +16,7 @@ class SerializableModel(models.Model):
16
16
  return (self.history_id,)
17
17
 
18
18
  def related_visit_model_attr(self):
19
- try:
20
- return self.history_object.related_visit_model_attr()
21
- except AttributeError:
22
- raise
19
+ return self.history_object.related_visit_model_attr()
23
20
 
24
21
  @property
25
22
  def related_visit(self):
@@ -61,10 +58,6 @@ class HistoricalRecords(SimpleHistoricalRecords):
61
58
  super().__init__(**kwargs)
62
59
 
63
60
  def contribute_to_class(self, cls, name):
64
- try:
65
- cls.related_visit_model_attr
66
- except AttributeError:
67
- pass
68
- else:
61
+ if getattr(cls, "related_visit_model_attr", None):
69
62
  self.model_cls = SerializableCrfModel
70
63
  return super().contribute_to_class(cls, name)
@@ -5,7 +5,7 @@ from django.urls import reverse
5
5
  from django.urls.exceptions import NoReverseMatch
6
6
 
7
7
 
8
- class UrlModelMixinNoReverseMatch(Exception):
8
+ class UrlModelMixinNoReverseMatch(Exception): # noqa: N818
9
9
  pass
10
10
 
11
11
 
@@ -23,7 +23,7 @@ class UrlModelMixin(models.Model):
23
23
  f"Tried {self.admin_url_name}. Got {e}. "
24
24
  "Check urls and confirm app is in INSTALLED_APPS. If running tests, "
25
25
  "you may need to set `use_test_urls=True`."
26
- )
26
+ ) from e
27
27
  return absolute_url
28
28
 
29
29
  def get_changelist_url(self, search_term=None) -> str:
@@ -1,37 +1,36 @@
1
1
  from datetime import date, datetime, timedelta
2
2
 
3
3
  from django.core.exceptions import ValidationError
4
-
5
- from edc_utils import get_utcnow
4
+ from django.utils import timezone
6
5
 
7
6
 
8
7
  def datetime_not_future(utc_datetime: datetime) -> None:
9
8
  time_error = timedelta(minutes=10)
10
- if utc_datetime > get_utcnow() + time_error:
9
+ if utc_datetime > timezone.now() + time_error:
11
10
  raise ValidationError("Cannot be a future date/time")
12
11
 
13
12
 
14
13
  def date_not_future(value: date) -> None:
15
- if value > get_utcnow().date():
14
+ if value > timezone.now().date():
16
15
  raise ValidationError("Cannot be a future date")
17
16
 
18
17
 
19
18
  def date_is_past(value: date) -> None:
20
- if value > get_utcnow().date():
19
+ if value > timezone.now().date():
21
20
  raise ValidationError("Expected a past date")
22
21
 
23
22
 
24
23
  def date_is_not_now(value: date) -> None:
25
- if value == get_utcnow().date():
24
+ if value == timezone.now().date():
26
25
  raise ValidationError("Cannot be today")
27
26
 
28
27
 
29
28
  def datetime_is_future(utc_datetime: datetime) -> None:
30
29
  time_error = timedelta(minutes=10)
31
- if utc_datetime < get_utcnow() + time_error:
30
+ if utc_datetime < timezone.now() + time_error:
32
31
  raise ValidationError("Expected a future date/time")
33
32
 
34
33
 
35
34
  def date_is_future(value: date) -> None:
36
- if value < get_utcnow().date():
35
+ if value < timezone.now().date():
37
36
  raise ValidationError("Expected a future date")
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from dateutil.relativedelta import MO, relativedelta
4
4
  from django.contrib.admin import SimpleListFilter
5
5
  from django.db.models import QuerySet
6
+ from django.utils import timezone
6
7
  from django.utils.translation import gettext as _
7
8
 
8
9
  from edc_constants.constants import (
@@ -16,7 +17,6 @@ from edc_constants.constants import (
16
17
  TODAY,
17
18
  TOMORROW,
18
19
  )
19
- from edc_utils import get_utcnow
20
20
 
21
21
 
22
22
  class FutureDateListFilter(SimpleListFilter):
@@ -42,9 +42,9 @@ class FutureDateListFilter(SimpleListFilter):
42
42
  return {}
43
43
 
44
44
  def queryset(self, request, queryset) -> QuerySet | None:
45
- morning = get_utcnow().replace(second=0, hour=0, minute=0)
45
+ morning = timezone.now().replace(second=0, hour=0, minute=0)
46
46
  monday = morning + relativedelta(weekday=MO(-1))
47
- night = get_utcnow().replace(second=59, hour=23, minute=59)
47
+ night = timezone.now().replace(second=59, hour=23, minute=59)
48
48
  qs = None
49
49
  if self.value() == NEXT_WEEK:
50
50
  qs = queryset.filter(
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from dateutil.relativedelta import MO, relativedelta
4
4
  from django.contrib.admin import SimpleListFilter
5
5
  from django.db.models import QuerySet
6
+ from django.utils import timezone
6
7
  from django.utils.translation import gettext as _
7
8
 
8
9
  from edc_constants.constants import (
@@ -17,7 +18,6 @@ from edc_constants.constants import (
17
18
  TODAY,
18
19
  YESTERDAY,
19
20
  )
20
- from edc_utils import get_utcnow
21
21
 
22
22
 
23
23
  class PastDateListFilter(SimpleListFilter):
@@ -45,9 +45,9 @@ class PastDateListFilter(SimpleListFilter):
45
45
  return {}
46
46
 
47
47
  def queryset(self, request, queryset) -> QuerySet | None:
48
- morning = get_utcnow().replace(second=0, hour=0, minute=0)
48
+ morning = timezone.now().replace(second=0, hour=0, minute=0)
49
49
  monday = morning + relativedelta(weekday=MO(-1))
50
- night = get_utcnow().replace(second=59, hour=23, minute=59)
50
+ night = timezone.now().replace(second=59, hour=23, minute=59)
51
51
  qs = None
52
52
  if self.value() == THIS_WEEK:
53
53
  qs = queryset.filter(
@@ -21,16 +21,15 @@ class ModelAdminProtectPiiMixin:
21
21
  the changelist, add the method name to `extra_pii_attrs`.
22
22
  """
23
23
 
24
- extra_pii_attrs: list[str] | None = []
24
+ extra_pii_attrs: tuple[str] | None = ()
25
25
 
26
26
  def get_extra_pii_attrs(self) -> list[str | tuple[str, str]]:
27
27
  return self.extra_pii_attrs or []
28
28
 
29
- def get_encrypted_fields(self) -> list[str]:
29
+ def get_encrypted_fields(self) -> tuple[str, ...]:
30
30
  encrypted_fields = [f.name for f in get_encrypted_fields(self.model)]
31
- encrypted_fields.extend(self.get_extra_pii_attrs())
32
- encrypted_fields = list(set(encrypted_fields))
33
- return encrypted_fields
31
+ encrypted_fields.extend(*self.get_extra_pii_attrs())
32
+ return tuple(set(encrypted_fields))
34
33
 
35
34
  def get_list_display(self, request) -> tuple[str]:
36
35
  list_display = super().get_list_display(request)
@@ -12,7 +12,7 @@ class OtherCharField(CharField):
12
12
  def __init__(self, metadata=None, *args, **kwargs):
13
13
  self.metadata = metadata
14
14
  kwargs.update(blank=True)
15
- kwargs.update(null=True)
15
+ kwargs.update(default="")
16
16
  kwargs.setdefault("max_length", self.DEFAULT_MAX_LENGTH)
17
17
  kwargs.setdefault("verbose_name", _("If other, please specify ..."))
18
18
  self.max_length = kwargs["max_length"]
@@ -25,7 +25,7 @@ class OtherCharField(CharField):
25
25
  def deconstruct(self):
26
26
  name, path, args, kwargs = super().deconstruct()
27
27
  kwargs.update(blank=True)
28
- kwargs.update(null=True)
28
+ kwargs.update(default="")
29
29
  kwargs.update(max_length=self.max_length)
30
30
  kwargs.update(verbose_name=self.verbose_name)
31
31
  if self.metadata is not None: