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
@@ -21,11 +21,11 @@ class Note(NonUniqueSubjectIdentifierFieldMixin, NoteModelMixin):
21
21
  class Meta(BaseUuidModel.Meta):
22
22
  verbose_name = "Note"
23
23
  verbose_name_plural = "Notes"
24
- constraints = [
24
+ constraints = (
25
25
  UniqueConstraint(
26
26
  fields=["report_model", "subject_identifier"],
27
27
  name="%(app_label)s_%(class)s_report_model_subj_uniq",
28
- )
29
- ]
28
+ ),
29
+ )
30
30
  indexes = BaseUuidModel.Meta.indexes
31
- default_permissions = default_permissions + ("viewallsites",)
31
+ default_permissions = *default_permissions, "viewallsites"
@@ -1,7 +1,6 @@
1
1
  from django.contrib.sites.models import Site
2
2
  from django.db import models
3
-
4
- from edc_utils import get_utcnow
3
+ from django.utils import timezone
5
4
 
6
5
  from ..model_mixins import qa_reports_permissions
7
6
 
@@ -10,7 +9,7 @@ class QaReportLog(models.Model):
10
9
  username = models.CharField(max_length=100)
11
10
  site = models.ForeignKey(Site, on_delete=models.CASCADE)
12
11
  report_model = models.CharField(max_length=100)
13
- accessed = models.DateTimeField(default=get_utcnow)
12
+ accessed = models.DateTimeField(default=timezone.now)
14
13
 
15
14
  class Meta:
16
15
  verbose_name = "QA Report Log"
@@ -1,3 +1,5 @@
1
+ import contextlib
2
+
1
3
  from django.conf import settings
2
4
  from django.contrib import admin
3
5
  from django.contrib.admin.sites import AlreadyRegistered
@@ -65,7 +67,8 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
65
67
 
66
68
  def get_readonly_fields(self, request, obj=None):
67
69
  readonly_fields = super().get_readonly_fields(request, obj=obj)
68
- readonly_fields += (
70
+ readonly_fields = (
71
+ *readonly_fields,
69
72
  "subject_identifier",
70
73
  "sid",
71
74
  "site_name",
@@ -75,7 +78,8 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
75
78
  "allocated_datetime",
76
79
  "allocated_site",
77
80
  "randomizer_name",
78
- ) + audit_fields
81
+ *audit_fields,
82
+ )
79
83
  return tuple(set(readonly_fields))
80
84
 
81
85
  def get_queryset(self, request):
@@ -90,7 +94,7 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
90
94
  return qs
91
95
 
92
96
  def get_list_display(self, request) -> tuple[str, ...]:
93
- list_display = [
97
+ fields = (
94
98
  "sid",
95
99
  "assignment",
96
100
  "site_name",
@@ -98,22 +102,24 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
98
102
  "allocated_datetime",
99
103
  "allocated_site",
100
104
  "randomizer_name",
101
- ]
105
+ )
106
+ if flds := site_randomizers.get_by_model(
107
+ self.model._meta.label_lower
108
+ ).get_extra_list_display():
109
+ fields = list(fields)
110
+ for pos, fname in flds:
111
+ fields.insert(pos, fname)
112
+ fields = tuple(fields)
102
113
  if user_is_blinded(request.user.username) or (
103
114
  not user_is_blinded(request.user.username)
104
115
  and RANDO_UNBLINDED not in [g.name for g in request.user.groups.all()]
105
116
  ):
106
- list_display.remove("assignment")
107
- if flds := site_randomizers.get_by_model(
108
- self.model._meta.label_lower
109
- ).get_extra_list_display():
110
- for pos, fldname in flds:
111
- list_display.insert(pos, fldname)
112
- return tuple(list_display)
117
+ fields = tuple([fname for fname in fields if fname != "assignment"])
118
+ return fields
113
119
 
114
120
  @staticmethod
115
121
  def get_fieldnames(request) -> tuple[str, ...]:
116
- fields = [
122
+ fields = (
117
123
  "subject_identifier",
118
124
  "sid",
119
125
  "assignment",
@@ -122,33 +128,35 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
122
128
  "allocated_datetime",
123
129
  "allocated_site",
124
130
  "randomizer_name",
125
- ]
131
+ )
126
132
  if user_is_blinded(request.user.username) or (
127
133
  not user_is_blinded(request.user.username)
128
134
  and RANDO_UNBLINDED not in [g.name for g in request.user.groups.all()]
129
135
  ):
130
- fields.remove("assignment")
131
- return tuple(fields)
136
+ fields = tuple([fname for fname in fields if fname != "assignment"])
137
+ return fields
132
138
 
133
139
  def get_list_filter(self, request) -> tuple[str, ...]:
134
- list_filter = [
140
+ fields = (
135
141
  "assignment",
136
142
  "allocated_datetime",
137
143
  "allocated_site",
138
144
  "site_name",
139
145
  "randomizer_name",
140
- ]
146
+ )
141
147
  if flds := site_randomizers.get_by_model(
142
148
  self.model._meta.label_lower
143
149
  ).get_extra_list_filter():
144
- for pos, fldname in flds:
145
- list_filter.insert(pos, fldname)
150
+ fields = list(fields)
151
+ for pos, fname in flds:
152
+ fields.insert(pos, fname)
153
+ fields = tuple(fields)
146
154
  if user_is_blinded(request.user.username) or (
147
155
  not user_is_blinded(request.user.username)
148
156
  and RANDO_UNBLINDED not in [g.name for g in request.user.groups.all()]
149
157
  ):
150
- list_filter.remove("assignment")
151
- return tuple(list_filter)
158
+ fields = tuple([fname for fname in fields if fname != "assignment"])
159
+ return fields
152
160
 
153
161
 
154
162
  def register_admin():
@@ -156,10 +164,8 @@ def register_admin():
156
164
  for randomizer_cls in site_randomizers._registry.values():
157
165
  model = randomizer_cls.model_cls()
158
166
  admin_cls = type(f"{model.__name__}ModelAdmin", (RandomizationListModelAdmin,), {})
159
- try:
167
+ with contextlib.suppress(AlreadyRegistered):
160
168
  edc_randomization_admin.register(model, admin_cls)
161
- except AlreadyRegistered:
162
- pass
163
169
 
164
170
 
165
171
  register_admin()
@@ -9,11 +9,16 @@ from .auth_objects import (
9
9
  update_rando_group_permissions,
10
10
  )
11
11
 
12
- site_auths.add_post_update_func(
13
- "edc_randomization", remove_default_model_permissions_from_edc_permissions
14
- )
15
12
 
16
- site_auths.add_group(get_rando_permissions_codenames, name=RANDO_BLINDED, view_only=True)
17
- site_auths.add_group(get_rando_permissions_codenames, name=RANDO_UNBLINDED, view_only=True)
18
- site_auths.add_post_update_func("edc_randomization", update_rando_group_permissions)
19
- site_auths.add_post_update_func("edc_randomization", make_randomizationlist_view_only)
13
+ def update_site_auths() -> None:
14
+ site_auths.add_post_update_func(
15
+ "edc_randomization", remove_default_model_permissions_from_edc_permissions
16
+ )
17
+
18
+ site_auths.add_group(get_rando_permissions_codenames, name=RANDO_BLINDED, view_only=True)
19
+ site_auths.add_group(get_rando_permissions_codenames, name=RANDO_UNBLINDED, view_only=True)
20
+ site_auths.add_post_update_func("edc_randomization", update_rando_group_permissions)
21
+ site_auths.add_post_update_func("edc_randomization", make_randomizationlist_view_only)
22
+
23
+
24
+ update_site_auths()
@@ -8,7 +8,7 @@ class RegisterRandomizerError(Exception):
8
8
  pass
9
9
 
10
10
 
11
- def register(site=None, **kwargs) -> Any: # noqa
11
+ def register(site=None, **kwargs) -> Any:
12
12
  """Registers a randomizer class."""
13
13
  site = site or site_randomizers
14
14
 
@@ -12,6 +12,7 @@ import django_revision.revision_field
12
12
  from django.db import migrations, models
13
13
 
14
14
  import edc_sites.models
15
+ import django.utils.timezone
15
16
 
16
17
 
17
18
  class Migration(migrations.Migration):
@@ -38,14 +39,14 @@ class Migration(migrations.Migration):
38
39
  "created",
39
40
  models.DateTimeField(
40
41
  blank=True,
41
- default=django_audit_fields.models.audit_model_mixin.utcnow,
42
+ default=django.utils.timezone.now,
42
43
  ),
43
44
  ),
44
45
  (
45
46
  "modified",
46
47
  models.DateTimeField(
47
48
  blank=True,
48
- default=django_audit_fields.models.audit_model_mixin.utcnow,
49
+ default=django.utils.timezone.now,
49
50
  ),
50
51
  ),
51
52
  (
@@ -13,6 +13,7 @@ import django_revision.revision_field
13
13
  import simple_history.models
14
14
  from django.conf import settings
15
15
  from django.db import migrations, models
16
+ import django.utils.timezone
16
17
 
17
18
 
18
19
  class Migration(migrations.Migration):
@@ -41,14 +42,14 @@ class Migration(migrations.Migration):
41
42
  "created",
42
43
  models.DateTimeField(
43
44
  blank=True,
44
- default=django_audit_fields.models.audit_model_mixin.utcnow,
45
+ default=django.utils.timezone.now,
45
46
  ),
46
47
  ),
47
48
  (
48
49
  "modified",
49
50
  models.DateTimeField(
50
51
  blank=True,
51
- default=django_audit_fields.models.audit_model_mixin.utcnow,
52
+ default=django.utils.timezone.now,
52
53
  ),
53
54
  ),
54
55
  (
@@ -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,141 @@
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_randomization", "0013_alter_edcpermissions_revision_and_more"),
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="historicalrandomizationlist",
51
+ name="allocated_user",
52
+ field=models.CharField(default="", max_length=50),
53
+ ),
54
+ migrations.AlterField(
55
+ model_name="historicalrandomizationlist",
56
+ name="device_created",
57
+ field=models.CharField(
58
+ blank=True, default="", max_length=10, verbose_name="Device created"
59
+ ),
60
+ ),
61
+ migrations.AlterField(
62
+ model_name="historicalrandomizationlist",
63
+ name="device_modified",
64
+ field=models.CharField(
65
+ blank=True, default="", max_length=10, verbose_name="Device modified"
66
+ ),
67
+ ),
68
+ migrations.AlterField(
69
+ model_name="historicalrandomizationlist",
70
+ name="locale_created",
71
+ field=models.CharField(
72
+ blank=True,
73
+ default="",
74
+ help_text="Auto-updated by Modeladmin",
75
+ max_length=10,
76
+ verbose_name="Locale created",
77
+ ),
78
+ ),
79
+ migrations.AlterField(
80
+ model_name="historicalrandomizationlist",
81
+ name="locale_modified",
82
+ field=models.CharField(
83
+ blank=True,
84
+ default="",
85
+ help_text="Auto-updated by Modeladmin",
86
+ max_length=10,
87
+ verbose_name="Locale modified",
88
+ ),
89
+ ),
90
+ migrations.AlterField(
91
+ model_name="historicalrandomizationlist",
92
+ name="verified_user",
93
+ field=models.CharField(default="", max_length=50),
94
+ ),
95
+ migrations.AlterField(
96
+ model_name="randomizationlist",
97
+ name="allocated_user",
98
+ field=models.CharField(default="", max_length=50),
99
+ ),
100
+ migrations.AlterField(
101
+ model_name="randomizationlist",
102
+ name="device_created",
103
+ field=models.CharField(
104
+ blank=True, default="", max_length=10, verbose_name="Device created"
105
+ ),
106
+ ),
107
+ migrations.AlterField(
108
+ model_name="randomizationlist",
109
+ name="device_modified",
110
+ field=models.CharField(
111
+ blank=True, default="", max_length=10, verbose_name="Device modified"
112
+ ),
113
+ ),
114
+ migrations.AlterField(
115
+ model_name="randomizationlist",
116
+ name="locale_created",
117
+ field=models.CharField(
118
+ blank=True,
119
+ default="",
120
+ help_text="Auto-updated by Modeladmin",
121
+ max_length=10,
122
+ verbose_name="Locale created",
123
+ ),
124
+ ),
125
+ migrations.AlterField(
126
+ model_name="randomizationlist",
127
+ name="locale_modified",
128
+ field=models.CharField(
129
+ blank=True,
130
+ default="",
131
+ help_text="Auto-updated by Modeladmin",
132
+ max_length=10,
133
+ verbose_name="Locale modified",
134
+ ),
135
+ ),
136
+ migrations.AlterField(
137
+ model_name="randomizationlist",
138
+ name="verified_user",
139
+ field=models.CharField(default="", max_length=50),
140
+ ),
141
+ ]
@@ -34,7 +34,7 @@ class RandomizationListModelMixin(models.Model):
34
34
 
35
35
  randomizer_name = models.CharField(max_length=50, default="default")
36
36
 
37
- subject_identifier = models.CharField(
37
+ subject_identifier = models.CharField( # noqa: DJ001
38
38
  verbose_name="Subject Identifier", max_length=50, null=True, unique=True
39
39
  )
40
40
 
@@ -48,7 +48,7 @@ class RandomizationListModelMixin(models.Model):
48
48
 
49
49
  allocated_datetime = models.DateTimeField(null=True)
50
50
 
51
- allocated_user = models.CharField(max_length=50, null=True)
51
+ allocated_user = models.CharField(max_length=50, default="")
52
52
 
53
53
  allocated_site = models.ForeignKey(
54
54
  Site, null=True, on_delete=models.PROTECT, related_name="+"
@@ -58,7 +58,7 @@ class RandomizationListModelMixin(models.Model):
58
58
 
59
59
  verified_datetime = models.DateTimeField(null=True)
60
60
 
61
- verified_user = models.CharField(max_length=50, null=True)
61
+ verified_user = models.CharField(max_length=50, default="")
62
62
 
63
63
  objects = RandomizationListManager()
64
64
 
@@ -74,14 +74,14 @@ class RandomizationListModelMixin(models.Model):
74
74
  try:
75
75
  self.assignment_description
76
76
  except RandomizationError as e:
77
- raise RandomizationListModelError(e)
77
+ raise RandomizationListModelError(e) from e
78
78
  try:
79
79
  Site.objects.get(name=self.site_name)
80
- except ObjectDoesNotExist:
80
+ except ObjectDoesNotExist as e:
81
81
  site_names = [obj.name for obj in Site.objects.all()]
82
82
  raise RandomizationListModelError(
83
83
  f"Invalid site name. Got {self.site_name}. Expected one of {site_names}."
84
- )
84
+ ) from e
85
85
  super().save(*args, **kwargs)
86
86
 
87
87
  @property
@@ -109,10 +109,10 @@ class RandomizationListModelMixin(models.Model):
109
109
 
110
110
  class Meta:
111
111
  abstract = True
112
- constraints = [
112
+ constraints = (
113
113
  UniqueConstraint(
114
114
  fields=["site_name", "sid"],
115
115
  name="%(app_label)s_%(class)s_site_sid_uniq",
116
- )
117
- ]
116
+ ),
117
+ )
118
118
  permissions = (("display_assignment", "Can display assignment"),)
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  import warnings
4
5
  from datetime import datetime
5
6
  from pathlib import Path
@@ -8,6 +9,7 @@ from typing import TYPE_CHECKING, Any
8
9
  from django.apps import apps as django_apps
9
10
  from django.conf import settings
10
11
  from django.core.exceptions import ObjectDoesNotExist, ValidationError
12
+ from django.db.models import Q
11
13
 
12
14
  from edc_registration.utils import get_registered_subject_model_cls
13
15
 
@@ -25,15 +27,15 @@ if TYPE_CHECKING:
25
27
  from edc_registration.models import RegisteredSubject
26
28
 
27
29
 
28
- class InvalidAssignmentDescriptionMap(Exception):
30
+ class InvalidAssignmentDescriptionMap(Exception): # noqa: N818
29
31
  pass
30
32
 
31
33
 
32
- class RandomizationListFileNotFound(Exception):
34
+ class RandomizationListFileNotFound(Exception): # noqa: N818
33
35
  pass
34
36
 
35
37
 
36
- class RandomizationListNotLoaded(Exception):
38
+ class RandomizationListNotLoaded(Exception): # noqa: N818
37
39
  pass
38
40
 
39
41
 
@@ -41,7 +43,7 @@ class RandomizationError(Exception):
41
43
  pass
42
44
 
43
45
 
44
- class AlreadyRandomized(ValidationError):
46
+ class AlreadyRandomized(ValidationError): # noqa: N818
45
47
  pass
46
48
 
47
49
 
@@ -113,10 +115,10 @@ class Randomizer:
113
115
  subject_identifier: str | None = None,
114
116
  identifier_attr: str | None = None,
115
117
  identifier_object_name: str | None = None,
116
- report_datetime: datetime = None,
117
- site: Any = None,
118
- user: str = None,
119
- **kwargs,
118
+ report_datetime: datetime | None = None,
119
+ site: Any | None = None,
120
+ user: str | None = None,
121
+ **kwargs, # noqa: ARG002
120
122
  ):
121
123
  self._model_obj = None
122
124
  self._registration_obj = None
@@ -208,8 +210,10 @@ class Randomizer:
208
210
  @property
209
211
  def sid(self):
210
212
  """Returns the SID."""
211
- if self.model_obj.sid is None:
212
- raise RandomizationError(f"SID cannot be None. See {self.model_obj}.")
213
+ if not self.model_obj.sid:
214
+ raise RandomizationError(
215
+ f"SID cannot be None. See {self.model_obj}. Got {self.model_obj.sid}"
216
+ )
213
217
  return self.model_obj.sid
214
218
 
215
219
  @property
@@ -233,7 +237,7 @@ class Randomizer:
233
237
  if not self._model_obj:
234
238
  try:
235
239
  obj = self.model_cls().objects.get(**self.identifier_opts)
236
- except ObjectDoesNotExist:
240
+ except ObjectDoesNotExist as e:
237
241
  opts = dict(site_name=self.site.name, **self.extra_model_obj_options)
238
242
  self._model_obj = (
239
243
  self.model_cls()
@@ -245,7 +249,7 @@ class Randomizer:
245
249
  fld_str = ", ".join([f"{k}=`{v}`" for k, v in opts.items()])
246
250
  raise AllocationError(
247
251
  f"Randomization failed. No additional SIDs available for {fld_str}."
248
- )
252
+ ) from e
249
253
  else:
250
254
  raise AlreadyRandomized(
251
255
  f"{self.identifier_object_name.title()} already randomized. "
@@ -282,7 +286,7 @@ class Randomizer:
282
286
  Called by `registration_obj`.
283
287
  """
284
288
  return self.get_registration_model_cls().objects.get(
285
- sid__isnull=True, **self.identifier_opts
289
+ (Q(sid__isnull=True) | Q(sid="")), **self.identifier_opts
286
290
  )
287
291
 
288
292
  @property
@@ -298,14 +302,14 @@ class Randomizer:
298
302
  if not self._registration_obj:
299
303
  try:
300
304
  self._registration_obj = self.get_unallocated_registration_obj()
301
- except ObjectDoesNotExist:
305
+ except ObjectDoesNotExist as e:
302
306
  try:
303
307
  obj = self.get_registration_model_cls().objects.get(**self.identifier_opts)
304
- except ObjectDoesNotExist:
308
+ except ObjectDoesNotExist as e:
305
309
  raise RandomizationError(
306
310
  f"{self.identifier_object_name.title()} does not exist. "
307
311
  f"Got {getattr(self, self.identifier_attr)}"
308
- )
312
+ ) from e
309
313
  else:
310
314
  raise AlreadyRandomized(
311
315
  f"{self.identifier_object_name.title()} already randomized. "
@@ -313,7 +317,7 @@ class Randomizer:
313
317
  f"Got {getattr(obj, self.identifier_attr)} "
314
318
  f"SID={obj.sid}",
315
319
  code=self.get_registration_model_cls()._meta.label_lower,
316
- )
320
+ ) from e
317
321
  return self._registration_obj
318
322
 
319
323
  @property
@@ -347,7 +351,7 @@ class Randomizer:
347
351
  "Randomization list file not found. "
348
352
  f"Got `{cls.get_randomizationlist_path()}`. See Randomizer {cls.name}."
349
353
  )
350
- try:
354
+ with contextlib.suppress(RandomizationListAlreadyImported):
351
355
  result = cls.importer_cls(
352
356
  assignment_map=cls.assignment_map,
353
357
  randomizationlist_path=cls.get_randomizationlist_path(),
@@ -356,8 +360,6 @@ class Randomizer:
356
360
  extra_csv_fieldnames=cls.extra_csv_fieldnames,
357
361
  **kwargs,
358
362
  ).import_list(**kwargs)
359
- except RandomizationListAlreadyImported:
360
- pass
361
363
  return result
362
364
 
363
365
  @classmethod