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
@@ -11,7 +11,7 @@ from edc_export.choices import EXPORT_FORMATS
11
11
  from edc_export.constants import CSV
12
12
  from edc_notification.model_mixins import NotificationUserProfileModelMixin
13
13
 
14
- from ..auth_objects import CUSTOM_ROLE, STAFF_ROLE
14
+ from ..constants import CUSTOM_ROLE, STAFF_ROLE
15
15
  from .role import Role
16
16
 
17
17
 
@@ -32,21 +32,21 @@ class UserProfile(NotificationUserProfileModelMixin, models.Model):
32
32
 
33
33
  sites = models.ManyToManyField(Site, blank=True)
34
34
 
35
- job_title = models.CharField(max_length=100, null=True, blank=True)
35
+ job_title = models.CharField(max_length=100, default="", blank=True)
36
36
 
37
- alternate_email = models.EmailField(_("Alternate email address"), blank=True, null=True)
37
+ alternate_email = models.EmailField(_("Alternate email address"), blank=True, default="")
38
38
 
39
39
  mobile = models.CharField(
40
40
  max_length=25,
41
41
  validators=[RegexValidator(regex=r"^\+\d+")],
42
- null=True,
42
+ default="",
43
43
  blank=True,
44
44
  help_text="e.g. +1234567890",
45
45
  )
46
46
 
47
47
  clinic_label_printer = models.CharField(
48
48
  max_length=100,
49
- null=True,
49
+ default="",
50
50
  blank=True,
51
51
  help_text=format_html(
52
52
  'Change in <a href="{href}">{label}</a>',
@@ -57,7 +57,7 @@ class UserProfile(NotificationUserProfileModelMixin, models.Model):
57
57
 
58
58
  lab_label_printer = models.CharField(
59
59
  max_length=100,
60
- null=True,
60
+ default="",
61
61
  blank=True,
62
62
  help_text=format_html(
63
63
  'Change in <a href="{href}">{label}</a>',
@@ -68,7 +68,7 @@ class UserProfile(NotificationUserProfileModelMixin, models.Model):
68
68
 
69
69
  print_server = models.CharField(
70
70
  max_length=100,
71
- null=True,
71
+ default="",
72
72
  blank=True,
73
73
  help_text=format_html(
74
74
  'Change in <a href="{href}">{label}</a>',
@@ -82,7 +82,6 @@ class UserProfile(NotificationUserProfileModelMixin, models.Model):
82
82
  max_length=25,
83
83
  choices=EXPORT_FORMATS,
84
84
  default=CSV,
85
- null=True,
86
85
  blank=True,
87
86
  help_text="Note: requires export permissions",
88
87
  )
@@ -101,9 +100,13 @@ class UserProfile(NotificationUserProfileModelMixin, models.Model):
101
100
  group_names = [group.name for group in self.user.groups.all()]
102
101
  add_group_names = []
103
102
  for role in self.roles.all():
104
- for group in role.groups.all():
105
- if group.name not in group_names:
106
- add_group_names.append(group.name)
103
+ add_group_names.extend(
104
+ [
105
+ group.name
106
+ for group in role.groups.all()
107
+ if group.name not in group_names
108
+ ]
109
+ )
107
110
  add_group_names = list(set(add_group_names))
108
111
  for name in add_group_names:
109
112
  self.user.groups.add(Group.objects.get(name=name))
@@ -6,7 +6,7 @@ from django.core.management.color import color_style
6
6
  style = color_style()
7
7
 
8
8
 
9
- def post_migrate_user_groups_and_roles(sender=None, **kwargs): # noqa
9
+ def post_migrate_user_groups_and_roles(sender=None, **kwargs):
10
10
  """Update Groups, Role model with EDC defaults."""
11
11
 
12
12
  from .auth_updater import AuthUpdater
edc_auth/site_auths.py CHANGED
@@ -9,30 +9,40 @@ from django.apps import apps as django_apps
9
9
  from django.conf import settings
10
10
  from django.utils.module_loading import import_module, module_has_submodule
11
11
 
12
- from .auth_objects import default_groups, default_pii_models, default_roles
13
-
14
-
15
- class AlreadyRegistered(Exception):
12
+ from .auth_objects import default_pii_models, get_default_groups, get_default_roles
13
+ from .constants import (
14
+ CUSTOM_PERMISSIONS_TUPLES_KEY,
15
+ GROUPS_KEY,
16
+ PII_MODELS_KEY,
17
+ POST_UPDATE_FUNCS_KEY,
18
+ PRE_UPDATE_FUNCS_KEY,
19
+ ROLES_KEY,
20
+ UPDATE_GROUPS_KEY,
21
+ UPDATE_ROLES_KEY,
22
+ )
23
+
24
+
25
+ class AlreadyRegistered(Exception): # noqa: N818
16
26
  pass
17
27
 
18
28
 
19
- class InvalidGroup(Exception):
29
+ class InvalidGroup(Exception): # noqa: N818
20
30
  pass
21
31
 
22
32
 
23
- class InvalidRole(Exception):
33
+ class InvalidRole(Exception): # noqa: N818
24
34
  pass
25
35
 
26
36
 
27
- class RoleAlreadyExists(Exception):
37
+ class RoleAlreadyExists(Exception): # noqa: N818
28
38
  pass
29
39
 
30
40
 
31
- class GroupAlreadyExists(Exception):
41
+ class GroupAlreadyExists(Exception): # noqa: N818
32
42
  pass
33
43
 
34
44
 
35
- class PiiModelAlreadyExists(Exception):
45
+ class PiiModelAlreadyExists(Exception): # noqa: N818
36
46
  pass
37
47
 
38
48
 
@@ -96,39 +106,39 @@ class SiteAuths:
96
106
 
97
107
  def initialize(self):
98
108
  self.registry = {
99
- "groups": default_groups,
100
- "roles": default_roles,
101
- "update_groups": {},
102
- "update_roles": {},
103
- "custom_permissions_tuples": {},
104
- "pre_update_funcs": [],
105
- "post_update_funcs": [],
106
- "pii_models": default_pii_models,
109
+ GROUPS_KEY: get_default_groups(),
110
+ ROLES_KEY: get_default_roles(),
111
+ UPDATE_GROUPS_KEY: {},
112
+ UPDATE_ROLES_KEY: {},
113
+ CUSTOM_PERMISSIONS_TUPLES_KEY: {},
114
+ PRE_UPDATE_FUNCS_KEY: [],
115
+ POST_UPDATE_FUNCS_KEY: [],
116
+ PII_MODELS_KEY: default_pii_models,
107
117
  }
108
118
 
109
119
  def clear(self):
110
120
  self.registry = {
111
- "groups": {},
112
- "roles": {},
113
- "update_groups": {},
114
- "update_roles": {},
115
- "custom_permissions_tuples": {},
116
- "pre_update_funcs": [],
117
- "post_update_funcs": [],
118
- "pii_models": [],
121
+ GROUPS_KEY: {},
122
+ ROLES_KEY: {},
123
+ UPDATE_GROUPS_KEY: {},
124
+ UPDATE_ROLES_KEY: {},
125
+ CUSTOM_PERMISSIONS_TUPLES_KEY: {},
126
+ PRE_UPDATE_FUNCS_KEY: [],
127
+ POST_UPDATE_FUNCS_KEY: [],
128
+ PII_MODELS_KEY: [],
119
129
  }
120
130
 
121
131
  def clear_values(self):
122
132
  registry = deepcopy(self.registry)
123
133
  self.registry = {
124
- "groups": {k: [] for k in registry.get("groups")},
125
- "roles": {k: [] for k in self.registry.get("roles")},
126
- "update_groups": {},
127
- "update_roles": {},
128
- "custom_permissions_tuples": {},
129
- "pre_update_funcs": [],
130
- "post_update_funcs": [],
131
- "pii_models": [],
134
+ GROUPS_KEY: {k: [] for k in registry.get(GROUPS_KEY)},
135
+ ROLES_KEY: {k: [] for k in self.registry.get(ROLES_KEY)},
136
+ UPDATE_GROUPS_KEY: {},
137
+ UPDATE_ROLES_KEY: {},
138
+ CUSTOM_PERMISSIONS_TUPLES_KEY: {},
139
+ PRE_UPDATE_FUNCS_KEY: [],
140
+ POST_UPDATE_FUNCS_KEY: [],
141
+ PII_MODELS_KEY: [],
132
142
  }
133
143
 
134
144
  @property
@@ -136,15 +146,15 @@ class SiteAuths:
136
146
  return getattr(settings, "EDC_AUTH_SKIP_SITE_AUTHS", False)
137
147
 
138
148
  def add_pre_update_func(self, func):
139
- self.registry["pre_update_funcs"].append(func)
149
+ self.registry[PRE_UPDATE_FUNCS_KEY].append(func)
140
150
 
141
151
  def add_post_update_func(self, app_label: str, func: Callable):
142
- self.registry["post_update_funcs"].append((app_label, func))
152
+ self.registry[POST_UPDATE_FUNCS_KEY].append((app_label, func))
143
153
 
144
154
  def add_pii_model(self, model_name):
145
- if model_name in self.registry["pii_models"]:
155
+ if model_name in self.registry[PII_MODELS_KEY]:
146
156
  raise PiiModelAlreadyExists(f"PII model already exists. Got {model_name}")
147
- self.registry["pii_models"].append(model_name)
157
+ self.registry[PII_MODELS_KEY].append(model_name)
148
158
 
149
159
  def add_groups(self, data: dict):
150
160
  for name, codenames in data.items():
@@ -162,7 +172,7 @@ class SiteAuths:
162
172
  convert_to_export=None,
163
173
  no_delete=None,
164
174
  ):
165
- if name in self.registry["groups"]:
175
+ if name in self.registry[GROUPS_KEY]:
166
176
  raise GroupAlreadyExists(f"Group name already exists. Got {name}.")
167
177
  if no_delete:
168
178
  codenames_or_func = self.remove_delete_codenames(codenames_or_func)
@@ -170,18 +180,18 @@ class SiteAuths:
170
180
  codenames_or_func = self.get_view_only_codenames(codenames_or_func)
171
181
  if convert_to_export:
172
182
  codenames_or_func = self.convert_to_export_codenames(codenames_or_func)
173
- self.registry["groups"].update({name: codenames_or_func})
183
+ self.registry[GROUPS_KEY].update({name: codenames_or_func})
174
184
 
175
185
  def add_role(self, *group_names, name=None):
176
- if name in self.registry["roles"]:
186
+ if name in self.registry[ROLES_KEY]:
177
187
  raise RoleAlreadyExists(f"Role name already exists. Got {name}.")
178
188
  group_names = list(set(group_names))
179
- self.registry["roles"].update({name: group_names})
189
+ self.registry[ROLES_KEY].update({name: group_names})
180
190
 
181
191
  def update_group(
182
192
  self, *codenames_or_func, name=None, key=None, view_only=None, no_delete=None
183
193
  ) -> None:
184
- key = key or "update_groups"
194
+ key = key or UPDATE_GROUPS_KEY
185
195
  if no_delete:
186
196
  codenames_or_func = self.remove_delete_codenames(codenames_or_func)
187
197
  if view_only:
@@ -191,30 +201,30 @@ class SiteAuths:
191
201
  try:
192
202
  existing_codenames = list(set(existing_codenames))
193
203
  except TypeError as e:
194
- raise TypeError(f"{e}. Got {name}")
204
+ raise TypeError(f"{e}. Got {name}") from e
195
205
  existing_codenames.extend(codenames_or_func)
196
206
  existing_codenames = list(set(existing_codenames))
197
207
  self.registry[key].update({name: existing_codenames})
198
208
 
199
209
  def update_role(self, *group_names, name=None, key=None) -> None:
200
- key = key or "update_roles"
210
+ key = key or UPDATE_ROLES_KEY
201
211
  group_names = list(set(group_names))
202
- existing_group_names = deepcopy(self.registry[key].get(name)) or []
203
- existing_group_names = list(set(existing_group_names))
212
+ existing_group_names = [
213
+ name for name in self.registry[key].get(name) or [] if name not in group_names
214
+ ]
204
215
  existing_group_names.extend(group_names)
205
- existing_group_names = list(set(existing_group_names))
206
216
  self.registry[key].update({name: existing_group_names})
207
217
 
208
218
  def add_custom_permissions_tuples(
209
219
  self, model: str, codename_tuples: tuple[tuple[str, str], ...]
210
220
  ):
211
221
  try:
212
- self.registry["custom_permissions_tuples"][model]
222
+ self.registry[CUSTOM_PERMISSIONS_TUPLES_KEY][model]
213
223
  except KeyError:
214
- self.registry["custom_permissions_tuples"].update({model: []})
224
+ self.registry[CUSTOM_PERMISSIONS_TUPLES_KEY].update({model: []})
215
225
  for codename_tuple in codename_tuples:
216
- if codename_tuple not in self.registry["custom_permissions_tuples"][model]:
217
- self.registry["custom_permissions_tuples"][model].append(codename_tuple)
226
+ if codename_tuple not in self.registry[CUSTOM_PERMISSIONS_TUPLES_KEY][model]:
227
+ self.registry[CUSTOM_PERMISSIONS_TUPLES_KEY][model].append(codename_tuple)
218
228
 
219
229
  @staticmethod
220
230
  def get_view_only_codenames(codenames):
@@ -225,7 +235,8 @@ class SiteAuths:
225
235
  Does not remove `edc_navbar`, 'nav_' or `edc_dashboard`
226
236
  codenames.
227
237
  """
228
- callables = [lambda: view_only_wrapper(c) for c in codenames if callable(c)]
238
+ # callables = [lambda: view_only_wrapper(c) for c in codenames if callable(c)]
239
+ callables = [lambda c=c: view_only_wrapper(c) for c in codenames if callable(c)]
229
240
  view_only_codenames = [
230
241
  codename
231
242
  for codename in codenames
@@ -263,27 +274,27 @@ class SiteAuths:
263
274
 
264
275
  @property
265
276
  def roles(self):
266
- return self.registry["roles"]
277
+ return self.registry[ROLES_KEY]
267
278
 
268
279
  @property
269
280
  def groups(self):
270
- return self.registry["groups"]
281
+ return self.registry[GROUPS_KEY]
271
282
 
272
283
  @property
273
284
  def pii_models(self):
274
- return self.registry["pii_models"]
285
+ return self.registry[PII_MODELS_KEY]
275
286
 
276
287
  @property
277
288
  def pre_update_funcs(self):
278
- return self.registry["pre_update_funcs"]
289
+ return self.registry[PRE_UPDATE_FUNCS_KEY]
279
290
 
280
291
  @property
281
292
  def post_update_funcs(self) -> tuple[str, Callable]:
282
- return self.registry["post_update_funcs"]
293
+ return self.registry[POST_UPDATE_FUNCS_KEY]
283
294
 
284
295
  @property
285
296
  def custom_permissions_tuples(self):
286
- return self.registry["custom_permissions_tuples"]
297
+ return self.registry[CUSTOM_PERMISSIONS_TUPLES_KEY]
287
298
 
288
299
  def verify_and_populate(
289
300
  self, app_name: str | None = None, warn_only: bool | None = None
@@ -294,28 +305,30 @@ class SiteAuths:
294
305
  * Updates data from `update_groups` -> `groups`
295
306
  * Updates data from `update_roles` -> `roles`
296
307
  """
297
- for name, codenames in self.registry["update_groups"].items():
298
- if name not in self.registry["groups"]:
308
+ for name, codenames in self.registry[UPDATE_GROUPS_KEY].items():
309
+ if name not in self.registry[GROUPS_KEY]:
299
310
  msg = (
300
311
  f"Cannot update group. Group name does not exist. See app={app_name}"
301
312
  f"update_groups['groups']={codenames}. Got {name}"
302
313
  )
303
314
  if warn_only:
304
- warn(msg)
315
+ warn(msg, stacklevel=2)
305
316
  else:
306
317
  raise InvalidGroup(msg)
307
- self.update_group(*codenames, name=name, key="groups")
308
- for name, group_names in self.registry["update_roles"].items():
309
- if name not in self.registry["roles"]:
318
+ self.update_group(*codenames, name=name, key=GROUPS_KEY)
319
+ self.registry[UPDATE_GROUPS_KEY] = {}
320
+ for name, group_names in self.registry[UPDATE_ROLES_KEY].items():
321
+ if name not in self.registry[ROLES_KEY]:
310
322
  msg = (
311
323
  f"Cannot update role. Role name does not exist. See app={app_name}. "
312
324
  f"update_roles['groups']={group_names}. Got {name}"
313
325
  )
314
326
  if warn_only:
315
- warn(msg)
327
+ warn(msg, stacklevel=2)
316
328
  else:
317
329
  raise InvalidRole(msg)
318
- self.update_role(*group_names, name=name, key="roles")
330
+ self.update_role(*group_names, name=name, key=ROLES_KEY)
331
+ self.registry[UPDATE_ROLES_KEY] = {}
319
332
 
320
333
  def autodiscover(self, module_name=None, verbose=True):
321
334
  """Autodiscovers in the auths.py file of any INSTALLED_APP."""
@@ -335,7 +348,7 @@ class SiteAuths:
335
348
  except ImportError as e:
336
349
  site_auths.registry = before_import_registry
337
350
  if module_has_submodule(mod, module_name):
338
- raise SiteAuthError(str(e))
351
+ raise SiteAuthError(str(e)) from e
339
352
  except ImportError:
340
353
  pass
341
354
  self.verify_and_populate(app_name=app_name)
edc_consent/actions.py CHANGED
@@ -1,13 +1,12 @@
1
1
  from django.contrib import messages
2
-
3
- from edc_utils import get_utcnow
2
+ from django.utils import timezone
4
3
 
5
4
 
6
5
  def verify_consent(request=None, consent_obj=None):
7
6
  if consent_obj.is_verified:
8
7
  return None
9
8
  consent_obj.is_verified = True
10
- consent_obj.is_verified_datetime = get_utcnow()
9
+ consent_obj.is_verified_datetime = timezone.now()
11
10
  consent_obj.verified_by = request.user.username
12
11
  consent_obj.save(update_fields=["is_verified", "is_verified_datetime", "verified_by"])
13
12
  return consent_obj
@@ -21,7 +20,7 @@ def unverify_consent(consent_obj=None):
21
20
  return consent_obj
22
21
 
23
22
 
24
- def flag_as_verified_against_paper(modeladmin, request, queryset, **kwargs): # noqa
23
+ def flag_as_verified_against_paper(modeladmin, request, queryset, **kwargs):
25
24
  """Flags instance as verified against the paper document."""
26
25
  for consent_obj in queryset:
27
26
  if not consent_obj.open_data_queries.count() > 0:
@@ -46,7 +45,7 @@ def flag_as_verified_against_paper(modeladmin, request, queryset, **kwargs): #
46
45
  flag_as_verified_against_paper.short_description = "Verify consent against paper document"
47
46
 
48
47
 
49
- def unflag_as_verified_against_paper(modeladmin, request, queryset, **kwargs): # noqa
48
+ def unflag_as_verified_against_paper(modeladmin, request, queryset, **kwargs):
50
49
  """Unflags instance as verified."""
51
50
  for consent_obj in queryset:
52
51
  unverify_consent(consent_obj)
edc_consent/auths.py CHANGED
@@ -6,15 +6,21 @@ from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
6
6
 
7
7
  from .auth_objects import consent_codenames, navbar_tuples
8
8
 
9
- site_auths.add_post_update_func(
10
- "edc_consent",
11
- remove_default_model_permissions_from_edc_permissions,
12
- )
13
-
14
- site_auths.add_custom_permissions_tuples(
15
- model="edc_consent.edcpermissions", codename_tuples=navbar_tuples
16
- )
17
-
18
- site_auths.update_group(*consent_codenames, name=PII, no_delete=True)
19
- site_auths.update_group(*consent_codenames, name=PII_VIEW, view_only=True)
20
- site_auths.add_pii_model(settings.SUBJECT_CONSENT_MODEL)
9
+
10
+ def update_site_auths():
11
+
12
+ site_auths.add_post_update_func(
13
+ "edc_consent",
14
+ remove_default_model_permissions_from_edc_permissions,
15
+ )
16
+
17
+ site_auths.add_custom_permissions_tuples(
18
+ model="edc_consent.edcpermissions", codename_tuples=navbar_tuples
19
+ )
20
+
21
+ site_auths.update_group(*consent_codenames, name=PII, no_delete=True)
22
+ site_auths.update_group(*consent_codenames, name=PII_VIEW, view_only=True)
23
+ site_auths.add_pii_model(settings.SUBJECT_CONSENT_MODEL)
24
+
25
+
26
+ update_site_auths()
@@ -7,6 +7,7 @@ import django_audit_fields.fields.uuid_auto_field
7
7
  import django_audit_fields.models.audit_model_mixin
8
8
  import django_revision.revision_field
9
9
  from django.db import migrations, models
10
+ import django.utils.timezone
10
11
 
11
12
 
12
13
  class Migration(migrations.Migration):
@@ -33,14 +34,14 @@ class Migration(migrations.Migration):
33
34
  "created",
34
35
  models.DateTimeField(
35
36
  blank=True,
36
- default=django_audit_fields.models.audit_model_mixin.utcnow,
37
+ default=django.utils.timezone.now,
37
38
  ),
38
39
  ),
39
40
  (
40
41
  "modified",
41
42
  models.DateTimeField(
42
43
  blank=True,
43
- default=django_audit_fields.models.audit_model_mixin.utcnow,
44
+ default=django.utils.timezone.now,
44
45
  ),
45
46
  ),
46
47
  (
@@ -0,0 +1,49 @@
1
+ # Generated by Django 5.2.6 on 2025-09-17 16:53
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("edc_consent", "0005_alter_edcpermissions_revision"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="edcpermissions",
15
+ name="device_created",
16
+ field=models.CharField(
17
+ blank=True, default="", max_length=10, verbose_name="Device created"
18
+ ),
19
+ ),
20
+ migrations.AlterField(
21
+ model_name="edcpermissions",
22
+ name="device_modified",
23
+ field=models.CharField(
24
+ blank=True, default="", max_length=10, verbose_name="Device modified"
25
+ ),
26
+ ),
27
+ migrations.AlterField(
28
+ model_name="edcpermissions",
29
+ name="locale_created",
30
+ field=models.CharField(
31
+ blank=True,
32
+ default="",
33
+ help_text="Auto-updated by Modeladmin",
34
+ max_length=10,
35
+ verbose_name="Locale created",
36
+ ),
37
+ ),
38
+ migrations.AlterField(
39
+ model_name="edcpermissions",
40
+ name="locale_modified",
41
+ field=models.CharField(
42
+ blank=True,
43
+ default="",
44
+ help_text="Auto-updated by Modeladmin",
45
+ max_length=10,
46
+ verbose_name="Locale modified",
47
+ ),
48
+ ),
49
+ ]
@@ -1,10 +1,10 @@
1
1
  from django.db import models
2
+ from django.utils import timezone
2
3
  from django.utils.translation import gettext as _
3
4
  from django_crypto_fields.fields import EncryptedTextField
4
5
 
5
6
  from edc_constants.choices import YES_NO_NA
6
7
  from edc_identifier.model_mixins import UniqueSubjectIdentifierModelMixin
7
- from edc_utils import get_utcnow
8
8
 
9
9
  from .. import site_consents
10
10
  from ..consent_definition_extension import ConsentDefinitionExtension
@@ -14,11 +14,10 @@ ___all__ = ["ConsentExtensionModelMixin"]
14
14
 
15
15
 
16
16
  class ConsentExtensionModelMixin(UniqueSubjectIdentifierModelMixin, models.Model):
17
-
18
17
  # declare with an FK your subject consent!
19
18
  subject_consent = models.ForeignKey("mysubjectconsent", on_delete=models.PROTECT)
20
19
 
21
- report_datetime = models.DateTimeField(default=get_utcnow)
20
+ report_datetime = models.DateTimeField(default=timezone.now)
22
21
 
23
22
  agrees_to_extension = models.CharField(
24
23
  verbose_name=_(
@@ -35,14 +34,14 @@ class ConsentExtensionModelMixin(UniqueSubjectIdentifierModelMixin, models.Model
35
34
  consent_extension_version = models.CharField(
36
35
  verbose_name="Consent extension version",
37
36
  max_length=10,
38
- null=True,
37
+ default="",
39
38
  editable=False,
40
39
  )
41
40
 
42
41
  consent_extension_definition_name = models.CharField(
43
42
  verbose_name="Consent extension definition",
44
43
  max_length=50,
45
- null=True,
44
+ default="",
46
45
  editable=False,
47
46
  )
48
47
 
@@ -4,9 +4,9 @@ from django.db import models
4
4
  class RequiresConsentFieldsModelMixin(models.Model):
5
5
  """See pre-save signal that checks if subject is consented"""
6
6
 
7
- consent_model = models.CharField(max_length=50, null=True, blank=True)
7
+ consent_model = models.CharField(max_length=50, default="", blank=True)
8
8
 
9
- consent_version = models.CharField(max_length=10, null=True, blank=True)
9
+ consent_version = models.CharField(max_length=10, default="", blank=True)
10
10
 
11
11
  class Meta:
12
12
  abstract = True
edc_consent/utils.py CHANGED
@@ -76,7 +76,7 @@ def verify_initials_against_full_name(
76
76
  first_name: str | None = None,
77
77
  last_name: str | None = None,
78
78
  initials: str | None = None,
79
- **kwargs, # noqa
79
+ **kwargs,
80
80
  ) -> None:
81
81
  if first_name and initials and last_name:
82
82
  try:
@@ -135,6 +135,7 @@ NOT_SURE = "not_sure"
135
135
  NOT_TESTED = "not_tested"
136
136
  NO_EXAM = "NO_EXAM"
137
137
  NO_UNCONFIRMED = "no_unconfirmed"
138
+ NULL_STRING = ""
138
139
  OBSERVATION = "observation"
139
140
  OK = "OK"
140
141
  OFF_STUDY = "off study"
edc_crf/auths.py CHANGED
@@ -0,0 +1,5 @@
1
+ def update_site_auths():
2
+ return None
3
+
4
+
5
+ update_site_auths()
@@ -7,6 +7,7 @@ import django_audit_fields.fields.uuid_auto_field
7
7
  import django_audit_fields.models.audit_model_mixin
8
8
  import django_revision.revision_field
9
9
  from django.db import migrations, models
10
+ import django.utils.timezone
10
11
 
11
12
 
12
13
  class Migration(migrations.Migration):
@@ -33,14 +34,14 @@ class Migration(migrations.Migration):
33
34
  "created",
34
35
  models.DateTimeField(
35
36
  blank=True,
36
- default=django_audit_fields.models.audit_model_mixin.utcnow,
37
+ default=django.utils.timezone.now,
37
38
  ),
38
39
  ),
39
40
  (
40
41
  "modified",
41
42
  models.DateTimeField(
42
43
  blank=True,
43
- default=django_audit_fields.models.audit_model_mixin.utcnow,
44
+ default=django.utils.timezone.now,
44
45
  ),
45
46
  ),
46
47
  (