clinicedc 2.0.3__py3-none-any.whl → 2.0.5__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 (478) hide show
  1. {clinicedc-2.0.3.dist-info → clinicedc-2.0.5.dist-info}/METADATA +41 -45
  2. {clinicedc-2.0.3.dist-info → clinicedc-2.0.5.dist-info}/RECORD +478 -478
  3. edc_action_item/action.py +76 -54
  4. edc_action_item/action_item_notification.py +1 -3
  5. edc_action_item/action_with_notification.py +4 -4
  6. edc_action_item/create_action_item.py +2 -2
  7. edc_action_item/create_or_update_action_type.py +1 -1
  8. edc_action_item/data_fixers.py +4 -5
  9. edc_action_item/delete_action_item.py +0 -1
  10. edc_action_item/modeladmin_mixins.py +2 -4
  11. edc_action_item/modelform_mixins/modelform_mixins.py +1 -1
  12. edc_action_item/models/action_item.py +53 -38
  13. edc_action_item/models/action_model_mixin.py +17 -17
  14. edc_action_item/models/action_type.py +33 -13
  15. edc_action_item/models/reference.py +3 -2
  16. edc_action_item/site_action_items.py +13 -14
  17. edc_action_item/stubs.py +9 -9
  18. edc_action_item/templatetags/action_item_extras.py +2 -2
  19. edc_action_item/view_utils/action_item_button.py +3 -3
  20. edc_action_item/view_utils/action_item_popover_list_item.py +4 -4
  21. edc_adherence/form_validator_mixin.py +1 -1
  22. edc_adverse_event/action_items/ae_followup_action.py +1 -1
  23. edc_adverse_event/action_items/ae_initial_action.py +1 -1
  24. edc_adverse_event/action_items/ae_susar_action.py +1 -1
  25. edc_adverse_event/action_items/ae_tmg_action.py +1 -1
  26. edc_adverse_event/action_items/death_report_action.py +1 -2
  27. edc_adverse_event/action_items/death_report_tmg_action.py +1 -1
  28. edc_adverse_event/action_items/death_report_tmg_second_action.py +1 -1
  29. edc_adverse_event/action_items/hospitalization_action.py +1 -1
  30. edc_adverse_event/form_validator_mixins/death_report_form_validator.py +3 -3
  31. edc_adverse_event/form_validator_mixins/requires_death_report_form_validator_mixin.py +3 -4
  32. edc_adverse_event/form_validators/death_report_tmg.py +1 -1
  33. edc_adverse_event/model_mixins/ae_followup/ae_followup_methods_model_mixin.py +3 -3
  34. edc_adverse_event/model_mixins/ae_followup/ae_followup_model_mixin.py +3 -3
  35. edc_adverse_event/model_mixins/ae_initial/ae_initial_fields_model_mixin.py +0 -1
  36. edc_adverse_event/model_mixins/ae_initial/ae_initial_methods_model_mixin.py +3 -3
  37. edc_adverse_event/model_mixins/ae_initial/ae_initial_model_mixin.py +3 -3
  38. edc_adverse_event/model_mixins/ae_special_interest/aesi_methods_model_mixin.py +4 -4
  39. edc_adverse_event/model_mixins/ae_special_interest/aesi_model_mixin.py +3 -3
  40. edc_adverse_event/model_mixins/ae_susar/ae_susar_methods_model_mixin.py +4 -4
  41. edc_adverse_event/model_mixins/ae_susar/ae_susar_model_mixin.py +3 -3
  42. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_fields_model_mixin.py +4 -5
  43. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_methods_model_mixin.py +4 -4
  44. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_model_mixin.py +3 -3
  45. edc_adverse_event/model_mixins/death_report/death_report_extra_fields_model_mixin.py +2 -3
  46. edc_adverse_event/model_mixins/death_report/death_report_model_mixin.py +11 -11
  47. edc_adverse_event/model_mixins/death_report/death_report_tmg_model_mixin.py +9 -11
  48. edc_adverse_event/model_mixins/death_report/simple_death_report_model_mixin.py +7 -6
  49. edc_adverse_event/model_mixins/hospitaization/hospitalization_model_mixin.py +0 -1
  50. edc_adverse_event/modeladmin_mixins/ae_followup_admin_mixin.py +9 -11
  51. edc_adverse_event/modeladmin_mixins/ae_initial_admin_mixin.py +4 -6
  52. edc_adverse_event/modeladmin_mixins/modeladmin_mixins.py +1 -1
  53. edc_adverse_event/modeladmin_mixins/utils.py +4 -4
  54. edc_adverse_event/models/signals.py +7 -19
  55. edc_adverse_event/pdf_reports/death_pdf_report.py +8 -9
  56. edc_adverse_event/templatetags/edc_adverse_event_extras.py +10 -20
  57. edc_adverse_event/urls.py +7 -3
  58. edc_adverse_event/utils.py +2 -2
  59. edc_adverse_event/view_utils/tmg_button.py +4 -4
  60. edc_appconfig/system_checks.py +1 -1
  61. edc_appointment/appointment_status_updater.py +14 -15
  62. edc_appointment/creators/appointment_creator.py +7 -11
  63. edc_appointment/creators/appointments_creator.py +1 -1
  64. edc_appointment/creators/unscheduled_appointment_creator.py +12 -13
  65. edc_appointment/form_validator_mixins/next_appointment_crf_form_validator_mixin.py +1 -3
  66. edc_appointment/form_validator_mixins/window_period_form_validator_mixin.py +7 -6
  67. edc_appointment/form_validators/appointment_form_validator.py +2 -4
  68. edc_appointment/form_validators/utils.py +4 -4
  69. edc_appointment/managers.py +4 -4
  70. edc_appointment/model_mixins/appointment_methods_model_mixin.py +2 -2
  71. edc_appointment/model_mixins/appointment_model_mixin.py +5 -5
  72. edc_appointment/model_mixins/window_period_model_mixin.py +1 -1
  73. edc_appointment/models/appointment.py +1 -1
  74. edc_appointment/skip_appointments.py +5 -6
  75. edc_appointment/stubs.py +12 -12
  76. edc_appointment/utils.py +41 -47
  77. edc_appointment/view_utils/appointment_button.py +3 -5
  78. edc_auth/admin/role_admin.py +5 -7
  79. edc_auth/auth_objects/default_roles.py +1 -3
  80. edc_auth/auth_updater/auth_updater.py +5 -7
  81. edc_auth/auth_updater/group_updater.py +8 -8
  82. edc_auth/auth_updater/role_updater.py +1 -2
  83. edc_auth/get_app_codenames.py +1 -3
  84. edc_auth/import_users.py +19 -19
  85. edc_auth/models/role.py +4 -3
  86. edc_auth/password_setter.py +6 -7
  87. edc_auth/send_new_credentials_to_user.py +2 -3
  88. edc_auth/site_auths.py +3 -3
  89. edc_consent/actions.py +4 -5
  90. edc_consent/consent_definition.py +8 -11
  91. edc_consent/consent_definition_extension.py +3 -3
  92. edc_consent/form_validators/__init__.py +1 -1
  93. edc_consent/model_mixins/__init__.py +2 -2
  94. edc_consent/model_mixins/consent_version_model_mixin.py +1 -1
  95. edc_consent/modeladmin_mixins/consent_model_admin_mixin.py +1 -2
  96. edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_validation_mixin.py +3 -5
  97. edc_consent/site_consents.py +10 -15
  98. edc_consent/stubs.py +2 -2
  99. edc_consent/utils.py +1 -1
  100. edc_constants/utils.py +2 -4
  101. edc_crf/crf_form_validator_mixins.py +2 -2
  102. edc_crf/model_mixins/crf_no_manager_model_mixin.py +2 -2
  103. edc_crf/model_mixins/crf_status_model_mixin.py +1 -1
  104. edc_crf/models/crf_status.py +10 -12
  105. edc_crf/update_crf_status_command.py +1 -3
  106. edc_dashboard/management/commands/update_search_slugs.py +8 -11
  107. edc_dashboard/templatetags/edc_dashboard_extras.py +1 -1
  108. edc_dashboard/url_config.py +3 -3
  109. edc_dashboard/utils.py +3 -4
  110. edc_dashboard/views/dashboard_view.py +1 -1
  111. edc_data_manager/models/data_dictionary.py +1 -2
  112. edc_data_manager/models/data_query.py +3 -3
  113. edc_data_manager/models/query_rule.py +2 -2
  114. edc_data_manager/tasks.py +0 -2
  115. edc_device/device.py +1 -1
  116. edc_device/view_mixins.py +1 -1
  117. edc_document_status/fieldsets.py +1 -3
  118. edc_document_status/model_mixins.py +2 -2
  119. edc_document_status/modeladmin_mixins.py +1 -4
  120. edc_egfr/admin/egfr_drop_notification_admin_mixin.py +5 -7
  121. edc_egfr/egfr.py +6 -7
  122. edc_egfr/get_drop_notification_model.py +1 -1
  123. edc_egfr/model_mixins/egfr_drop_notification_model_mixin.py +1 -1
  124. edc_export/archive_exporter.py +26 -27
  125. edc_export/exportables.py +2 -3
  126. edc_export/management/commands/import_receipts.py +6 -6
  127. edc_export/model_exporter/file_history_updater.py +1 -1
  128. edc_export/model_exporter/model_exporter.py +1 -1
  129. edc_export/model_exporter/value_getter.py +6 -6
  130. edc_export/model_mixins/notification_model_mixin.py +4 -4
  131. edc_export/models/data_request.py +3 -3
  132. edc_export/models/data_request_history.py +2 -2
  133. edc_export/models/export_receipt.py +1 -1
  134. edc_export/models/file_history.py +5 -5
  135. edc_export/models/plan.py +4 -4
  136. edc_export/models/upload_export_receipt_file.py +2 -2
  137. edc_export/utils.py +1 -1
  138. edc_facility/facility.py +6 -6
  139. edc_facility/holidays.py +2 -5
  140. edc_facility/import_holidays.py +6 -6
  141. edc_facility/model_mixins.py +1 -1
  142. edc_facility/models/holiday.py +1 -1
  143. edc_facility/utils.py +3 -3
  144. edc_fieldsets/fieldsets.py +1 -1
  145. edc_form_describer/forms_reference.py +5 -8
  146. edc_form_describer/make_forms_reference.py +1 -1
  147. edc_form_describer/management/commands/make_forms_reference.py +1 -1
  148. edc_form_label/custom_label_condition.py +1 -1
  149. edc_form_label/form_label.py +2 -2
  150. edc_form_runners/exceptions.py +1 -1
  151. edc_form_runners/models/issue.py +3 -2
  152. edc_form_runners/site.py +2 -2
  153. edc_form_runners/templatetags/form_runners_extras.py +1 -1
  154. edc_form_runners/utils.py +5 -5
  155. edc_form_validators/applicable_field_validator.py +32 -32
  156. edc_form_validators/base_form_validator.py +1 -1
  157. edc_form_validators/extra_mixins/study_day_form_validator.py +1 -1
  158. edc_form_validators/many_to_many_field_validator.py +38 -43
  159. edc_form_validators/other_specify_field_validator.py +9 -17
  160. edc_form_validators/required_field_validator.py +34 -39
  161. edc_form_validators/test_case_mixin.py +5 -5
  162. edc_identifier/admin.py +1 -3
  163. edc_identifier/identifier.py +2 -3
  164. edc_identifier/model_mixins.py +5 -8
  165. edc_identifier/research_identifier.py +10 -12
  166. edc_identifier/short_identifier.py +1 -1
  167. edc_identifier/simple_identifier.py +22 -22
  168. edc_identifier/utils.py +1 -1
  169. edc_lab/admin/fieldsets.py +7 -9
  170. edc_lab/admin/modeladmin_mixins.py +5 -6
  171. edc_lab/form_validators/requisition_form_validator_mixin.py +2 -3
  172. edc_lab/forms/box_form.py +5 -11
  173. edc_lab/identifiers/aliquot_identifier.py +5 -8
  174. edc_lab/identifiers/prefix.py +2 -5
  175. edc_lab/lab/aliquot_creator.py +1 -2
  176. edc_lab/lab/aliquot_type.py +2 -4
  177. edc_lab/lab/manifest.py +5 -7
  178. edc_lab/lab/requisition_panel.py +2 -4
  179. edc_lab/lab/requisition_panel_group.py +5 -7
  180. edc_lab/model_mixins/requisition/requisition_model_mixin.py +5 -4
  181. edc_lab/model_mixins/shipping/manifest_model_mixin.py +6 -6
  182. edc_lab/model_mixins/shipping/verify_model_mixin.py +4 -3
  183. edc_lab/models/aliquot.py +1 -1
  184. edc_lab/models/box.py +2 -2
  185. edc_lab/models/box_item.py +1 -1
  186. edc_lab/models/box_type.py +1 -1
  187. edc_lab/models/manifest/manifest_item.py +1 -1
  188. edc_lab/pdf_reports/manifest_pdf_report.py +3 -7
  189. edc_lab/site_labs.py +3 -3
  190. edc_lab_dashboard/dashboard_templates.py +1 -1
  191. edc_lab_dashboard/view_mixins/box_view_mixin.py +4 -9
  192. edc_lab_dashboard/views/action_views/action_view.py +1 -2
  193. edc_lab_dashboard/views/action_views/manage_manifest_view.py +1 -1
  194. edc_lab_dashboard/views/action_views/manifest_view.py +9 -11
  195. edc_lab_dashboard/views/action_views/pack_view.py +11 -14
  196. edc_lab_results/calculate_missing.py +2 -2
  197. edc_lab_results/fieldsets.py +6 -6
  198. edc_lab_results/model_mixin_factories/__init__.py +2 -2
  199. edc_lab_results/model_mixin_factories/reportable_result_model_mixin_factory.py +7 -9
  200. edc_lab_results/model_mixin_factories/result_model_mixin_factory.py +7 -9
  201. edc_lab_results/model_mixins/fbg_model_mixin.py +1 -1
  202. edc_lab_results/model_mixins/glucose_model_mixin.py +1 -1
  203. edc_label/admin.py +1 -3
  204. edc_label/label.py +2 -4
  205. edc_label/label_template.py +1 -1
  206. edc_label/subject_label.py +1 -3
  207. edc_list_data/admin.py +3 -5
  208. edc_list_data/load_list_data.py +1 -1
  209. edc_list_data/load_model_data.py +1 -3
  210. edc_list_data/model_mixins.py +3 -5
  211. edc_list_data/preload_data.py +2 -4
  212. edc_list_data/site_list_data.py +6 -7
  213. edc_listboard/filters/listboard_filter.py +2 -2
  214. edc_listboard/views/listboard_view.py +1 -3
  215. edc_locator/models.py +2 -2
  216. edc_locator/view_mixins/subject_locator_view_mixins.py +2 -2
  217. edc_ltfu/action_items.py +1 -1
  218. edc_ltfu/model_mixins.py +0 -2
  219. edc_ltfu/models.py +4 -4
  220. edc_metadata/admin/modeladmin_mixins.py +1 -1
  221. edc_metadata/admin/requisition_metadata.py +2 -4
  222. edc_metadata/metadata/crf_metadata_getter.py +1 -3
  223. edc_metadata/metadata/requisition_metadata_getter.py +1 -3
  224. edc_metadata/metadata_handler.py +2 -2
  225. edc_metadata/metadata_helper/metadata_helper_mixin.py +2 -3
  226. edc_metadata/metadata_mixins/source_model_metadata_mixin.py +2 -2
  227. edc_metadata/metadata_refresher.py +1 -1
  228. edc_metadata/metadata_rules/crf/crf_rule_group.py +3 -5
  229. edc_metadata/metadata_rules/logic.py +1 -1
  230. edc_metadata/metadata_rules/requisition/requisition_rule.py +4 -4
  231. edc_metadata/metadata_rules/requisition/requisition_rule_group.py +2 -2
  232. edc_metadata/metadata_rules/rule_evaluator.py +5 -6
  233. edc_metadata/metadata_rules/rule_group_metaclass.py +2 -3
  234. edc_metadata/metadata_rules/site.py +6 -7
  235. edc_metadata/metadata_updater.py +2 -2
  236. edc_metadata/metadata_wrappers/metadata_wrapper.py +4 -4
  237. edc_metadata/model_mixins/creates/creates_metadata_model_mixin.py +6 -7
  238. edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +2 -2
  239. edc_metadata/model_mixins/updates/updates_metadata_model_mixin.py +2 -2
  240. edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +2 -2
  241. edc_metadata/models/crf_metadata.py +27 -29
  242. edc_metadata/models/crf_metadata_model_mixin.py +1 -1
  243. edc_metadata/models/requisition_metadata.py +29 -31
  244. edc_metadata/stubs.py +17 -17
  245. edc_metadata/utils.py +4 -4
  246. edc_model/__init__.py +2 -2
  247. edc_model/models/historical_records.py +2 -2
  248. edc_model/models/signals.py +1 -1
  249. edc_model/models/url_model_mixin.py +1 -1
  250. edc_model/utils.py +0 -2
  251. edc_model_admin/dashboard/model_admin_dashboard_mixin.py +1 -3
  252. edc_model_admin/history/model_admin_simple_history.py +7 -9
  253. edc_model_admin/mixins/base_model_admin_redirect_mixin.py +4 -6
  254. edc_model_admin/mixins/model_admin_limit_to_selected_foreignkey.py +2 -2
  255. edc_model_admin/mixins/model_admin_model_redirect_mixin.py +2 -10
  256. edc_model_admin/mixins/model_admin_next_url_redirect_mixin.py +8 -9
  257. edc_model_admin/utils.py +6 -9
  258. edc_model_form/mixins/__init__.py +1 -1
  259. edc_model_form/mixins/base_model_form_mixin.py +1 -1
  260. edc_model_to_dataframe/model_to_dataframe.py +2 -4
  261. edc_navbar/site_navbars.py +2 -2
  262. edc_navbar/system_checks.py +1 -1
  263. edc_notification/mailing_list_manager.py +5 -8
  264. edc_notification/management/commands/list_recipients_by_notification.py +1 -1
  265. edc_notification/models/__init__.py +4 -2
  266. edc_notification/notification/graded_event_notification.py +2 -4
  267. edc_notification/notification/model_notification.py +1 -3
  268. edc_notification/notification/notification.py +14 -17
  269. edc_notification/site_notifications.py +7 -7
  270. edc_notification/stubs.py +6 -6
  271. edc_notification/update_mailing_lists_in_m2m.py +2 -2
  272. edc_offstudy/action_items.py +2 -2
  273. edc_offstudy/model_mixins/offstudy_model_mixin.py +1 -1
  274. edc_offstudy/models.py +1 -1
  275. edc_pdf_reports/crf_pdf_report.py +2 -2
  276. edc_pdf_reports/model_mixins.py +2 -2
  277. edc_pdf_reports/report.py +4 -5
  278. edc_pdf_reports/utils.py +1 -1
  279. edc_pdutils/dataframes/get_subject_consent.py +1 -3
  280. edc_pdutils/df_exporters/csv_exporter.py +5 -7
  281. edc_pdutils/df_exporters/tables_exporter.py +2 -2
  282. edc_pdutils/df_handlers/crf_df_handler.py +1 -2
  283. edc_pdutils/dialects/crf_dialect.py +3 -3
  284. edc_pdutils/management/commands/export_models.py +6 -7
  285. edc_pdutils/site_values_mappings.py +2 -2
  286. edc_pdutils/utils/datetime_to_date.py +1 -2
  287. edc_pdutils/utils/refresh_model_from_dataframe.py +1 -3
  288. edc_pdutils/utils/table_names.py +2 -4
  289. edc_pdutils/utils/undash.py +1 -1
  290. edc_pharmacy/admin/medication/assignment_admin.py +2 -4
  291. edc_pharmacy/admin/medication/dosage_guideline_admin.py +2 -4
  292. edc_pharmacy/admin/medication/formulation_admin.py +3 -5
  293. edc_pharmacy/admin/medication/medication_admin.py +3 -5
  294. edc_pharmacy/admin/prescription/rx_refill_admin.py +7 -9
  295. edc_pharmacy/admin/stock/lot_admin.py +5 -7
  296. edc_pharmacy/admin/stock/order_admin.py +2 -2
  297. edc_pharmacy/admin/stock/order_item_admin.py +6 -6
  298. edc_pharmacy/admin/stock/receive_admin.py +1 -1
  299. edc_pharmacy/admin/stock/receive_item_admin.py +2 -2
  300. edc_pharmacy/admin/stock/stock_request_admin.py +4 -6
  301. edc_pharmacy/dosage_calculator.py +4 -4
  302. edc_pharmacy/form_validators/crf/study_medication_form_validator.py +8 -8
  303. edc_pharmacy/forms/stock/stock_request_form.py +2 -4
  304. edc_pharmacy/model_mixins/study_medication_crf_model_mixin.py +2 -2
  305. edc_pharmacy/models/medication/formulation.py +3 -4
  306. edc_pharmacy/models/medication/medication.py +1 -2
  307. edc_pharmacy/models/prescription/rx.py +2 -2
  308. edc_pharmacy/models/prescription/rx_refill.py +7 -9
  309. edc_pharmacy/models/reports/stock_availability.py +3 -4
  310. edc_pharmacy/models/stock/confirmation_at_site.py +1 -3
  311. edc_pharmacy/models/stock/order.py +1 -2
  312. edc_pharmacy/models/stock/receive.py +3 -4
  313. edc_pharmacy/models/stock/receive_item.py +2 -3
  314. edc_pharmacy/models/stock/stock.py +1 -2
  315. edc_pharmacy/models/stock/stock_adjustment.py +1 -2
  316. edc_pharmacy/models/stock/stock_request.py +4 -5
  317. edc_pharmacy/models/storage/box.py +1 -1
  318. edc_pharmacy/models/storage/items/container_model_mixin.py +1 -1
  319. edc_pharmacy/models/storage/room.py +1 -1
  320. edc_pharmacy/models/storage/shelf.py +1 -1
  321. edc_pharmacy/models/storage/utils.py +15 -16
  322. edc_pharmacy/prescribe/create_prescription.py +5 -5
  323. edc_pharmacy/refill/refill_creator.py +1 -1
  324. edc_pharmacy/sample_usb_printing/usb_printing.py +1 -1
  325. edc_pharmacy/utils/__init__.py +1 -1
  326. edc_pharmacy/utils/confirm_stock.py +3 -3
  327. edc_pharmacy/utils/confirm_stock_at_site.py +9 -8
  328. edc_pharmacy/utils/dispense.py +5 -8
  329. edc_pharmacy/utils/format_qty.py +3 -3
  330. edc_pharmacy/utils/get_codenames.py +1 -1
  331. edc_pharmacy/utils/miscellaneous.py +1 -1
  332. edc_pharmacy/utils/process_repack_request.py +18 -19
  333. edc_pharmacy/utils/process_repack_request_queryset.py +0 -2
  334. edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py +2 -3
  335. edc_pharmacy/views/add_to_storage_bin_view.py +1 -1
  336. edc_pharmacy/views/allocate_to_subject_view.py +2 -6
  337. edc_pharmacy/views/confirm_stock_from_instance_view.py +4 -4
  338. edc_pharmacy/views/confirm_stock_from_queryset_view.py +1 -5
  339. edc_pharmacy/views/confirmation_at_site_view.py +2 -3
  340. edc_pharmacy/views/dispense_view.py +1 -1
  341. edc_pharmacy/views/move_to_storage_bin_view.py +2 -3
  342. edc_pharmacy/views/print_labels_view.py +30 -31
  343. edc_pharmacy/views/transfer_stock_view.py +1 -1
  344. edc_prn/prn.py +1 -1
  345. edc_prn/site_prn_forms.py +1 -2
  346. edc_protocol_incident/action_items.py +2 -2
  347. edc_protocol_incident/model_mixins/protocol_deviation_violation_model_mixin.py +3 -3
  348. edc_protocol_incident/model_mixins/protocol_incident_model_mixin.py +3 -5
  349. edc_protocol_incident/modeladmin_mixins.py +3 -5
  350. edc_protocol_incident/models/protocol_deviation_violation.py +5 -5
  351. edc_protocol_incident/models/protocol_incident.py +5 -5
  352. edc_pylabels/site_label_configs.py +5 -5
  353. edc_qareports/model_mixins/qa_report_model_mixin.py +4 -2
  354. edc_qareports/models/qa_reports_log.py +1 -2
  355. edc_qareports/utils.py +1 -2
  356. edc_randomization/admin.py +3 -5
  357. edc_randomization/auth_objects.py +2 -3
  358. edc_randomization/blinding.py +1 -1
  359. edc_randomization/constants.py +2 -4
  360. edc_randomization/model_mixins.py +3 -3
  361. edc_randomization/randomization_list_importer.py +13 -14
  362. edc_randomization/randomization_list_verifier.py +11 -13
  363. edc_randomization/randomizer.py +2 -2
  364. edc_randomization/system_checks.py +1 -2
  365. edc_randomization/utils.py +5 -5
  366. edc_refusal/admin.py +1 -1
  367. edc_refusal/model_mixins.py +1 -1
  368. edc_refusal/utils.py +1 -1
  369. edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +2 -2
  370. edc_registration/modeladmin_mixins.py +4 -7
  371. edc_registration/models/registered_subject.py +7 -9
  372. edc_registration/utils.py +3 -4
  373. edc_reportable/data/grading_data/daids_july_2017.py +3 -3
  374. edc_reportable/evaluator.py +5 -5
  375. edc_reportable/formula.py +1 -1
  376. edc_reportable/reference_range_evaluator.py +3 -3
  377. edc_reportable/utils/convert_units.py +10 -12
  378. edc_reportable/utils/grading_data_model_cls.py +2 -2
  379. edc_reportable/utils/grading_exception_model_cls.py +2 -2
  380. edc_reportable/utils/molecular_weight_model_cls.py +2 -2
  381. edc_reportable/utils/normal_data_model_cls.py +2 -2
  382. edc_reportable/utils/reference_range_colllection_model_cls.py +2 -2
  383. edc_screening/age_evaluator.py +2 -4
  384. edc_screening/eligibility.py +1 -1
  385. edc_screening/form_validator_mixins.py +2 -2
  386. edc_screening/model_mixins/screening_methods_model_mixin.py +1 -1
  387. edc_screening/screening_eligibility.py +1 -1
  388. edc_screening/utils.py +4 -6
  389. edc_search/model_mixins.py +1 -1
  390. edc_search/search_slug.py +1 -3
  391. edc_search/updater.py +1 -1
  392. edc_sites/admin/site_model_admin_mixin.py +2 -2
  393. edc_sites/model_mixins/site_model_mixin.py +1 -2
  394. edc_sites/modelform_mixins.py +2 -2
  395. edc_sites/models/__init__.py +1 -1
  396. edc_sites/models/site_profile.py +4 -4
  397. edc_sites/site.py +24 -32
  398. edc_sites/system_checks.py +1 -1
  399. edc_sites/utils/get_message_text.py +1 -1
  400. edc_sites/utils/valid_site_for_subject_or_raise.py +5 -6
  401. edc_subject_dashboard/requisition_report.py +4 -5
  402. edc_subject_dashboard/requisition_verifier.py +4 -2
  403. edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +16 -17
  404. edc_subject_dashboard/view_utils/__init__.py +1 -1
  405. edc_subject_dashboard/view_utils/crf_button.py +1 -3
  406. edc_subject_dashboard/view_utils/subject_consent_dashboard_button.py +2 -2
  407. edc_subject_dashboard/view_utils/subject_consent_listboard_button.py +2 -2
  408. edc_subject_dashboard/view_utils/timepoint_status_button.py +1 -4
  409. edc_subject_dashboard/views/requisition_print_actions_view.py +2 -2
  410. edc_subject_dashboard/views/subject_dashboard_view.py +1 -1
  411. edc_timepoint/model_mixins.py +2 -2
  412. edc_transfer/action_items.py +1 -1
  413. edc_transfer/model_mixins.py +4 -3
  414. edc_transfer/modeladmin_mixins.py +3 -6
  415. edc_unblinding/action_items.py +2 -2
  416. edc_unblinding/models/unblinding_request.py +3 -3
  417. edc_unblinding/models/unblinding_review.py +3 -3
  418. edc_utils/__init__.py +6 -5
  419. edc_utils/age.py +1 -1
  420. edc_utils/celery.py +3 -8
  421. edc_utils/get_static_file.py +1 -1
  422. edc_utils/text.py +5 -6
  423. edc_view_utils/__init__.py +3 -3
  424. edc_view_utils/dashboard_model_button.py +4 -4
  425. edc_view_utils/model_button.py +7 -8
  426. edc_view_utils/perms.py +3 -3
  427. edc_visit_schedule/action_items.py +2 -2
  428. edc_visit_schedule/admin/list_filters.py +11 -9
  429. edc_visit_schedule/admin/subject_schedule_history_admin.py +13 -15
  430. edc_visit_schedule/admin/visit_schedule_admin.py +7 -7
  431. edc_visit_schedule/fieldsets.py +4 -6
  432. edc_visit_schedule/management/commands/find_invalid_onschedules.py +1 -1
  433. edc_visit_schedule/model_mixins/off_schedule_model_mixin.py +3 -1
  434. edc_visit_schedule/model_mixins/on_schedule_model_mixin.py +1 -1
  435. edc_visit_schedule/modelform_mixins/__init__.py +1 -1
  436. edc_visit_schedule/modelform_mixins/off_schedule_modelform_mixin.py +1 -2
  437. edc_visit_schedule/models/subject_schedule_history.py +2 -1
  438. edc_visit_schedule/models/visit_schedule.py +2 -2
  439. edc_visit_schedule/schedule/schedule.py +11 -12
  440. edc_visit_schedule/schedule/visit_collection.py +4 -3
  441. edc_visit_schedule/schedule/window.py +19 -12
  442. edc_visit_schedule/site_visit_schedules.py +9 -9
  443. edc_visit_schedule/subject_schedule.py +6 -7
  444. edc_visit_schedule/system_checks.py +8 -4
  445. edc_visit_schedule/view_mixins.py +1 -1
  446. edc_visit_schedule/visit/crf.py +3 -7
  447. edc_visit_schedule/visit/requisition.py +2 -2
  448. edc_visit_schedule/visit/visit.py +6 -7
  449. edc_visit_schedule/visit/window_period.py +8 -8
  450. edc_visit_schedule/visit_schedule/schedules_collection.py +2 -5
  451. edc_visit_tracking/action_items.py +11 -13
  452. edc_visit_tracking/form_validators/visit_form_validator.py +5 -5
  453. edc_visit_tracking/model_mixins/base/visit_methods_model_mixin.py +3 -4
  454. edc_visit_tracking/model_mixins/crfs/visit_tracking_crf_model_mixin.py +2 -2
  455. edc_visit_tracking/model_mixins/subject_visit_missed_model_mixin.py +2 -3
  456. edc_visit_tracking/model_mixins/utils.py +1 -1
  457. edc_visit_tracking/model_mixins/visit_model_mixin/previous_visit_model_mixin.py +2 -2
  458. edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_mixin.py +3 -3
  459. edc_visit_tracking/modeladmin_mixins/crf_model_admin_mixin.py +6 -4
  460. edc_visit_tracking/modeladmin_mixins/visit_model_admin_mixin.py +6 -7
  461. edc_visit_tracking/modelform_mixins/crf/visit_tracking_crf_modelform_mixin.py +4 -5
  462. edc_visit_tracking/modelform_mixins/visit_tracking_modelform_mixin.py +3 -4
  463. edc_visit_tracking/models/subject_visit.py +1 -1
  464. edc_visit_tracking/models/subject_visit_missed.py +1 -1
  465. edc_visit_tracking/stubs.py +7 -7
  466. edc_visit_tracking/typing_stubs.py +11 -11
  467. edc_visit_tracking/utils.py +5 -5
  468. edc_visit_tracking/view_utils/related_visit_button.py +5 -5
  469. edc_vitals/calculators/bmi.py +1 -1
  470. edc_vitals/form_validators/blood_pressure_form_validator_mixin.py +8 -12
  471. edc_vitals/form_validators/bmi_form_validator_mixin.py +1 -1
  472. edc_vitals/form_validators/weight_height_with_bmi_form_validator_mixin.py +5 -2
  473. edc_vitals/model_mixins/blood_pressure_model_mixin.py +3 -4
  474. edc_vitals/model_mixins/weight_height_bmi_model_mixin.py +4 -4
  475. edc_vitals/utils.py +1 -1
  476. edc_vitals/validators.py +1 -1
  477. {clinicedc-2.0.3.dist-info → clinicedc-2.0.5.dist-info}/WHEEL +0 -0
  478. {clinicedc-2.0.3.dist-info → clinicedc-2.0.5.dist-info}/licenses/LICENSE +0 -0
edc_action_item/action.py CHANGED
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from typing import TYPE_CHECKING, List, Optional, Type
4
+ from contextlib import suppress
5
+ from typing import TYPE_CHECKING
5
6
 
6
7
  from django.apps import apps as django_apps
7
8
  from django.conf import settings
@@ -27,6 +28,29 @@ logger = logging.getLogger(__name__)
27
28
  style = color_style()
28
29
 
29
30
  REFERENCE_MODEL_ERROR_CODE = "reference_model"
31
+ ACTION_CLASS_MISMATCH = (
32
+ "Action class mismatch for given ActionItem. "
33
+ "{action_item_action_cls} incorrectly passed "
34
+ "to Action {action_cls}."
35
+ )
36
+ ACTION_GOT_UNLISTED_PARENT_ACTION_ITEM = (
37
+ "Action class received an unlisted parent_action_item. "
38
+ "Expected one of {parent_action_names}. "
39
+ "Got '{parent_action_cls_name}' "
40
+ "subject_identifier `{subject_identifier}`."
41
+ "See Action {action_cls}."
42
+ )
43
+
44
+ ACTION_EXPECTS_RELATED_ACTION_ITEM = (
45
+ "Action class expects a related_action_item. "
46
+ "related_reference_fk_attr=`{related_reference_fk_attr}`. "
47
+ "Got None for action based on action_item `{action_item}`. "
48
+ "See Action {action_cls}."
49
+ )
50
+
51
+ ACTION_ITEM_DOES_NOT_EXIST = "{err} Got action_identifier={action_identifier}."
52
+ ACTION_ITEM_GET_OR_CREATE_FAILED = "Unable to get or create ActionItem. Got {opts}."
53
+ ACTION_NAME_IS_NONE = "Action name cannot be None. See {action_cls}."
30
54
 
31
55
 
32
56
  class Action:
@@ -45,7 +69,7 @@ class Action:
45
69
  help_text: str = None
46
70
  instructions = None
47
71
  name: str = None
48
- parent_action_names: Optional[List[str]] = None
72
+ parent_action_names: tuple[str] | None = None
49
73
  enforce_parent_action_names: bool = True
50
74
  priority: bool = None
51
75
  reference_model: str = None
@@ -63,23 +87,23 @@ class Action:
63
87
  action_type_model: str = "edc_action_item.actiontype"
64
88
  next_actions: list[str] | None = None # a list of Action classes which may include 'self'
65
89
 
66
- def __init__(
90
+ def __init__( # noqa: PLR0913
67
91
  self,
68
92
  action_item: ActionItemStub = None,
69
93
  reference_obj: models.Model = None,
70
- subject_identifier: str = None,
71
- action_identifier: str = None,
72
- parent_action_item: Optional[ActionItemStub] = None,
73
- related_action_item: Optional[ActionItemStub] = None,
74
- using: Optional[str] = None,
75
- readonly: Optional[bool] = None,
94
+ subject_identifier: str | None = None,
95
+ action_identifier: str | None = None,
96
+ parent_action_item: ActionItemStub | None = None,
97
+ related_action_item: ActionItemStub | None = None,
98
+ using: str | None = None,
99
+ readonly: bool | None = None,
76
100
  skip_get_current_site: bool | None = None,
77
101
  site_id: int | None = None,
78
102
  ) -> None:
79
103
  self._action_item = action_item
80
104
  self._reference_obj = reference_obj
81
105
 
82
- self.parent_action_names = self.parent_action_names or []
106
+ self.parent_action_names = self.parent_action_names or ()
83
107
 
84
108
  self.messages: dict = {}
85
109
 
@@ -94,9 +118,10 @@ class Action:
94
118
 
95
119
  if self.action_item.action_cls != self.__class__:
96
120
  raise ActionError(
97
- f"Action class mismatch for given ActionItem. "
98
- f"{self.action_item.action_cls} incorrectly passed "
99
- f"to Action {self.__class__}",
121
+ ACTION_CLASS_MISMATCH.format(
122
+ action_item_action_cls=self.action_item.action_cls,
123
+ action_cls=self.__class__,
124
+ ),
100
125
  code="class type mismatch",
101
126
  )
102
127
  self.action_identifier = self.action_item.action_identifier
@@ -112,18 +137,20 @@ class Action:
112
137
  and self.parent_action_item.action_cls.name not in self.parent_action_names
113
138
  ):
114
139
  raise ActionError(
115
- f"Action class received an unlisted parent_action_item. "
116
- f"Expected one of {self.parent_action_names}. "
117
- f"Got '{self.parent_action_item.action_cls.name}' "
118
- f"subject_identifier `{self.subject_identifier}`."
119
- f"See Action {self.__class__}."
140
+ ACTION_GOT_UNLISTED_PARENT_ACTION_ITEM.format(
141
+ parent_action_names=self.parent_action_names,
142
+ parent_action_cls_name=self.parent_action_item.action_cls.name,
143
+ subject_identifier=self.subject_identifier,
144
+ action_cls=self.__class__,
145
+ )
120
146
  )
121
147
  if not self.related_action_item and self.related_reference_fk_attr:
122
148
  raise ActionError(
123
- "Action class expects a related_action_item. "
124
- f"related_reference_fk_attr=`{self.related_reference_fk_attr}`. "
125
- f"Got None for action based on action_item `{self.action_item}`. "
126
- f"See `{repr(self)}`"
149
+ ACTION_EXPECTS_RELATED_ACTION_ITEM.format(
150
+ related_reference_fk_attr=self.related_reference_fk_attr,
151
+ action_item=self.action_item,
152
+ action_cls=self.__class__,
153
+ )
127
154
  )
128
155
 
129
156
  if self.reference_obj and not self.readonly:
@@ -157,19 +184,18 @@ class Action:
157
184
  The reference model instance "should" exist if
158
185
  action item exists and is CLOSED. If not, re-open.
159
186
  """
160
- if not self._reference_obj:
161
- if self.action_identifier:
162
- try:
163
- self._reference_obj = (
164
- self.reference_model_cls()
165
- .objects.using(self.using)
166
- .get(action_identifier=self.action_identifier)
167
- )
168
- except ObjectDoesNotExist:
169
- if self.action_item and self.action_item.status == CLOSED:
170
- self.action_item.status = OPEN
171
- self.action_item.save(update_fields=["status"], using=self.using)
172
- self.action_item.refresh_from_db()
187
+ if not self._reference_obj and self.action_identifier:
188
+ try:
189
+ self._reference_obj = (
190
+ self.reference_model_cls()
191
+ .objects.using(self.using)
192
+ .get(action_identifier=self.action_identifier)
193
+ )
194
+ except ObjectDoesNotExist:
195
+ if self.action_item and self.action_item.status == CLOSED:
196
+ self.action_item.status = OPEN
197
+ self.action_item.save(update_fields=["status"], using=self.using)
198
+ self.action_item.refresh_from_db()
173
199
  return self._reference_obj
174
200
 
175
201
  @property
@@ -188,8 +214,10 @@ class Action:
188
214
  )
189
215
  except ObjectDoesNotExist as e:
190
216
  raise ObjectDoesNotExist(
191
- f"{e} Got action_identifier={self.action_identifier}."
192
- )
217
+ ACTION_ITEM_DOES_NOT_EXIST.format(
218
+ err=str(e), action_identifier=self.action_identifier
219
+ )
220
+ ) from e
193
221
  elif self.reference_obj:
194
222
  self._action_item = self.reference_obj.action_item
195
223
  else:
@@ -221,10 +249,10 @@ class Action:
221
249
  parent_action_item=self.parent_action_item,
222
250
  related_action_item=self.related_action_item,
223
251
  )
224
- raise ActionError(f"Unable to get or create ActionItem. Got {opts}.")
252
+ raise ActionError(ACTION_ITEM_GET_OR_CREATE_FAILED.format(opts=opts))
225
253
  return self._action_item
226
254
 
227
- def _create_new_action_item(self, subject_identifier: str = None, **opts):
255
+ def _create_new_action_item(self, subject_identifier: str | None = None, **opts):
228
256
  """Create a new action item.
229
257
 
230
258
  Called only after checking.
@@ -254,12 +282,12 @@ class Action:
254
282
  )
255
283
 
256
284
  @classmethod
257
- def action_item_model_cls(cls) -> Type[ActionItem]:
285
+ def action_item_model_cls(cls) -> type[ActionItem]:
258
286
  """Returns the ActionItem model class."""
259
287
  return django_apps.get_model(cls.action_item_model)
260
288
 
261
289
  @classmethod
262
- def action_type_model_cls(cls) -> Type[ActionType]:
290
+ def action_type_model_cls(cls) -> type[ActionType]:
263
291
  """Returns the ActionType model class."""
264
292
  return django_apps.get_model(cls.action_type_model)
265
293
 
@@ -282,14 +310,10 @@ class Action:
282
310
  def as_dict(cls) -> dict:
283
311
  """Returns select class attrs as a dictionary."""
284
312
  dct = {k: v for k, v in cls.__dict__.items() if not k.startswith("_")}
285
- try:
313
+ with suppress(AttributeError):
286
314
  dct.update(reference_model=cls.get_reference_model().lower())
287
- except AttributeError:
288
- pass
289
- try:
315
+ with suppress(AttributeError):
290
316
  dct.update(related_reference_model=cls.related_reference_model.lower())
291
- except AttributeError:
292
- pass
293
317
  dct.update(
294
318
  name=cls.name,
295
319
  display_name=cls.display_name,
@@ -305,7 +329,7 @@ class Action:
305
329
  )
306
330
  return dct
307
331
 
308
- def get_next_actions(self) -> List[str]:
332
+ def get_next_actions(self) -> list[str]:
309
333
  """Returns a list of action classes to be created
310
334
  again by this model if the first has been closed on post_save.
311
335
  """
@@ -376,7 +400,7 @@ class Action:
376
400
  self.reference_obj.action_identifier
377
401
  )
378
402
  )
379
- | models.Q(related_action_item=self.reference_obj.action_item) # noqa
403
+ | models.Q(related_action_item=self.reference_obj.action_item)
380
404
  ),
381
405
  status=CLOSED,
382
406
  )
@@ -445,12 +469,10 @@ class Action:
445
469
  Will not append if the ActionItem for the next action
446
470
  already exists.
447
471
  """
448
- try:
472
+ with suppress(AttributeError):
449
473
  action_name = action_cls.name
450
- except AttributeError:
451
- pass
452
474
  if not action_name:
453
- raise ActionError(f"Action name cannot be None. See {self}.")
475
+ raise ActionError(ACTION_NAME_IS_NONE.format(action_cls=self))
454
476
  next_actions = next_actions or []
455
477
  required = True if required is None else required
456
478
  opts = dict(
@@ -491,7 +513,7 @@ class Action:
491
513
  parent_action_item=parent_action_item,
492
514
  status=NEW,
493
515
  )
494
- for index, obj in enumerate(
516
+ for index, obj in enumerate( # noqa: B007
495
517
  self.action_item_model_cls().objects.using(self.using).filter(**opts)
496
518
  ):
497
519
  obj.delete(using=self.using)
@@ -1,5 +1,3 @@
1
- from typing import List
2
-
3
1
  from django.core.exceptions import ObjectDoesNotExist
4
2
 
5
3
  from edc_constants.constants import CLOSED, NEW, OPEN
@@ -35,7 +33,7 @@ class ActionItemNotification(Notification):
35
33
  notification_action_name: str = None
36
34
  # set by class user, list of field(s) from
37
35
  # the action_item.reference_obj
38
- notification_fields: List[str] = []
36
+ notification_fields: list[str] = []
39
37
 
40
38
  # TODO: not sure what this does?
41
39
  notify_on_new: bool = False
@@ -1,4 +1,4 @@
1
- from typing import Type, cast
1
+ from typing import cast
2
2
 
3
3
  from django.db import models
4
4
 
@@ -14,7 +14,7 @@ class ActionWithNotification(Action):
14
14
  notification_email_to: list[str] = None
15
15
  notification_display_name: str = None
16
16
  notification_fields: list[str] = None
17
- notification_super_cls: Type[ActionItemNotification] = ActionItemNotification
17
+ notification_super_cls: type[ActionItemNotification] = ActionItemNotification
18
18
  notify_on_changed_reference_obj: models.Model = True
19
19
  notify_on_close: bool = False
20
20
  notify_on_new: bool = False
@@ -22,10 +22,10 @@ class ActionWithNotification(Action):
22
22
  notify_on_open: bool = False
23
23
 
24
24
  @classmethod
25
- def notification_cls(cls) -> Type[ActionItemNotification]:
25
+ def notification_cls(cls) -> type[ActionItemNotification]:
26
26
  """Returns a subclass of ActionItemModelNotification."""
27
27
  return cast(
28
- Type[ActionItemNotification],
28
+ type[ActionItemNotification],
29
29
  type(
30
30
  f"{cls.__name__}Notification",
31
31
  (cls.notification_super_cls,),
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Type
3
+ from typing import TYPE_CHECKING
4
4
 
5
5
  from django.core.exceptions import ObjectDoesNotExist
6
6
 
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
13
13
 
14
14
 
15
15
  def create_action_item(
16
- action_cls: Type[Action],
16
+ action_cls: type[Action],
17
17
  subject_identifier: str = None,
18
18
  action_identifier: str | None = None,
19
19
  related_action_item: ActionItem | None = None,
@@ -27,7 +27,7 @@ def create_or_update_action_type(
27
27
  if options:
28
28
  fields = [f.name for f in action_type_model_cls._meta.get_fields() if f.name != "name"]
29
29
  for attr, value in options.items():
30
- if attr in fields:
30
+ if attr in fields and value is not None:
31
31
  opts.update({attr: value})
32
32
  try:
33
33
  action_type = action_type_model_cls.objects.get(name=name)
@@ -64,7 +64,7 @@ def fix_null_action_item_fk(apps, app_label, models):
64
64
  try:
65
65
  obj.save()
66
66
  except MultipleObjectsReturned:
67
- sys.stdout.write(f"Failed to resave {repr(obj)}")
67
+ sys.stdout.write(f"Failed to resave {obj!r}")
68
68
 
69
69
 
70
70
  def fix_null_action_items(apps):
@@ -114,8 +114,7 @@ def fix_null_action_items(apps):
114
114
  .update(related_action_item=related_action_item)
115
115
  )
116
116
  sys.stdout.write(
117
- f" setting related_action_items for "
118
- f"{related_action_item}. Got {updated}\n"
117
+ f" setting related_action_items for {related_action_item}. Got {updated}\n"
119
118
  )
120
119
 
121
120
 
@@ -157,7 +156,7 @@ def fix_null_related_action_items(apps): # noqa
157
156
  action_item.related_action_item = related_action_item
158
157
  action_item.save()
159
158
  if reference_obj:
160
- reference_obj.related_action_item = related_action_item # noqa
159
+ reference_obj.related_action_item = related_action_item
161
160
  reference_obj.save()
162
161
  if (
163
162
  action_item_cls.objects.filter(
@@ -239,7 +238,7 @@ def fix_duplicate_singleton_action_items(apps, name=None):
239
238
  ).delete()
240
239
 
241
240
 
242
- def fix_null_related_action_items2(delete_orphans=None): # noqa
241
+ def fix_null_related_action_items2(delete_orphans=None):
243
242
  """Used for early action items where related_action_item
244
243
  was not set, e.g. v0.1.1
245
244
 
@@ -42,4 +42,3 @@ def delete_action_item(action_cls=None, subject_identifier=None, using=None):
42
42
  obj.delete(using=using)
43
43
  else:
44
44
  obj.delete(using=using)
45
- return None
@@ -1,10 +1,8 @@
1
- from typing import Tuple
2
-
3
1
  from .fieldsets import action_fields
4
2
 
5
3
 
6
4
  class ActionItemModelAdminMixin:
7
- def get_readonly_fields(self, request, obj=None) -> Tuple[str, ...]:
5
+ def get_readonly_fields(self, request, obj=None) -> tuple[str, ...]:
8
6
  """
9
7
  Returns a list of readonly field names.
10
8
 
@@ -15,7 +13,7 @@ class ActionItemModelAdminMixin:
15
13
  fields += action_fields
16
14
  return tuple(f for f in fields if f != "action_identifier")
17
15
 
18
- def get_search_fields(self, request) -> Tuple[str, ...]:
16
+ def get_search_fields(self, request) -> tuple[str, ...]:
19
17
  search_fields = super().get_search_fields(request)
20
18
  custom_fields = ("action_identifier",)
21
19
  if "subject_identifier" in [f.name for f in self.model._meta.fields]:
@@ -29,7 +29,7 @@ class ActionItemModelFormMixin:
29
29
  )
30
30
  except ActionError as e:
31
31
  raise forms.ValidationError(
32
- f"{str(e.message)}. Please contact your data manager."
32
+ f"{e.message!s}. Please contact your data manager."
33
33
  )
34
34
  return cleaned_data
35
35
 
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from datetime import datetime
4
- from typing import TYPE_CHECKING, Type
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from django.apps import apps as django_apps
7
7
  from django.core.exceptions import ObjectDoesNotExist
@@ -29,9 +29,27 @@ if TYPE_CHECKING:
29
29
 
30
30
  class PrnModel(BaseUuidModel):
31
31
  subject_identifier: str
32
- ... # noqa
32
+ ...
33
33
 
34
- class CrfModel(CrfModelMixin): ... # noqa
34
+ class CrfModel(CrfModelMixin): ...
35
+
36
+
37
+ RELATED_ACTION_ITEM_DOES_NOT_EXIST = (
38
+ "Related ActionItem does not exist. Got {action_identifier}."
39
+ )
40
+ PARENT_ACTION_ITEM_DOES_NOT_EXIST = (
41
+ "Parent ActionItem does not exist. Got {action_identifier}."
42
+ )
43
+ INVALID_ACTION_ITEM_STATUS = (
44
+ "Invalid action item status. Reference model exists. "
45
+ "Got `{status}`. Perhaps catch this in the form"
46
+ )
47
+
48
+ INVALID_SUBJECT_IDENTIFIER = (
49
+ "Invalid subject identifier. Attempt to create {class_name} failed. "
50
+ "Subject does not exist in '{subject_identifier_model}'. "
51
+ "Got '{subject_identifier}'."
52
+ )
35
53
 
36
54
 
37
55
  class CurrentSiteManager(BaseCurrentSiteManager):
@@ -82,17 +100,14 @@ class ActionItem(
82
100
 
83
101
  related_action_identifier = models.CharField(
84
102
  max_length=50,
85
- null=True,
86
103
  blank=True,
87
104
  help_text=(
88
- "May be left blank. e.g. action identifier from "
89
- "source model that opened the item."
105
+ "May be left blank. e.g. action identifier from source model that opened the item."
90
106
  ),
91
107
  )
92
108
 
93
109
  parent_action_identifier = models.CharField(
94
110
  max_length=50,
95
- null=True,
96
111
  blank=True,
97
112
  help_text=(
98
113
  "May be left blank. e.g. action identifier from "
@@ -103,7 +118,6 @@ class ActionItem(
103
118
  priority = models.CharField(
104
119
  max_length=25,
105
120
  choices=PRIORITY,
106
- null=True,
107
121
  blank=True,
108
122
  help_text="Leave blank to use default for this action type.",
109
123
  )
@@ -128,13 +142,11 @@ class ActionItem(
128
142
 
129
143
  status = models.CharField(max_length=25, default=NEW, choices=ACTION_STATUS)
130
144
 
131
- instructions = models.TextField(
132
- null=True, blank=True, help_text="populated by action class"
133
- )
145
+ instructions = models.TextField(blank=True, help_text="populated by action class")
134
146
 
135
147
  auto_created = models.BooleanField(default=False)
136
148
 
137
- auto_created_comment = models.CharField(max_length=25, null=True, blank=True)
149
+ auto_created_comment = models.CharField(max_length=25, blank=True)
138
150
 
139
151
  objects = ActionItemManager()
140
152
 
@@ -168,29 +180,28 @@ class ActionItem(
168
180
  subject_identifier_model_cls.objects.get(
169
181
  subject_identifier=self.subject_identifier
170
182
  )
171
- except ObjectDoesNotExist:
183
+ except ObjectDoesNotExist as e:
172
184
  raise SubjectDoesNotExist(
173
- f"Attempt to create {self.__class__.__name__} failed. "
174
- f"Invalid subject identifier. Subject does not exist "
175
- f"in '{self.subject_identifier_model}'. "
176
- f"Got '{self.subject_identifier}'."
177
- )
185
+ INVALID_SUBJECT_IDENTIFIER.format(
186
+ class_name=self.__class__.__name__,
187
+ subject_identifier_model=self.subject_identifier_model,
188
+ subject_identifier=self.subject_identifier,
189
+ )
190
+ ) from e
178
191
  self.priority = self.priority or self.action_type.priority
179
- else:
180
- if (
181
- self.status in [NEW, CANCELLED]
182
- and django_apps.get_model(self.reference_model)
183
- .objects.filter(action_identifier=self.action_identifier)
184
- .exists()
185
- ):
186
- raise ActionItemStatusError(
187
- "Invalid action item status. Reference model exists. "
188
- f"Got `{self.get_status_display()}`. Perhaps catch this in the form"
189
- )
192
+ elif (
193
+ self.status in [NEW, CANCELLED]
194
+ and django_apps.get_model(self.reference_model)
195
+ .objects.filter(action_identifier=self.action_identifier)
196
+ .exists()
197
+ ):
198
+ raise ActionItemStatusError(
199
+ INVALID_ACTION_ITEM_STATUS.format(status=self.get_status_display())
200
+ )
190
201
  super().save(*args, **kwargs)
191
202
 
192
203
  def natural_key(self) -> tuple[str]:
193
- return (self.action_identifier,) # noqa
204
+ return (self.action_identifier,)
194
205
 
195
206
  @property
196
207
  def last_updated(self) -> datetime | str:
@@ -201,7 +212,7 @@ class ActionItem(
201
212
  return None if self.status == NEW else self.user_modified or self.user_created
202
213
 
203
214
  @property
204
- def action_cls(self) -> Type[Action]:
215
+ def action_cls(self) -> type[Action]:
205
216
  """Returns the action_cls."""
206
217
  return site_action_items.get(self.action_type.name)
207
218
 
@@ -219,7 +230,7 @@ class ActionItem(
219
230
  return self.action_type.reference_model
220
231
 
221
232
  @property
222
- def reference_model_cls(self) -> Type[PrnModel | CrfModel]:
233
+ def reference_model_cls(self) -> type[PrnModel | CrfModel]:
223
234
  return django_apps.get_model(self.action_type.reference_model)
224
235
 
225
236
  @property
@@ -230,7 +241,9 @@ class ActionItem(
230
241
  def parent_reference_obj(self) -> PrnModel | CrfModel:
231
242
  if not self.parent_action_item:
232
243
  raise ObjectDoesNotExist(
233
- f"Parent ActionItem does not exist for {self.action_identifier}."
244
+ PARENT_ACTION_ITEM_DOES_NOT_EXIST.format(
245
+ action_identifier=self.action_identifier
246
+ )
234
247
  )
235
248
  return self.parent_action_item.reference_obj
236
249
 
@@ -242,7 +255,9 @@ class ActionItem(
242
255
  def related_reference_obj(self) -> PrnModel | CrfModel:
243
256
  if not self.related_action_item:
244
257
  raise ObjectDoesNotExist(
245
- f"Related ActionItem does not exist for {self.action_identifier}."
258
+ RELATED_ACTION_ITEM_DOES_NOT_EXIST.format(
259
+ action_identifier=self.action_identifier
260
+ )
246
261
  )
247
262
  return self.related_action_item.reference_obj
248
263
 
@@ -266,9 +281,9 @@ class ActionItem(
266
281
  verbose_name = "Action Item"
267
282
  verbose_name_plural = "Action Items"
268
283
  indexes = (
269
- BaseUuidModel.Meta.indexes
270
- + NonUniqueSubjectIdentifierFieldMixin.Meta.indexes
271
- + [
284
+ *BaseUuidModel.Meta.indexes,
285
+ *NonUniqueSubjectIdentifierFieldMixin.Meta.indexes,
286
+ *[
272
287
  models.Index(
273
288
  fields=[
274
289
  "subject_identifier",
@@ -279,5 +294,5 @@ class ActionItem(
279
294
  "report_datetime",
280
295
  ],
281
296
  ),
282
- ]
297
+ ],
283
298
  )
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Type
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from django.db import models
6
6
  from django.db.models.deletion import PROTECT
@@ -53,6 +53,19 @@ class ActionNoManagersModelMixin(models.Model):
53
53
 
54
54
  action_item_reason = models.TextField(null=True, editable=False)
55
55
 
56
+ class Meta:
57
+ abstract = True
58
+ indexes = (
59
+ models.Index(
60
+ fields=[
61
+ "action_identifier",
62
+ "action_item",
63
+ "related_action_item",
64
+ "parent_action_item",
65
+ ]
66
+ ),
67
+ )
68
+
56
69
  def __str__(self) -> str:
57
70
  if self.action_identifier:
58
71
  return f"{self.action_identifier[-9:]}"
@@ -61,7 +74,7 @@ class ActionNoManagersModelMixin(models.Model):
61
74
  def save(self: Any, *args, **kwargs):
62
75
  # ensure action class is defined
63
76
  if not self.get_action_cls():
64
- raise ActionClassNotDefined(f"Action class name not defined. See {repr(self)}")
77
+ raise ActionClassNotDefined(f"Action class name not defined. See {self!r}")
65
78
 
66
79
  # ensure subject_identifier
67
80
  if not self.subject_identifier:
@@ -98,13 +111,13 @@ class ActionNoManagersModelMixin(models.Model):
98
111
  super().save(*args, **kwargs) # type: ignore
99
112
 
100
113
  def natural_key(self: Any) -> tuple:
101
- return (self.action_identifier,) # noqa
114
+ return (self.action_identifier,)
102
115
 
103
116
  # noinspection PyTypeHints
104
117
  natural_key.dependencies = ["edc_action_item.actionitem"] # type:ignore
105
118
 
106
119
  @classmethod
107
- def get_action_cls(cls) -> Type[Action]:
120
+ def get_action_cls(cls) -> type[Action]:
108
121
  return site_action_items.get(cls.action_name)
109
122
 
110
123
  @property
@@ -123,19 +136,6 @@ class ActionNoManagersModelMixin(models.Model):
123
136
  """Returns a shortened action_identifier"""
124
137
  return self.action_identifier[-9:]
125
138
 
126
- class Meta:
127
- abstract = True
128
- indexes = [
129
- models.Index(
130
- fields=[
131
- "action_identifier",
132
- "action_item",
133
- "related_action_item",
134
- "parent_action_item",
135
- ]
136
- )
137
- ]
138
-
139
139
 
140
140
  class ActionModelMixin(ActionNoManagersModelMixin):
141
141
  objects = ActionIdentifierModelManager()