clinicedc 2.0.5__py3-none-any.whl → 2.0.7__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 (387) hide show
  1. {clinicedc-2.0.5.dist-info → clinicedc-2.0.7.dist-info}/METADATA +2 -2
  2. {clinicedc-2.0.5.dist-info → clinicedc-2.0.7.dist-info}/RECORD +387 -336
  3. edc_action_item/action.py +1 -1
  4. edc_action_item/action_item_notification.py +2 -2
  5. edc_action_item/create_action_item.py +1 -1
  6. edc_action_item/decorators.py +1 -1
  7. edc_action_item/migrations/0001_initial.py +21 -22
  8. edc_action_item/migrations/0006_auto_20180707_1659.py +4 -4
  9. edc_action_item/migrations/0008_auto_20180809_0303.py +4 -4
  10. edc_action_item/migrations/0015_auto_20190114_0250.py +4 -4
  11. edc_action_item/migrations/0017_auto_20190305_0123.py +11 -30
  12. edc_action_item/migrations/0030_edcpermissions.py +3 -2
  13. edc_action_item/migrations/0039_alter_actionitem_auto_created_comment_and_more.py +398 -0
  14. edc_action_item/migrations/0040_alter_actionitem_report_datetime_and_more.py +34 -0
  15. edc_action_item/models/action_item.py +9 -4
  16. edc_action_item/models/action_model_mixin.py +4 -4
  17. edc_action_item/models/reference.py +2 -2
  18. edc_action_item/post_migrate_signals.py +1 -1
  19. edc_action_item/send_email.py +0 -85
  20. edc_adherence/migrations/0005_alter_nonadherencereasons_extra_value_and_more.py +36 -0
  21. edc_adherence/model_mixins.py +1 -1
  22. edc_adverse_event/migrations/0001_initial.py +5 -4
  23. edc_adverse_event/migrations/0002_auto_20190802_0059.py +3 -2
  24. edc_adverse_event/migrations/0008_auto_20220825_0451.py +3 -2
  25. edc_adverse_event/migrations/0009_auto_20220907_0157.py +3 -2
  26. edc_adverse_event/migrations/0016_alter_aeactionclassification_device_created_and_more.py +285 -0
  27. edc_adverse_event/model_mixins/ae_followup/ae_followup_fields_model_mixin.py +2 -2
  28. edc_adverse_event/model_mixins/ae_initial/ae_initial_fields_model_mixin.py +6 -5
  29. edc_adverse_event/model_mixins/ae_special_interest/aesi_fields_model_mixin.py +2 -2
  30. edc_adverse_event/model_mixins/ae_susar/ae_susar_fields_model_mixin.py +2 -2
  31. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_fields_model_mixin.py +15 -11
  32. edc_adverse_event/model_mixins/death_report/death_report_model_mixin.py +5 -4
  33. edc_adverse_event/model_mixins/death_report/death_report_tmg_model_mixin.py +6 -3
  34. edc_adverse_event/model_mixins/death_report/simple_death_report_model_mixin.py +2 -2
  35. edc_adverse_event/model_mixins/hospitaization/hospitalization_model_mixin.py +4 -3
  36. edc_adverse_event/modeladmin_mixins/death_report_admin_mixin.py +3 -3
  37. edc_adverse_event/models/signals.py +7 -7
  38. edc_adverse_event/templatetags/edc_adverse_event_extras.py +5 -4
  39. edc_adverse_event/view_mixins/ae/ae_listboard_view_mixin.py +3 -5
  40. edc_adverse_event/view_mixins/ae/death_report_listboard_view_mixin.py +6 -8
  41. edc_adverse_event/view_mixins/tmg/tmg_ae_listboard_view_mixin.py +2 -2
  42. edc_adverse_event/views/tmg/summary_listboard_view.py +3 -2
  43. edc_appointment/admin/appointment_admin.py +2 -2
  44. edc_appointment/admin/list_filters.py +2 -2
  45. edc_appointment/form_validators/appointment_form_validator.py +108 -82
  46. edc_appointment/migrations/0003_auto_20161127_2226.py +5 -6
  47. edc_appointment/migrations/0006_auto_20170106_2118.py +5 -5
  48. edc_appointment/migrations/0018_auto_20190305_0123.py +5 -12
  49. edc_appointment/migrations/0050_alter_appointment_appt_type_and_more.py +220 -0
  50. edc_appointment/model_mixins/appointment_fields_model_mixin.py +1 -2
  51. edc_appointment/model_mixins/window_period_model_mixin.py +6 -7
  52. edc_appointment/models/signals.py +44 -35
  53. edc_appointment/view_utils/appointment_button.py +2 -2
  54. edc_auth/admin/user_admin.py +1 -1
  55. edc_auth/migrations/0001_squashed_0033_alter_userprofile_is_multisite_viewer.py +5 -5
  56. edc_auth/migrations/0012_auto_20191026_0034.py +3 -2
  57. edc_auth/migrations/0013_auto_20191026_0055.py +3 -2
  58. edc_auth/migrations/0025_permissions.py +3 -2
  59. edc_auth/migrations/0035_alter_edcpermissions_device_created_and_more.py +85 -0
  60. edc_auth/post_migrate_signals.py +1 -1
  61. edc_consent/actions.py +4 -5
  62. edc_consent/migrations/0001_initial.py +3 -2
  63. edc_consent/migrations/0006_alter_edcpermissions_device_created_and_more.py +49 -0
  64. edc_consent/model_mixins/consent_extension_model_mixin.py +4 -5
  65. edc_consent/model_mixins/requires_consent_fields_model_mixin.py +2 -2
  66. edc_consent/utils.py +1 -1
  67. edc_crf/migrations/0001_initial.py +3 -2
  68. edc_crf/migrations/0009_alter_crfstatus_device_created_and_more.py +54 -0
  69. edc_dashboard/migrations/0001_initial.py +3 -2
  70. edc_dashboard/migrations/0006_alter_edcpermissions_device_created_and_more.py +49 -0
  71. edc_dashboard/templatetags/edc_dashboard_extras.py +3 -2
  72. edc_data_manager/action_items.py +4 -6
  73. edc_data_manager/admin/actions.py +5 -5
  74. edc_data_manager/handlers/handlers.py +6 -7
  75. edc_data_manager/migrations/0001_initial.py +19 -19
  76. edc_data_manager/migrations/0025_edcpermissions.py +3 -2
  77. edc_data_manager/migrations/0040_alter_datadictionary_device_created_and_more.py +327 -0
  78. edc_data_manager/migrations/0041_alter_dataquery_dm_user_and_more.py +164 -0
  79. edc_data_manager/models/data_query.py +5 -5
  80. edc_data_manager/models/model_mixins.py +8 -8
  81. edc_data_manager/rule/rule_runner.py +4 -3
  82. edc_document_status/model_mixins.py +1 -1
  83. edc_egfr/egfr.py +11 -7
  84. edc_egfr/model_mixins/egfr_model_mixin.py +3 -7
  85. edc_export/admin/data_request_admin.py +2 -2
  86. edc_export/archive_exporter.py +4 -3
  87. edc_export/files_archiver.py +3 -2
  88. edc_export/migrations/0001_initial.py +26 -26
  89. edc_export/migrations/0004_auto_20190305_0123.py +25 -72
  90. edc_export/migrations/0013_edcpermissions.py +3 -2
  91. edc_export/migrations/0022_alter_datarequest_description_and_more.py +611 -0
  92. edc_export/migrations/0023_alter_datarequesthistory_archive_filename_and_more.py +29 -0
  93. edc_export/model_exporter/file_history_updater.py +10 -10
  94. edc_export/model_exporter/model_exporter.py +13 -14
  95. edc_export/model_exporter/object_history_helpers.py +2 -2
  96. edc_export/models/data_request_history.py +4 -4
  97. edc_export/utils.py +10 -15
  98. edc_facility/facility.py +10 -15
  99. edc_facility/import_holidays.py +13 -13
  100. edc_facility/migrations/0005_healthfacility_healthfacilitytypes_and_more.py +8 -7
  101. edc_facility/migrations/0016_alter_healthfacility_device_created_and_more.py +139 -0
  102. edc_facility/migrations/0017_alter_healthfacility_gps_and_more.py +54 -0
  103. edc_facility/model_mixins.py +5 -5
  104. edc_form_runners/form_runner.py +3 -5
  105. edc_form_runners/migrations/0001_initial.py +3 -2
  106. edc_form_runners/migrations/0005_alter_issue_device_created_and_more.py +49 -0
  107. edc_identifier/migrations/0001_squashed_0018_auto_20180128_1054.py +3 -3
  108. edc_identifier/migrations/0002_auto_20190305_0123.py +3 -6
  109. edc_identifier/migrations/0006_auto_20161127_2226.py +15 -15
  110. edc_identifier/migrations/0007_auto_20161204_2227.py +3 -3
  111. edc_identifier/migrations/0009_auto_20161221_2323.py +3 -5
  112. edc_identifier/migrations/0010_auto_20170112_0602.py +17 -17
  113. edc_identifier/migrations/0011_alter_identifiermodel_device_created_and_more.py +79 -0
  114. edc_identifier/model_mixins.py +1 -1
  115. edc_identifier/models.py +9 -9
  116. edc_identifier/simple_identifier.py +1 -3
  117. edc_identifier/subject_identifier.py +3 -2
  118. edc_lab/migrations/0001_initial.py +29 -29
  119. edc_lab/migrations/0008_auto_20170921_0719.py +3 -3
  120. edc_lab/migrations/0010_auto_20171127_1541.py +15 -15
  121. edc_lab/migrations/0012_auto_20180114_1438.py +3 -3
  122. edc_lab/migrations/0019_auto_20190305_0123.py +43 -127
  123. edc_lab/migrations/0036_alter_aliquot_comment_alter_aliquot_device_created_and_more.py +1139 -0
  124. edc_lab/migrations/0037_alter_historicalorder_order_datetime_and_more.py +31 -0
  125. edc_lab/model_mixins/aliquot/aliquot_identifier_model_mixin.py +2 -2
  126. edc_lab/model_mixins/aliquot/aliquot_model_mixin.py +2 -2
  127. edc_lab/model_mixins/panel_model_mixin.py +18 -7
  128. edc_lab/model_mixins/requisition/crf_with_requisition_model_mixin.py +2 -4
  129. edc_lab/model_mixins/requisition/requisition_model_mixin.py +7 -8
  130. edc_lab/model_mixins/requisition/requisition_verify_model_mixin.py +1 -1
  131. edc_lab/model_mixins/result/result_item_model_mixin.py +4 -4
  132. edc_lab/model_mixins/shipping/manifest_model_mixin.py +4 -5
  133. edc_lab/model_mixins/shipping/verify_model_mixin.py +4 -6
  134. edc_lab/models/box.py +1 -1
  135. edc_lab/models/box_item.py +6 -7
  136. edc_lab/models/manifest/manifest.py +1 -1
  137. edc_lab/models/manifest/manifest_item.py +6 -6
  138. edc_lab/models/manifest/shipper.py +3 -4
  139. edc_lab/models/order.py +6 -4
  140. edc_lab/models/panel.py +4 -4
  141. edc_lab/models/result.py +2 -2
  142. edc_lab/models/result_item.py +2 -2
  143. edc_lab_dashboard/migrations/0001_initial.py +3 -2
  144. edc_lab_dashboard/migrations/0005_alter_edcpermissions_device_created_and_more.py +49 -0
  145. edc_lab_dashboard/views/action_views/receive_view.py +2 -2
  146. edc_lab_dashboard/views/action_views/verify_box_item_view.py +2 -2
  147. edc_lab_results/model_mixins/blood_result_model_mixin.py +7 -7
  148. edc_lab_results/model_mixins/fbg_model_mixin.py +3 -3
  149. edc_lab_results/model_mixins/glucose_model_mixin.py +3 -3
  150. edc_lab_results/model_mixins/hba1c_model_mixin.py +3 -3
  151. edc_label/migrations/0001_initial.py +3 -2
  152. edc_label/migrations/0007_alter_zpllabeltemplates_device_created_and_more.py +49 -0
  153. edc_list_data/list_model_maker.py +2 -2
  154. edc_list_data/load_list_data.py +11 -14
  155. edc_list_data/model_mixins.py +12 -4
  156. edc_list_data/preload_data.py +3 -3
  157. edc_listboard/migrations/0001_initial.py +3 -2
  158. edc_listboard/migrations/0007_alter_listboard_device_created_and_more.py +49 -0
  159. edc_locator/migrations/0001_initial.py +7 -7
  160. edc_locator/migrations/0018_auto_20190305_0123.py +5 -12
  161. edc_locator/migrations/0040_alter_historicalsubjectlocator_action_identifier_and_more.py +187 -0
  162. edc_locator/migrations/0041_alter_historicalsubjectlocator_report_datetime_and_more.py +27 -0
  163. edc_locator/model_mixins/locator_model_mixin.py +2 -2
  164. edc_locator/model_mixins/subject_contact_fields_mixin.py +1 -1
  165. edc_locator/modeladmin_mixins.py +5 -4
  166. edc_ltfu/model_mixins.py +7 -5
  167. edc_metadata/metadata_rules/decorators.py +1 -1
  168. edc_metadata/migrations/0002_auto_20161127_2226.py +5 -5
  169. edc_metadata/migrations/0005_auto_20170112_0602.py +5 -5
  170. edc_metadata/migrations/0011_auto_20190305_0123.py +5 -12
  171. edc_metadata/migrations/0031_alter_crfmetadata_device_created_and_more.py +120 -0
  172. edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +1 -1
  173. edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +1 -1
  174. edc_metadata/models/crf_metadata.py +5 -5
  175. edc_metadata/models/crf_metadata_model_mixin.py +4 -5
  176. edc_metadata/models/requisition_metadata.py +6 -6
  177. edc_metadata/views/refresh_metadata_actions_view.py +3 -3
  178. edc_model/models/address_mixin.py +6 -6
  179. edc_model/models/fields/initials_field.py +2 -1
  180. edc_model/models/historical_records.py +2 -9
  181. edc_model/models/url_model_mixin.py +2 -2
  182. edc_model/validators/date.py +7 -8
  183. edc_model_admin/list_filters/future_date_list_filter.py +3 -3
  184. edc_model_admin/list_filters/past_date_list_filter.py +3 -3
  185. edc_model_admin/mixins/model_admin_protect_pii_mixin.py +4 -5
  186. edc_model_fields/fields/other_charfield.py +2 -2
  187. edc_navbar/migrations/0004_auto_20220825_0451.py +3 -2
  188. edc_navbar/migrations/0009_alter_edcpermissions_device_created_and_more.py +49 -0
  189. edc_notification/migrations/0001_initial.py +3 -3
  190. edc_notification/migrations/0004_auto_20190305_0123.py +3 -6
  191. edc_notification/migrations/0011_alter_notification_device_created_and_more.py +59 -0
  192. edc_notification/models/notification.py +1 -1
  193. edc_notification/notification/notification.py +12 -12
  194. edc_offstudy/migrations/0001_initial.py +4 -4
  195. edc_offstudy/migrations/0002_auto_20180921_0434.py +4 -4
  196. edc_offstudy/migrations/0005_auto_20190305_0123.py +5 -12
  197. edc_offstudy/migrations/0023_alter_historicalsubjectoffstudy_action_identifier_and_more.py +188 -0
  198. edc_offstudy/migrations/0024_alter_historicalsubjectoffstudy_offstudy_datetime_and_more.py +43 -0
  199. edc_offstudy/model_mixins/offstudy_model_mixin.py +3 -2
  200. edc_pdutils/df_exporters/csv_exporter.py +33 -37
  201. edc_pdutils/df_exporters/csv_model_exporter.py +2 -1
  202. edc_pdutils/migrations/0001_initial.py +8 -8
  203. edc_pharmacy/admin/prescription/rx_refill_admin.py +4 -3
  204. edc_pharmacy/admin/reports/stock_availability_admin.py +5 -6
  205. edc_pharmacy/migrations/0001_initial.py +64 -63
  206. edc_pharmacy/migrations/0015_auto_20220913_2139.py +26 -26
  207. edc_pharmacy/migrations/0024_allocation_assignment_containerunits_dispense_and_more.py +81 -86
  208. edc_pharmacy/migrations/0037_remove_historicalstock_confirmed_at_site_by_and_more.py +5 -5
  209. edc_pharmacy/migrations/0039_remove_dispense_registered_subject_and_more.py +7 -8
  210. edc_pharmacy/migrations/0049_remove_stocktransferconfirmation_stock_and_more.py +12 -12
  211. edc_pharmacy/migrations/0050_remove_stocktransferconfirmation2_location_and_more.py +5 -5
  212. edc_pharmacy/migrations/0051_alter_historicalstocktransferconfirmationitem_options_and_more.py +3 -4
  213. edc_pharmacy/migrations/0053_alter_location_managers_alter_historicalstock_lot_and_more.py +3 -3
  214. edc_pharmacy/migrations/0057_scanduplicates.py +3 -3
  215. edc_pharmacy/migrations/0060_alter_container_max_per_subject_and_more.py +13 -15
  216. edc_pharmacy/migrations/0068_stockout.py +2 -3
  217. edc_pharmacy/migrations/0076_historicalstockadjustment_stockadjustment.py +5 -5
  218. edc_pharmacy/migrations/0077_historicalstockadjustment_adjustment_datetime_and_more.py +3 -4
  219. edc_pharmacy/migrations/0081_historicalconfirmation_confirmation.py +8 -8
  220. edc_pharmacy/migrations/0084_confirmationatsiteitem_and_more.py +0 -2
  221. edc_pharmacy/migrations/0089_alter_allocation_allocated_by_and_more.py +3284 -0
  222. edc_pharmacy/migrations/0090_alter_allocation_allocation_datetime_and_more.py +227 -0
  223. edc_pharmacy/model_mixins/study_medication_refill_model_mixin.py +2 -2
  224. edc_pharmacy/models/medication/medication.py +3 -3
  225. edc_pharmacy/models/model_mixins.py +10 -11
  226. edc_pharmacy/models/prescription/rx.py +13 -12
  227. edc_pharmacy/models/prescription/rx_refill.py +0 -1
  228. edc_pharmacy/models/stock/allocation.py +3 -3
  229. edc_pharmacy/models/stock/confirmation.py +3 -3
  230. edc_pharmacy/models/stock/confirmation_at_site.py +3 -2
  231. edc_pharmacy/models/stock/confirmation_at_site_item.py +3 -4
  232. edc_pharmacy/models/stock/dispense.py +5 -6
  233. edc_pharmacy/models/stock/dispense_item.py +2 -2
  234. edc_pharmacy/models/stock/location.py +3 -4
  235. edc_pharmacy/models/stock/lot.py +6 -7
  236. edc_pharmacy/models/stock/order.py +1 -1
  237. edc_pharmacy/models/stock/receive.py +3 -3
  238. edc_pharmacy/models/stock/receive_item.py +4 -4
  239. edc_pharmacy/models/stock/repack_request.py +2 -2
  240. edc_pharmacy/models/stock/stock.py +12 -18
  241. edc_pharmacy/models/stock/stock_adjustment.py +4 -4
  242. edc_pharmacy/models/stock/stock_request.py +2 -2
  243. edc_pharmacy/models/stock/stock_request_item.py +2 -2
  244. edc_pharmacy/models/stock/stock_transfer.py +3 -3
  245. edc_pharmacy/models/stock/stock_transfer_item.py +2 -2
  246. edc_pharmacy/models/stock/storage_bin.py +3 -4
  247. edc_pharmacy/models/stock/storage_bin_item.py +2 -2
  248. edc_pharmacy/models/stock/supplier.py +3 -4
  249. edc_pharmacy/models/storage/box.py +2 -2
  250. edc_pharmacy/models/storage/items/container_model_mixin.py +3 -4
  251. edc_pharmacy/models/storage/items/pill_bottle_model_mixin.py +2 -2
  252. edc_pharmacy/models/storage/items/subject_pill_bottle.py +1 -1
  253. edc_pharmacy/models/storage/room.py +2 -2
  254. edc_pharmacy/models/storage/shelf.py +2 -2
  255. edc_pharmacy/models/storage/utils.py +1 -1
  256. edc_pharmacy/utils/allocate_stock.py +2 -3
  257. edc_pharmacy/utils/confirm_stock.py +2 -3
  258. edc_pharmacy/utils/confirm_stock_at_site.py +3 -4
  259. edc_pharmacy/utils/dispense.py +3 -4
  260. edc_pharmacy/utils/process_repack_request.py +4 -5
  261. edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py +2 -3
  262. edc_pharmacy/utils/transfer_stock.py +2 -3
  263. edc_pharmacy/views/add_to_storage_bin_view.py +2 -2
  264. edc_pharmacy/views/allocate_to_subject_view.py +2 -2
  265. edc_pharmacy/views/dispense_view.py +2 -2
  266. edc_pharmacy/views/move_to_storage_bin_view.py +2 -3
  267. edc_pharmacy/views/print_labels_view.py +2 -2
  268. edc_protocol/research_protocol_config.py +2 -3
  269. edc_protocol_incident/migrations/0001_initial.py +7 -7
  270. edc_protocol_incident/migrations/0001_squashed_0015_auto_20220927_0401.py +13 -13
  271. edc_protocol_incident/migrations/0005_protocolincident_historicalprotocolincident_and_more.py +7 -7
  272. edc_protocol_incident/migrations/0024_alter_actionsrequired_extra_value_and_more.py +625 -0
  273. edc_protocol_incident/migrations/0025_alter_historicalprotocoldeviationviolation_report_datetime_and_more.py +42 -0
  274. edc_protocol_incident/model_mixins/protocol_deviation_violation_model_mixin.py +18 -11
  275. edc_protocol_incident/model_mixins/protocol_incident_model_mixin.py +26 -11
  276. edc_pylabels/actions.py +3 -5
  277. edc_pylabels/migrations/0002_alter_label_options_label_created_and_more.py +3 -7
  278. edc_pylabels/migrations/0005_labelconfiguration_delete_label_and_more.py +3 -3
  279. edc_pylabels/migrations/0013_alter_labelconfiguration_device_created_and_more.py +49 -0
  280. edc_qareports/migrations/0001_initial.py +4 -5
  281. edc_qareports/migrations/0005_edcpermissions.py +3 -3
  282. edc_qareports/migrations/0006_qareportlog.py +2 -3
  283. edc_qareports/migrations/0019_alter_edcpermissions_device_created_and_more.py +102 -0
  284. edc_qareports/migrations/0020_alter_note_report_datetime_and_more.py +24 -0
  285. edc_qareports/model_mixins/note_model_mixin.py +7 -4
  286. edc_qareports/model_mixins/on_study_missing_values_model_mixin.py +4 -4
  287. edc_qareports/model_mixins/qa_report_model_mixin.py +2 -3
  288. edc_qareports/models/note.py +4 -4
  289. edc_qareports/models/qa_reports_log.py +2 -3
  290. edc_randomization/decorators.py +1 -1
  291. edc_randomization/migrations/0001_initial.py +3 -2
  292. edc_randomization/migrations/0002_historicalrandomizationlist.py +3 -2
  293. edc_randomization/migrations/0009_edcpermissions.py +3 -2
  294. edc_randomization/migrations/0014_alter_edcpermissions_device_created_and_more.py +141 -0
  295. edc_randomization/model_mixins.py +9 -9
  296. edc_randomization/utils.py +2 -2
  297. edc_refusal/migrations/0001_initial.py +4 -4
  298. edc_refusal/migrations/0002_historicalsubjectrefusal.py +4 -4
  299. edc_refusal/migrations/0012_alter_historicalsubjectrefusal_comment_and_more.py +122 -0
  300. edc_refusal/migrations/0013_alter_historicalsubjectrefusal_report_datetime_and_more.py +28 -0
  301. edc_refusal/model_mixins.py +3 -3
  302. edc_registration/migrations/0002_auto_20161127_2226.py +3 -4
  303. edc_registration/migrations/0005_auto_20170111_1809.py +4 -2
  304. edc_registration/migrations/0016_historicalregisteredsubject.py +3 -2
  305. edc_registration/migrations/0017_auto_20190305_0123.py +5 -12
  306. edc_registration/migrations/0033_alter_historicalregisteredsubject_additional_key_and_more.py +302 -0
  307. edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +2 -3
  308. edc_registration/models/registered_subject.py +27 -26
  309. edc_reportable/age_evaluator.py +4 -2
  310. edc_reportable/formula.py +5 -5
  311. edc_reportable/migrations/0001_initial.py +7 -7
  312. edc_reportable/migrations/0003_referencerangecollection_grade1_and_more.py +3 -3
  313. edc_reportable/migrations/0004_alter_referencerangecollection_grade3_and_more.py +5 -5
  314. edc_reportable/migrations/0006_alter_gradingdata_revision_and_more.py +5 -6
  315. edc_reportable/migrations/0007_alter_gradingdata_age_phrase_and_more.py +509 -0
  316. edc_reportable/models/normal_data.py +4 -5
  317. edc_reportable/models/reference_model_mixins.py +10 -11
  318. edc_reportable/models/reference_range_collection.py +12 -13
  319. edc_reportable/post_migrate_signals.py +1 -1
  320. edc_reportable/utils/convert_units.py +11 -45
  321. edc_reportable/utils/get_normal_data_or_raise.py +2 -2
  322. edc_reportable/utils/load_data.py +4 -4
  323. edc_reportable/utils/update_grading_data.py +5 -5
  324. edc_review_dashboard/migrations/0001_initial.py +3 -2
  325. edc_review_dashboard/migrations/0006_alter_edcpermissions_device_created_and_more.py +49 -0
  326. edc_screening/age_evaluator.py +3 -3
  327. edc_screening/migrations/0001_initial.py +3 -2
  328. edc_screening/migrations/0005_alter_edcpermissions_device_created_and_more.py +49 -0
  329. edc_screening/model_mixins/eligibility_model_mixin.py +3 -4
  330. edc_screening/model_mixins/screening_fields_model_mixin.py +6 -8
  331. edc_search/model_mixins.py +2 -3
  332. edc_search/search_slug.py +9 -8
  333. edc_search/updater.py +2 -2
  334. edc_sites/migrations/0007_edcpermissions.py +3 -2
  335. edc_sites/migrations/0010_alter_edcpermissions_device_created_and_more.py +69 -0
  336. edc_sites/model_mixins/site_model_mixin.py +2 -2
  337. edc_sites/site.py +5 -5
  338. edc_sites/system_checks.py +1 -1
  339. edc_subject_dashboard/migrations/0001_initial.py +3 -2
  340. edc_subject_dashboard/migrations/0005_alter_edcpermissions_device_created_and_more.py +49 -0
  341. edc_subject_dashboard/requisition_report.py +6 -4
  342. edc_subject_dashboard/requisition_verifier.py +6 -6
  343. edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +2 -2
  344. edc_subject_dashboard/view_mixins/subject_visit_view_mixin.py +7 -8
  345. edc_subject_dashboard/view_utils/subject_consent_listboard_button.py +3 -2
  346. edc_subject_dashboard/view_utils/timepoint_status_button.py +2 -2
  347. edc_timepoint/model_mixins.py +22 -20
  348. edc_transfer/model_mixins.py +4 -4
  349. edc_transfer/modeladmin_mixins.py +1 -1
  350. edc_unblinding/migrations/0001_initial.py +13 -13
  351. edc_unblinding/migrations/0014_alter_historicalunblindingrequest_action_identifier_and_more.py +291 -0
  352. edc_unblinding/migrations/0015_alter_historicalunblindingrequest_report_datetime_and_more.py +45 -0
  353. edc_unblinding/models/unblinding_request.py +2 -2
  354. edc_unblinding/models/unblinding_review.py +3 -3
  355. edc_utils/age.py +10 -16
  356. edc_visit_schedule/migrations/0001_initial.py +3 -3
  357. edc_visit_schedule/migrations/0002_auto_20190305_0123.py +3 -6
  358. edc_visit_schedule/migrations/0003_historicalvisitschedule_visitschedule.py +5 -4
  359. edc_visit_schedule/migrations/0015_historicalonschedule_offschedule_onschedule.py +10 -10
  360. edc_visit_schedule/migrations/0019_alter_historicalonschedule_device_created_and_more.py +229 -0
  361. edc_visit_schedule/migrations/0020_alter_historicalonschedule_onschedule_datetime_and_more.py +65 -0
  362. edc_visit_schedule/model_mixins/off_schedule_model_mixin.py +5 -4
  363. edc_visit_schedule/model_mixins/on_schedule_model_mixin.py +5 -4
  364. edc_visit_schedule/model_mixins/visit_schedule/visit_code_fields_model_mixin.py +1 -1
  365. edc_visit_schedule/models/subject_schedule_history.py +6 -6
  366. edc_visit_schedule/schedule/visit_collection.py +1 -1
  367. edc_visit_schedule/subject_schedule.py +5 -4
  368. edc_visit_schedule/view_mixins.py +2 -3
  369. edc_visit_schedule/visit/visit.py +3 -2
  370. edc_visit_tracking/migrations/0004_subjectvisit_subjectvisitmissedreasons_extra_value_and_more.py +13 -13
  371. edc_visit_tracking/migrations/0009_alter_historicalsubjectvisit_comments_and_more.py +450 -0
  372. edc_visit_tracking/migrations/0010_alter_historicalsubjectvisit_report_datetime_and_more.py +68 -0
  373. edc_visit_tracking/model_mixins/base/visit_methods_model_mixin.py +3 -3
  374. edc_visit_tracking/model_mixins/crfs/visit_tracking_crf_model_mixin.py +2 -2
  375. edc_visit_tracking/model_mixins/requisitions/visit_tracking_requisition_model_mixin.py +2 -2
  376. edc_visit_tracking/model_mixins/subject_visit_missed_model_mixin.py +2 -2
  377. edc_visit_tracking/model_mixins/visit_model_mixin/caretaker_fields_mixin.py +2 -3
  378. edc_visit_tracking/model_mixins/visit_model_mixin/previous_visit_model_mixin.py +1 -1
  379. edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_fields_mixin.py +8 -11
  380. edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_mixin.py +1 -1
  381. edc_visit_tracking/models/signals.py +1 -1
  382. edc_visit_tracking/models/subject_visit.py +1 -1
  383. edc_vitals/calculators/bmi.py +7 -5
  384. edc_vitals/form_validators/blood_pressure_form_validator_mixin.py +3 -1
  385. edc_vitals/utils.py +1 -1
  386. {clinicedc-2.0.5.dist-info → clinicedc-2.0.7.dist-info}/WHEEL +0 -0
  387. {clinicedc-2.0.5.dist-info → clinicedc-2.0.7.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,5 @@
1
1
  from django.db import models
2
+ from django.utils import timezone
2
3
 
3
4
  from edc_action_item.managers import (
4
5
  ActionIdentifierModelManager,
@@ -9,7 +10,6 @@ from edc_identifier.model_mixins import UniqueSubjectIdentifierFieldMixin
9
10
  from edc_model.validators import date_not_future, datetime_not_future
10
11
  from edc_protocol.validators import datetime_not_before_study_start
11
12
  from edc_sites.model_mixins import SiteModelMixin
12
- from edc_utils import get_utcnow
13
13
 
14
14
  from ...constants import DEATH_REPORT_ACTION
15
15
 
@@ -27,7 +27,7 @@ class SimpleDeathReportModelMixin(
27
27
  report_datetime = models.DateTimeField(
28
28
  verbose_name="Report Date",
29
29
  validators=[datetime_not_before_study_start, datetime_not_future],
30
- default=get_utcnow,
30
+ default=timezone.now,
31
31
  )
32
32
 
33
33
  death_datetime = models.DateTimeField(
@@ -1,22 +1,23 @@
1
1
  from django.db import models
2
+ from django.utils import timezone
2
3
 
3
4
  from edc_constants.choices import YES_NO, YES_NO_UNKNOWN
4
5
  from edc_constants.constants import NOT_APPLICABLE
5
6
  from edc_identifier.model_mixins import NonUniqueSubjectIdentifierFieldMixin
6
7
  from edc_model import models as edc_models
7
8
  from edc_model.validators import date_not_future, datetime_not_future
9
+ from edc_model_fields.fields import IsDateEstimatedField
8
10
  from edc_protocol.validators import (
9
11
  date_not_before_study_start,
10
12
  datetime_not_before_study_start,
11
13
  )
12
- from edc_utils import get_utcnow
13
14
 
14
15
 
15
16
  class HospitalizationModelMixin(NonUniqueSubjectIdentifierFieldMixin, models.Model):
16
17
  report_datetime = models.DateTimeField(
17
18
  verbose_name="Report Date",
18
19
  validators=[datetime_not_before_study_start, datetime_not_future],
19
- default=get_utcnow,
20
+ default=timezone.now,
20
21
  )
21
22
 
22
23
  have_details = models.CharField(
@@ -30,7 +31,7 @@ class HospitalizationModelMixin(NonUniqueSubjectIdentifierFieldMixin, models.Mod
30
31
  validators=[date_not_future, date_not_before_study_start],
31
32
  )
32
33
 
33
- admitted_date_estimated = edc_models.IsDateEstimatedField(
34
+ admitted_date_estimated = IsDateEstimatedField(
34
35
  verbose_name="Is this date estimated?",
35
36
  )
36
37
 
@@ -4,6 +4,7 @@ from django.contrib import admin
4
4
  from django.contrib.admin import display
5
5
  from django.template.loader import render_to_string
6
6
  from django.urls import NoReverseMatch, reverse
7
+ from django.utils import timezone
7
8
  from django_audit_fields.admin import audit_fieldset_tuple
8
9
 
9
10
  from edc_action_item.fieldsets import action_fieldset_tuple
@@ -12,7 +13,6 @@ from edc_constants.constants import CANCELLED, OTHER
12
13
  from edc_model_admin.dashboard import ModelAdminSubjectDashboardMixin
13
14
  from edc_model_admin.list_filters import ReportDateListFilter
14
15
  from edc_pdf_reports.admin import PdfButtonModelAdminMixin, print_selected_to_pdf_action
15
- from edc_utils import get_utcnow
16
16
 
17
17
  from ..utils import get_adverse_event_app_label
18
18
  from .list_filters import CauseOfDeathListFilter, DeathDateListFilter
@@ -111,14 +111,14 @@ class DeathReportModelAdminMixin(
111
111
  def report_datetime_with_ago(self, obj=None):
112
112
  return render_to_string(
113
113
  "edc_adverse_event/datetime_with_ago.html",
114
- dict(utc_date=get_utcnow().date, report_datetime=obj.report_datetime),
114
+ dict(utc_date=timezone.now().date, report_datetime=obj.report_datetime),
115
115
  )
116
116
 
117
117
  @display(description="Death date", ordering="death_datetime")
118
118
  def death_datetime_with_ago(self, obj=None):
119
119
  return render_to_string(
120
120
  "edc_adverse_event/datetime_with_ago.html",
121
- dict(utc_date=get_utcnow().date, report_datetime=obj.death_datetime),
121
+ dict(utc_date=timezone.now().date, report_datetime=obj.death_datetime),
122
122
  )
123
123
 
124
124
  @display(description="Action item", ordering="action_identifier")
@@ -2,17 +2,17 @@ from django.core.exceptions import ObjectDoesNotExist
2
2
  from django.db import transaction
3
3
  from django.db.models.signals import m2m_changed, post_delete, post_save
4
4
  from django.dispatch.dispatcher import receiver
5
+ from django.utils import timezone
5
6
 
6
7
  from edc_constants.constants import NO, YES
7
8
  from edc_notification.models import Notification
8
- from edc_utils import get_utcnow
9
9
 
10
10
  from ..constants import AE_TMG_ACTION, DEATH_REPORT_TMG_ACTION, TMG
11
11
  from ..utils import get_ae_model
12
12
 
13
13
 
14
14
  @receiver(m2m_changed, weak=False, dispatch_uid="update_ae_notifications_for_tmg_group")
15
- def update_ae_notifications_for_tmg_group(action, instance, **kwargs): # noqa: ARG001
15
+ def update_ae_notifications_for_tmg_group(action, instance, **kwargs):
16
16
  if getattr(instance, "userprofile", None):
17
17
  try:
18
18
  tmg_ae_notification = Notification.objects.get(name=AE_TMG_ACTION)
@@ -29,7 +29,7 @@ def update_ae_notifications_for_tmg_group(action, instance, **kwargs): # noqa:
29
29
 
30
30
 
31
31
  @receiver(post_save, weak=False, dispatch_uid="update_ae_initial_for_susar")
32
- def update_ae_initial_for_susar(sender, instance, raw, update_fields, **kwargs): # noqa: ARG001
32
+ def update_ae_initial_for_susar(sender, instance, raw, update_fields, **kwargs):
33
33
  if not raw and not update_fields:
34
34
  try:
35
35
  ae_susar_model_cls = get_ae_model("AeSusar")
@@ -55,7 +55,7 @@ def update_ae_initial_for_susar(sender, instance, raw, update_fields, **kwargs):
55
55
  weak=False,
56
56
  dispatch_uid="update_ae_initial_susar_reported",
57
57
  )
58
- def update_ae_initial_susar_reported(sender, instance, raw, update_fields, **kwargs): # noqa: ARG001
58
+ def update_ae_initial_susar_reported(sender, instance, raw, update_fields, **kwargs):
59
59
  if not raw and not update_fields:
60
60
  try:
61
61
  ae_initial_model_cls = get_ae_model("AeInitial")
@@ -72,12 +72,12 @@ def update_ae_initial_susar_reported(sender, instance, raw, update_fields, **kwa
72
72
  ae_susar_model_cls.objects.get(ae_initial=instance)
73
73
  except ObjectDoesNotExist:
74
74
  ae_susar_model_cls.objects.create(
75
- ae_initial=instance, submitted_datetime=get_utcnow()
75
+ ae_initial=instance, submitted_datetime=timezone.now()
76
76
  )
77
77
 
78
78
 
79
79
  @receiver(post_delete, weak=False, dispatch_uid="post_delete_ae_susar")
80
- def post_delete_ae_susar(instance, **kwargs): # noqa: ARG001
80
+ def post_delete_ae_susar(instance, **kwargs):
81
81
  try:
82
82
  ae_susar_model_cls = get_ae_model("AeSusar")
83
83
  except LookupError:
@@ -93,7 +93,7 @@ def post_delete_ae_susar(instance, **kwargs): # noqa: ARG001
93
93
 
94
94
 
95
95
  @receiver(m2m_changed, weak=False, dispatch_uid="update_death_notifications_for_tmg_group")
96
- def update_death_notifications_for_tmg_group(action, instance, **kwargs): # noqa: ARG001
96
+ def update_death_notifications_for_tmg_group(action, instance, **kwargs):
97
97
  if getattr(instance, "userprofile", None):
98
98
  try:
99
99
  tmg_death_notification = Notification.objects.get(name=DEATH_REPORT_TMG_ACTION)
@@ -10,6 +10,7 @@ from django.conf import settings
10
10
  from django.contrib.messages import ERROR
11
11
  from django.core.exceptions import ObjectDoesNotExist
12
12
  from django.template.loader import select_template
13
+ from django.utils import timezone
13
14
  from django.utils.html import format_html
14
15
  from django.utils.safestring import mark_safe
15
16
  from django.utils.translation import gettext as _
@@ -17,7 +18,7 @@ from django.utils.translation import gettext as _
17
18
  from edc_action_item.utils import get_reference_obj
18
19
  from edc_constants.constants import CLOSED, OPEN, OTHER, YES
19
20
  from edc_model_admin.utils import add_to_messages_once
20
- from edc_utils import escape_braces, get_utcnow
21
+ from edc_utils import escape_braces
21
22
 
22
23
  from ..constants import (
23
24
  AE_TMG_ACTION,
@@ -81,7 +82,7 @@ def select_description_template(model):
81
82
 
82
83
  @register.inclusion_tag(select_description_template("aeinitial"), takes_context=True)
83
84
  def format_ae_description(context, ae_initial, wrap_length):
84
- context["utc_date"] = get_utcnow().date()
85
+ context["utc_date"] = timezone.now().date()
85
86
  context["SHORT_DATE_FORMAT"] = settings.SHORT_DATE_FORMAT
86
87
  context["OTHER"] = OTHER
87
88
  context["YES"] = YES
@@ -103,7 +104,7 @@ def format_ae_description(context, ae_initial, wrap_length):
103
104
  @register.inclusion_tag(select_description_template("aefollowup"), takes_context=True)
104
105
  def format_ae_followup_description(context, ae_followup, wrap_length):
105
106
  context["AE_WITHDRAWN"] = AE_WITHDRAWN
106
- context["utc_date"] = get_utcnow().date()
107
+ context["utc_date"] = timezone.now().date()
107
108
  context["SHORT_DATE_FORMAT"] = settings.SHORT_DATE_FORMAT
108
109
  context["OTHER"] = OTHER
109
110
  context["YES"] = YES
@@ -131,7 +132,7 @@ def format_ae_followup_description(context, ae_followup, wrap_length):
131
132
 
132
133
  @register.inclusion_tag(select_description_template("aesusar"), takes_context=True)
133
134
  def format_ae_susar_description(context, ae_susar, wrap_length):
134
- context["utc_date"] = get_utcnow().date()
135
+ context["utc_date"] = timezone.now().date()
135
136
  context["SHORT_DATE_FORMAT"] = settings.SHORT_DATE_FORMAT
136
137
  context["OTHER"] = OTHER
137
138
  context["YES"] = YES
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from django.core.exceptions import ObjectDoesNotExist
6
6
  from django.template.loader import render_to_string
7
+ from django.utils import timezone
7
8
  from django.utils.html import format_html
8
9
  from django.utils.safestring import mark_safe
9
10
 
@@ -12,7 +13,6 @@ from edc_dashboard.view_mixins import EdcViewMixin
12
13
  from edc_listboard.view_mixins import ListboardFilterViewMixin, SearchFormViewMixin
13
14
  from edc_listboard.views import ListboardView as BaseListboardView
14
15
  from edc_navbar import NavbarViewMixin
15
- from edc_utils import get_utcnow
16
16
 
17
17
  from ...constants import AE_INITIAL_ACTION
18
18
  from ...pdf_reports import AePdfReport
@@ -43,9 +43,7 @@ class AeListboardViewMixin(
43
43
 
44
44
  listboard_instructions = format_html(
45
45
  "{}",
46
- mark_safe(
47
- render_to_string("edc_adverse_event/ae/ae_listboard_instructions.html")
48
- ), # nosec B703 B308,
46
+ mark_safe(render_to_string("edc_adverse_event/ae/ae_listboard_instructions.html")), # nosec B703 B308,
49
47
  )
50
48
  navbar_selected_item = "ae_home"
51
49
  ordering = "-report_datetime"
@@ -78,7 +76,7 @@ class AeListboardViewMixin(
78
76
  def get_context_data(self, **kwargs) -> dict[str, Any]:
79
77
  kwargs.update(
80
78
  AE_INITIAL_ACTION=AE_INITIAL_ACTION,
81
- utc_date=get_utcnow().date(),
79
+ utc_date=timezone.now().date(),
82
80
  **self.add_url_to_context(
83
81
  new_key="ae_home_url",
84
82
  existing_key=self.home_url,
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from django.core.exceptions import ObjectDoesNotExist
6
+ from django.utils import timezone
6
7
  from django.utils.html import format_html
7
8
  from django.utils.safestring import mark_safe
8
9
 
@@ -11,7 +12,6 @@ from edc_dashboard.view_mixins import EdcViewMixin
11
12
  from edc_listboard.view_mixins import ListboardFilterViewMixin, SearchFormViewMixin
12
13
  from edc_listboard.views import ListboardView as BaseListboardView
13
14
  from edc_navbar import NavbarViewMixin
14
- from edc_utils import get_utcnow
15
15
 
16
16
  from ...constants import DEATH_REPORT_ACTION
17
17
  from ...pdf_reports import DeathPdfReport
@@ -40,25 +40,23 @@ class DeathReportListboardViewMixin(
40
40
  listboard_view_permission_codename = "edc_adverse_event.view_ae_listboard"
41
41
  listboard_instructions = format_html(
42
42
  "{}",
43
- mark_safe(
44
- "edc_adverse_event/ae/death_report_listboard_instructions.html"
45
- ), # nosec B703 B308
43
+ mark_safe("edc_adverse_event/ae/death_report_listboard_instructions.html"), # nosec B703 B308
46
44
  )
47
45
 
48
46
  navbar_selected_item = "ae_home"
49
47
  ordering = "-report_datetime"
50
48
  paginate_by = 25
51
49
  search_form_url = "death_report_listboard_url"
52
- action_type_names = [DEATH_REPORT_ACTION]
50
+ action_type_names = (DEATH_REPORT_ACTION,)
53
51
 
54
- search_fields = [
52
+ search_fields = (
55
53
  "subject_identifier",
56
54
  "action_identifier",
57
55
  "parent_action_item__action_identifier",
58
56
  "related_action_item__action_identifier",
59
57
  "user_created",
60
58
  "user_modified",
61
- ]
59
+ )
62
60
 
63
61
  def get(self, request, *args, **kwargs):
64
62
  if request.GET.get("pdf"):
@@ -71,7 +69,7 @@ class DeathReportListboardViewMixin(
71
69
  def get_context_data(self, **kwargs) -> dict[str, Any]:
72
70
  kwargs.update(
73
71
  DEATH_REPORT_ACTION=DEATH_REPORT_ACTION,
74
- utc_date=get_utcnow().date(),
72
+ utc_date=timezone.now().date(),
75
73
  **self.add_url_to_context(
76
74
  new_key="ae_home_url",
77
75
  existing_key=self.home_url,
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from django.db.models import Min
6
+ from django.utils import timezone
6
7
 
7
8
  from edc_constants.constants import CLOSED, NEW, OPEN
8
9
  from edc_dashboard.view_mixins import EdcViewMixin
@@ -10,7 +11,6 @@ from edc_listboard.view_mixins import ListboardFilterViewMixin, SearchFormViewMi
10
11
  from edc_listboard.views import ListboardView as BaseListboardView
11
12
  from edc_navbar import NavbarViewMixin
12
13
  from edc_navbar.get_default_navbar import get_default_navbar
13
- from edc_utils import get_utcnow
14
14
 
15
15
  from ...constants import (
16
16
  AE_TMG_ACTION,
@@ -69,7 +69,7 @@ class TmgAeListboardViewMixin(
69
69
  def get_context_data(self, **kwargs) -> dict[str, Any]:
70
70
  kwargs.update(
71
71
  AE_TMG_ACTION=AE_TMG_ACTION,
72
- utc_date=get_utcnow().date(),
72
+ utc_date=timezone.now().date(),
73
73
  subject_identifier=self.kwargs.get("subject_identifier"),
74
74
  )
75
75
  return super().get_context_data(**kwargs)
@@ -2,11 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
+ from django.utils import timezone
6
+
5
7
  from edc_dashboard.view_mixins import EdcViewMixin
6
8
  from edc_listboard.view_mixins import ListboardFilterViewMixin, SearchFormViewMixin
7
9
  from edc_listboard.views import ListboardView as BaseListboardView
8
10
  from edc_navbar import NavbarViewMixin
9
- from edc_utils import get_utcnow
10
11
 
11
12
  from ...constants import (
12
13
  AE_FOLLOWUP_ACTION,
@@ -57,7 +58,7 @@ class SummaryListboardView(
57
58
  ]
58
59
 
59
60
  def get_context_data(self, **kwargs) -> dict[str, Any]:
60
- kwargs.update(AE_TMG_ACTION=AE_TMG_ACTION, utc_date=get_utcnow().date())
61
+ kwargs.update(AE_TMG_ACTION=AE_TMG_ACTION, utc_date=timezone.now().date())
61
62
  return super().get_context_data(**kwargs)
62
63
 
63
64
  def get_queryset_filter_options(self, request, *args, **kwargs) -> tuple[Q, dict]:
@@ -7,6 +7,7 @@ from django.contrib import admin
7
7
  from django.db.models import DurationField, ExpressionWrapper, F
8
8
  from django.template.loader import render_to_string
9
9
  from django.urls import reverse
10
+ from django.utils import timezone
10
11
  from django.utils.html import format_html
11
12
  from django.utils.translation import gettext as _
12
13
  from django_audit_fields.admin import audit_fieldset_tuple
@@ -19,7 +20,6 @@ from edc_document_status.modeladmin_mixins import DocumentStatusModelAdminMixin
19
20
  from edc_model_admin.dashboard import ModelAdminSubjectDashboardMixin
20
21
  from edc_model_admin.history import SimpleHistoryAdmin
21
22
  from edc_sites.admin import SiteModelAdminMixin
22
- from edc_utils import get_utcnow
23
23
  from edc_visit_schedule.admin import ScheduleStatusListFilter
24
24
  from edc_visit_schedule.exceptions import OnScheduleError
25
25
  from edc_visit_schedule.fieldsets import (
@@ -290,7 +290,7 @@ class AppointmentAdmin(
290
290
 
291
291
  def get_queryset(self, request):
292
292
  qs = super().get_queryset(request)
293
- now = get_utcnow().replace(second=59, hour=23, minute=59)
293
+ now = timezone.now().replace(second=59, hour=23, minute=59)
294
294
  return qs.annotate(
295
295
  appt_timepoint_delta=ExpressionWrapper(
296
296
  (F("appt_datetime") - F("timepoint_datetime")),
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from dateutil.relativedelta import relativedelta
4
4
  from django.contrib.admin import SimpleListFilter
5
5
  from django.db.models import QuerySet
6
+ from django.utils import timezone
6
7
  from django.utils.translation import gettext as _
7
8
 
8
9
  from edc_appointment.choices import APPT_STATUS
@@ -18,7 +19,6 @@ from edc_appointment.constants import (
18
19
  LT_30_DAYS,
19
20
  )
20
21
  from edc_model_admin.list_filters import FutureDateListFilter
21
- from edc_utils import get_utcnow
22
22
 
23
23
 
24
24
  class AppointmentListFilter(FutureDateListFilter):
@@ -62,7 +62,7 @@ class AppointmentOverdueListFilter(SimpleListFilter):
62
62
  )
63
63
 
64
64
  def queryset(self, request, queryset) -> QuerySet | None:
65
- now = get_utcnow().replace(second=59, hour=23, minute=59)
65
+ now = timezone.now().replace(second=59, hour=23, minute=59)
66
66
  qs = None
67
67
  if self.value() == LT_30_DAYS:
68
68
  qs = queryset.filter(
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any
6
6
  from django.apps import apps as django_apps
7
7
  from django.conf import settings
8
8
  from django.urls import reverse
9
+ from django.utils import timezone
9
10
  from django.utils.html import format_html
10
11
  from django.utils.safestring import mark_safe
11
12
  from django.utils.translation import gettext_lazy as _
@@ -16,7 +17,7 @@ from edc_form_validators import INVALID_ERROR
16
17
  from edc_form_validators.form_validator import FormValidator
17
18
  from edc_metadata.metadata_helper import MetadataHelperMixin
18
19
  from edc_sites.form_validator_mixin import SiteFormValidatorMixin
19
- from edc_utils import formatted_datetime, get_utcnow, to_utc
20
+ from edc_utils import formatted_datetime, to_utc
20
21
  from edc_utils.date import to_local
21
22
  from edc_visit_schedule.site_visit_schedules import site_visit_schedules
22
23
  from edc_visit_schedule.subject_schedule import NotOnScheduleError
@@ -136,19 +137,23 @@ class AppointmentFormValidator(
136
137
  self.instance, "id", None
137
138
  ):
138
139
  previous_appt = get_previous_appointment(self.instance, include_interim=True)
139
- if previous_appt and previous_appt.appt_status not in [
140
- CANCELLED_APPT,
141
- SKIPPED_APPT,
142
- ]:
143
- if not previous_appt.related_visit:
144
- self.raise_validation_error(
145
- message=(
146
- "A previous appointment requires a visit report. "
147
- f"Update appointment {previous_appt.visit_code}."
148
- f"{previous_appt.visit_code_sequence} first."
149
- ),
150
- error_code=INVALID_PREVIOUS_VISIT_MISSING,
151
- )
140
+ if (
141
+ previous_appt
142
+ and previous_appt.appt_status
143
+ not in [
144
+ CANCELLED_APPT,
145
+ SKIPPED_APPT,
146
+ ]
147
+ and not previous_appt.related_visit
148
+ ):
149
+ self.raise_validation_error(
150
+ message=(
151
+ "A previous appointment requires a visit report. "
152
+ f"Update appointment {previous_appt.visit_code}."
153
+ f"{previous_appt.visit_code_sequence} first."
154
+ ),
155
+ error_code=INVALID_PREVIOUS_VISIT_MISSING,
156
+ )
152
157
  return True
153
158
 
154
159
  def validate_appt_sequence(self: Any) -> bool:
@@ -158,33 +163,45 @@ class AppointmentFormValidator(
158
163
  Check if previous appointment appt_status is NEW_APPT
159
164
 
160
165
  """
161
- if self.cleaned_data.get("appt_status") in [
162
- IN_PROGRESS_APPT,
163
- INCOMPLETE_APPT,
164
- COMPLETE_APPT,
165
- ]:
166
- if self.instance.previous:
167
- if obj := (
168
- self.appointment_model_cls.objects.filter(
169
- subject_identifier=self.subject_identifier,
170
- visit_schedule_name=self.instance.visit_schedule_name,
171
- schedule_name=self.instance.schedule_name,
172
- appt_status=NEW_APPT,
173
- appt_datetime__lt=self.instance.appt_datetime,
174
- )
175
- .order_by("timepoint", "visit_code_sequence")
176
- .first()
177
- ):
178
- self.raise_validation_error(
179
- {
180
- "__all__": _(
181
- "A previous appointment requires updating. "
182
- f"Update appointment {obj.visit_code}."
183
- f"{obj.visit_code_sequence} first."
184
- )
185
- },
186
- INVALID_PREVIOUS_APPOINTMENT_NOT_UPDATED,
166
+ if (
167
+ self.cleaned_data.get("appt_status")
168
+ in [
169
+ IN_PROGRESS_APPT,
170
+ INCOMPLETE_APPT,
171
+ COMPLETE_APPT,
172
+ ]
173
+ and self.instance.previous
174
+ ) and (
175
+ obj := (
176
+ self.appointment_model_cls.objects.filter(
177
+ subject_identifier=self.subject_identifier,
178
+ visit_schedule_name=self.instance.visit_schedule_name,
179
+ schedule_name=self.instance.schedule_name,
180
+ appt_status=NEW_APPT,
181
+ appt_datetime__lt=self.instance.appt_datetime,
182
+ )
183
+ .order_by("timepoint", "visit_code_sequence")
184
+ .first()
185
+ )
186
+ ):
187
+ errmsg = (
188
+ "A previous appointment requires updating. "
189
+ "Update appointment %(visit_code)s."
190
+ "%(visit_code_sequence)s first."
191
+ )
192
+
193
+ self.raise_validation_error(
194
+ {
195
+ "__all__": _(
196
+ errmsg
197
+ % dict(
198
+ visit_code=obj.visit_code,
199
+ visit_code_sequence=obj.visit_code_sequence,
200
+ )
187
201
  )
202
+ },
203
+ INVALID_PREVIOUS_APPOINTMENT_NOT_UPDATED,
204
+ )
188
205
  return True
189
206
 
190
207
  def validate_timepoint(self: Any):
@@ -202,12 +219,15 @@ class AppointmentFormValidator(
202
219
  def validate_not_future_appt_datetime(self: Any) -> None:
203
220
  appt_datetime = self.cleaned_data.get("appt_datetime")
204
221
  appt_status = self.cleaned_data.get("appt_status")
205
- if appt_datetime and appt_status != NEW_APPT:
206
- if to_utc(appt_datetime) > get_utcnow():
207
- self.raise_validation_error(
208
- {"appt_datetime": "Cannot be a future date/time."},
209
- INVALID_APPT_DATE,
210
- )
222
+ if (
223
+ appt_datetime
224
+ and appt_status != NEW_APPT
225
+ and to_utc(appt_datetime) > timezone.now()
226
+ ):
227
+ self.raise_validation_error(
228
+ {"appt_datetime": "Cannot be a future date/time."},
229
+ INVALID_APPT_DATE,
230
+ )
211
231
 
212
232
  def validate_appt_datetime_not_before_consent_datetime(self: Any) -> None:
213
233
  if (
@@ -291,42 +311,46 @@ class AppointmentFormValidator(
291
311
  def validate_appt_datetime_not_before_previous_appt_datetime(self):
292
312
  appt_datetime = self.cleaned_data.get("appt_datetime")
293
313
  appt_status = self.cleaned_data.get("appt_status")
294
- if appt_datetime and appt_status and appt_status != NEW_APPT:
295
- if self.instance.relative_previous:
296
- if to_utc(appt_datetime) < self.instance.relative_previous.appt_datetime:
297
- formatted_date = formatted_datetime(
298
- self.instance.relative_previous.appt_datetime
299
- )
300
- self.raise_validation_error(
301
- {
302
- "appt_datetime": (
303
- "Cannot be before previous appointment. Previous appointment "
304
- f"is {self.instance.relative_previous.visit_label} "
305
- f"on {formatted_date}."
306
- )
307
- },
308
- INVALID_APPT_DATE,
314
+ if (
315
+ appt_datetime
316
+ and appt_status
317
+ and appt_status != NEW_APPT
318
+ and self.instance.relative_previous
319
+ and to_utc(appt_datetime) < self.instance.relative_previous.appt_datetime
320
+ ):
321
+ formatted_date = formatted_datetime(self.instance.relative_previous.appt_datetime)
322
+ self.raise_validation_error(
323
+ {
324
+ "appt_datetime": (
325
+ "Cannot be before previous appointment. Previous appointment "
326
+ f"is {self.instance.relative_previous.visit_label} "
327
+ f"on {formatted_date}."
309
328
  )
329
+ },
330
+ INVALID_APPT_DATE,
331
+ )
310
332
 
311
333
  def validate_appt_datetime_not_after_next_appt_datetime(self) -> None:
312
334
  appt_datetime = self.cleaned_data.get("appt_datetime")
313
335
  appt_status = self.cleaned_data.get("appt_status")
314
- if appt_datetime and appt_status and appt_status != NEW_APPT:
315
- if self.instance.relative_next:
316
- if to_utc(appt_datetime) > self.instance.relative_next.appt_datetime:
317
- formatted_date = formatted_datetime(
318
- self.instance.relative_next.appt_datetime
319
- )
320
- self.raise_validation_error(
321
- {
322
- "appt_datetime": (
323
- "Cannot be after next appointment. Next appointment is "
324
- f"{self.instance.relative_next.visit_label} "
325
- f"on {formatted_date}."
326
- )
327
- },
328
- INVALID_APPT_DATE,
336
+ if (
337
+ appt_datetime
338
+ and appt_status
339
+ and appt_status != NEW_APPT
340
+ and self.instance.relative_next
341
+ and to_utc(appt_datetime) > self.instance.relative_next.appt_datetime
342
+ ):
343
+ formatted_date = formatted_datetime(self.instance.relative_next.appt_datetime)
344
+ self.raise_validation_error(
345
+ {
346
+ "appt_datetime": (
347
+ "Cannot be after next appointment. Next appointment is "
348
+ f"{self.instance.relative_next.visit_label} "
349
+ f"on {formatted_date}."
329
350
  )
351
+ },
352
+ INVALID_APPT_DATE,
353
+ )
330
354
 
331
355
  def validate_appt_incomplete_and_visit_report(self: Any) -> None:
332
356
  """Require a visit report, at least, if wanting to set appt_status
@@ -502,12 +526,14 @@ class AppointmentFormValidator(
502
526
 
503
527
  Is this still used?
504
528
  """
505
- if self.cleaned_data.get("facility_name"):
506
- if self.cleaned_data.get("facility_name") not in get_facilities():
507
- self.raise_validation_error(
508
- {"__all__": f"Facility '{self.facility_name}' does not exist."},
509
- INVALID_ERROR,
510
- )
529
+ if (
530
+ self.cleaned_data.get("facility_name")
531
+ and self.cleaned_data.get("facility_name") not in get_facilities()
532
+ ):
533
+ self.raise_validation_error(
534
+ {"__all__": f"Facility '{self.facility_name}' does not exist."},
535
+ INVALID_ERROR,
536
+ )
511
537
 
512
538
  def validate_appt_type(self):
513
539
  self.not_applicable_if(SKIPPED_APPT, field="appt_status", field_applicable="appt_type")