clinicedc 2.0.4__py3-none-any.whl → 2.0.6__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.4.dist-info → clinicedc-2.0.6.dist-info}/METADATA +41 -45
  2. {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.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 +1 -1
  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 -10
  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.4.dist-info → clinicedc-2.0.6.dist-info}/WHEEL +0 -0
  478. {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.dist-info}/licenses/LICENSE +0 -0
@@ -31,7 +31,7 @@ class UrlModelMixin(models.Model):
31
31
  f"{self.admin_site_name}:{self._meta.label_lower.replace('.', '_')}_changelist"
32
32
  )
33
33
  if search_term:
34
- url = f"{url}?q={str(search_term)}"
34
+ url = f"{url}?q={search_term!s}"
35
35
  return url
36
36
 
37
37
  @property
edc_model/utils.py CHANGED
@@ -15,7 +15,6 @@ from django.urls import reverse
15
15
  from .constants import REPORT_DATETIME_FIELD_NAME
16
16
 
17
17
  if TYPE_CHECKING:
18
-
19
18
  from edc_visit_tracking.model_mixins import VisitTrackingCrfModelMixin
20
19
 
21
20
  from .models import BaseUuidModel
@@ -136,7 +135,6 @@ def raise_on_invalid_field_name(data: dict | models.Model, attrname: str) -> Non
136
135
  raise InvalidFieldName(f"{e} Got {data}")
137
136
  else:
138
137
  raise InvalidFieldName(f"Field name cannot be None. Got {data}")
139
- return None
140
138
 
141
139
 
142
140
  def model_exists_or_raise(
@@ -85,7 +85,5 @@ class ModelAdminDashboardMixin:
85
85
  if callable(super().view_on_site):
86
86
  url = super().view_on_site(obj)
87
87
  else:
88
- raise NoReverseMatch(
89
- f"{e}. See subject_dashboard_url_name for {repr(self)}."
90
- )
88
+ raise NoReverseMatch(f"{e}. See subject_dashboard_url_name for {self!r}.")
91
89
  return url
@@ -1,5 +1,3 @@
1
- from typing import Optional, Tuple
2
-
3
1
  from django.apps import apps as django_apps
4
2
  from django.contrib import admin
5
3
  from django.utils.functional import lazy
@@ -12,7 +10,7 @@ format_html_lazy = lazy(format_html, str)
12
10
 
13
11
 
14
12
  class SimpleHistoryAdmin(BaseSimpleHistoryAdmin):
15
- history_list_display: Tuple[str, ...] = ("dashboard", "change_message")
13
+ history_list_display: tuple[str, ...] = ("dashboard", "change_message")
16
14
  object_history_template = "edc_model_admin/admin/object_history.html"
17
15
  object_history_form_template = "edc_model_admin/admin/object_history_form.html"
18
16
 
@@ -20,7 +18,7 @@ class SimpleHistoryAdmin(BaseSimpleHistoryAdmin):
20
18
  save_as_continue = False
21
19
 
22
20
  @admin.display(description=_("Change Message"))
23
- def change_message(self, obj) -> Optional[str]:
21
+ def change_message(self, obj) -> str | None:
24
22
  log_entry_model_cls = django_apps.get_model("admin.logentry")
25
23
  log_entry = (
26
24
  log_entry_model_cls.objects.filter(
@@ -36,7 +34,7 @@ class SimpleHistoryAdmin(BaseSimpleHistoryAdmin):
36
34
  )
37
35
  return None
38
36
 
39
- def dashboard(self, obj) -> Optional[str]:
37
+ def dashboard(self, obj) -> str | None:
40
38
  if callable(self.view_on_site):
41
39
  return format_html_lazy(
42
40
  '<A href="{url}">Dashboard</A>',
@@ -44,16 +42,16 @@ class SimpleHistoryAdmin(BaseSimpleHistoryAdmin):
44
42
  )
45
43
  return None
46
44
 
47
- def get_list_display(self, request) -> Tuple[str, ...]:
45
+ def get_list_display(self, request) -> tuple[str, ...]:
48
46
  return tuple(super().get_list_display(request))
49
47
 
50
- def get_list_filter(self, request) -> Tuple[str, ...]:
48
+ def get_list_filter(self, request) -> tuple[str, ...]:
51
49
  return tuple(super().get_list_filter(request))
52
50
 
53
- def get_search_fields(self, request) -> Tuple[str, ...]:
51
+ def get_search_fields(self, request) -> tuple[str, ...]:
54
52
  return tuple(super().get_search_fields(request))
55
53
 
56
- def get_readonly_fields(self, request, obj=None) -> Tuple[str, ...]:
54
+ def get_readonly_fields(self, request, obj=None) -> tuple[str, ...]:
57
55
  return tuple(super().get_readonly_fields(request, obj=obj))
58
56
 
59
57
  def history_view_title(self, request, obj):
@@ -1,21 +1,19 @@
1
- from typing import Optional
2
-
3
1
  from django.http.response import HttpResponseRedirect
4
2
 
5
3
 
6
4
  class BaseModelAdminRedirectMixin:
7
5
  """Redirect on add, change, or delete."""
8
6
 
9
- def redirect_url(self, request, obj, post_url_continue=None) -> Optional[str]:
7
+ def redirect_url(self, request, obj, post_url_continue=None) -> str | None:
10
8
  pass
11
9
 
12
- def redirect_url_on_add(self, request, obj, post_url_continue=None) -> Optional[str]:
10
+ def redirect_url_on_add(self, request, obj, post_url_continue=None) -> str | None:
13
11
  return self.redirect_url(request, obj, post_url_continue=post_url_continue)
14
12
 
15
- def redirect_url_on_change(self, request, obj, post_url_continue=None) -> Optional[str]:
13
+ def redirect_url_on_change(self, request, obj, post_url_continue=None) -> str | None:
16
14
  return self.redirect_url(request, obj, post_url_continue=post_url_continue)
17
15
 
18
- def redirect_url_on_delete(self, request, obj_display, obj_id) -> Optional[str]:
16
+ def redirect_url_on_delete(self, request, obj_display, obj_id) -> str | None:
19
17
  pass
20
18
 
21
19
  def response_add(self, request, obj, post_url_continue=None):
@@ -1,4 +1,4 @@
1
- from typing import Any, Tuple
1
+ from typing import Any
2
2
 
3
3
  from django.core.handlers.wsgi import WSGIRequest
4
4
 
@@ -11,7 +11,7 @@ class ModelAdminLimitToSelectedForeignkeyError(Exception):
11
11
 
12
12
 
13
13
  class ModelAdminLimitToSelectedForeignkey:
14
- limit_fk_field_to_selected: list[Tuple[str, Any]] = None
14
+ limit_fk_field_to_selected: list[tuple[str, Any]] = None
15
15
 
16
16
  def formfield_for_foreignkey(self, db_field, request: WSGIRequest, **kwargs):
17
17
  db = kwargs.get("using")
@@ -29,11 +29,7 @@ class ModelAdminModelRedirectMixin(BaseModelAdminRedirectMixin):
29
29
  namespace = namespace or self.redirect_namespace
30
30
  return "{}?q={}".format(
31
31
  reverse(
32
- "{namespace}:{app_label}_{model_name}_changelist".format(
33
- namespace=namespace,
34
- app_label=self.redirect_app_label,
35
- model_name=self.redirect_model_name,
36
- )
32
+ f"{namespace}:{self.redirect_app_label}_{self.redirect_model_name}_changelist"
37
33
  ),
38
34
  self.search_value(obj) or "",
39
35
  )
@@ -41,9 +37,5 @@ class ModelAdminModelRedirectMixin(BaseModelAdminRedirectMixin):
41
37
  def redirect_url_on_delete(self, request, obj_display, obj_id, namespace=None):
42
38
  namespace = namespace or self.redirect_namespace
43
39
  return reverse(
44
- "{namespace}:{app_label}_{model_name}_changelist".format(
45
- namespace=namespace,
46
- app_label=self.redirect_app_label,
47
- model_name=self.redirect_model_name,
48
- )
40
+ f"{namespace}:{self.redirect_app_label}_{self.redirect_model_name}_changelist"
49
41
  )
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Optional
3
+ from typing import TYPE_CHECKING
4
4
  from urllib.parse import urlencode
5
5
 
6
6
  from django.apps import apps as django_apps
@@ -82,15 +82,15 @@ class ModelAdminNextUrlRedirectMixin(BaseModelAdminRedirectMixin):
82
82
  extra_context.update(show_cancel=self.show_cancel)
83
83
  return extra_context
84
84
 
85
- def redirect_url(self, request, obj, post_url_continue=None) -> Optional[str]:
85
+ def redirect_url(self, request, obj, post_url_continue=None) -> str | None:
86
86
  redirect_url = None
87
87
  if self.show_save_next and request.POST.get("_savenext"):
88
88
  redirect_url = self.get_savenext_redirect_url(request=request, obj=obj)
89
89
  if not redirect_url:
90
90
  redirect_url = self.get_next_redirect_url(request=request, obj=obj)
91
- elif self.show_cancel and request.POST.get("_cancel"):
92
- redirect_url = self.get_next_redirect_url(request=request, obj=obj)
93
- elif request.GET.get(self.next_querystring_attr):
91
+ elif (self.show_cancel and request.POST.get("_cancel")) or request.GET.get(
92
+ self.next_querystring_attr
93
+ ):
94
94
  redirect_url = self.get_next_redirect_url(request=request, obj=obj)
95
95
  if not redirect_url:
96
96
  redirect_url = super().redirect_url(
@@ -98,7 +98,7 @@ class ModelAdminNextUrlRedirectMixin(BaseModelAdminRedirectMixin):
98
98
  )
99
99
  return redirect_url
100
100
 
101
- def get_next_redirect_url(self, request=None, **kwargs) -> Optional[str]:
101
+ def get_next_redirect_url(self, request=None, **kwargs) -> str | None:
102
102
  """Returns a redirect url determined from the "next" attr
103
103
  in the querystring.
104
104
  """
@@ -120,7 +120,7 @@ class ModelAdminNextUrlRedirectMixin(BaseModelAdminRedirectMixin):
120
120
  redirect_url = f"{redirect_url}?q={options['q']}"
121
121
  return redirect_url
122
122
 
123
- def get_savenext_redirect_url(self, request=None, obj=None) -> Optional[str]:
123
+ def get_savenext_redirect_url(self, request=None, obj=None) -> str | None:
124
124
  """Returns a redirect_url for the next form in
125
125
  the visit schedule.
126
126
 
@@ -159,8 +159,7 @@ class ModelAdminNextUrlRedirectMixin(BaseModelAdminRedirectMixin):
159
159
  querystring = urlencode(querystring_opts)
160
160
  if redirect_url:
161
161
  return (
162
- f"{redirect_url}?{self.next_querystring_attr}="
163
- f"{next_querystring}&{querystring}"
162
+ f"{redirect_url}?{self.next_querystring_attr}={next_querystring}&{querystring}"
164
163
  )
165
164
  return None
166
165
 
edc_model_admin/utils.py CHANGED
@@ -41,15 +41,12 @@ def get_value_from_lookup_string(search_field_name: str = None, obj=None, reques
41
41
  if request:
42
42
  value = request.GET.get(field, "")
43
43
  break
44
- else:
45
- try:
46
- value = getattr(value or obj, field)
47
- except AttributeError as e:
48
- raise SearchTermLookupError(
49
- f"Invalid search term. `{search_field_name}`. Got {e}"
50
- )
51
- if value is None:
52
- break
44
+ try:
45
+ value = getattr(value or obj, field)
46
+ except AttributeError as e:
47
+ raise SearchTermLookupError(f"Invalid search term. `{search_field_name}`. Got {e}")
48
+ if value is None:
49
+ break
53
50
  return value
54
51
 
55
52
 
@@ -2,8 +2,8 @@ from .base_model_form_mixin import BaseModelFormMixin, BaseModelFormMixinError
2
2
  from .inline_model_form_mixin import InlineModelFormMixin, InlineModelFormMixinError
3
3
 
4
4
  __all__ = [
5
- "BaseModelFormMixinError",
6
5
  "BaseModelFormMixin",
6
+ "BaseModelFormMixinError",
7
7
  "InlineModelFormMixin",
8
8
  "InlineModelFormMixinError",
9
9
  ]
@@ -13,7 +13,7 @@ class BaseModelFormMixinError(Exception):
13
13
  pass
14
14
 
15
15
 
16
- __all__ = ["BaseModelFormMixinError", "BaseModelFormMixin"]
16
+ __all__ = ["BaseModelFormMixin", "BaseModelFormMixinError"]
17
17
 
18
18
 
19
19
  class BaseModelFormMixin(ReportDatetimeModelFormMixin):
@@ -24,7 +24,7 @@ if TYPE_CHECKING:
24
24
  pass
25
25
 
26
26
 
27
- __all__ = ["ModelToDataframeError", "ModelToDataframe"]
27
+ __all__ = ["ModelToDataframe", "ModelToDataframeError"]
28
28
 
29
29
 
30
30
  class ModelToDataframeError(Exception):
@@ -124,9 +124,7 @@ class ModelToDataframe:
124
124
  try:
125
125
  row_count = self.queryset.count()
126
126
  except OperationalError as e:
127
- if "The user specified as a definer" in str(e) and getattr(
128
- self.model_cls, "recreate_db_view"
129
- ):
127
+ if "The user specified as a definer" in str(e) and self.model_cls.recreate_db_view:
130
128
  self.model_cls.recreate_db_view()
131
129
  row_count = self.queryset.count()
132
130
  else:
@@ -52,14 +52,14 @@ class NavbarCollection:
52
52
  except KeyError:
53
53
  raise NavbarError(
54
54
  f"Navbar '{name}' does not exist. Expected one of "
55
- f"{list(self.registry.keys())}. See {repr(self)}."
55
+ f"{list(self.registry.keys())}. See {self!r}."
56
56
  )
57
57
  else:
58
58
  # does the navbar have items?
59
59
  if not navbar.navbar_items:
60
60
  raise NavbarError(
61
61
  f"Navbar '{navbar.name}' has no navbar_item. Expected "
62
- f"'{selected_item}'. See {repr(self)}"
62
+ f"'{selected_item}'. See {self!r}"
63
63
  )
64
64
  # does the selected item exist?
65
65
  if selected_item:
@@ -25,7 +25,7 @@ def edc_navbar_checks(app_configs, **kwargs) -> list[CheckMessage]:
25
25
  msg = (
26
26
  f"Invalid app_label in codename. Expected format "
27
27
  f"'<app_label>.<some_codename>'. Got {navbar_item.codename}. "
28
- f"See {repr(navbar_item)}."
28
+ f"See {navbar_item!r}."
29
29
  )
30
30
  errors.append(Error(msg, id="edc_navbar.E002"))
31
31
  try:
@@ -47,8 +47,7 @@ class MailingListManager:
47
47
  """Returns the api_url or None."""
48
48
  if not self._api_url:
49
49
  error_msg = (
50
- f"Email is enabled but API_URL is not set. "
51
- f"See settings.{self.api_url_attr}"
50
+ f"Email is enabled but API_URL is not set. See settings.{self.api_url_attr}"
52
51
  )
53
52
  try:
54
53
  self._api_url = getattr(settings, self.api_url_attr)
@@ -64,8 +63,7 @@ class MailingListManager:
64
63
  """Returns the api_key or None."""
65
64
  if not self._api_key:
66
65
  error_msg = (
67
- f"Email is enabled but API_KEY is not set. "
68
- f"See settings.{self.api_key_attr}"
66
+ f"Email is enabled but API_KEY is not set. See settings.{self.api_key_attr}"
69
67
  )
70
68
  try:
71
69
  self._api_key = getattr(settings, self.api_key_attr)
@@ -91,7 +89,7 @@ class MailingListManager:
91
89
  "subscribed": True,
92
90
  "address": user.email,
93
91
  "name": f"{user.first_name} {user.last_name}",
94
- "description": f'{user.userprofile.job_title or ""}',
92
+ "description": f"{user.userprofile.job_title or ''}",
95
93
  "upsert": "yes",
96
94
  },
97
95
  timeout=10,
@@ -124,7 +122,7 @@ class MailingListManager:
124
122
  except KeyError:
125
123
  sys.stdout.write(
126
124
  f"{action.title()} failed. Got response={response.status_code} "
127
- f"{str(response.json())}"
125
+ f"{response.json()!s}"
128
126
  )
129
127
  else:
130
128
  sys.stdout.write(
@@ -149,8 +147,7 @@ class MailingListManager:
149
147
  )
150
148
  if verbose:
151
149
  sys.stdout.write(
152
- f"Creating mailing list {self.address}. "
153
- f"Got response={response.status_code}.\n"
150
+ f"Creating mailing list {self.address}. Got response={response.status_code}.\n"
154
151
  )
155
152
  return response
156
153
 
@@ -9,7 +9,7 @@ class Command(BaseCommand):
9
9
  def handle(self, *args, **options):
10
10
  for notification_cls in site_notifications.registry.values():
11
11
  notification = notification_cls()
12
- print("")
12
+ print()
13
13
  print(notification.name)
14
14
  print(notification.display_name)
15
15
  print(f"email: {notification.email_to}")
@@ -1,3 +1,5 @@
1
1
  from .notification import Notification
2
- from .signals import manage_mailists_on_userprofile_m2m_changed # noqa
3
- from .signals import notification_on_post_create_historical_record # noqa
2
+ from .signals import (
3
+ manage_mailists_on_userprofile_m2m_changed,
4
+ notification_on_post_create_historical_record,
5
+ )
@@ -1,11 +1,9 @@
1
- from typing import Optional
2
-
3
1
  from .model_notification import ModelNotification
4
2
 
5
3
 
6
4
  class GradedEventNotification(ModelNotification):
7
- grade: Optional[int] = None
8
- model: Optional[str] = None
5
+ grade: int | None = None
6
+ model: str | None = None
9
7
  create_fields = ["ae_grade"]
10
8
  update_fields = ["ae_grade"]
11
9
 
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from django.apps import apps as django_apps
4
2
 
5
3
  from edc_model.stubs import BaseUuidHistoryModelStub
@@ -17,7 +15,7 @@ class ModelNotification(Notification):
17
15
  modification or deletion.
18
16
  """
19
17
 
20
- model: Optional[str] = None # label_lower format
18
+ model: str | None = None # label_lower format
21
19
 
22
20
  model_operations = [CREATE, UPDATE, DELETE]
23
21
 
@@ -1,5 +1,3 @@
1
- from typing import List, Optional
2
-
3
1
  from django.apps import apps as django_apps
4
2
  from django.conf import settings
5
3
  from django.core.exceptions import ObjectDoesNotExist
@@ -27,13 +25,13 @@ class Notification:
27
25
  """A generic class to generate a notification on a condition"""
28
26
 
29
27
  # app_name: str = None
30
- name: Optional[str] = None
31
- display_name: Optional[str] = None
28
+ name: str | None = None
29
+ display_name: str | None = None
32
30
 
33
31
  sms_client = Client
34
32
 
35
- email_from: List[str] = get_email_contacts("data_manager")
36
- email_to: Optional[List[str]] = None # usually a mailing list address
33
+ email_from: list[str] = get_email_contacts("data_manager")
34
+ email_to: list[str] | None = None # usually a mailing list address
37
35
  email_message_cls = EmailMessage
38
36
 
39
37
  email_body_template: str = (
@@ -50,7 +48,7 @@ class Notification:
50
48
  "Thanks."
51
49
  )
52
50
  email_subject_template: str = (
53
- "{test_subject_line}{protocol_name}: " "{display_name} " "for {subject_identifier}"
51
+ "{test_subject_line}{protocol_name}: {display_name} for {subject_identifier}"
54
52
  )
55
53
  email_footer_template: str = (
56
54
  "\n\n-----------------\n"
@@ -72,7 +70,7 @@ class Notification:
72
70
  sms_test_line: str = "TEST MESSAGE. NO ACTION REQUIRED - "
73
71
 
74
72
  def __init__(self) -> None:
75
- self._notification_enabled: Optional[bool] = None
73
+ self._notification_enabled: bool | None = None
76
74
  self._template_opts: dict = {}
77
75
  self.email_to = self.email_to or self.default_email_to
78
76
  self.test_message: bool = False
@@ -90,7 +88,7 @@ class Notification:
90
88
  return f"{self.name}: {self.display_name}"
91
89
 
92
90
  @property
93
- def default_email_to(self) -> List[str]:
91
+ def default_email_to(self) -> list[str]:
94
92
  return [f"{self.name}.{settings.APP_NAME}@mg.clinicedc.org"]
95
93
 
96
94
  def notify(
@@ -113,8 +111,8 @@ class Notification:
113
111
  * instance
114
112
  * user
115
113
  """
116
- email_sent: Optional[int] = None
117
- sms_sent: Optional[dict] = None
114
+ email_sent: int | None = None
115
+ sms_sent: dict | None = None
118
116
  use_email = use_email or get_email_enabled()
119
117
  use_sms = use_sms or getattr(settings, "TWILIO_ENABLED", False)
120
118
  if force_notify or self._notify_on_condition(**kwargs):
@@ -140,8 +138,7 @@ class Notification:
140
138
  """Returns the value of `notify_on_condition` or False."""
141
139
  if test_message:
142
140
  return True
143
- else:
144
- return self.enabled and self.notify_on_condition(**kwargs)
141
+ return self.enabled and self.notify_on_condition(**kwargs)
145
142
 
146
143
  def post_notification_actions(self, **kwargs):
147
144
  pass
@@ -217,8 +214,8 @@ class Notification:
217
214
 
218
215
  def send_email(
219
216
  self,
220
- fail_silently: Optional[bool] = None,
221
- email_to: List[str] = None,
217
+ fail_silently: bool | None = None,
218
+ email_to: list[str] = None,
222
219
  email_body_template: str = None,
223
220
  **kwargs,
224
221
  ) -> int:
@@ -232,8 +229,8 @@ class Notification:
232
229
 
233
230
  def send_sms(
234
231
  self,
235
- fail_silently: Optional[bool] = None,
236
- sms_recipient: Optional[str] = None,
232
+ fail_silently: bool | None = None,
233
+ sms_recipient: str | None = None,
237
234
  **kwargs,
238
235
  ) -> dict:
239
236
  status = {}
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import copy
4
4
  import sys
5
5
  from json.decoder import JSONDecodeError
6
- from typing import TYPE_CHECKING, Any, Type
6
+ from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from django.apps import apps as django_apps
9
9
  from django.conf import settings
@@ -39,7 +39,7 @@ class NotificationNotRegistered(Exception):
39
39
 
40
40
  class SiteNotifications:
41
41
  def __init__(self):
42
- self._registry: dict[str, Type[ActionItemNotification | Notification]] = {}
42
+ self._registry: dict[str, type[ActionItemNotification | Notification]] = {}
43
43
  self.loaded = False
44
44
  self.models = {}
45
45
 
@@ -55,7 +55,7 @@ class SiteNotifications:
55
55
  )
56
56
  return self._registry
57
57
 
58
- def get(self, name) -> Type[Notification]:
58
+ def get(self, name) -> type[Notification]:
59
59
  """Returns a Notification by name."""
60
60
  if not self.loaded:
61
61
  raise RegistryNotLoaded(self)
@@ -63,7 +63,7 @@ class SiteNotifications:
63
63
  raise NotificationNotRegistered(f"Notification not registered. Got '{name}'.")
64
64
  return self._registry.get(name)
65
65
 
66
- def register(self, notification_cls: Type[Notification] = None):
66
+ def register(self, notification_cls: type[Notification] = None):
67
67
  """Registers a Notification class unique by name."""
68
68
  self.loaded = True
69
69
  display_names = [n.display_name for n in self.registry.values()]
@@ -75,7 +75,7 @@ class SiteNotifications:
75
75
 
76
76
  models = getattr(notification_cls, "models", [])
77
77
  if not models and getattr(notification_cls, "model", None):
78
- models = [getattr(notification_cls, "model")]
78
+ models = [notification_cls.model]
79
79
  for model in models:
80
80
  try:
81
81
  if notification_cls.name not in [n.name for n in self.models[model]]:
@@ -104,7 +104,7 @@ class SiteNotifications:
104
104
 
105
105
  def update_notification_list(
106
106
  self, apps: django_apps = None, schema_editor=None, verbose=False
107
- ): # noqa
107
+ ):
108
108
  """Updates the notification model to ensure all registered
109
109
  notifications classes are listed.
110
110
 
@@ -174,7 +174,7 @@ class SiteNotifications:
174
174
  response = manager.create()
175
175
  except ConnectionError as e:
176
176
  sys.stdout.write(
177
- style.ERROR(f" * Failed to create mailing list {name}. " f"Got {e}\n")
177
+ style.ERROR(f" * Failed to create mailing list {name}. Got {e}\n")
178
178
  )
179
179
  else:
180
180
  if verbose:
edc_notification/stubs.py CHANGED
@@ -1,17 +1,17 @@
1
1
  from datetime import datetime
2
- from typing import List, Optional, Protocol
2
+ from typing import List, Protocol
3
3
 
4
4
 
5
5
  class BaseNotificationStub(Protocol):
6
- display_name: Optional[str]
6
+ display_name: str | None
7
7
  email_body_template: str
8
8
  email_footer_template: str
9
- email_from: List[str]
9
+ email_from: list[str]
10
10
  # email_message_cls = Type[EmailMessage]
11
11
  email_subject_template: str
12
12
  email_test_body_line: str
13
- email_to: Optional[List[str]]
14
- name: Optional[str]
13
+ email_to: list[str] | None
14
+ name: str | None
15
15
  # sms_client: Type[Client]
16
16
  sms_template: str
17
17
  sms_test_line: str
@@ -29,7 +29,7 @@ class BaseNotificationStub(Protocol):
29
29
  ) -> bool: ...
30
30
 
31
31
 
32
- class NotificationStub(BaseNotificationStub, Protocol): ... # noqa
32
+ class NotificationStub(BaseNotificationStub, Protocol): ...
33
33
 
34
34
 
35
35
  class NotificationModelStub(BaseNotificationStub, Protocol):
@@ -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.apps import apps as django_apps
6
6
 
@@ -38,7 +38,7 @@ def update_mailing_lists_in_m2m(
38
38
  and site_notifications.loaded
39
39
  and userprofile.email_notifications.through == sender
40
40
  ):
41
- notification_model_cls: Type[Notification] = django_apps.get_model(
41
+ notification_model_cls: type[Notification] = django_apps.get_model(
42
42
  "edc_notification.Notification"
43
43
  )
44
44
  for notification_obj in notification_model_cls.objects.filter(
@@ -14,11 +14,11 @@ class EndOfStudyAction(ActionWithNotification):
14
14
  name = END_OF_STUDY_ACTION
15
15
  display_name = "Submit End of Study Report"
16
16
  notification_display_name = "End of Study Report"
17
- parent_action_names = [
17
+ parent_action_names = (
18
18
  UNBLINDING_REVIEW_ACTION,
19
19
  DEATH_REPORT_ACTION,
20
20
  LTFU_ACTION,
21
- ]
21
+ )
22
22
  show_link_to_changelist = True
23
23
  priority = HIGH_PRIORITY
24
24
  singleton = True
@@ -56,7 +56,7 @@ class OffstudyModelMixin(UniqueSubjectIdentifierFieldMixin, models.Model):
56
56
  verbose_name="Please provide further details if possible",
57
57
  max_length=500,
58
58
  blank=True,
59
- null=True,
59
+ default="",
60
60
  )
61
61
 
62
62
  def __str__(self):
edc_offstudy/models.py CHANGED
@@ -25,4 +25,4 @@ class SubjectOffstudy(
25
25
  class Meta(BaseUuidModel.Meta):
26
26
  verbose_name = "Subject Offstudy"
27
27
  verbose_name_plural = "Subject Offstudy"
28
- indexes = ActionNoManagersModelMixin.Meta.indexes + BaseUuidModel.Meta.indexes
28
+ indexes = (*ActionNoManagersModelMixin.Meta.indexes, *BaseUuidModel.Meta.indexes)
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  from textwrap import fill
5
- from typing import TYPE_CHECKING, Type
5
+ from typing import TYPE_CHECKING
6
6
 
7
7
  from bs4 import BeautifulSoup
8
8
  from django.apps import apps as django_apps
@@ -131,7 +131,7 @@ class CrfPdfReport(Report):
131
131
  return f"{slugify(cls.get_verbose_name().lower())}s.pdf"
132
132
 
133
133
  @classmethod
134
- def get_model_cls(cls) -> Type[CrfModelMixin | UniqueSubjectIdentifierModelMixin]:
134
+ def get_model_cls(cls) -> type[CrfModelMixin | UniqueSubjectIdentifierModelMixin]:
135
135
  return django_apps.get_model(cls.model)
136
136
 
137
137
  @classmethod