clinicedc 2.0.4__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.4.dist-info → clinicedc-2.0.5.dist-info}/METADATA +41 -45
  2. {clinicedc-2.0.4.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 +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 -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.4.dist-info → clinicedc-2.0.5.dist-info}/WHEEL +0 -0
  478. {clinicedc-2.0.4.dist-info → clinicedc-2.0.5.dist-info}/licenses/LICENSE +0 -0
@@ -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.conf import settings
@@ -41,7 +41,7 @@ if TYPE_CHECKING:
41
41
  class RelatedVisitModel(SiteModelMixin, Base, BaseUuidModel):
42
42
  pass
43
43
 
44
- class OnScheduleLikeModel(OnScheduleModelMixin): ... # noqa
44
+ class OnScheduleLikeModel(OnScheduleModelMixin): ...
45
45
 
46
46
 
47
47
  class SubjectSchedule:
@@ -80,19 +80,19 @@ class SubjectSchedule:
80
80
  return f"{self.subject_identifier} {self.visit_schedule_name}.{self.schedule_name}"
81
81
 
82
82
  @property
83
- def onschedule_model_cls(self) -> Type[OnSchedule]:
83
+ def onschedule_model_cls(self) -> type[OnSchedule]:
84
84
  return django_apps.get_model(self.onschedule_model)
85
85
 
86
86
  @property
87
- def offschedule_model_cls(self) -> Type[OffSchedule]:
87
+ def offschedule_model_cls(self) -> type[OffSchedule]:
88
88
  return django_apps.get_model(self.offschedule_model)
89
89
 
90
90
  @property
91
- def history_model_cls(self) -> Type[SubjectScheduleHistory]:
91
+ def history_model_cls(self) -> type[SubjectScheduleHistory]:
92
92
  return django_apps.get_model(self.history_model)
93
93
 
94
94
  @property
95
- def appointment_model_cls(self) -> Type[Appointment]:
95
+ def appointment_model_cls(self) -> type[Appointment]:
96
96
  return django_apps.get_model(self.appointment_model)
97
97
 
98
98
  def put_on_schedule(
@@ -397,4 +397,3 @@ class SubjectSchedule:
397
397
  f"Got '{self.subject_identifier}' was taken "
398
398
  f"off this schedule on '{formatted_offschedule_datetime}'."
399
399
  )
400
- return None
@@ -86,7 +86,7 @@ def check_onschedule_exists_in_subject_schedule_history(app_configs, **kwargs) -
86
86
  for visit_schedule in site_visit_schedules.visit_schedules.values():
87
87
  for schedule in visit_schedule.schedules.values():
88
88
  try:
89
- onschedule_model_cls = getattr(schedule, "onschedule_model_cls")
89
+ onschedule_model_cls = schedule.onschedule_model_cls
90
90
  except LookupError:
91
91
  pass
92
92
  else:
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
13
13
  from .schedule import Schedule
14
14
  from .visit_schedule import VisitSchedule
15
15
 
16
- class OnScheduleLikeModel(OnScheduleModelMixin): ... # noqa
16
+ class OnScheduleLikeModel(OnScheduleModelMixin): ...
17
17
 
18
18
 
19
19
  class VisitScheduleViewMixin:
@@ -1,7 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Type
4
-
5
3
  from django.apps import apps as django_apps
6
4
  from django.db import models
7
5
 
@@ -38,9 +36,7 @@ class Crf:
38
36
  )
39
37
 
40
38
  def __repr__(self) -> str:
41
- return (
42
- f"{self.__class__.__name__}({self.show_order}, " f"{self.model}, {self.required})"
43
- )
39
+ return f"{self.__class__.__name__}({self.show_order}, {self.model}, {self.required})"
44
40
 
45
41
  def __str__(self) -> str:
46
42
  required = "Required" if self.required else ""
@@ -57,11 +53,11 @@ class Crf:
57
53
  except LookupError as e:
58
54
  raise CrfLookupError(e) from e
59
55
 
60
- def get_model_cls(self) -> Type[models.Model]:
56
+ def get_model_cls(self) -> type[models.Model]:
61
57
  return self.model_cls
62
58
 
63
59
  @property
64
- def model_cls(self) -> Type[models.Model]:
60
+ def model_cls(self) -> type[models.Model]:
65
61
  return django_apps.get_model(self.model)
66
62
 
67
63
  @property
@@ -20,7 +20,7 @@ class Requisition(Crf):
20
20
  if not self.panel.requisition_model:
21
21
  raise RequisitionError(
22
22
  f"Invalid requisition model. Got None. "
23
- f"See {repr(panel)}. "
23
+ f"See {panel!r}. "
24
24
  f"Was the panel referred to by this schedule's requisition "
25
25
  f"added to a lab profile and registered with site_labs?"
26
26
  )
@@ -67,5 +67,5 @@ class Requisition(Crf):
67
67
  for panel in panels:
68
68
  if panel.name not in lab_profile.panels:
69
69
  raise ScheduledRequisitionError(
70
- f"Panel does not exist in lab profiles. " f"Got {repr(panel)}"
70
+ f"Panel does not exist in lab profiles. Got {panel!r}"
71
71
  )
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
7
7
  from django.apps import apps as django_apps
8
8
 
9
9
  from edc_facility.utils import get_default_facility_name, get_facility
10
- from edc_utils import get_utcnow, to_utc
10
+ from edc_utils import get_utcnow, to_local
11
11
 
12
12
  from .crf_collection import CrfCollection
13
13
  from .forms_collection import FormsCollection
@@ -73,14 +73,14 @@ class VisitDate:
73
73
 
74
74
  @base.setter
75
75
  def base(self, dt: datetime = None):
76
- self._base = to_utc(dt)
76
+ self._base = to_local(dt)
77
77
  self._lower, self._upper = self._window_period.get_window(dt=self._base)
78
78
 
79
79
  @property
80
80
  def lower(self) -> datetime:
81
81
  if not self.base:
82
82
  raise BaseDatetimeNotSet(
83
- "Base datetime is None, set the base datetime " "before accessing attr lower"
83
+ "Base datetime is None, set the base datetime before accessing attr lower"
84
84
  )
85
85
  return self._lower
86
86
 
@@ -88,7 +88,7 @@ class VisitDate:
88
88
  def upper(self) -> datetime:
89
89
  if not self.base:
90
90
  raise BaseDatetimeNotSet(
91
- "Base datetime is None, set the base datetime " "before accessing attr upper"
91
+ "Base datetime is None, set the base datetime before accessing attr upper"
92
92
  )
93
93
  return self._upper
94
94
 
@@ -159,8 +159,7 @@ class Visit:
159
159
  self.grouping = grouping
160
160
  if not code or isinstance(code, int) or not re.match(self.code_regex, code):
161
161
  raise VisitCodeError(f"Invalid visit code. Got '{code}'")
162
- else:
163
- self.code = code # unique
162
+ self.code = code # unique
164
163
  self.dates = self.visit_date_cls(
165
164
  rlower=rlower,
166
165
  rupper=rupper,
@@ -272,7 +271,7 @@ class Visit:
272
271
 
273
272
  @timepoint_datetime.setter
274
273
  def timepoint_datetime(self, dt=None):
275
- self.dates.base = to_utc(dt)
274
+ self.dates.base = to_local(dt)
276
275
 
277
276
  def to_dict(self):
278
277
  return dict(
@@ -3,12 +3,12 @@ from __future__ import annotations
3
3
  from collections import namedtuple
4
4
  from datetime import datetime
5
5
  from decimal import Decimal
6
- from typing import Tuple
7
6
  from zoneinfo import ZoneInfo
8
7
 
9
8
  from dateutil.relativedelta import relativedelta
9
+ from django.conf import settings
10
10
 
11
- from edc_utils import to_utc
11
+ from edc_utils import to_local
12
12
 
13
13
 
14
14
  class WindowPeriod:
@@ -30,21 +30,21 @@ class WindowPeriod:
30
30
  if self.timepoint == base_timepoint:
31
31
  self.no_floor = True
32
32
 
33
- def get_window(self, dt=None) -> Tuple[datetime, datetime]:
34
- """Returns a tuple of the lower and upper datetimes in UTC."""
33
+ def get_window(self, dt=None) -> tuple[datetime, datetime]:
34
+ """Returns a tuple of the lower and upper datetimes in local time."""
35
35
 
36
36
  dt_floor = (
37
- to_utc(dt)
37
+ to_local(dt)
38
38
  if self.no_floor
39
39
  else dt.replace(hour=0, minute=0, second=0, microsecond=0).astimezone(
40
- ZoneInfo("UTC")
40
+ ZoneInfo(settings.TIME_ZONE)
41
41
  )
42
42
  )
43
43
  dt_ceil = (
44
- to_utc(dt)
44
+ to_local(dt)
45
45
  if self.no_ceil
46
46
  else dt.replace(hour=23, minute=59, second=59, microsecond=999999).astimezone(
47
- ZoneInfo("UTC")
47
+ ZoneInfo(settings.TIME_ZONE)
48
48
  )
49
49
  )
50
50
  window = namedtuple("Window", ["lower", "upper"])
@@ -22,9 +22,7 @@ class SchedulesCollection(OrderedCollection):
22
22
  if model:
23
23
  model = model.lower()
24
24
  for item in self.values():
25
- if item.onschedule_model == model:
26
- schedule = item
27
- elif item.offschedule_model == model:
25
+ if item.onschedule_model == model or item.offschedule_model == model:
28
26
  schedule = item
29
27
  if schedule:
30
28
  break
@@ -32,8 +30,7 @@ class SchedulesCollection(OrderedCollection):
32
30
  schedule = self.get(schedule_name)
33
31
  if not schedule:
34
32
  raise SchedulesCollectionError(
35
- f"Schedule does not exist. Using model={model}, "
36
- f"schedule_name={schedule_name}."
33
+ f"Schedule does not exist. Using model={model}, schedule_name={schedule_name}."
37
34
  )
38
35
  return schedule
39
36
 
@@ -1,5 +1,3 @@
1
- from typing import List
2
-
3
1
  from edc_action_item.action import Action
4
2
  from edc_action_item.action_with_notification import ActionWithNotification
5
3
  from edc_constants.constants import HIGH_PRIORITY, YES
@@ -9,16 +7,16 @@ from .constants import VISIT_MISSED_ACTION
9
7
 
10
8
 
11
9
  class VisitMissedAction(ActionWithNotification):
12
- name: str = VISIT_MISSED_ACTION
13
- display_name: str = "Submit Missed Visit"
14
- notification_display_name: str = " Submit Missed Visit"
15
- parent_action_names: List[str] = []
16
- show_link_to_changelist: bool = True
17
- priority: str = HIGH_PRIORITY
18
- loss_to_followup_action_name: str = LTFU_ACTION
10
+ name = VISIT_MISSED_ACTION
11
+ display_name = "Submit Missed Visit"
12
+ notification_display_name = " Submit Missed Visit"
13
+ parent_action_names = ()
14
+ show_link_to_changelist = True
15
+ priority = HIGH_PRIORITY
16
+ loss_to_followup_action_name = LTFU_ACTION
19
17
 
20
- reference_model: str = None # "inte_subject.subjectvisitmissed"
21
- admin_site_name: str = None # "inte_prn_admin"
18
+ reference_model = None # "inte_subject.subjectvisitmissed"
19
+ admin_site_name = None # "inte_prn_admin"
22
20
 
23
21
  def get_loss_to_followup_action_name(self) -> str:
24
22
  return self.loss_to_followup_action_name
@@ -26,8 +24,8 @@ class VisitMissedAction(ActionWithNotification):
26
24
  def is_ltfu(self) -> bool:
27
25
  return self.reference_obj.ltfu == YES
28
26
 
29
- def get_next_actions(self) -> List[Action]:
30
- next_actions: List[Action] = []
27
+ def get_next_actions(self) -> list[Action]:
28
+ next_actions: list[Action] = []
31
29
  next_actions = self.append_to_next_if_required(
32
30
  next_actions=next_actions,
33
31
  action_name=self.get_loss_to_followup_action_name(),
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from copy import deepcopy
4
4
  from datetime import datetime
5
- from typing import TYPE_CHECKING, Any, List, Optional
5
+ from typing import TYPE_CHECKING, Any
6
6
  from zoneinfo import ZoneInfo
7
7
 
8
8
  from django import forms
@@ -144,14 +144,14 @@ class VisitFormValidator(WindowPeriodFormValidatorMixin, FormValidator):
144
144
  visit_schedule_name=self.instance.visit_schedule_name,
145
145
  schedule_name=self.instance.schedule_name,
146
146
  )
147
- if getattr(self.instance, "id"):
147
+ if self.instance.id:
148
148
  qs = qs.exclude(id=self.instance.id)
149
149
  if qs.count() > 1:
150
150
  raise self.raise_validation_error(
151
151
  {"report_datetime": "Visit report already exist for this date (M)"},
152
152
  INVALID_ERROR,
153
153
  )
154
- elif qs.count() == 1:
154
+ if qs.count() == 1:
155
155
  raise self.raise_validation_error(
156
156
  {
157
157
  "report_datetime": (
@@ -284,8 +284,8 @@ class VisitFormValidator(WindowPeriodFormValidatorMixin, FormValidator):
284
284
  def metadata_exists_for(
285
285
  self,
286
286
  entry_status: str = None,
287
- filter_models: Optional[List[str]] = None,
288
- exclude_models: Optional[List[str]] = None,
287
+ filter_models: list[str] | None = None,
288
+ exclude_models: list[str] | None = None,
289
289
  ) -> int:
290
290
  """Returns True if metadata exists for this visit for
291
291
  the given entry_status.
@@ -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.conf import settings
6
6
  from django.contrib.admin.utils import NotRelationField, get_model_from_relation
@@ -57,7 +57,7 @@ class VisitMethodsModelMixin(models.Model):
57
57
  return related_visit_field_cls
58
58
 
59
59
  @classmethod
60
- def related_visit_model_cls(cls) -> Type[VisitModelMixin]:
60
+ def related_visit_model_cls(cls) -> type[VisitModelMixin]:
61
61
  """Returns the 'model' class of the related visit foreign
62
62
  key attribute.
63
63
  """
@@ -109,8 +109,7 @@ class VisitMethodsModelMixin(models.Model):
109
109
  "Perhaps catch this in the form."
110
110
  )
111
111
  break
112
- else:
113
- related_model = None
112
+ related_model = None
114
113
  if not related_model:
115
114
  raise ImproperlyConfigured(
116
115
  f"Model is missing a FK to a related visit model. See {self.__class__}."
@@ -55,7 +55,7 @@ class VisitTrackingCrfModelMixin(VisitMethodsModelMixin, CrfScheduleModelMixin,
55
55
  class Meta:
56
56
  abstract = True
57
57
  # assuming subject_visit
58
- indexes = [
58
+ indexes = (
59
59
  models.Index(fields=["subject_visit", "site", "id"]),
60
60
  models.Index(fields=["subject_visit", "report_datetime"]),
61
- ]
61
+ )
@@ -58,8 +58,7 @@ class SubjectVisitMissedModelMixin(models.Model):
58
58
 
59
59
  contact_attempts_count = models.IntegerField(
60
60
  verbose_name=_(
61
- "Number of attempts made to contact participant"
62
- "since the expected appointment date"
61
+ "Number of attempts made to contact participantsince the expected appointment date"
63
62
  ),
64
63
  validators=[MinValueValidator(1)],
65
64
  help_text=_(
@@ -112,4 +111,4 @@ class SubjectVisitMissedModelMixin(models.Model):
112
111
 
113
112
  class Meta:
114
113
  abstract = True
115
- indexes = [models.Index(fields=["action_identifier", "site", "id"])]
114
+ indexes = (models.Index(fields=["action_identifier", "site", "id"]),)
@@ -31,7 +31,7 @@ def get_related_visit_model_attr(model_cls) -> str:
31
31
  f"More than one field is related to the visit model. See {model_cls}. "
32
32
  f"Got {attrs}. "
33
33
  )
34
- elif len(attrs) == 0:
34
+ if len(attrs) == 0:
35
35
  raise RelatedVisitFieldError(
36
36
  f"{model_cls} has no related visit model. "
37
37
  f"Expected the related visit model to be an instance "
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Self, Type
3
+ from typing import TYPE_CHECKING, Any, Self
4
4
 
5
5
  from django.db import models
6
6
 
@@ -24,7 +24,7 @@ class PreviousVisitModelMixin(models.Model):
24
24
  * If the visit is the first in the sequence, save() is allowed.
25
25
  """
26
26
 
27
- visit_sequence_cls: Type[VisitSequence] = VisitSequence
27
+ visit_sequence_cls: type[VisitSequence] = VisitSequence
28
28
 
29
29
  def save(self, *args, **kwargs) -> None:
30
30
  self.validate_visit_sequence()
@@ -116,7 +116,7 @@ class VisitModelMixin(
116
116
  "report_datetime", # implies one visit per day!
117
117
  ),
118
118
  )
119
- indexes = [
119
+ indexes = (
120
120
  models.Index(
121
121
  fields=[
122
122
  "subject_identifier",
@@ -126,5 +126,5 @@ class VisitModelMixin(
126
126
  "visit_code_sequence",
127
127
  "report_datetime",
128
128
  ]
129
- )
130
- ]
129
+ ),
130
+ )
@@ -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
  from uuid import UUID
5
5
 
6
6
  from edc_consent.fieldsets import REQUIRES_CONSENT_FIELDS
@@ -42,8 +42,10 @@ class CrfModelAdminMixin:
42
42
  "visit_code",
43
43
  "visit_reason",
44
44
  )
45
- return fields_first + tuple(
46
- f for f in list_display if f not in fields_first + ("__str__",)
45
+ return (
46
+ *fields_first,
47
+ *[f for f in list_display if f not in fields_first],
48
+ "__str__",
47
49
  )
48
50
 
49
51
  def get_search_fields(self, request: WSGIRequest) -> tuple[str, ...]:
@@ -76,7 +78,7 @@ class CrfModelAdminMixin:
76
78
  return self.model.related_visit_model_attr()
77
79
 
78
80
  @property
79
- def related_visit_model_cls(self) -> Type[VisitModelMixin]:
81
+ def related_visit_model_cls(self) -> type[VisitModelMixin]:
80
82
  return self.model.related_visit_model_cls()
81
83
 
82
84
  def related_visit(self, request: WSGIRequest, obj=None) -> VisitModelMixin | None:
@@ -56,7 +56,7 @@ class VisitModelAdminMixin(DocumentStatusModelAdminMixin):
56
56
  audit_fieldset_tuple,
57
57
  )
58
58
 
59
- radio_fields = {
59
+ radio_fields = { # noqa: RUF012
60
60
  "reason": admin.VERTICAL,
61
61
  "reason_unscheduled": admin.VERTICAL,
62
62
  "reason_missed": admin.VERTICAL,
@@ -79,11 +79,10 @@ class VisitModelAdminMixin(DocumentStatusModelAdminMixin):
79
79
  def visit_reason(obj=None) -> str:
80
80
  if obj.reason != UNSCHEDULED:
81
81
  visit_reason = obj.get_reason_display()
82
+ elif obj.reason_unscheduled == OTHER:
83
+ visit_reason = obj.reason_unscheduled_other
82
84
  else:
83
- if obj.reason_unscheduled == OTHER:
84
- visit_reason = obj.reason_unscheduled_other
85
- else:
86
- visit_reason = obj.get_reason_unscheduled_display()
85
+ visit_reason = obj.get_reason_unscheduled_display()
87
86
  return visit_reason
88
87
 
89
88
  @staticmethod
@@ -114,7 +113,7 @@ class VisitModelAdminMixin(DocumentStatusModelAdminMixin):
114
113
  "status",
115
114
  "scheduled_data",
116
115
  )
117
- return custom_fields + tuple(f for f in list_display if f not in custom_fields)
116
+ return *custom_fields, *tuple(f for f in list_display if f not in custom_fields)
118
117
 
119
118
  def get_list_filter(self, request: WSGIRequest) -> tuple[str, ...]:
120
119
  list_filter = super().get_list_filter(request)
@@ -125,7 +124,7 @@ class VisitModelAdminMixin(DocumentStatusModelAdminMixin):
125
124
  "reason",
126
125
  "require_crfs",
127
126
  )
128
- return custom_fields + tuple(f for f in list_filter if f not in custom_fields)
127
+ return *custom_fields, *tuple(f for f in list_filter if f not in custom_fields)
129
128
 
130
129
  def get_readonly_fields(self, request, obj=None) -> tuple[str, ...]:
131
130
  readonly_fields = super().get_readonly_fields(request, obj=obj)
@@ -89,11 +89,10 @@ class VisitTrackingCrfModelFormMixin:
89
89
  if not self.related_visit:
90
90
  if self.related_visit_model_attr in self.cleaned_data:
91
91
  raise forms.ValidationError({self.related_visit_model_attr: ""})
92
- else:
93
- raise forms.ValidationError(
94
- f"Field `{self.related_visit_model_attr}` is required (1)."
95
- )
96
- elif self.cleaned_data.get(self.report_datetime_field_attr):
92
+ raise forms.ValidationError(
93
+ f"Field `{self.related_visit_model_attr}` is required (1)."
94
+ )
95
+ if self.cleaned_data.get(self.report_datetime_field_attr):
97
96
  try:
98
97
  self.crf_date_validator_cls(
99
98
  report_datetime_allowance=self.report_datetime_allowance,
@@ -31,11 +31,11 @@ class VisitTrackingModelFormMixin(SiteModelFormMixin):
31
31
  raise forms.ValidationError(
32
32
  {
33
33
  "reason": (
34
- "Invalid. Appointment is missed. " "Expected visit to be missed also."
34
+ "Invalid. Appointment is missed. Expected visit to be missed also."
35
35
  )
36
36
  }
37
37
  )
38
- elif (
38
+ if (
39
39
  cleaned_data.get("reason")
40
40
  and self.appointment.appt_timing == MISSED_APPT
41
41
  and cleaned_data.get("reason") != MISSED_VISIT
@@ -43,8 +43,7 @@ class VisitTrackingModelFormMixin(SiteModelFormMixin):
43
43
  raise forms.ValidationError(
44
44
  {
45
45
  "reason": (
46
- "Invalid. Appointment is not missed. "
47
- "Did not expected a missed visit."
46
+ "Invalid. Appointment is not missed. Did not expected a missed visit."
48
47
  )
49
48
  }
50
49
  )
@@ -53,4 +53,4 @@ class SubjectVisit(
53
53
  history = HistoricalRecords()
54
54
 
55
55
  class Meta(VisitModelMixin.Meta, BaseUuidModel.Meta):
56
- indexes = VisitModelMixin.Meta.indexes + BaseUuidModel.Meta.indexes
56
+ indexes = (*VisitModelMixin.Meta.indexes, *BaseUuidModel.Meta.indexes)
@@ -27,4 +27,4 @@ class SubjectVisitMissed(
27
27
  class Meta(CrfModelMixin.Meta, BaseUuidModel.Meta):
28
28
  verbose_name = _("Missed Visit Report")
29
29
  verbose_name_plural = _("Missed Visit Report")
30
- indexes = CrfModelMixin.Meta.indexes + BaseUuidModel.Meta.indexes
30
+ indexes = (*CrfModelMixin.Meta.indexes, *BaseUuidModel.Meta.indexes)
@@ -1,5 +1,5 @@
1
1
  from datetime import datetime
2
- from typing import Protocol, TypeVar, Union
2
+ from typing import Protocol, TypeVar
3
3
 
4
4
  from django.db import models
5
5
 
@@ -7,15 +7,15 @@ from edc_model.stubs import ModelMetaStub
7
7
 
8
8
 
9
9
  class RelatedVisitModelStub(Protocol):
10
- report_datetime: Union[datetime, models.DateTimeField]
11
- subject_identifier: Union[str, models.CharField]
10
+ report_datetime: datetime | models.DateTimeField
11
+ subject_identifier: str | models.CharField
12
12
  reason: str
13
13
  reason_unscheduled: str
14
14
  reason_unscheduled_other: str
15
- visit_code: Union[str, models.CharField]
16
- visit_code_sequence: Union[int, models.IntegerField]
17
- visit_schedule: Union[str, models.CharField]
18
- schedule: Union[str, models.CharField]
15
+ visit_code: str | models.CharField
16
+ visit_code_sequence: int | models.IntegerField
17
+ visit_schedule: str | models.CharField
18
+ schedule: str | models.CharField
19
19
  study_status: str
20
20
  require_crfs: bool
21
21
 
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from datetime import datetime
4
- from typing import Any, Generic, Protocol, Tuple, Type, TypeVar
4
+ from typing import Any, Generic, Protocol, Tuple, TypeVar
5
5
  from uuid import UUID
6
6
 
7
7
  from django.contrib.contenttypes.fields import GenericForeignKey
@@ -24,23 +24,23 @@ _Self = TypeVar("_Self", bound=Model)
24
24
 
25
25
  class ModelBase(type):
26
26
  @property
27
- def objects(cls: type[_Self]) -> BaseManager[_Self]: ... # noqa
27
+ def objects(cls: type[_Self]) -> BaseManager[_Self]: ...
28
28
 
29
29
  @property
30
- def _default_manager(cls: type[_Self]) -> BaseManager[_Self]: ... # noqa
30
+ def _default_manager(cls: type[_Self]) -> BaseManager[_Self]: ...
31
31
 
32
32
  @property
33
- def _base_manager(cls: type[_Self]) -> BaseManager[_Self]: ... # noqa
33
+ def _base_manager(cls: type[_Self]) -> BaseManager[_Self]: ...
34
34
 
35
35
 
36
36
  class Options(Generic[_M]):
37
- def label_lower(self) -> str: ... # noqa
37
+ def label_lower(self) -> str: ...
38
38
 
39
39
  def fields(self) -> Tuple[Field]: ... # noqa
40
40
 
41
41
  def get_fields(
42
- self, include_parents: bool = ..., include_hidden: bool = ... # noqa
43
- ) -> list[Field | ForeignObjectRel | GenericForeignKey]: ... # noqa
42
+ self, include_parents: bool = ..., include_hidden: bool = ...
43
+ ) -> list[Field | ForeignObjectRel | GenericForeignKey]: ...
44
44
 
45
45
 
46
46
  class SiteFieldsProtocol(Protocol):
@@ -49,11 +49,11 @@ class SiteFieldsProtocol(Protocol):
49
49
 
50
50
 
51
51
  class RelatedVisitProtocol(VisitScheduleFieldsProtocol, Protocol):
52
- metadata_cls: Type[Metadata]
53
- metadata_destroyer_cls: Type[Destroyer]
54
- metadata_rule_evaluator_cls: Type[MetadataRuleEvaluator]
52
+ metadata_cls: type[Metadata]
53
+ metadata_destroyer_cls: type[Destroyer]
54
+ metadata_rule_evaluator_cls: type[MetadataRuleEvaluator]
55
55
 
56
- class Meta: ... # noqa
56
+ class Meta: ...
57
57
 
58
58
  _meta: Options[Any]
59
59