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
@@ -86,26 +86,22 @@ class AppointmentCreator:
86
86
  try:
87
87
  if is_naive(timepoint_datetime):
88
88
  raise ValueError(
89
- f"Naive datetime not allowed. {repr(self)}. " f"Got {timepoint_datetime}"
89
+ f"Naive datetime not allowed. {self!r}. Got {timepoint_datetime}"
90
90
  )
91
- else:
92
- self.timepoint_datetime = timepoint_datetime
91
+ self.timepoint_datetime = timepoint_datetime
93
92
  except AttributeError:
94
93
  raise AppointmentCreatorError(
95
- f"Expected 'timepoint_datetime'. Got None. {repr(self)}."
94
+ f"Expected 'timepoint_datetime'. Got None. {self!r}."
96
95
  )
97
96
  # suggested_datetime (defaults to timepoint_datetime)
98
97
  # If provided, the rules for window period/rdelta relative
99
98
  # to timepoint_datetime still apply.
100
99
  if suggested_datetime and is_naive(suggested_datetime):
101
- raise ValueError(
102
- f"Naive datetime not allowed. {repr(self)}. " f"Got {suggested_datetime}"
103
- )
104
- else:
105
- self.suggested_datetime = suggested_datetime or self.timepoint_datetime
100
+ raise ValueError(f"Naive datetime not allowed. {self!r}. Got {suggested_datetime}")
101
+ self.suggested_datetime = suggested_datetime or self.timepoint_datetime
106
102
  self.facility = facility or visit.facility
107
103
  if not self.facility:
108
- raise AppointmentCreatorError(f"facility_name not defined. See {repr(visit)}")
104
+ raise AppointmentCreatorError(f"facility_name not defined. See {visit!r}")
109
105
  self.get_appointment()
110
106
 
111
107
  def __repr__(self):
@@ -217,7 +213,7 @@ class AppointmentCreator:
217
213
  )
218
214
  except FacilityError as e:
219
215
  raise CreateAppointmentDateError(
220
- f"{e} Visit={repr(self.visit)}. "
216
+ f"{e} Visit={self.visit!r}. "
221
217
  f"Try setting 'best_effort_available_datetime=True' on facility."
222
218
  )
223
219
  else:
@@ -84,7 +84,7 @@ class AppointmentsCreator:
84
84
  facility = get_facility(visit.facility_name)
85
85
  except FacilityError as e:
86
86
  raise CreateAppointmentError(
87
- f"{e} See {repr(visit)}. Got facility_name={visit.facility_name}"
87
+ f"{e} See {visit!r}. Got facility_name={visit.facility_name}"
88
88
  )
89
89
  appointment = self.update_or_create_appointment(
90
90
  visit=visit,
@@ -100,7 +100,7 @@ class UnscheduledAppointmentCreator:
100
100
  f"schedule_name='{self.schedule_name}',"
101
101
  f"visit_code='{self.visit_code}'" + "}"
102
102
  )
103
- elif not value.allow_unscheduled:
103
+ if not value.allow_unscheduled:
104
104
  raise UnscheduledAppointmentNotAllowed(
105
105
  f"Not allowed. Visit {self.visit_code} is not configured for "
106
106
  "unscheduled appointments."
@@ -241,18 +241,17 @@ class UnscheduledAppointmentCreator:
241
241
  "visit form is not submitted. "
242
242
  f"Got appointment '{self.visit_code}.0'."
243
243
  )
244
- else:
245
- if self._parent_appointment.appt_status not in [
246
- COMPLETE_APPT,
247
- INCOMPLETE_APPT,
248
- ]:
249
- raise InvalidParentAppointmentStatusError(
250
- "Unable to create unscheduled appointment. An unscheduled "
251
- "appointment cannot be created if the parent appointment "
252
- "is 'new' or 'in progress'. Got appointment "
253
- f"'{self.visit_code}' is "
254
- f"{self._parent_appointment.get_appt_status_display().lower()}."
255
- )
244
+ if self._parent_appointment.appt_status not in [
245
+ COMPLETE_APPT,
246
+ INCOMPLETE_APPT,
247
+ ]:
248
+ raise InvalidParentAppointmentStatusError(
249
+ "Unable to create unscheduled appointment. An unscheduled "
250
+ "appointment cannot be created if the parent appointment "
251
+ "is 'new' or 'in progress'. Got appointment "
252
+ f"'{self.visit_code}' is "
253
+ f"{self._parent_appointment.get_appt_status_display().lower()}."
254
+ )
256
255
 
257
256
  return self._parent_appointment
258
257
 
@@ -24,7 +24,6 @@ class NextAppointmentCrfFormValidatorMixin(FormValidator):
24
24
  super().__init__(**kwargs)
25
25
 
26
26
  def clean(self):
27
-
28
27
  self.required_if(
29
28
  NO,
30
29
  field="offschedule_today",
@@ -119,8 +118,7 @@ class NextAppointmentCrfFormValidatorMixin(FormValidator):
119
118
  except ValidationError as e:
120
119
  if e.message_dict.get("appt_datetime"):
121
120
  raise ValidationError({"appt_date": e.message_dict["appt_datetime"]})
122
- else:
123
- raise
121
+ raise
124
122
 
125
123
  @property
126
124
  def appt_datetime(self) -> datetime:
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
6
6
  from dateutil.relativedelta import relativedelta
7
7
  from django.utils.translation import gettext as _
8
8
 
9
- from edc_utils import formatted_date, to_utc
9
+ from edc_utils import formatted_date
10
10
  from edc_utils.date import floor_secs, to_local
11
11
  from edc_visit_schedule.exceptions import (
12
12
  ScheduledVisitWindowError,
@@ -38,7 +38,7 @@ class WindowPeriodFormValidatorMixin:
38
38
  and appointment.visit_code_sequence > 0
39
39
  and appointment.next
40
40
  and appointment.next.appt_status in [INCOMPLETE_APPT, COMPLETE_APPT]
41
- and to_utc(proposed_appt_datetime) < appointment.next.appt_datetime
41
+ and to_local(proposed_appt_datetime) < to_local(appointment.next.appt_datetime)
42
42
  ):
43
43
  value = True
44
44
  return value
@@ -50,14 +50,15 @@ class WindowPeriodFormValidatorMixin:
50
50
  form_field: str,
51
51
  ):
52
52
  if proposed_appt_datetime:
53
- proposed_appt_datetime = to_utc(proposed_appt_datetime)
54
53
  try:
55
54
  appointment.schedule.datetime_in_window(
56
- timepoint_datetime=appointment.timepoint_datetime,
57
- dt=proposed_appt_datetime,
55
+ timepoint_datetime=to_local(appointment.timepoint_datetime),
56
+ dt=to_local(proposed_appt_datetime),
58
57
  visit_code=appointment.visit_code,
59
58
  visit_code_sequence=appointment.visit_code_sequence,
60
- baseline_timepoint_datetime=self.baseline_timepoint_datetime(appointment),
59
+ baseline_timepoint_datetime=to_local(
60
+ self.baseline_timepoint_datetime(appointment)
61
+ ),
61
62
  )
62
63
  except UnScheduledVisitWindowError:
63
64
  if not self.ignore_window_period_for_unscheduled(
@@ -236,10 +236,8 @@ class AppointmentFormValidator(
236
236
  self.raise_validation_error(
237
237
  {
238
238
  "appt_datetime": (
239
- (
240
- "Invalid. Cannot be before consent date. "
241
- f"Got consented on {formatted_date}"
242
- )
239
+ "Invalid. Cannot be before consent date. "
240
+ f"Got consented on {formatted_date}"
243
241
  )
244
242
  },
245
243
  INVALID_APPT_DATE,
@@ -1,5 +1,5 @@
1
1
  from datetime import datetime
2
- from typing import Any, Optional
2
+ from typing import Any
3
3
  from zoneinfo import ZoneInfo
4
4
 
5
5
  from django.urls import reverse
@@ -16,7 +16,7 @@ def get_appointment_url(appointment_pk: str, subject_identifier: str) -> str:
16
16
  args=(appointment_pk,),
17
17
  )
18
18
  rev_url = (
19
- f'{rev_url}?next={url_names.get("subject_dashboard_url")},'
19
+ f"{rev_url}?next={url_names.get('subject_dashboard_url')},"
20
20
  f"subject_identifier"
21
21
  f"&subject_identifier={subject_identifier}"
22
22
  )
@@ -27,7 +27,7 @@ def validate_appt_datetime_unique(
27
27
  form_validator: Any,
28
28
  appointment: Any,
29
29
  appt_datetime: datetime,
30
- form_field: Optional[str] = None,
30
+ form_field: str | None = None,
31
31
  ):
32
32
  """Assert one visit report per day"""
33
33
  if appt_datetime:
@@ -45,7 +45,7 @@ def validate_appt_datetime_unique(
45
45
  {form_field: "An appointment already exists for this date (M)"},
46
46
  INVALID_ERROR,
47
47
  )
48
- elif other_appts.count() == 1:
48
+ if other_appts.count() == 1:
49
49
  if appointment and other_appts[0].id != appointment.id:
50
50
  appointment_url = get_appointment_url(
51
51
  (
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Dict
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from django.db import models, transaction
6
6
  from django.db.models.deletion import ProtectedError
@@ -43,7 +43,7 @@ class AppointmentManager(models.Manager):
43
43
  )
44
44
 
45
45
  @staticmethod
46
- def get_query_options(**kwargs) -> Dict[Any]:
46
+ def get_query_options(**kwargs) -> dict[Any]:
47
47
  """Returns a dictionary or options.
48
48
 
49
49
  Dictionary is based on the appointment instance or everything
@@ -53,7 +53,7 @@ class AppointmentManager(models.Manager):
53
53
  schedule_name = kwargs.get("schedule_name")
54
54
  subject_identifier = kwargs.get("subject_identifier")
55
55
  visit_schedule_name = kwargs.get("visit_schedule_name")
56
- options: Dict[Any] = dict(visit_code_sequence=0)
56
+ options: dict[Any] = dict(visit_code_sequence=0)
57
57
  try:
58
58
  options.update(
59
59
  subject_identifier=appointment.subject_identifier,
@@ -76,7 +76,7 @@ class AppointmentManager(models.Manager):
76
76
  f"Expected visit_schedule_name for schedule_name "
77
77
  f"'{schedule_name}'. Got {visit_schedule_name}"
78
78
  )
79
- elif schedule_name:
79
+ if schedule_name:
80
80
  options.update(schedule_name=schedule_name)
81
81
  return options
82
82
 
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Type, TypeVar
3
+ from typing import TYPE_CHECKING, TypeVar
4
4
 
5
5
  from django.core.exceptions import ObjectDoesNotExist
6
6
  from django.db import models
@@ -48,7 +48,7 @@ class AppointmentMethodsModelMixin(models.Model):
48
48
  return get_related_visit_model_attr(cls)
49
49
 
50
50
  @classmethod
51
- def related_visit_model_cls(cls: Appointment) -> Type[VisitModel]:
51
+ def related_visit_model_cls(cls: Appointment) -> type[VisitModel]:
52
52
  return getattr(cls, cls.related_visit_model_attr()).related.related_model
53
53
 
54
54
  @property
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import uuid
4
4
  from datetime import datetime
5
- from typing import TYPE_CHECKING, Union
5
+ from typing import TYPE_CHECKING
6
6
  from uuid import UUID
7
7
 
8
8
  from dateutil.relativedelta import relativedelta
@@ -67,7 +67,7 @@ class AppointmentModelMixin(
67
67
  return f"{self.subject_identifier} {self.visit_code}.{self.visit_code_sequence}"
68
68
 
69
69
  def save(self: Appointment, *args, **kwargs):
70
- if not kwargs.get("update_fields", None):
70
+ if not kwargs.get("update_fields"):
71
71
  if self.id and is_baseline(instance=self):
72
72
  visit_schedule = site_visit_schedules.get_visit_schedule(
73
73
  self.visit_schedule_name
@@ -116,7 +116,7 @@ class AppointmentModelMixin(
116
116
  )
117
117
 
118
118
  @property
119
- def str_pk(self: Appointment) -> Union[str, uuid.UUID]:
119
+ def str_pk(self: Appointment) -> str | uuid.UUID:
120
120
  if isinstance(self.id, UUID):
121
121
  return str(self.pk)
122
122
  return self.pk
@@ -193,7 +193,7 @@ class AppointmentModelMixin(
193
193
  name="unique_%(app_label)s_%(class)s_200",
194
194
  ),
195
195
  ]
196
- indexes = [
196
+ indexes = (
197
197
  models.Index(fields=["appt_datetime"]),
198
198
  models.Index(fields=["appt_status"]),
199
199
  models.Index(fields=["timepoint", "visit_code_sequence"]),
@@ -217,4 +217,4 @@ class AppointmentModelMixin(
217
217
  "visit_code_sequence",
218
218
  ]
219
219
  ),
220
- ]
220
+ )
@@ -20,7 +20,7 @@ class WindowPeriodModelMixin(models.Model):
20
20
  window_period_checks_enabled: bool = True
21
21
 
22
22
  def save(self: Any, *args, **kwargs) -> None:
23
- if not kwargs.get("update_fields", None):
23
+ if not kwargs.get("update_fields"):
24
24
  self.raise_on_appt_datetime_not_in_window()
25
25
  super().save(*args, **kwargs)
26
26
 
@@ -26,4 +26,4 @@ class Appointment(AppointmentModelMixin, SiteModelMixin, BaseUuidModel):
26
26
  natural_key.dependencies = ["sites.Site"] # type: ignore
27
27
 
28
28
  class Meta(AppointmentModelMixin.Meta, SiteModelMixin.Meta, BaseUuidModel.Meta):
29
- indexes = AppointmentModelMixin.Meta.indexes + BaseUuidModel.Meta.indexes
29
+ indexes = (*AppointmentModelMixin.Meta.indexes, *BaseUuidModel.Meta.indexes)
@@ -177,12 +177,11 @@ class SkipAppointments:
177
177
  raise AppointmentAlreadyStarted(
178
178
  f"Unable update as next. Appointment already started. Got {appointment}."
179
179
  )
180
- else:
181
- appointment.appt_status = NEW_APPT
182
- appointment.appt_datetime = self.next_appt_datetime
183
- appointment.comment = ""
184
- self.validate_appointment_as_next(appointment)
185
- appointment.save(update_fields=["appt_status", "appt_datetime", "comment"])
180
+ appointment.appt_status = NEW_APPT
181
+ appointment.appt_datetime = self.next_appt_datetime
182
+ appointment.comment = ""
183
+ self.validate_appointment_as_next(appointment)
184
+ appointment.save(update_fields=["appt_status", "appt_datetime", "comment"])
186
185
 
187
186
  @property
188
187
  def last_crf_obj(self):
edc_appointment/stubs.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from datetime import datetime
2
- from typing import Any, Optional, Protocol, TypeVar, Union
2
+ from typing import Any, Protocol, TypeVar
3
3
  from uuid import UUID
4
4
 
5
5
  from django.db import models
@@ -8,23 +8,23 @@ from edc_visit_schedule.schedule import Schedule
8
8
 
9
9
 
10
10
  class AppointmentModelStub(Protocol):
11
- id: Union[UUID, models.UUIDField]
12
- pk: Union[UUID, models.UUIDField]
13
- subject_identifier: Union[str, models.CharField]
14
- appt_datetime: Union[datetime, models.DateTimeField]
15
- visit_code: Union[str, models.CharField]
16
- visit_code_sequence: Union[int, models.IntegerField]
17
- visit_schedule_name: Union[str, models.CharField]
18
- schedule_name: Union[str, models.CharField]
19
- facility_name: Union[str, models.CharField]
20
- timepoint: Union[int, models.IntegerField]
11
+ id: UUID | models.UUIDField
12
+ pk: UUID | models.UUIDField
13
+ subject_identifier: str | models.CharField
14
+ appt_datetime: datetime | models.DateTimeField
15
+ visit_code: str | models.CharField
16
+ visit_code_sequence: int | models.IntegerField
17
+ visit_schedule_name: str | models.CharField
18
+ schedule_name: str | models.CharField
19
+ facility_name: str | models.CharField
20
+ timepoint: int | models.IntegerField
21
21
  timepoint_datetime: datetime
22
22
  schedule: Schedule
23
23
  _meta: Any
24
24
 
25
25
  objects: models.Manager
26
26
 
27
- last_visit_code_sequence: Optional[int]
27
+ last_visit_code_sequence: int | None
28
28
  next: "AppointmentModelStub"
29
29
  previous: "AppointmentModelStub"
30
30
  get_next: "AppointmentModelStub"
edc_appointment/utils.py CHANGED
@@ -4,7 +4,7 @@ import calendar
4
4
  import sys
5
5
  import warnings
6
6
  from datetime import datetime
7
- from typing import TYPE_CHECKING, Any, Type
7
+ from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from dateutil.relativedelta import relativedelta
10
10
  from django.apps import apps as django_apps
@@ -23,9 +23,8 @@ from django.db.models import Count, ProtectedError
23
23
  from django.urls import reverse
24
24
  from django.utils.translation import gettext as _
25
25
 
26
- from edc_constants.constants import CLINIC
26
+ from edc_constants.constants import CLINIC, NOT_APPLICABLE, OK
27
27
  from edc_constants.constants import ERROR as ERROR_CODE
28
- from edc_constants.constants import NOT_APPLICABLE, OK
29
28
  from edc_dashboard.url_names import url_names
30
29
  from edc_form_validators import INVALID_ERROR
31
30
  from edc_metadata.constants import CRF, REQUIRED, REQUISITION
@@ -89,7 +88,7 @@ def get_appointment_model_name() -> str:
89
88
  return "edc_appointment.appointment"
90
89
 
91
90
 
92
- def get_appointment_model_cls() -> Type[Appointment]:
91
+ def get_appointment_model_cls() -> type[Appointment]:
93
92
  return django_apps.get_model(get_appointment_model_name())
94
93
 
95
94
 
@@ -97,7 +96,7 @@ def get_appointment_type_model_name() -> str:
97
96
  return "edc_appointment.appointmenttype"
98
97
 
99
98
 
100
- def get_appointment_type_model_cls() -> Type[AppointmentType]:
99
+ def get_appointment_type_model_cls() -> type[AppointmentType]:
101
100
  return django_apps.get_model(get_appointment_type_model_name())
102
101
 
103
102
 
@@ -369,7 +368,6 @@ def delete_appointment_in_sequence(appointment: Any, from_post_delete=None) -> N
369
368
  schedule_name=appointment.schedule_name,
370
369
  visit_code=appointment.visit_code,
371
370
  )
372
- return None
373
371
 
374
372
 
375
373
  def update_appt_status(appointment: Appointment, save: bool | None = None):
@@ -383,14 +381,13 @@ def update_appt_status(appointment: Appointment, save: bool | None = None):
383
381
  pass
384
382
  elif not appointment.related_visit:
385
383
  appointment.appt_status = NEW_APPT
384
+ elif (
385
+ appointment.crf_metadata_required_exists
386
+ or appointment.requisition_metadata_required_exists
387
+ ):
388
+ appointment.appt_status = INCOMPLETE_APPT
386
389
  else:
387
- if (
388
- appointment.crf_metadata_required_exists
389
- or appointment.requisition_metadata_required_exists
390
- ):
391
- appointment.appt_status = INCOMPLETE_APPT
392
- else:
393
- appointment.appt_status = COMPLETE_APPT
390
+ appointment.appt_status = COMPLETE_APPT
394
391
  if save:
395
392
  appointment.save_base(update_fields=["appt_status"])
396
393
  appointment.refresh_from_db()
@@ -578,7 +575,7 @@ def appt_datetime_in_next_window_adjusted_for_gap(
578
575
  in_window = False
579
576
  gap_days = get_window_gap_days(appointment)
580
577
  max_gap = get_max_window_gap_to_lower(appointment)
581
- gap_days = max_gap if gap_days > max_gap else gap_days
578
+ gap_days = min(gap_days, max_gap)
582
579
  if gap_days > 0:
583
580
  next_lower_datetime = (
584
581
  appointment.next.timepoint_datetime
@@ -639,22 +636,21 @@ def get_appointment_by_datetime(
639
636
  f"Date falls in a `window period gap` between {appointment.visit_code} "
640
637
  f"and {appointment.next.visit_code}. Got {dt}."
641
638
  )
642
- elif (
639
+ if (
643
640
  in_gap
644
641
  and in_next_window_adjusted
645
642
  and appointment.next.visit.add_window_gap_to_lower
646
643
  ):
647
644
  appointment = appointment.next
648
645
  break
649
- elif (
646
+ if (
650
647
  in_gap
651
648
  and not in_next_window_adjusted
652
649
  and appointment.next.visit.add_window_gap_to_lower
653
650
  ):
654
651
  appointment = None
655
652
  break
656
- else:
657
- appointment = appointment.next
653
+ appointment = appointment.next
658
654
  else:
659
655
  break
660
656
  return appointment
@@ -674,27 +670,26 @@ def reset_appointment(appointment: Appointment, **kwargs):
674
670
  raise AppointmentAlreadyStarted(
675
671
  f"Unable to reset. Appointment already started. Got {appointment}."
676
672
  )
677
- else:
678
- defaults = dict(
679
- appt_status=appointment._meta.get_field("appt_status").default,
680
- appt_timing=appointment._meta.get_field("appt_timing").default,
681
- appt_type=None,
682
- appt_type_other=None,
683
- appt_datetime=appointment.timepoint_datetime,
684
- comment="",
685
- )
686
- defaults.update(**kwargs)
687
- for k, v in defaults.items():
673
+ defaults = dict(
674
+ appt_status=appointment._meta.get_field("appt_status").default,
675
+ appt_timing=appointment._meta.get_field("appt_timing").default,
676
+ appt_type=None,
677
+ appt_type_other=None,
678
+ appt_datetime=appointment.timepoint_datetime,
679
+ comment="",
680
+ )
681
+ defaults.update(**kwargs)
682
+ for k, v in defaults.items():
683
+ try:
684
+ related_model = get_model_from_relation(appointment._meta.get_field(k))
685
+ except NotRelationField:
686
+ setattr(appointment, k, v)
687
+ else:
688
688
  try:
689
- related_model = get_model_from_relation(appointment._meta.get_field(k))
690
- except NotRelationField:
691
- setattr(appointment, k, v)
692
- else:
693
- try:
694
- setattr(appointment, k, related_model.objects.get(name=v))
695
- except ObjectDoesNotExist:
696
- setattr(appointment, k, None)
697
- appointment.save_base(update_fields=[*defaults.keys()])
689
+ setattr(appointment, k, related_model.objects.get(name=v))
690
+ except ObjectDoesNotExist:
691
+ setattr(appointment, k, None)
692
+ appointment.save_base(update_fields=[*defaults.keys()])
698
693
 
699
694
 
700
695
  def skip_appointment(appointment: Appointment, comment: str | None = None):
@@ -705,14 +700,13 @@ def skip_appointment(appointment: Appointment, comment: str | None = None):
705
700
  raise AppointmentAlreadyStarted(
706
701
  f"Unable to skip. Appointment already started. Got {appointment}."
707
702
  )
708
- else:
709
- reset_appointment(
710
- appointment,
711
- appt_status=SKIPPED_APPT,
712
- appt_timing=NOT_APPLICABLE,
713
- appt_type=NOT_APPLICABLE,
714
- comment=comment,
715
- )
703
+ reset_appointment(
704
+ appointment,
705
+ appt_status=SKIPPED_APPT,
706
+ appt_timing=NOT_APPLICABLE,
707
+ appt_type=NOT_APPLICABLE,
708
+ comment=comment,
709
+ )
716
710
 
717
711
 
718
712
  def get_unscheduled_appointment_url(appointment: Appointment = None) -> str:
@@ -820,7 +814,7 @@ def validate_date_is_on_clinic_day(
820
814
  raise raise_validation_error(
821
815
  {"appt_date": "Cannot be equal to the report datetime"}, INVALID_ERROR
822
816
  )
823
- elif appt_date <= report_date:
817
+ if appt_date <= report_date:
824
818
  raise raise_validation_error(
825
819
  {"appt_date": "Cannot be before the report datetime"}, INVALID_ERROR
826
820
  )
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass, field
4
- from typing import TYPE_CHECKING, Type
4
+ from typing import TYPE_CHECKING
5
5
  from uuid import UUID
6
6
 
7
7
  from django.apps import apps as django_apps
@@ -24,7 +24,7 @@ __all__ = ["AppointmentButton"]
24
24
  class AppointmentButton(DashboardModelButton):
25
25
  model_obj: Appointment = None
26
26
  colors: tuple[str, str, str] = field(default=(3 * ("default",)))
27
- model_cls: Type[Appointment] = field(default=None, init=False)
27
+ model_cls: type[Appointment] = field(default=None, init=False)
28
28
  appointment: Appointment = field(default=None, init=False)
29
29
 
30
30
  def __post_init__(self):
@@ -33,9 +33,7 @@ class AppointmentButton(DashboardModelButton):
33
33
  @property
34
34
  def disabled(self) -> str:
35
35
  disabled = "disabled"
36
- if self.model_obj.appt_status == IN_PROGRESS_APPT and self.perms.change:
37
- disabled = ""
38
- elif (
36
+ if (self.model_obj.appt_status == IN_PROGRESS_APPT and self.perms.change) or (
39
37
  self.model_obj.appt_status != NEW_APPT
40
38
  and not self.perms.add
41
39
  and not self.perms.change
@@ -1,5 +1,3 @@
1
- from typing import Tuple
2
-
3
1
  from django.contrib import admin
4
2
  from django.utils.html import format_html
5
3
  from django.utils.safestring import mark_safe
@@ -14,15 +12,15 @@ from ..models import Role
14
12
  class RoleAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
15
13
  fieldsets = ((None, ({"fields": ("display_name", "name", "display_index", "groups")})),)
16
14
 
17
- list_display_links: Tuple[str, ...] = ("display_name", "group_list")
15
+ list_display_links: tuple[str, ...] = ("display_name", "group_list")
18
16
 
19
- list_display: Tuple[str, ...] = ("display_name", "name", "group_list")
17
+ list_display: tuple[str, ...] = ("display_name", "name", "group_list")
20
18
 
21
- filter_horizontal: Tuple[str, ...] = ("groups",)
19
+ filter_horizontal: tuple[str, ...] = ("groups",)
22
20
 
23
- search_fields: Tuple[str, ...] = ("display_name", "name", "groups__name")
21
+ search_fields: tuple[str, ...] = ("display_name", "name", "groups__name")
24
22
 
25
- ordering: Tuple[str, ...] = ("display_index", "display_name")
23
+ ordering: tuple[str, ...] = ("display_index", "display_name")
26
24
 
27
25
  list_filter = ("groups__name",)
28
26
 
@@ -1,5 +1,3 @@
1
- from typing import Dict, List
2
-
3
1
  from ..constants import (
4
2
  ACCOUNT_MANAGER,
5
3
  ACCOUNT_MANAGER_ROLE,
@@ -20,7 +18,7 @@ from ..constants import (
20
18
  )
21
19
 
22
20
  # Format {ROLE_NAME: [GROUP_NAME, GROUP_NAME, ...]
23
- default_roles: Dict[str, List[str]] = {
21
+ default_roles: dict[str, list[str]] = {
24
22
  ACCOUNT_MANAGER_ROLE: [ACCOUNT_MANAGER],
25
23
  AUDITOR_ROLE: [
26
24
  AUDITOR,
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import sys
4
- from typing import Callable
4
+ from collections.abc import Callable
5
5
 
6
6
  from django.apps import apps as django_apps
7
7
  from django.conf import settings
@@ -89,9 +89,8 @@ class AuthUpdater:
89
89
  for func in pre_updates:
90
90
  sys.stdout.write(f" * {func.__name__}\n")
91
91
  func(self)
92
- else:
93
- if self.verbose:
94
- sys.stdout.write(" * nothing to do\n")
92
+ elif self.verbose:
93
+ sys.stdout.write(" * nothing to do\n")
95
94
  if self.verbose:
96
95
  sys.stdout.write(" Done.\n")
97
96
 
@@ -103,9 +102,8 @@ class AuthUpdater:
103
102
  for app_label, func in post_updates:
104
103
  sys.stdout.write(f" * {func.__name__}({app_label})\n")
105
104
  func(self, app_label)
106
- else:
107
- if self.verbose:
108
- sys.stdout.write(" * nothing to do\n")
105
+ elif self.verbose:
106
+ sys.stdout.write(" * nothing to do\n")
109
107
  if self.verbose:
110
108
  sys.stdout.write(" Done.\n")
111
109