clinicedc 2.0.4__py3-none-any.whl → 2.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of clinicedc might be problematic. Click here for more details.

Files changed (478) hide show
  1. {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.dist-info}/METADATA +41 -45
  2. {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.dist-info}/RECORD +478 -478
  3. edc_action_item/action.py +76 -54
  4. edc_action_item/action_item_notification.py +1 -3
  5. edc_action_item/action_with_notification.py +4 -4
  6. edc_action_item/create_action_item.py +2 -2
  7. edc_action_item/create_or_update_action_type.py +1 -1
  8. edc_action_item/data_fixers.py +4 -5
  9. edc_action_item/delete_action_item.py +0 -1
  10. edc_action_item/modeladmin_mixins.py +2 -4
  11. edc_action_item/modelform_mixins/modelform_mixins.py +1 -1
  12. edc_action_item/models/action_item.py +53 -38
  13. edc_action_item/models/action_model_mixin.py +17 -17
  14. edc_action_item/models/action_type.py +33 -13
  15. edc_action_item/models/reference.py +3 -2
  16. edc_action_item/site_action_items.py +13 -14
  17. edc_action_item/stubs.py +9 -9
  18. edc_action_item/templatetags/action_item_extras.py +2 -2
  19. edc_action_item/view_utils/action_item_button.py +3 -3
  20. edc_action_item/view_utils/action_item_popover_list_item.py +4 -4
  21. edc_adherence/form_validator_mixin.py +1 -1
  22. edc_adverse_event/action_items/ae_followup_action.py +1 -1
  23. edc_adverse_event/action_items/ae_initial_action.py +1 -1
  24. edc_adverse_event/action_items/ae_susar_action.py +1 -1
  25. edc_adverse_event/action_items/ae_tmg_action.py +1 -1
  26. edc_adverse_event/action_items/death_report_action.py +1 -2
  27. edc_adverse_event/action_items/death_report_tmg_action.py +1 -1
  28. edc_adverse_event/action_items/death_report_tmg_second_action.py +1 -1
  29. edc_adverse_event/action_items/hospitalization_action.py +1 -1
  30. edc_adverse_event/form_validator_mixins/death_report_form_validator.py +3 -3
  31. edc_adverse_event/form_validator_mixins/requires_death_report_form_validator_mixin.py +3 -4
  32. edc_adverse_event/form_validators/death_report_tmg.py +1 -1
  33. edc_adverse_event/model_mixins/ae_followup/ae_followup_methods_model_mixin.py +3 -3
  34. edc_adverse_event/model_mixins/ae_followup/ae_followup_model_mixin.py +3 -3
  35. edc_adverse_event/model_mixins/ae_initial/ae_initial_fields_model_mixin.py +0 -1
  36. edc_adverse_event/model_mixins/ae_initial/ae_initial_methods_model_mixin.py +3 -3
  37. edc_adverse_event/model_mixins/ae_initial/ae_initial_model_mixin.py +3 -3
  38. edc_adverse_event/model_mixins/ae_special_interest/aesi_methods_model_mixin.py +4 -4
  39. edc_adverse_event/model_mixins/ae_special_interest/aesi_model_mixin.py +3 -3
  40. edc_adverse_event/model_mixins/ae_susar/ae_susar_methods_model_mixin.py +4 -4
  41. edc_adverse_event/model_mixins/ae_susar/ae_susar_model_mixin.py +3 -3
  42. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_fields_model_mixin.py +4 -5
  43. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_methods_model_mixin.py +4 -4
  44. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_model_mixin.py +3 -3
  45. edc_adverse_event/model_mixins/death_report/death_report_extra_fields_model_mixin.py +2 -3
  46. edc_adverse_event/model_mixins/death_report/death_report_model_mixin.py +11 -11
  47. edc_adverse_event/model_mixins/death_report/death_report_tmg_model_mixin.py +9 -11
  48. edc_adverse_event/model_mixins/death_report/simple_death_report_model_mixin.py +7 -6
  49. edc_adverse_event/model_mixins/hospitaization/hospitalization_model_mixin.py +0 -1
  50. edc_adverse_event/modeladmin_mixins/ae_followup_admin_mixin.py +9 -11
  51. edc_adverse_event/modeladmin_mixins/ae_initial_admin_mixin.py +4 -6
  52. edc_adverse_event/modeladmin_mixins/modeladmin_mixins.py +1 -1
  53. edc_adverse_event/modeladmin_mixins/utils.py +4 -4
  54. edc_adverse_event/models/signals.py +7 -19
  55. edc_adverse_event/pdf_reports/death_pdf_report.py +8 -9
  56. edc_adverse_event/templatetags/edc_adverse_event_extras.py +10 -20
  57. edc_adverse_event/urls.py +7 -3
  58. edc_adverse_event/utils.py +2 -2
  59. edc_adverse_event/view_utils/tmg_button.py +4 -4
  60. edc_appconfig/system_checks.py +1 -1
  61. edc_appointment/appointment_status_updater.py +14 -15
  62. edc_appointment/creators/appointment_creator.py +7 -11
  63. edc_appointment/creators/appointments_creator.py +1 -1
  64. edc_appointment/creators/unscheduled_appointment_creator.py +12 -13
  65. edc_appointment/form_validator_mixins/next_appointment_crf_form_validator_mixin.py +1 -3
  66. edc_appointment/form_validator_mixins/window_period_form_validator_mixin.py +7 -6
  67. edc_appointment/form_validators/appointment_form_validator.py +2 -4
  68. edc_appointment/form_validators/utils.py +4 -4
  69. edc_appointment/managers.py +4 -4
  70. edc_appointment/model_mixins/appointment_methods_model_mixin.py +2 -2
  71. edc_appointment/model_mixins/appointment_model_mixin.py +5 -5
  72. edc_appointment/model_mixins/window_period_model_mixin.py +1 -1
  73. edc_appointment/models/appointment.py +1 -1
  74. edc_appointment/skip_appointments.py +5 -6
  75. edc_appointment/stubs.py +12 -12
  76. edc_appointment/utils.py +41 -47
  77. edc_appointment/view_utils/appointment_button.py +3 -5
  78. edc_auth/admin/role_admin.py +5 -7
  79. edc_auth/auth_objects/default_roles.py +1 -3
  80. edc_auth/auth_updater/auth_updater.py +5 -7
  81. edc_auth/auth_updater/group_updater.py +8 -8
  82. edc_auth/auth_updater/role_updater.py +1 -2
  83. edc_auth/get_app_codenames.py +1 -3
  84. edc_auth/import_users.py +19 -19
  85. edc_auth/models/role.py +4 -3
  86. edc_auth/password_setter.py +6 -7
  87. edc_auth/send_new_credentials_to_user.py +2 -3
  88. edc_auth/site_auths.py +3 -3
  89. edc_consent/actions.py +4 -5
  90. edc_consent/consent_definition.py +8 -11
  91. edc_consent/consent_definition_extension.py +3 -3
  92. edc_consent/form_validators/__init__.py +1 -1
  93. edc_consent/model_mixins/__init__.py +2 -2
  94. edc_consent/model_mixins/consent_version_model_mixin.py +1 -1
  95. edc_consent/modeladmin_mixins/consent_model_admin_mixin.py +1 -2
  96. edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_validation_mixin.py +3 -5
  97. edc_consent/site_consents.py +10 -15
  98. edc_consent/stubs.py +2 -2
  99. edc_consent/utils.py +1 -1
  100. edc_constants/utils.py +2 -4
  101. edc_crf/crf_form_validator_mixins.py +2 -2
  102. edc_crf/model_mixins/crf_no_manager_model_mixin.py +2 -2
  103. edc_crf/model_mixins/crf_status_model_mixin.py +1 -1
  104. edc_crf/models/crf_status.py +10 -12
  105. edc_crf/update_crf_status_command.py +1 -3
  106. edc_dashboard/management/commands/update_search_slugs.py +8 -11
  107. edc_dashboard/templatetags/edc_dashboard_extras.py +1 -1
  108. edc_dashboard/url_config.py +3 -3
  109. edc_dashboard/utils.py +3 -4
  110. edc_dashboard/views/dashboard_view.py +1 -1
  111. edc_data_manager/models/data_dictionary.py +1 -2
  112. edc_data_manager/models/data_query.py +3 -3
  113. edc_data_manager/models/query_rule.py +2 -2
  114. edc_data_manager/tasks.py +0 -2
  115. edc_device/device.py +1 -1
  116. edc_device/view_mixins.py +1 -1
  117. edc_document_status/fieldsets.py +1 -3
  118. edc_document_status/model_mixins.py +2 -2
  119. edc_document_status/modeladmin_mixins.py +1 -4
  120. edc_egfr/admin/egfr_drop_notification_admin_mixin.py +5 -7
  121. edc_egfr/egfr.py +6 -7
  122. edc_egfr/get_drop_notification_model.py +1 -1
  123. edc_egfr/model_mixins/egfr_drop_notification_model_mixin.py +1 -1
  124. edc_export/archive_exporter.py +26 -27
  125. edc_export/exportables.py +2 -3
  126. edc_export/management/commands/import_receipts.py +6 -6
  127. edc_export/model_exporter/file_history_updater.py +1 -1
  128. edc_export/model_exporter/model_exporter.py +1 -1
  129. edc_export/model_exporter/value_getter.py +6 -6
  130. edc_export/model_mixins/notification_model_mixin.py +4 -4
  131. edc_export/models/data_request.py +3 -3
  132. edc_export/models/data_request_history.py +2 -2
  133. edc_export/models/export_receipt.py +1 -1
  134. edc_export/models/file_history.py +5 -5
  135. edc_export/models/plan.py +4 -4
  136. edc_export/models/upload_export_receipt_file.py +2 -2
  137. edc_export/utils.py +1 -1
  138. edc_facility/facility.py +6 -6
  139. edc_facility/holidays.py +2 -5
  140. edc_facility/import_holidays.py +6 -6
  141. edc_facility/model_mixins.py +1 -1
  142. edc_facility/models/holiday.py +1 -1
  143. edc_facility/utils.py +3 -3
  144. edc_fieldsets/fieldsets.py +1 -1
  145. edc_form_describer/forms_reference.py +5 -8
  146. edc_form_describer/make_forms_reference.py +1 -1
  147. edc_form_describer/management/commands/make_forms_reference.py +1 -1
  148. edc_form_label/custom_label_condition.py +1 -1
  149. edc_form_label/form_label.py +2 -2
  150. edc_form_runners/exceptions.py +1 -1
  151. edc_form_runners/models/issue.py +3 -2
  152. edc_form_runners/site.py +2 -2
  153. edc_form_runners/templatetags/form_runners_extras.py +1 -1
  154. edc_form_runners/utils.py +5 -5
  155. edc_form_validators/applicable_field_validator.py +32 -32
  156. edc_form_validators/base_form_validator.py +1 -1
  157. edc_form_validators/extra_mixins/study_day_form_validator.py +1 -1
  158. edc_form_validators/many_to_many_field_validator.py +38 -43
  159. edc_form_validators/other_specify_field_validator.py +9 -17
  160. edc_form_validators/required_field_validator.py +34 -39
  161. edc_form_validators/test_case_mixin.py +5 -5
  162. edc_identifier/admin.py +1 -3
  163. edc_identifier/identifier.py +2 -3
  164. edc_identifier/model_mixins.py +5 -8
  165. edc_identifier/research_identifier.py +10 -12
  166. edc_identifier/short_identifier.py +1 -1
  167. edc_identifier/simple_identifier.py +22 -22
  168. edc_identifier/utils.py +1 -1
  169. edc_lab/admin/fieldsets.py +7 -9
  170. edc_lab/admin/modeladmin_mixins.py +5 -6
  171. edc_lab/form_validators/requisition_form_validator_mixin.py +2 -3
  172. edc_lab/forms/box_form.py +5 -11
  173. edc_lab/identifiers/aliquot_identifier.py +5 -8
  174. edc_lab/identifiers/prefix.py +2 -5
  175. edc_lab/lab/aliquot_creator.py +1 -2
  176. edc_lab/lab/aliquot_type.py +2 -4
  177. edc_lab/lab/manifest.py +5 -7
  178. edc_lab/lab/requisition_panel.py +2 -4
  179. edc_lab/lab/requisition_panel_group.py +5 -7
  180. edc_lab/model_mixins/requisition/requisition_model_mixin.py +5 -4
  181. edc_lab/model_mixins/shipping/manifest_model_mixin.py +6 -6
  182. edc_lab/model_mixins/shipping/verify_model_mixin.py +4 -3
  183. edc_lab/models/aliquot.py +1 -1
  184. edc_lab/models/box.py +2 -2
  185. edc_lab/models/box_item.py +1 -1
  186. edc_lab/models/box_type.py +1 -1
  187. edc_lab/models/manifest/manifest_item.py +1 -1
  188. edc_lab/pdf_reports/manifest_pdf_report.py +3 -7
  189. edc_lab/site_labs.py +3 -3
  190. edc_lab_dashboard/dashboard_templates.py +1 -1
  191. edc_lab_dashboard/view_mixins/box_view_mixin.py +4 -9
  192. edc_lab_dashboard/views/action_views/action_view.py +1 -2
  193. edc_lab_dashboard/views/action_views/manage_manifest_view.py +1 -1
  194. edc_lab_dashboard/views/action_views/manifest_view.py +9 -11
  195. edc_lab_dashboard/views/action_views/pack_view.py +11 -14
  196. edc_lab_results/calculate_missing.py +2 -2
  197. edc_lab_results/fieldsets.py +6 -6
  198. edc_lab_results/model_mixin_factories/__init__.py +2 -2
  199. edc_lab_results/model_mixin_factories/reportable_result_model_mixin_factory.py +7 -9
  200. edc_lab_results/model_mixin_factories/result_model_mixin_factory.py +7 -9
  201. edc_lab_results/model_mixins/fbg_model_mixin.py +1 -1
  202. edc_lab_results/model_mixins/glucose_model_mixin.py +1 -1
  203. edc_label/admin.py +1 -3
  204. edc_label/label.py +2 -4
  205. edc_label/label_template.py +1 -1
  206. edc_label/subject_label.py +1 -3
  207. edc_list_data/admin.py +3 -5
  208. edc_list_data/load_list_data.py +1 -1
  209. edc_list_data/load_model_data.py +1 -3
  210. edc_list_data/model_mixins.py +3 -5
  211. edc_list_data/preload_data.py +2 -4
  212. edc_list_data/site_list_data.py +6 -7
  213. edc_listboard/filters/listboard_filter.py +2 -2
  214. edc_listboard/views/listboard_view.py +1 -3
  215. edc_locator/models.py +2 -2
  216. edc_locator/view_mixins/subject_locator_view_mixins.py +2 -2
  217. edc_ltfu/action_items.py +1 -1
  218. edc_ltfu/model_mixins.py +0 -2
  219. edc_ltfu/models.py +4 -4
  220. edc_metadata/admin/modeladmin_mixins.py +1 -1
  221. edc_metadata/admin/requisition_metadata.py +2 -4
  222. edc_metadata/metadata/crf_metadata_getter.py +1 -3
  223. edc_metadata/metadata/requisition_metadata_getter.py +1 -3
  224. edc_metadata/metadata_handler.py +2 -2
  225. edc_metadata/metadata_helper/metadata_helper_mixin.py +2 -3
  226. edc_metadata/metadata_mixins/source_model_metadata_mixin.py +2 -2
  227. edc_metadata/metadata_refresher.py +1 -1
  228. edc_metadata/metadata_rules/crf/crf_rule_group.py +3 -5
  229. edc_metadata/metadata_rules/logic.py +1 -1
  230. edc_metadata/metadata_rules/requisition/requisition_rule.py +4 -4
  231. edc_metadata/metadata_rules/requisition/requisition_rule_group.py +2 -2
  232. edc_metadata/metadata_rules/rule_evaluator.py +5 -6
  233. edc_metadata/metadata_rules/rule_group_metaclass.py +2 -3
  234. edc_metadata/metadata_rules/site.py +6 -7
  235. edc_metadata/metadata_updater.py +2 -2
  236. edc_metadata/metadata_wrappers/metadata_wrapper.py +4 -4
  237. edc_metadata/model_mixins/creates/creates_metadata_model_mixin.py +6 -7
  238. edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +2 -2
  239. edc_metadata/model_mixins/updates/updates_metadata_model_mixin.py +2 -2
  240. edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +2 -2
  241. edc_metadata/models/crf_metadata.py +27 -29
  242. edc_metadata/models/crf_metadata_model_mixin.py +1 -1
  243. edc_metadata/models/requisition_metadata.py +29 -31
  244. edc_metadata/stubs.py +17 -17
  245. edc_metadata/utils.py +4 -4
  246. edc_model/__init__.py +2 -2
  247. edc_model/models/historical_records.py +2 -2
  248. edc_model/models/signals.py +1 -1
  249. edc_model/models/url_model_mixin.py +1 -1
  250. edc_model/utils.py +0 -2
  251. edc_model_admin/dashboard/model_admin_dashboard_mixin.py +1 -3
  252. edc_model_admin/history/model_admin_simple_history.py +7 -9
  253. edc_model_admin/mixins/base_model_admin_redirect_mixin.py +4 -6
  254. edc_model_admin/mixins/model_admin_limit_to_selected_foreignkey.py +2 -2
  255. edc_model_admin/mixins/model_admin_model_redirect_mixin.py +2 -10
  256. edc_model_admin/mixins/model_admin_next_url_redirect_mixin.py +8 -9
  257. edc_model_admin/utils.py +6 -9
  258. edc_model_form/mixins/__init__.py +1 -1
  259. edc_model_form/mixins/base_model_form_mixin.py +1 -1
  260. edc_model_to_dataframe/model_to_dataframe.py +2 -4
  261. edc_navbar/site_navbars.py +2 -2
  262. edc_navbar/system_checks.py +1 -1
  263. edc_notification/mailing_list_manager.py +5 -8
  264. edc_notification/management/commands/list_recipients_by_notification.py +1 -1
  265. edc_notification/models/__init__.py +4 -2
  266. edc_notification/notification/graded_event_notification.py +2 -4
  267. edc_notification/notification/model_notification.py +1 -3
  268. edc_notification/notification/notification.py +14 -17
  269. edc_notification/site_notifications.py +7 -7
  270. edc_notification/stubs.py +6 -6
  271. edc_notification/update_mailing_lists_in_m2m.py +2 -2
  272. edc_offstudy/action_items.py +2 -2
  273. edc_offstudy/model_mixins/offstudy_model_mixin.py +1 -1
  274. edc_offstudy/models.py +1 -1
  275. edc_pdf_reports/crf_pdf_report.py +2 -2
  276. edc_pdf_reports/model_mixins.py +2 -2
  277. edc_pdf_reports/report.py +4 -5
  278. edc_pdf_reports/utils.py +1 -1
  279. edc_pdutils/dataframes/get_subject_consent.py +1 -3
  280. edc_pdutils/df_exporters/csv_exporter.py +5 -7
  281. edc_pdutils/df_exporters/tables_exporter.py +2 -2
  282. edc_pdutils/df_handlers/crf_df_handler.py +1 -2
  283. edc_pdutils/dialects/crf_dialect.py +3 -3
  284. edc_pdutils/management/commands/export_models.py +6 -7
  285. edc_pdutils/site_values_mappings.py +2 -2
  286. edc_pdutils/utils/datetime_to_date.py +1 -2
  287. edc_pdutils/utils/refresh_model_from_dataframe.py +1 -3
  288. edc_pdutils/utils/table_names.py +2 -4
  289. edc_pdutils/utils/undash.py +1 -1
  290. edc_pharmacy/admin/medication/assignment_admin.py +2 -4
  291. edc_pharmacy/admin/medication/dosage_guideline_admin.py +2 -4
  292. edc_pharmacy/admin/medication/formulation_admin.py +3 -5
  293. edc_pharmacy/admin/medication/medication_admin.py +3 -5
  294. edc_pharmacy/admin/prescription/rx_refill_admin.py +7 -9
  295. edc_pharmacy/admin/stock/lot_admin.py +5 -7
  296. edc_pharmacy/admin/stock/order_admin.py +2 -2
  297. edc_pharmacy/admin/stock/order_item_admin.py +6 -6
  298. edc_pharmacy/admin/stock/receive_admin.py +1 -1
  299. edc_pharmacy/admin/stock/receive_item_admin.py +2 -2
  300. edc_pharmacy/admin/stock/stock_request_admin.py +4 -6
  301. edc_pharmacy/dosage_calculator.py +4 -4
  302. edc_pharmacy/form_validators/crf/study_medication_form_validator.py +8 -8
  303. edc_pharmacy/forms/stock/stock_request_form.py +2 -4
  304. edc_pharmacy/model_mixins/study_medication_crf_model_mixin.py +2 -2
  305. edc_pharmacy/models/medication/formulation.py +3 -4
  306. edc_pharmacy/models/medication/medication.py +1 -2
  307. edc_pharmacy/models/prescription/rx.py +2 -2
  308. edc_pharmacy/models/prescription/rx_refill.py +7 -9
  309. edc_pharmacy/models/reports/stock_availability.py +3 -4
  310. edc_pharmacy/models/stock/confirmation_at_site.py +1 -3
  311. edc_pharmacy/models/stock/order.py +1 -2
  312. edc_pharmacy/models/stock/receive.py +3 -4
  313. edc_pharmacy/models/stock/receive_item.py +2 -3
  314. edc_pharmacy/models/stock/stock.py +1 -2
  315. edc_pharmacy/models/stock/stock_adjustment.py +1 -2
  316. edc_pharmacy/models/stock/stock_request.py +4 -5
  317. edc_pharmacy/models/storage/box.py +1 -1
  318. edc_pharmacy/models/storage/items/container_model_mixin.py +1 -1
  319. edc_pharmacy/models/storage/room.py +1 -1
  320. edc_pharmacy/models/storage/shelf.py +1 -1
  321. edc_pharmacy/models/storage/utils.py +15 -16
  322. edc_pharmacy/prescribe/create_prescription.py +5 -5
  323. edc_pharmacy/refill/refill_creator.py +1 -1
  324. edc_pharmacy/sample_usb_printing/usb_printing.py +1 -1
  325. edc_pharmacy/utils/__init__.py +1 -1
  326. edc_pharmacy/utils/confirm_stock.py +3 -3
  327. edc_pharmacy/utils/confirm_stock_at_site.py +9 -8
  328. edc_pharmacy/utils/dispense.py +5 -8
  329. edc_pharmacy/utils/format_qty.py +3 -3
  330. edc_pharmacy/utils/get_codenames.py +1 -1
  331. edc_pharmacy/utils/miscellaneous.py +1 -1
  332. edc_pharmacy/utils/process_repack_request.py +18 -19
  333. edc_pharmacy/utils/process_repack_request_queryset.py +0 -2
  334. edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py +2 -3
  335. edc_pharmacy/views/add_to_storage_bin_view.py +1 -1
  336. edc_pharmacy/views/allocate_to_subject_view.py +2 -6
  337. edc_pharmacy/views/confirm_stock_from_instance_view.py +4 -4
  338. edc_pharmacy/views/confirm_stock_from_queryset_view.py +1 -5
  339. edc_pharmacy/views/confirmation_at_site_view.py +2 -3
  340. edc_pharmacy/views/dispense_view.py +1 -1
  341. edc_pharmacy/views/move_to_storage_bin_view.py +2 -3
  342. edc_pharmacy/views/print_labels_view.py +30 -31
  343. edc_pharmacy/views/transfer_stock_view.py +1 -1
  344. edc_prn/prn.py +1 -1
  345. edc_prn/site_prn_forms.py +1 -2
  346. edc_protocol_incident/action_items.py +2 -2
  347. edc_protocol_incident/model_mixins/protocol_deviation_violation_model_mixin.py +3 -3
  348. edc_protocol_incident/model_mixins/protocol_incident_model_mixin.py +3 -5
  349. edc_protocol_incident/modeladmin_mixins.py +3 -5
  350. edc_protocol_incident/models/protocol_deviation_violation.py +5 -5
  351. edc_protocol_incident/models/protocol_incident.py +5 -5
  352. edc_pylabels/site_label_configs.py +5 -5
  353. edc_qareports/model_mixins/qa_report_model_mixin.py +4 -2
  354. edc_qareports/models/qa_reports_log.py +1 -2
  355. edc_qareports/utils.py +1 -2
  356. edc_randomization/admin.py +3 -5
  357. edc_randomization/auth_objects.py +2 -3
  358. edc_randomization/blinding.py +1 -1
  359. edc_randomization/constants.py +2 -4
  360. edc_randomization/model_mixins.py +3 -3
  361. edc_randomization/randomization_list_importer.py +13 -14
  362. edc_randomization/randomization_list_verifier.py +11 -13
  363. edc_randomization/randomizer.py +2 -2
  364. edc_randomization/system_checks.py +1 -2
  365. edc_randomization/utils.py +5 -5
  366. edc_refusal/admin.py +1 -1
  367. edc_refusal/model_mixins.py +1 -1
  368. edc_refusal/utils.py +1 -1
  369. edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +2 -2
  370. edc_registration/modeladmin_mixins.py +4 -7
  371. edc_registration/models/registered_subject.py +7 -9
  372. edc_registration/utils.py +3 -4
  373. edc_reportable/data/grading_data/daids_july_2017.py +3 -3
  374. edc_reportable/evaluator.py +5 -5
  375. edc_reportable/formula.py +1 -1
  376. edc_reportable/reference_range_evaluator.py +3 -3
  377. edc_reportable/utils/convert_units.py +10 -12
  378. edc_reportable/utils/grading_data_model_cls.py +2 -2
  379. edc_reportable/utils/grading_exception_model_cls.py +2 -2
  380. edc_reportable/utils/molecular_weight_model_cls.py +2 -2
  381. edc_reportable/utils/normal_data_model_cls.py +2 -2
  382. edc_reportable/utils/reference_range_colllection_model_cls.py +2 -2
  383. edc_screening/age_evaluator.py +2 -4
  384. edc_screening/eligibility.py +1 -1
  385. edc_screening/form_validator_mixins.py +2 -2
  386. edc_screening/model_mixins/screening_methods_model_mixin.py +1 -1
  387. edc_screening/screening_eligibility.py +1 -1
  388. edc_screening/utils.py +4 -6
  389. edc_search/model_mixins.py +1 -1
  390. edc_search/search_slug.py +1 -3
  391. edc_search/updater.py +1 -1
  392. edc_sites/admin/site_model_admin_mixin.py +2 -2
  393. edc_sites/model_mixins/site_model_mixin.py +1 -2
  394. edc_sites/modelform_mixins.py +2 -2
  395. edc_sites/models/__init__.py +1 -1
  396. edc_sites/models/site_profile.py +4 -4
  397. edc_sites/site.py +24 -32
  398. edc_sites/system_checks.py +1 -1
  399. edc_sites/utils/get_message_text.py +1 -1
  400. edc_sites/utils/valid_site_for_subject_or_raise.py +5 -6
  401. edc_subject_dashboard/requisition_report.py +4 -5
  402. edc_subject_dashboard/requisition_verifier.py +4 -2
  403. edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +16 -17
  404. edc_subject_dashboard/view_utils/__init__.py +1 -1
  405. edc_subject_dashboard/view_utils/crf_button.py +1 -3
  406. edc_subject_dashboard/view_utils/subject_consent_dashboard_button.py +2 -2
  407. edc_subject_dashboard/view_utils/subject_consent_listboard_button.py +2 -2
  408. edc_subject_dashboard/view_utils/timepoint_status_button.py +1 -4
  409. edc_subject_dashboard/views/requisition_print_actions_view.py +2 -2
  410. edc_subject_dashboard/views/subject_dashboard_view.py +1 -1
  411. edc_timepoint/model_mixins.py +2 -2
  412. edc_transfer/action_items.py +1 -1
  413. edc_transfer/model_mixins.py +4 -3
  414. edc_transfer/modeladmin_mixins.py +3 -6
  415. edc_unblinding/action_items.py +2 -2
  416. edc_unblinding/models/unblinding_request.py +3 -3
  417. edc_unblinding/models/unblinding_review.py +3 -3
  418. edc_utils/__init__.py +6 -5
  419. edc_utils/age.py +1 -1
  420. edc_utils/celery.py +3 -8
  421. edc_utils/get_static_file.py +1 -1
  422. edc_utils/text.py +5 -6
  423. edc_view_utils/__init__.py +3 -3
  424. edc_view_utils/dashboard_model_button.py +4 -4
  425. edc_view_utils/model_button.py +7 -8
  426. edc_view_utils/perms.py +3 -3
  427. edc_visit_schedule/action_items.py +2 -2
  428. edc_visit_schedule/admin/list_filters.py +11 -9
  429. edc_visit_schedule/admin/subject_schedule_history_admin.py +13 -15
  430. edc_visit_schedule/admin/visit_schedule_admin.py +7 -7
  431. edc_visit_schedule/fieldsets.py +4 -6
  432. edc_visit_schedule/management/commands/find_invalid_onschedules.py +1 -1
  433. edc_visit_schedule/model_mixins/off_schedule_model_mixin.py +3 -1
  434. edc_visit_schedule/model_mixins/on_schedule_model_mixin.py +1 -1
  435. edc_visit_schedule/modelform_mixins/__init__.py +1 -1
  436. edc_visit_schedule/modelform_mixins/off_schedule_modelform_mixin.py +1 -2
  437. edc_visit_schedule/models/subject_schedule_history.py +2 -1
  438. edc_visit_schedule/models/visit_schedule.py +2 -2
  439. edc_visit_schedule/schedule/schedule.py +11 -12
  440. edc_visit_schedule/schedule/visit_collection.py +4 -3
  441. edc_visit_schedule/schedule/window.py +19 -12
  442. edc_visit_schedule/site_visit_schedules.py +9 -9
  443. edc_visit_schedule/subject_schedule.py +6 -7
  444. edc_visit_schedule/system_checks.py +1 -1
  445. edc_visit_schedule/view_mixins.py +1 -1
  446. edc_visit_schedule/visit/crf.py +3 -7
  447. edc_visit_schedule/visit/requisition.py +2 -2
  448. edc_visit_schedule/visit/visit.py +6 -7
  449. edc_visit_schedule/visit/window_period.py +8 -8
  450. edc_visit_schedule/visit_schedule/schedules_collection.py +2 -5
  451. edc_visit_tracking/action_items.py +11 -13
  452. edc_visit_tracking/form_validators/visit_form_validator.py +5 -5
  453. edc_visit_tracking/model_mixins/base/visit_methods_model_mixin.py +3 -4
  454. edc_visit_tracking/model_mixins/crfs/visit_tracking_crf_model_mixin.py +2 -2
  455. edc_visit_tracking/model_mixins/subject_visit_missed_model_mixin.py +2 -3
  456. edc_visit_tracking/model_mixins/utils.py +1 -1
  457. edc_visit_tracking/model_mixins/visit_model_mixin/previous_visit_model_mixin.py +2 -2
  458. edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_mixin.py +3 -3
  459. edc_visit_tracking/modeladmin_mixins/crf_model_admin_mixin.py +6 -4
  460. edc_visit_tracking/modeladmin_mixins/visit_model_admin_mixin.py +6 -7
  461. edc_visit_tracking/modelform_mixins/crf/visit_tracking_crf_modelform_mixin.py +4 -5
  462. edc_visit_tracking/modelform_mixins/visit_tracking_modelform_mixin.py +3 -4
  463. edc_visit_tracking/models/subject_visit.py +1 -1
  464. edc_visit_tracking/models/subject_visit_missed.py +1 -1
  465. edc_visit_tracking/stubs.py +7 -7
  466. edc_visit_tracking/typing_stubs.py +11 -11
  467. edc_visit_tracking/utils.py +5 -5
  468. edc_visit_tracking/view_utils/related_visit_button.py +5 -5
  469. edc_vitals/calculators/bmi.py +1 -1
  470. edc_vitals/form_validators/blood_pressure_form_validator_mixin.py +8 -10
  471. edc_vitals/form_validators/bmi_form_validator_mixin.py +1 -1
  472. edc_vitals/form_validators/weight_height_with_bmi_form_validator_mixin.py +5 -2
  473. edc_vitals/model_mixins/blood_pressure_model_mixin.py +3 -4
  474. edc_vitals/model_mixins/weight_height_bmi_model_mixin.py +4 -4
  475. edc_vitals/utils.py +1 -1
  476. edc_vitals/validators.py +1 -1
  477. {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.dist-info}/WHEEL +0 -0
  478. {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,3 @@
1
- from ..managers import CurrentSiteManager # noqa (leave for old migrations)
1
+ from ..managers import CurrentSiteManager
2
2
  from .edc_permissions import EdcPermissions
3
3
  from .site_profile import SiteProfile
@@ -9,13 +9,13 @@ from django.db import models
9
9
  class SiteProfile(models.Model):
10
10
  id = models.BigAutoField(primary_key=True)
11
11
 
12
- country = models.CharField(max_length=250, null=True)
12
+ country = models.CharField(max_length=250, default="")
13
13
 
14
- country_code = models.CharField(max_length=15, null=True)
14
+ country_code = models.CharField(max_length=15, default="")
15
15
 
16
- languages = models.TextField(null=True)
16
+ languages = models.TextField(default="")
17
17
 
18
- title = models.CharField(max_length=250, null=True)
18
+ title = models.CharField(max_length=250, default="")
19
19
 
20
20
  site = models.OneToOneField(Site, on_delete=models.PROTECT)
21
21
 
edc_sites/site.py CHANGED
@@ -35,19 +35,19 @@ if TYPE_CHECKING:
35
35
 
36
36
 
37
37
  __all__ = [
38
- "SiteDoesNotExist",
39
38
  "AlreadyRegistered",
40
- "AlreadyRegisteredName",
41
39
  "AlreadyRegisteredDomain",
40
+ "AlreadyRegisteredName",
41
+ "SiteDoesNotExist",
42
42
  "SiteNotRegistered",
43
43
  "SitesCheckError",
44
44
  "SitesError",
45
- "get_register_default_site",
45
+ "get_autodiscover_sites",
46
46
  "get_default_country",
47
47
  "get_default_country_code",
48
48
  "get_default_domain",
49
49
  "get_insert_uat_subdomain",
50
- "get_autodiscover_sites",
50
+ "get_register_default_site",
51
51
  "sites",
52
52
  ]
53
53
 
@@ -160,13 +160,13 @@ class Sites:
160
160
  single_site = dataclasses.replace(single_site, domain=domain)
161
161
 
162
162
  if single_site.site_id in self._registry:
163
- raise AlreadyRegistered(f"Site already registered. Got `{single_site}`.")
164
- elif single_site.name in [s.name for s in self._registry.values()]:
165
- raise AlreadyRegisteredName(
163
+ raise AlreadyRegistered(f"Site already registered. Got `{single_site}`.") # noqa: TRY003
164
+ if single_site.name in [s.name for s in self._registry.values()]:
165
+ raise AlreadyRegisteredName( # noqa: TRY003
166
166
  f"Site with this name is already registered. Got `{single_site}`."
167
167
  )
168
- elif single_site.domain in [s.domain for s in self._registry.values()]:
169
- raise AlreadyRegisteredDomain(
168
+ if single_site.domain in [s.domain for s in self._registry.values()]:
169
+ raise AlreadyRegisteredDomain( # noqa: TRY003
170
170
  f"Site with this domain is already registered. Got `{single_site}`."
171
171
  )
172
172
  self._registry.update({single_site.site_id: single_site})
@@ -187,8 +187,8 @@ class Sites:
187
187
  msg = "In fact, no sites have been registered!"
188
188
  else:
189
189
  msg = f"Expected one of {[s.site_id for s in self.all(aslist=True)]}."
190
- raise SiteNotRegistered(
191
- f"Site not registered. {msg} See {repr(self)}. Got `{site_id}`."
190
+ raise SiteNotRegistered( # noqa: TRY003
191
+ f"Site not registered. {msg} See {self!r}. Got `{site_id}`."
192
192
  )
193
193
  return self._registry.get(site_id)
194
194
 
@@ -196,7 +196,7 @@ class Sites:
196
196
  for single_site in self._registry.values():
197
197
  if getattr(single_site, attrname) == value:
198
198
  return single_site
199
- raise SiteDoesNotExist(f"No site exists with `{attrname}`==`{value}`.")
199
+ raise SiteDoesNotExist(f"No site exists with `{attrname}`==`{value}`.") # noqa: TRY003
200
200
 
201
201
  def all(self, aslist: bool | None = None) -> dict[int, SingleSite] | list[SingleSite]:
202
202
  if aslist:
@@ -237,9 +237,10 @@ class Sites:
237
237
  if request:
238
238
  user = request.user
239
239
  site_id = request.site.id
240
- return [site_id] + self.get_view_only_site_ids_for_user(
241
- request=request, user=user, site_id=site_id
242
- )
240
+ return [
241
+ site_id,
242
+ *self.get_view_only_site_ids_for_user(request=request, user=user, site_id=site_id),
243
+ ]
243
244
 
244
245
  @staticmethod
245
246
  def get_view_only_site_ids_for_user(
@@ -276,14 +277,6 @@ class Sites:
276
277
  add_to_messages_once(
277
278
  request, messages.WARNING, get_message_text(messages.WARNING)
278
279
  )
279
- # else:
280
- # if self.has_viewallsites_permission(request):
281
- # site_ids = [
282
- # s.id
283
- # for s in request.user.userprofile.sites.all()
284
- # if s.id != request.site.id
285
- # ]
286
-
287
280
  return site_ids
288
281
 
289
282
  def user_may_view_other_sites(
@@ -291,25 +284,24 @@ class Sites:
291
284
  request: WSGIRequest = None,
292
285
  user: User = None,
293
286
  site_id: int = None,
294
- ) -> bool:
295
- if self.get_view_only_site_ids_for_user(
287
+ ) -> list[int]:
288
+ return self.get_view_only_site_ids_for_user(
296
289
  request=request,
297
290
  user=user,
298
291
  site_id=site_id,
299
- ):
300
- return True
301
- return False
292
+ )
302
293
 
303
294
  @staticmethod
304
295
  def site_in_profile_or_raise(user: User, site_id: int) -> None:
305
296
  """Raises if user does not have site in their UserProfile."""
306
297
  try:
307
- user.userprofile.sites.get(id=site_id).id
308
- except ObjectDoesNotExist:
309
- raise InvalidSiteForUser(
298
+ user.userprofile.sites.get(id=site_id).id # noqa: B018
299
+ except ObjectDoesNotExist as e:
300
+ errmsg = (
310
301
  "User is not configured to access this site. See also UserProfile. "
311
302
  f"Got {site_id}."
312
303
  )
304
+ raise InvalidSiteForUser(errmsg) from e
313
305
 
314
306
  def get_language_choices_tuple(
315
307
  self, site: Site | None = None, site_id: int | None = None, other=None
@@ -363,7 +355,7 @@ class Sites:
363
355
  except ImportError as e:
364
356
  sites._registry = before_import_registry
365
357
  if module_has_submodule(mod, module_name):
366
- raise SitesError(str(e))
358
+ raise SitesError(str(e)) from e
367
359
  except ImportError:
368
360
  pass
369
361
 
@@ -79,7 +79,7 @@ def match_country_and_country_code_or_raise(single_site: SingleSite, site_obj):
79
79
 
80
80
  def match_languages_or_raise(single_site: SingleSite, site_obj):
81
81
  value1 = single_site.languages
82
- value2 = json.loads(getattr(site_obj.siteprofile, "languages"))
82
+ value2 = json.loads(site_obj.siteprofile.languages)
83
83
  if value1 != value2:
84
84
  raise SitesCheckError(
85
85
  f"Site table is out of sync. Checking {site_obj.id} "
@@ -7,7 +7,7 @@ def get_message_text(level: int) -> str:
7
7
  return _(
8
8
  "You have permissions to view forms and data from sites other than the current. "
9
9
  )
10
- elif level == messages.ERROR:
10
+ if level == messages.ERROR:
11
11
  return _(
12
12
  "Showing data from the current site only. Although you have permissions to view "
13
13
  "data from multiple sites you also have permissions to add, change or delete "
@@ -46,10 +46,9 @@ def valid_site_for_subject_or_raise(
46
46
  "Site not defined for registered subject! "
47
47
  f"Subject identifier=`{subject_identifier}`. "
48
48
  )
49
- else:
50
- raise InvalidSiteForSubjectError(
51
- f"Invalid site for subject. Subject identifier=`{subject_identifier}`. "
52
- f"Expected `{registered_subject.site.name}`. "
53
- f"Got site_id=`{site_obj.id}`"
54
- )
49
+ raise InvalidSiteForSubjectError(
50
+ f"Invalid site for subject. Subject identifier=`{subject_identifier}`. "
51
+ f"Expected `{registered_subject.site.name}`. "
52
+ f"Got site_id=`{site_obj.id}`"
53
+ )
55
54
  return site_obj
@@ -122,8 +122,8 @@ class RequisitionReport(Report):
122
122
  [Paragraph("TEL/MOBILE/FAX", self.styles["line_label"]), ""],
123
123
  [
124
124
  Paragraph(
125
- f'T:{self.shipper.telephone or "?"} M:{self.shipper.mobile or "?"} '
126
- f'F:{self.shipper.fax or "?"}',
125
+ f"T:{self.shipper.telephone or '?'} M:{self.shipper.mobile or '?'} "
126
+ f"F:{self.shipper.fax or '?'}",
127
127
  self.styles["line_data_large"],
128
128
  ),
129
129
  "",
@@ -257,7 +257,7 @@ class RequisitionReport(Report):
257
257
  if not self._requisitions:
258
258
  for k, v in self.appointment.related_visit_model_cls().__dict__.items():
259
259
  try:
260
- model_cls = getattr(getattr(v, "rel"), "related_model")
260
+ model_cls = v.rel.related_model
261
261
  except AttributeError:
262
262
  pass
263
263
  else:
@@ -350,8 +350,7 @@ class RequisitionReport(Report):
350
350
  barcode,
351
351
  Paragraph(str(index + 1), self.styles["row_data"]),
352
352
  Paragraph(
353
- f"{requisition.human_readable_identifier} "
354
- f"({count}/{item_count})",
353
+ f"{requisition.human_readable_identifier} ({count}/{item_count})",
355
354
  self.styles["row_data"],
356
355
  ),
357
356
  Paragraph(
@@ -35,7 +35,9 @@ class RequisitionVerifier:
35
35
  return f"{self.requisition_identifier} {self.verified}"
36
36
 
37
37
  def __repr__(self):
38
- return f"<{self.__class__.__name__}(" "{self.requisition_identifier}) {self.verified}>"
38
+ return (
39
+ f"<{self.__class__.__name__}({{self.requisition_identifier}}) {{self.verified}}>"
40
+ )
39
41
 
40
42
  @property
41
43
  def requisition(self):
@@ -60,7 +62,7 @@ class RequisitionVerifier:
60
62
  visit_model_cls = self.appointment.related_visit.__class__
61
63
  for attr in dir(visit_model_cls):
62
64
  try:
63
- obj = getattr(getattr(visit_model_cls, attr), "rel")
65
+ obj = getattr(visit_model_cls, attr).rel
64
66
  except AttributeError:
65
67
  pass
66
68
  else:
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections import namedtuple
4
- from typing import TYPE_CHECKING, Type, TypeVar
4
+ from typing import TYPE_CHECKING, TypeVar
5
5
 
6
6
  from dateutil.relativedelta import relativedelta
7
7
  from django import template
@@ -56,16 +56,16 @@ if TYPE_CHECKING:
56
56
 
57
57
  __all__ = [
58
58
  "appointment_in_progress",
59
- "render_appointment_status_icon",
60
59
  "print_requisition_popover",
61
- "render_prn_button",
62
60
  "render_appointment_button",
61
+ "render_appointment_status_icon",
63
62
  "render_crf_button_group",
64
- "render_gotoforms_button",
65
- "requisition_panel_actions",
66
63
  "render_crf_totals",
64
+ "render_gotoforms_button",
65
+ "render_prn_button",
67
66
  "render_subject_consent_dashboard_button",
68
67
  "render_unscheduled_appointment_button",
68
+ "requisition_panel_actions",
69
69
  ]
70
70
 
71
71
  register = template.Library()
@@ -302,7 +302,7 @@ def render_related_visit_button(context, appointment: Appointment = None):
302
302
  # from model_wrapper
303
303
  appointment = getattr(appointment, "object", appointment)
304
304
  related_visit: VisitModel = appointment.related_visit
305
- related_visit_model_cls: Type[VisitModel] = appointment.related_visit_model_cls()
305
+ related_visit_model_cls: type[VisitModel] = appointment.related_visit_model_cls()
306
306
  btn = RelatedVisitButton(
307
307
  model_obj=related_visit,
308
308
  model_cls=related_visit_model_cls,
@@ -322,7 +322,7 @@ def render_gotoforms_button(context, appointment: Appointment = None):
322
322
  # from model_wrapper
323
323
  appointment: Appointment = getattr(appointment, "object", appointment)
324
324
  related_visit: VisitModel = appointment.related_visit
325
- related_visit_model_cls: Type[VisitModel] = appointment.related_visit_model_cls()
325
+ related_visit_model_cls: type[VisitModel] = appointment.related_visit_model_cls()
326
326
  btn = GotToFormsButton(
327
327
  model_obj=related_visit,
328
328
  model_cls=related_visit_model_cls,
@@ -424,8 +424,7 @@ def render_unscheduled_appointment_button(
424
424
  ):
425
425
  show_button = True
426
426
  anchor_id = (
427
- f"unscheduled_appt_btn_{appointment.visit_code}_"
428
- f"{appointment.visit_code_sequence}"
427
+ f"unscheduled_appt_btn_{appointment.visit_code}_{appointment.visit_code_sequence}"
429
428
  )
430
429
  if view_appointment and appointment.site.id == context["request"].site.id:
431
430
  url = get_unscheduled_appointment_url(appointment)
@@ -482,9 +481,9 @@ def render_refresh_appointments_button(
482
481
  visit_schedule_name: str = None,
483
482
  schedule_name: str = None,
484
483
  ) -> dict:
485
- if context["request"].user.userprofile.is_multisite_viewer:
486
- url = None
487
- elif context["request"].user.userprofile.roles.filter(name=AUDITOR_ROLE):
484
+ if context["request"].user.userprofile.is_multisite_viewer or context[
485
+ "request"
486
+ ].user.userprofile.roles.filter(name=AUDITOR_ROLE):
488
487
  url = None
489
488
  else:
490
489
  url = reverse(
@@ -508,11 +507,11 @@ def render_refresh_data_collection_schedule_button(
508
507
  visit_schedule_name: str = None,
509
508
  schedule_name: str = None,
510
509
  ) -> dict:
511
- if context["request"].user.userprofile.is_multisite_viewer:
512
- url = None
513
- elif context["request"].user.userprofile.roles.filter(name=AUDITOR_ROLE):
514
- url = None
515
- elif not related_visit_id:
510
+ if (
511
+ context["request"].user.userprofile.is_multisite_viewer
512
+ or context["request"].user.userprofile.roles.filter(name=AUDITOR_ROLE)
513
+ or not related_visit_id
514
+ ):
516
515
  url = None
517
516
  else:
518
517
  url = reverse(
@@ -13,6 +13,6 @@ __all__ = [
13
13
  "RequisitionButton",
14
14
  "SubjectConsentDashboardButton",
15
15
  "SubjectConsentListboardButton",
16
- "TimepointStatusButton",
17
16
  "SubjectScreeningButton",
17
+ "TimepointStatusButton",
18
18
  ]
@@ -19,9 +19,7 @@ class CrfButton(DashboardModelButton):
19
19
  @property
20
20
  def disabled(self) -> str:
21
21
  disabled = "disabled"
22
- if not self.model_obj and self.perms.add:
23
- disabled = ""
24
- elif self.perms.change:
22
+ if (not self.model_obj and self.perms.add) or self.perms.change:
25
23
  disabled = ""
26
24
  elif self.perms.view:
27
25
  if self.model_obj:
@@ -5,7 +5,7 @@ from uuid import UUID
5
5
 
6
6
  __all__ = ["SubjectConsentDashboardButton"]
7
7
 
8
- from typing import TYPE_CHECKING, Type, TypeVar
8
+ from typing import TYPE_CHECKING, TypeVar
9
9
 
10
10
  from edc_view_utils import ModelButton
11
11
 
@@ -21,7 +21,7 @@ class SubjectConsentDashboardButton(ModelButton):
21
21
  """For the subject dashboard"""
22
22
 
23
23
  model_obj: ConsentModel = None
24
- model_cls: Type[ConsentModel] = None
24
+ model_cls: type[ConsentModel] = None
25
25
  appointment: Appointment = None
26
26
 
27
27
  def __post_init__(self):
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
  from dataclasses import dataclass, field
5
- from typing import TYPE_CHECKING, Type, TypeVar
5
+ from typing import TYPE_CHECKING, TypeVar
6
6
  from uuid import UUID
7
7
 
8
8
  from edc_consent import site_consents
@@ -37,7 +37,7 @@ class SubjectConsentListboardButton(ModelButton):
37
37
 
38
38
  screening_obj: ScreeningModel = field(default=None)
39
39
  model_obj: ConsentModel = field(default=None)
40
- model_cls: Type[ConsentModel] = field(default=None)
40
+ model_cls: type[ConsentModel] = field(default=None)
41
41
  consent_version: str = field(default=None, init=False)
42
42
 
43
43
  def __post_init__(self):
@@ -88,9 +88,6 @@ class TimepointStatusButton(AppointmentButton):
88
88
  @property
89
89
  def disabled(self) -> str:
90
90
  disabled = "disabled"
91
- if not self.model_obj and self.perms.add:
91
+ if (not self.model_obj and self.perms.add) or self.perms.change or self.perms.view:
92
92
  disabled = ""
93
- else:
94
- if self.perms.change or self.perms.view:
95
- disabled = ""
96
93
  return disabled
@@ -151,7 +151,7 @@ class RequisitionPrintActionsView(BaseRequisitionView):
151
151
  if not self._requisition_model_cls:
152
152
  for v in self.appointment.visit_model_cls().__dict__.values():
153
153
  try:
154
- model_cls = getattr(getattr(v, "rel"), "related_model")
154
+ model_cls = v.rel.related_model
155
155
  except AttributeError:
156
156
  pass
157
157
  else:
@@ -167,7 +167,7 @@ class RequisitionPrintActionsView(BaseRequisitionView):
167
167
  verified_requisitions = []
168
168
  for k, v in self.appointment.visit_model_cls().__dict__.items():
169
169
  try:
170
- model_cls = getattr(getattr(v, "rel"), "related_model")
170
+ model_cls = v.rel.related_model
171
171
  except AttributeError:
172
172
  pass
173
173
  else:
@@ -50,7 +50,7 @@ class SubjectDashboardView(
50
50
 
51
51
  def __init__(self, **kwargs):
52
52
  if not self.navbar_name:
53
- raise ImproperlyConfigured(f"'navbar_name' cannot be None. See {repr(self)}.")
53
+ raise ImproperlyConfigured(f"'navbar_name' cannot be None. See {self!r}.")
54
54
  super().__init__(**kwargs)
55
55
 
56
56
  @property
@@ -142,9 +142,9 @@ class TimepointModelMixin(models.Model):
142
142
  """Formats and returns the status for the change_list."""
143
143
  if self.timepoint_status == OPEN_TIMEPOINT:
144
144
  return '<span style="color:green;">Open</span>'
145
- elif self.timepoint_status == CLOSED_TIMEPOINT:
145
+ if self.timepoint_status == CLOSED_TIMEPOINT:
146
146
  return '<span style="color:red;">Closed</span>'
147
- elif self.timepoint_status == FEEDBACK:
147
+ if self.timepoint_status == FEEDBACK:
148
148
  return '<span style="color:orange;">Feedback</span>'
149
149
 
150
150
  # timepoint.allow_tags = True
@@ -12,7 +12,7 @@ class SubjectTransferAction(ActionWithNotification):
12
12
  name = SUBJECT_TRANSFER_ACTION
13
13
  display_name = "Submit Subject Transfer"
14
14
  notification_display_name = " Subject Transfer"
15
- parent_action_names = []
15
+ parent_action_names = ()
16
16
  show_link_to_changelist = True
17
17
  show_link_to_add = True
18
18
  priority = HIGH_PRIORITY
@@ -69,13 +69,14 @@ class SubjectTransferModelMixin(
69
69
  return f"{self.subject_identifier} on {transfer_date}."
70
70
 
71
71
  def natural_key(self):
72
- return (self.subject_identifier,) # noqa
72
+ return (self.subject_identifier,)
73
73
 
74
74
  class Meta(SiteModelMixin.Meta, ActionModelMixin.Meta):
75
75
  abstract = True
76
76
  verbose_name = "Subject Transfer"
77
77
  verbose_name_plural = "Subject Transfers"
78
- indexes = ActionModelMixin.Meta.indexes + [
78
+ indexes = (
79
+ *ActionModelMixin.Meta.indexes,
79
80
  models.Index(
80
81
  fields=[
81
82
  "subject_identifier",
@@ -84,4 +85,4 @@ class SubjectTransferModelMixin(
84
85
  "site",
85
86
  ]
86
87
  ),
87
- ]
88
+ )
@@ -1,5 +1,3 @@
1
- from typing import Tuple
2
-
3
1
  from django.contrib import admin
4
2
  from django_audit_fields.admin import audit_fieldset_tuple
5
3
 
@@ -38,7 +36,7 @@ class SubjectTransferModelAdminMixin:
38
36
  "may_contact": admin.VERTICAL,
39
37
  }
40
38
 
41
- def get_list_display(self, request) -> Tuple[str, ...]:
39
+ def get_list_display(self, request) -> tuple[str, ...]:
42
40
  list_display = super().get_list_display(request)
43
41
  custom_fields = (
44
42
  "subject_identifier",
@@ -50,8 +48,7 @@ class SubjectTransferModelAdminMixin:
50
48
  )
51
49
  return custom_fields + tuple(f for f in list_display if f not in custom_fields)
52
50
 
53
- #
54
- def get_list_filter(self, request) -> Tuple[str, ...]:
51
+ def get_list_filter(self, request) -> tuple[str, ...]:
55
52
  list_filter = super().get_list_filter(request)
56
53
  custom_fields = (
57
54
  "transfer_date",
@@ -61,7 +58,7 @@ class SubjectTransferModelAdminMixin:
61
58
  )
62
59
  return custom_fields + tuple(f for f in list_filter if f not in custom_fields)
63
60
 
64
- def get_search_fields(self, request) -> Tuple[str, ...]:
61
+ def get_search_fields(self, request) -> tuple[str, ...]:
65
62
  search_fields = super().get_search_fields(request)
66
63
  custom_fields = ("subject_identifier",)
67
64
  return tuple(set(custom_fields + search_fields))
@@ -12,7 +12,7 @@ class UnblindingRequestAction(ActionWithNotification):
12
12
  name = UNBLINDING_REQUEST_ACTION
13
13
  display_name = "Unblinding request"
14
14
  notification_display_name = " Unblinding request"
15
- parent_action_names = []
15
+ parent_action_names = ()
16
16
  show_link_to_changelist = True
17
17
  show_link_to_add = True
18
18
  priority = HIGH_PRIORITY
@@ -34,7 +34,7 @@ class UnblindingReviewAction(ActionWithNotification):
34
34
  name = UNBLINDING_REVIEW_ACTION
35
35
  display_name = "Unblinding review pending"
36
36
  notification_display_name = " Unblinding review needed"
37
- parent_action_names = [UNBLINDING_REQUEST_ACTION]
37
+ parent_action_names = (UNBLINDING_REQUEST_ACTION,)
38
38
  show_link_to_changelist = True
39
39
  priority = HIGH_PRIORITY
40
40
  color_style = "info"
@@ -66,6 +66,6 @@ class UnblindingRequest(
66
66
  class Meta(BaseUuidModel.Meta, NonUniqueSubjectIdentifierFieldMixin.Meta):
67
67
  verbose_name = "Unblinding Request"
68
68
  verbose_name_plural = "Unblinding Requests"
69
- indexes = [
70
- models.Index(fields=["subject_identifier", "action_identifier", "site", "id"])
71
- ]
69
+ indexes = (
70
+ models.Index(fields=["subject_identifier", "action_identifier", "site", "id"]),
71
+ )
@@ -48,6 +48,6 @@ class UnblindingReview(
48
48
  class Meta(BaseUuidModel.Meta, NonUniqueSubjectIdentifierFieldMixin.Meta):
49
49
  verbose_name = "Unblinding Review"
50
50
  verbose_name_plural = "Unblinding Reviews"
51
- indexes = [
52
- models.Index(fields=["subject_identifier", "action_identifier", "site", "id"])
53
- ]
51
+ indexes = (
52
+ models.Index(fields=["subject_identifier", "action_identifier", "site", "id"]),
53
+ )
edc_utils/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from .age import AgeValueError, age, formatted_age, get_age_in_days, get_dob
2
2
  from .context_processors_check import edc_context_processors_check
3
3
  from .dashboard_middleware_check import edc_middleware_check
4
- from .date import ceil_secs, floor_secs, get_utcnow, get_utcnow_as_date, to_utc
4
+ from .date import ceil_secs, floor_secs, get_utcnow, get_utcnow_as_date, to_local, to_utc
5
5
  from .disable_signals import DisableSignals
6
6
  from .get_datetime_from_env import get_datetime_from_env
7
7
  from .get_static_file import get_static_file
@@ -25,7 +25,6 @@ __all__ = [
25
25
  "DisableSignals",
26
26
  "age",
27
27
  "ceil_secs",
28
- "message_in_queue",
29
28
  "convert_from_camel",
30
29
  "convert_php_dateformat",
31
30
  "edc_context_processors_check",
@@ -43,13 +42,15 @@ __all__ = [
43
42
  "get_utcnow",
44
43
  "get_utcnow_as_date",
45
44
  "get_uuid",
45
+ "message_in_queue",
46
46
  "paths_for_urlpatterns",
47
- "round_up",
48
- "round_half_up",
49
47
  "round_half_away_from_zero",
48
+ "round_half_up",
49
+ "round_up",
50
50
  "safe_allowed_chars",
51
51
  "show_url_names",
52
52
  "show_urls",
53
- "truncate_string",
53
+ "to_local",
54
54
  "to_utc",
55
+ "truncate_string",
55
56
  ]
edc_utils/age.py CHANGED
@@ -52,7 +52,7 @@ def age(
52
52
  rdelta = relativedelta(reference_dt_utc, born_utc)
53
53
  if born_utc > reference_dt_utc:
54
54
  raise AgeValueError(
55
- f"Reference date {reference_dt} {str(reference_dt.tzinfo)} "
55
+ f"Reference date {reference_dt} {reference_dt.tzinfo!s} "
56
56
  f"precedes DOB {born} {timezone}. Got {rdelta}"
57
57
  )
58
58
  return rdelta
edc_utils/celery.py CHANGED
@@ -9,14 +9,9 @@ celery_enabled = getattr(settings, "CELERY_ENABLED", False)
9
9
  def run_task_sync_or_async(task, *args, **kwargs) -> AsyncResult:
10
10
  """Run a task with celery if running"""
11
11
 
12
- if not celery_enabled:
12
+ if not celery_enabled or not celery_is_active() or current_app.conf.task_always_eager:
13
13
  return task(*args, **kwargs)
14
- elif not celery_is_active():
15
- return task(*args, **kwargs)
16
- elif current_app.conf.task_always_eager:
17
- return task(*args, **kwargs)
18
- else:
19
- return task.delay(*args, **kwargs)
14
+ return task.delay(*args, **kwargs)
20
15
 
21
16
 
22
17
  def celery_is_active() -> dict:
@@ -45,4 +40,4 @@ def get_task_result(obj) -> AsyncResult | None:
45
40
  return result
46
41
 
47
42
 
48
- __all__ = ["run_task_sync_or_async", "get_task_result", "celery_is_active"]
43
+ __all__ = ["celery_is_active", "get_task_result", "run_task_sync_or_async"]
@@ -9,7 +9,7 @@ def get_static_file(app_label: str, filename: str) -> str:
9
9
  path = os.path.join(settings.STATIC_ROOT or "", app_label, filename)
10
10
  if os.path.isfile(path):
11
11
  try:
12
- with open(path, "r"):
12
+ with open(path):
13
13
  pass
14
14
  except FileNotFoundError:
15
15
  path = os.path.join(f"https://{settings.STATIC_URL}", app_label, filename)