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
edc_appointment/utils.py CHANGED
@@ -80,7 +80,7 @@ class AppointmentDateWindowPeriodGapError(Exception):
80
80
  pass
81
81
 
82
82
 
83
- class AppointmentAlreadyStarted(Exception):
83
+ class AppointmentAlreadyStarted(Exception): # noqa: N818
84
84
  pass
85
85
 
86
86
 
@@ -157,10 +157,9 @@ def raise_on_appt_may_not_be_missed(
157
157
 
158
158
 
159
159
  def get_appointment_form_meta_options() -> dict:
160
- options = getattr(
160
+ return getattr(
161
161
  settings, "EDC_APPOINTMENT_FORM_META_OPTIONS", dict(labels={}, help_texts={})
162
162
  )
163
- return options
164
163
 
165
164
 
166
165
  def get_appt_reason_choices() -> tuple[str, ...]:
@@ -516,11 +515,11 @@ def raise_on_appt_datetime_not_in_window(
516
515
  except ScheduledVisitWindowError as e:
517
516
  msg = str(e)
518
517
  msg.replace("Invalid datetime", "Invalid appointment datetime (S)")
519
- raise AppointmentWindowError(msg)
518
+ raise AppointmentWindowError(msg) from e
520
519
  except UnScheduledVisitWindowError as e:
521
520
  msg = str(e)
522
521
  msg.replace("Invalid datetime", "Invalid appointment datetime (U)")
523
- raise AppointmentWindowError(msg)
522
+ raise AppointmentWindowError(msg) from e
524
523
 
525
524
 
526
525
  def get_window_gap_days(appointment) -> int:
@@ -623,7 +622,7 @@ def get_appointment_by_datetime(
623
622
  raise_on_appt_datetime_not_in_window(
624
623
  appointment, appt_datetime=suggested_appt_datetime
625
624
  )
626
- except AppointmentWindowError:
625
+ except AppointmentWindowError as e:
627
626
  in_gap = appt_datetime_in_gap(appointment, suggested_appt_datetime)
628
627
  in_next_window_adjusted = appt_datetime_in_next_window_adjusted_for_gap(
629
628
  appointment, suggested_appt_datetime
@@ -635,7 +634,7 @@ def get_appointment_by_datetime(
635
634
  raise AppointmentDateWindowPeriodGapError(
636
635
  f"Date falls in a `window period gap` between {appointment.visit_code} "
637
636
  f"and {appointment.next.visit_code}. Got {dt}."
638
- )
637
+ ) from e
639
638
  if (
640
639
  in_gap
641
640
  and in_next_window_adjusted
@@ -674,7 +673,7 @@ def reset_appointment(appointment: Appointment, **kwargs):
674
673
  appt_status=appointment._meta.get_field("appt_status").default,
675
674
  appt_timing=appointment._meta.get_field("appt_timing").default,
676
675
  appt_type=None,
677
- appt_type_other=None,
676
+ appt_type_other="",
678
677
  appt_datetime=appointment.timepoint_datetime,
679
678
  comment="",
680
679
  )
@@ -705,7 +704,7 @@ def skip_appointment(appointment: Appointment, comment: str | None = None):
705
704
  appt_status=SKIPPED_APPT,
706
705
  appt_timing=NOT_APPLICABLE,
707
706
  appt_type=NOT_APPLICABLE,
708
- comment=comment,
707
+ comment=comment or "",
709
708
  )
710
709
 
711
710
 
@@ -722,11 +721,6 @@ def get_unscheduled_appointment_url(appointment: Appointment = None) -> str:
722
721
  visit_code_sequence=appointment.visit_code_sequence + 1,
723
722
  timepoint=appointment.timepoint,
724
723
  )
725
- # if appointment := (
726
- # appointment.__class__.objects.filter(visit_code_sequence__gt=0, **kwargs)
727
- # .order_by("visit_code_sequence")
728
- # .last()
729
- # ):
730
724
  kwargs.update(visit_code_sequence=str(appointment.visit_code_sequence + 1))
731
725
  kwargs.update(redirect_url=dashboard_url)
732
726
  return reverse(unscheduled_appointment_url_name, kwargs=kwargs)
@@ -741,19 +735,18 @@ def update_appt_status_for_timepoint(related_visit: RelatedVisitModel) -> None:
741
735
  ):
742
736
  related_visit.appointment.appt_status = INCOMPLETE_APPT
743
737
  related_visit.appointment.save_base(update_fields=["appt_status"])
744
- elif related_visit.appointment.appt_status == INCOMPLETE_APPT:
745
- if (
746
- not related_visit.metadata[CRF].filter(entry_status=REQUIRED).exists()
747
- and not related_visit.metadata[REQUISITION].filter(entry_status=REQUIRED).exists()
748
- ):
749
- related_visit.appointment.appt_status = COMPLETE_APPT
750
- related_visit.appointment.save_base(update_fields=["appt_status"])
738
+ elif related_visit.appointment.appt_status == INCOMPLETE_APPT and (
739
+ not related_visit.metadata[CRF].filter(entry_status=REQUIRED).exists()
740
+ and not related_visit.metadata[REQUISITION].filter(entry_status=REQUIRED).exists()
741
+ ):
742
+ related_visit.appointment.appt_status = COMPLETE_APPT
743
+ related_visit.appointment.save_base(update_fields=["appt_status"])
751
744
 
752
745
 
753
746
  def refresh_appointments(
754
- subject_identifier: str = None,
755
- visit_schedule_name: str = None,
756
- schedule_name: str = None,
747
+ subject_identifier: str | None = None,
748
+ visit_schedule_name: str | None = None,
749
+ schedule_name: str | None = None,
757
750
  request: WSGIRequest | None = None,
758
751
  warn_only: bool | None = None,
759
752
  ) -> tuple[str, str]:
@@ -839,6 +832,8 @@ def validate_date_is_on_clinic_day(
839
832
  and calendar.weekday(appt_date.year, appt_date.month, appt_date.day)
840
833
  not in clinic_days
841
834
  ):
835
+
836
+ days_str = [day_abbr[d] for d in clinic_days]
842
837
  days_str = []
843
838
  for d in clinic_days:
844
839
  days_str.append(day_abbr[d])
@@ -5,9 +5,9 @@ from typing import TYPE_CHECKING
5
5
  from uuid import UUID
6
6
 
7
7
  from django.apps import apps as django_apps
8
+ from django.utils import timezone
8
9
  from django.utils.translation import gettext as _
9
10
 
10
- from edc_utils import get_utcnow
11
11
  from edc_view_utils.dashboard_model_button import DashboardModelButton
12
12
  from edc_view_utils.model_button import ADD
13
13
 
@@ -51,7 +51,7 @@ class AppointmentButton(DashboardModelButton):
51
51
  color = super().color
52
52
  if (
53
53
  self.model_obj
54
- and self.model_obj.appt_datetime <= get_utcnow()
54
+ and self.model_obj.appt_datetime <= timezone.now()
55
55
  and not self.model_obj.related_visit
56
56
  ):
57
57
  color = self.colors[ADD]
@@ -15,7 +15,7 @@ from .user_profile_admin import UserProfileInline
15
15
  admin.site.unregister(User)
16
16
 
17
17
 
18
- def send_new_credentials_to_user_action(modeladmin, request, queryset): # noqa
18
+ def send_new_credentials_to_user_action(modeladmin, request, queryset):
19
19
  if request.user.has_perm("auth.change_user"):
20
20
  for obj in queryset:
21
21
  send_new_credentials_to_user(user=obj)
@@ -1,21 +1,3 @@
1
- from ..constants import (
2
- ACCOUNT_MANAGER,
3
- ACCOUNT_MANAGER_ROLE,
4
- ADMINISTRATION,
5
- AUDITOR,
6
- AUDITOR_ROLE,
7
- CELERY_MANAGER,
8
- CLINIC,
9
- CLINIC_SUPER,
10
- CLINICIAN_ROLE,
11
- CLINICIAN_SUPER_ROLE,
12
- CUSTOM_ROLE,
13
- EVERYONE,
14
- NURSE_ROLE,
15
- PII,
16
- PII_VIEW,
17
- STAFF_ROLE,
18
- )
19
1
  from .codenames import (
20
2
  account_manager,
21
3
  administration,
@@ -25,6 +7,6 @@ from .codenames import (
25
7
  clinic_super,
26
8
  everyone,
27
9
  )
28
- from .default_groups import default_groups
10
+ from .default_groups import get_default_groups
29
11
  from .default_pii_models import default_pii_models
30
- from .default_roles import default_roles
12
+ from .default_roles import get_default_roles
@@ -11,14 +11,16 @@ from ..constants import (
11
11
  )
12
12
  from .codenames import account_manager, administration, celery_manager, everyone
13
13
 
14
- default_groups = {
15
- ACCOUNT_MANAGER: account_manager,
16
- ADMINISTRATION: administration,
17
- AUDITOR: [],
18
- CELERY_MANAGER: celery_manager,
19
- CLINIC: [],
20
- CLINIC_SUPER: [],
21
- EVERYONE: everyone,
22
- PII: [],
23
- PII_VIEW: [],
24
- }
14
+
15
+ def get_default_groups():
16
+ return {
17
+ ACCOUNT_MANAGER: account_manager,
18
+ ADMINISTRATION: administration,
19
+ AUDITOR: [],
20
+ CELERY_MANAGER: celery_manager,
21
+ CLINIC: [],
22
+ CLINIC_SUPER: [],
23
+ EVERYONE: everyone,
24
+ PII: [],
25
+ PII_VIEW: [],
26
+ }
@@ -17,27 +17,29 @@ from ..constants import (
17
17
  STATISTICIAN,
18
18
  )
19
19
 
20
- # Format {ROLE_NAME: [GROUP_NAME, GROUP_NAME, ...]
21
- default_roles: dict[str, list[str]] = {
22
- ACCOUNT_MANAGER_ROLE: [ACCOUNT_MANAGER],
23
- AUDITOR_ROLE: [
24
- AUDITOR,
25
- PII_VIEW,
26
- ],
27
- CLINICIAN_ROLE: [
28
- CLINIC,
29
- PII,
30
- ],
31
- CLINICIAN_SUPER_ROLE: [
32
- CLINIC_SUPER,
33
- CLINIC,
34
- PII,
35
- ],
36
- CUSTOM_ROLE: [],
37
- NURSE_ROLE: [
38
- CLINIC,
39
- PII,
40
- ],
41
- STAFF_ROLE: [ADMINISTRATION, EVERYONE],
42
- STATISTICIAN: [AUDITOR],
43
- }
20
+
21
+ def get_default_roles() -> dict[str, list[str]]:
22
+ """Format {ROLE_NAME: [GROUP_NAME, GROUP_NAME, ...]"""
23
+ return {
24
+ ACCOUNT_MANAGER_ROLE: [ACCOUNT_MANAGER],
25
+ AUDITOR_ROLE: [
26
+ AUDITOR,
27
+ PII_VIEW,
28
+ ],
29
+ CLINICIAN_ROLE: [
30
+ CLINIC,
31
+ PII,
32
+ ],
33
+ CLINICIAN_SUPER_ROLE: [
34
+ CLINIC_SUPER,
35
+ CLINIC,
36
+ PII,
37
+ ],
38
+ CUSTOM_ROLE: [],
39
+ NURSE_ROLE: [
40
+ CLINIC,
41
+ PII,
42
+ ],
43
+ STAFF_ROLE: [ADMINISTRATION, EVERYONE],
44
+ STATISTICIAN: [AUDITOR],
45
+ }
@@ -9,6 +9,7 @@ from django.contrib.auth import get_user_model
9
9
  from django.core.exceptions import ObjectDoesNotExist
10
10
  from django.core.management.color import color_style
11
11
 
12
+ from ..constants import POST_UPDATE_FUNCS_KEY, PRE_UPDATE_FUNCS_KEY
12
13
  from ..site_auths import site_auths
13
14
  from .group_updater import GroupUpdater
14
15
  from .role_updater import RoleUpdater
@@ -17,6 +18,14 @@ style = color_style()
17
18
 
18
19
 
19
20
  class AuthUpdater:
21
+ """A class to update auth.Group, edc_auth.Role, auth.Permissions
22
+ models using the site_auth registry.
23
+
24
+ Called once on application startup. For example::
25
+
26
+ AuthUpdater(verbose=False, warn_only=True)
27
+ """
28
+
20
29
  group_updater_cls = GroupUpdater
21
30
  role_updater_cls = RoleUpdater
22
31
 
@@ -38,8 +47,10 @@ class AuthUpdater:
38
47
  )
39
48
  groups = groups or site_auths.groups
40
49
  pii_models = pii_models or site_auths.pii_models
41
- post_update_funcs = post_update_funcs or site_auths.post_update_funcs
42
- pre_update_funcs = pre_update_funcs or site_auths.pre_update_funcs
50
+ post_update_funcs = post_update_funcs or [f for f in site_auths.post_update_funcs]
51
+ site_auths.registry[POST_UPDATE_FUNCS_KEY] = []
52
+ pre_update_funcs = pre_update_funcs or [f for f in site_auths.pre_update_funcs]
53
+ site_auths.registry[PRE_UPDATE_FUNCS_KEY] = []
43
54
  roles = roles or site_auths.roles
44
55
  self.apps = apps
45
56
  if not self.edc_auth_skip_auth_updater:
@@ -11,7 +11,7 @@ from django.core.exceptions import (
11
11
  )
12
12
  from django.core.management.color import color_style
13
13
 
14
- from ..auth_objects import PII, PII_VIEW
14
+ from ..constants import PII, PII_VIEW
15
15
  from ..utils import make_view_only_group_permissions
16
16
 
17
17
  style = color_style()
@@ -27,7 +27,7 @@ class PermissionsCreatorError(ValidationError):
27
27
  pass
28
28
 
29
29
 
30
- class CodenameDoesNotExist(Exception):
30
+ class CodenameDoesNotExist(Exception): # noqa: N818
31
31
  pass
32
32
 
33
33
 
@@ -87,7 +87,7 @@ class GroupUpdater:
87
87
  group = self.group_model_cls.objects.get(name=group_name)
88
88
  except ObjectDoesNotExist as e:
89
89
  if not create_group:
90
- raise ObjectDoesNotExist(f"{e} Got {group_name}")
90
+ raise ObjectDoesNotExist(f"{e} Got {group_name}") from e
91
91
  group = self.group_model_cls.objects.create(name=group_name)
92
92
  else:
93
93
  group.permissions.clear()
@@ -122,7 +122,7 @@ class GroupUpdater:
122
122
  try:
123
123
  app_label, codename = self.get_from_dotted_codename(dotted_codename)
124
124
  except PermissionsCodenameError as e:
125
- warn(str(e))
125
+ warn(str(e), stacklevel=2)
126
126
  else:
127
127
  # if you add extra codenames you must also add custom
128
128
  # codename tuples to the Permissions model before you
@@ -138,7 +138,7 @@ class GroupUpdater:
138
138
  except ObjectDoesNotExist as e:
139
139
  errmsg = f"{e} Got codename={codename},app_label={app_label}"
140
140
  if not self.warn_only:
141
- raise CodenameDoesNotExist(errmsg)
141
+ raise CodenameDoesNotExist(errmsg) from e
142
142
  warn(style.ERROR(errmsg))
143
143
  except MultipleObjectsReturned as e:
144
144
  if not allow_multiple_objects:
@@ -166,15 +166,17 @@ class GroupUpdater:
166
166
  try:
167
167
  app_label, _codename = codename.split(".")
168
168
  except ValueError as e:
169
- raise PermissionsCodenameError(f"Invalid dotted codename. {e} Got {codename}.")
169
+ raise PermissionsCodenameError(
170
+ f"Invalid dotted codename. {e} Got {codename}."
171
+ ) from e
170
172
  else:
171
173
  try:
172
174
  self.apps.get_app_config(app_label)
173
- except LookupError:
175
+ except LookupError as e:
174
176
  raise PermissionsCodenameError(
175
177
  "Invalid app_label in codename. Expected format "
176
178
  f"'<app_label>.<some_codename>'. Got {codename}."
177
- )
179
+ ) from e
178
180
  prefix = _codename.split("_")[0]
179
181
  if prefix not in self.codename_prefixes:
180
182
  raise PermissionsCodenameError(
@@ -244,7 +246,7 @@ class GroupUpdater:
244
246
  except ObjectDoesNotExist as e:
245
247
  raise CodenameDoesNotExist(
246
248
  f"Unable to verify codename. {e} Got '{app_label}.{codename}'"
247
- )
249
+ ) from e
248
250
  except MultipleObjectsReturned as e:
249
251
  self.delete_and_raise_on_duplicate_codenames(codename, app_label, exception=e)
250
252
  return permission
@@ -267,7 +269,7 @@ class GroupUpdater:
267
269
  try:
268
270
  value, name = codename_tpl
269
271
  except ValueError as e:
270
- raise ValueError(f"{e} Got {codename_tpl}")
272
+ raise ValueError(f"{e} Got {codename_tpl}") from e
271
273
  _app_label, codename = value.split(".")
272
274
  if app_label and _app_label != app_label:
273
275
  raise PermissionsCreatorError(
@@ -56,11 +56,11 @@ class RoleUpdater:
56
56
  for group_name in groups:
57
57
  try:
58
58
  group = self.group_model_cls.objects.get(name=group_name)
59
- except ObjectDoesNotExist:
59
+ except ObjectDoesNotExist as e:
60
60
  raise RoleUpdaterError(
61
61
  "Invalid group specified for role. "
62
62
  f"`{group_name}` is not a group. See role `{role}`."
63
- )
63
+ ) from e
64
64
  # group = self.group_model_cls.objects.create(name=group_name)
65
65
  role.groups.add(group)
66
66
  index += 1
edc_auth/constants.py CHANGED
@@ -19,3 +19,13 @@ CUSTOM_ROLE = "custom"
19
19
  NURSE_ROLE = "research_nurse"
20
20
  STAFF_ROLE = "staff"
21
21
  STATISTICIAN = "statistician"
22
+
23
+ # keys for the site_auths registry
24
+ UPDATE_ROLES_KEY = "update_roles"
25
+ UPDATE_GROUPS_KEY = "update_groups"
26
+ PRE_UPDATE_FUNCS_KEY = "pre_update_funcs"
27
+ POST_UPDATE_FUNCS_KEY = "post_update_funcs"
28
+ GROUPS_KEY = "groups"
29
+ ROLES_KEY = "roles"
30
+ PII_MODELS_KEY = "pii_models"
31
+ CUSTOM_PERMISSIONS_TUPLES_KEY = "custom_permissions_tuples"
edc_auth/import_users.py CHANGED
@@ -183,9 +183,9 @@ class UserImporter:
183
183
  self.update_user_sites(site_names or [])
184
184
  self.update_user_roles(role_names or [STAFF_ROLE])
185
185
  self.user.save()
186
- self.user.userprofile.job_title = self.job_title
187
- self.user.userprofile.mobile = self.mobile
188
- self.user.userprofile.alternate_email = self.alternate_email
186
+ self.user.userprofile.job_title = self.job_title or ""
187
+ self.user.userprofile.mobile = self.mobile or ""
188
+ self.user.userprofile.alternate_email = self.alternate_email or ""
189
189
  self.user.userprofile.save()
190
190
 
191
191
  self.site_names = (
@@ -10,10 +10,10 @@ import django_audit_fields.models.audit_model_mixin
10
10
  import django_revision.revision_field
11
11
  from django.conf import settings
12
12
  from django.db import migrations, models
13
+ import django.utils.timezone
13
14
 
14
15
 
15
16
  class Migration(migrations.Migration):
16
-
17
17
  replaces = [
18
18
  ("edc_auth", "0001_initial"),
19
19
  ("edc_auth", "0002_auto_20180803_0515"),
@@ -78,14 +78,14 @@ class Migration(migrations.Migration):
78
78
  "created",
79
79
  models.DateTimeField(
80
80
  blank=True,
81
- default=django_audit_fields.models.audit_model_mixin.utcnow,
81
+ default=django.utils.timezone.now,
82
82
  ),
83
83
  ),
84
84
  (
85
85
  "modified",
86
86
  models.DateTimeField(
87
87
  blank=True,
88
- default=django_audit_fields.models.audit_model_mixin.utcnow,
88
+ default=django.utils.timezone.now,
89
89
  ),
90
90
  ),
91
91
  (
@@ -402,14 +402,14 @@ class Migration(migrations.Migration):
402
402
  "created",
403
403
  models.DateTimeField(
404
404
  blank=True,
405
- default=django_audit_fields.models.audit_model_mixin.utcnow,
405
+ default=django.utils.timezone.now,
406
406
  ),
407
407
  ),
408
408
  (
409
409
  "modified",
410
410
  models.DateTimeField(
411
411
  blank=True,
412
- default=django_audit_fields.models.audit_model_mixin.utcnow,
412
+ default=django.utils.timezone.now,
413
413
  ),
414
414
  ),
415
415
  (
@@ -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):
@@ -31,14 +32,14 @@ class Migration(migrations.Migration):
31
32
  "created",
32
33
  models.DateTimeField(
33
34
  blank=True,
34
- default=django_audit_fields.models.audit_model_mixin.utcnow,
35
+ default=django.utils.timezone.now,
35
36
  ),
36
37
  ),
37
38
  (
38
39
  "modified",
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
  (
@@ -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):
@@ -34,14 +35,14 @@ class Migration(migrations.Migration):
34
35
  "created",
35
36
  models.DateTimeField(
36
37
  blank=True,
37
- default=django_audit_fields.models.audit_model_mixin.utcnow,
38
+ default=django.utils.timezone.now,
38
39
  ),
39
40
  ),
40
41
  (
41
42
  "modified",
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
  (
@@ -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,85 @@
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_auth", "0034_alter_edcpermissions_revision_alter_role_revision"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="edcpermissions",
15
+ name="device_created",
16
+ field=models.CharField(
17
+ blank=True, default="", max_length=10, verbose_name="Device created"
18
+ ),
19
+ ),
20
+ migrations.AlterField(
21
+ model_name="edcpermissions",
22
+ name="device_modified",
23
+ field=models.CharField(
24
+ blank=True, default="", max_length=10, verbose_name="Device modified"
25
+ ),
26
+ ),
27
+ migrations.AlterField(
28
+ model_name="edcpermissions",
29
+ name="locale_created",
30
+ field=models.CharField(
31
+ blank=True,
32
+ default="",
33
+ help_text="Auto-updated by Modeladmin",
34
+ max_length=10,
35
+ verbose_name="Locale created",
36
+ ),
37
+ ),
38
+ migrations.AlterField(
39
+ model_name="edcpermissions",
40
+ name="locale_modified",
41
+ field=models.CharField(
42
+ blank=True,
43
+ default="",
44
+ help_text="Auto-updated by Modeladmin",
45
+ max_length=10,
46
+ verbose_name="Locale modified",
47
+ ),
48
+ ),
49
+ migrations.AlterField(
50
+ model_name="role",
51
+ name="device_created",
52
+ field=models.CharField(
53
+ blank=True, default="", max_length=10, verbose_name="Device created"
54
+ ),
55
+ ),
56
+ migrations.AlterField(
57
+ model_name="role",
58
+ name="device_modified",
59
+ field=models.CharField(
60
+ blank=True, default="", max_length=10, verbose_name="Device modified"
61
+ ),
62
+ ),
63
+ migrations.AlterField(
64
+ model_name="role",
65
+ name="locale_created",
66
+ field=models.CharField(
67
+ blank=True,
68
+ default="",
69
+ help_text="Auto-updated by Modeladmin",
70
+ max_length=10,
71
+ verbose_name="Locale created",
72
+ ),
73
+ ),
74
+ migrations.AlterField(
75
+ model_name="role",
76
+ name="locale_modified",
77
+ field=models.CharField(
78
+ blank=True,
79
+ default="",
80
+ help_text="Auto-updated by Modeladmin",
81
+ max_length=10,
82
+ verbose_name="Locale modified",
83
+ ),
84
+ ),
85
+ ]