clinicedc 2.0.6__py3-none-any.whl → 2.0.8__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 (495) hide show
  1. {clinicedc-2.0.6.dist-info → clinicedc-2.0.8.dist-info}/METADATA +2 -2
  2. {clinicedc-2.0.6.dist-info → clinicedc-2.0.8.dist-info}/RECORD +489 -441
  3. {clinicedc-2.0.6.dist-info → clinicedc-2.0.8.dist-info}/WHEEL +1 -1
  4. edc_action_item/action.py +1 -1
  5. edc_action_item/action_item_notification.py +2 -2
  6. edc_action_item/auths.py +37 -32
  7. edc_action_item/create_action_item.py +1 -1
  8. edc_action_item/decorators.py +1 -1
  9. edc_action_item/migrations/0001_initial.py +21 -22
  10. edc_action_item/migrations/0006_auto_20180707_1659.py +4 -4
  11. edc_action_item/migrations/0008_auto_20180809_0303.py +4 -4
  12. edc_action_item/migrations/0015_auto_20190114_0250.py +4 -4
  13. edc_action_item/migrations/0017_auto_20190305_0123.py +11 -30
  14. edc_action_item/migrations/0030_edcpermissions.py +3 -2
  15. edc_action_item/migrations/0039_alter_actionitem_auto_created_comment_and_more.py +398 -0
  16. edc_action_item/migrations/0040_alter_actionitem_report_datetime_and_more.py +34 -0
  17. edc_action_item/models/action_item.py +9 -4
  18. edc_action_item/models/action_model_mixin.py +5 -6
  19. edc_action_item/models/reference.py +2 -2
  20. edc_action_item/models/signals.py +22 -23
  21. edc_action_item/post_migrate_signals.py +1 -1
  22. edc_action_item/send_email.py +0 -85
  23. edc_action_item/site_action_items.py +5 -9
  24. edc_action_item/utils.py +3 -3
  25. edc_adherence/migrations/0005_alter_nonadherencereasons_extra_value_and_more.py +36 -0
  26. edc_adherence/model_mixins.py +1 -1
  27. edc_adverse_event/auths.py +55 -51
  28. edc_adverse_event/migrations/0001_initial.py +5 -4
  29. edc_adverse_event/migrations/0002_auto_20190802_0059.py +3 -2
  30. edc_adverse_event/migrations/0008_auto_20220825_0451.py +3 -2
  31. edc_adverse_event/migrations/0009_auto_20220907_0157.py +3 -2
  32. edc_adverse_event/migrations/0016_alter_aeactionclassification_device_created_and_more.py +285 -0
  33. edc_adverse_event/model_mixins/ae_followup/ae_followup_fields_model_mixin.py +2 -2
  34. edc_adverse_event/model_mixins/ae_initial/ae_initial_fields_model_mixin.py +6 -5
  35. edc_adverse_event/model_mixins/ae_special_interest/aesi_fields_model_mixin.py +2 -2
  36. edc_adverse_event/model_mixins/ae_susar/ae_susar_fields_model_mixin.py +2 -2
  37. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_fields_model_mixin.py +15 -11
  38. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_methods_model_mixin.py +2 -4
  39. edc_adverse_event/model_mixins/death_report/death_report_model_mixin.py +5 -4
  40. edc_adverse_event/model_mixins/death_report/death_report_tmg_model_mixin.py +6 -3
  41. edc_adverse_event/model_mixins/death_report/simple_death_report_model_mixin.py +2 -2
  42. edc_adverse_event/model_mixins/hospitaization/hospitalization_model_mixin.py +4 -3
  43. edc_adverse_event/modeladmin_mixins/death_report_admin_mixin.py +3 -3
  44. edc_adverse_event/models/signals.py +7 -7
  45. edc_adverse_event/templatetags/edc_adverse_event_extras.py +5 -4
  46. edc_adverse_event/view_mixins/ae/ae_listboard_view_mixin.py +3 -5
  47. edc_adverse_event/view_mixins/ae/death_report_listboard_view_mixin.py +6 -8
  48. edc_adverse_event/view_mixins/tmg/tmg_ae_listboard_view_mixin.py +2 -2
  49. edc_adverse_event/views/tmg/summary_listboard_view.py +3 -2
  50. edc_appointment/admin/appointment_admin.py +2 -2
  51. edc_appointment/admin/list_filters.py +2 -2
  52. edc_appointment/auths.py +14 -10
  53. edc_appointment/creators/appointment_creator.py +1 -1
  54. edc_appointment/creators/appointments_creator.py +1 -1
  55. edc_appointment/form_validators/appointment_form_validator.py +108 -82
  56. edc_appointment/migrations/0003_auto_20161127_2226.py +5 -6
  57. edc_appointment/migrations/0006_auto_20170106_2118.py +5 -5
  58. edc_appointment/migrations/0018_auto_20190305_0123.py +5 -12
  59. edc_appointment/migrations/0050_alter_appointment_appt_type_and_more.py +220 -0
  60. edc_appointment/model_mixins/appointment_fields_model_mixin.py +1 -2
  61. edc_appointment/model_mixins/appointment_methods_model_mixin.py +2 -3
  62. edc_appointment/model_mixins/appointment_model_mixin.py +31 -28
  63. edc_appointment/model_mixins/window_period_model_mixin.py +6 -7
  64. edc_appointment/models/appointment.py +1 -1
  65. edc_appointment/models/signals.py +44 -35
  66. edc_appointment/utils.py +19 -24
  67. edc_appointment/view_utils/appointment_button.py +2 -2
  68. edc_auth/admin/user_admin.py +1 -1
  69. edc_auth/auth_objects/__init__.py +2 -20
  70. edc_auth/auth_objects/default_groups.py +13 -11
  71. edc_auth/auth_objects/default_roles.py +26 -24
  72. edc_auth/auth_updater/auth_updater.py +13 -2
  73. edc_auth/auth_updater/group_updater.py +12 -10
  74. edc_auth/auth_updater/role_updater.py +2 -2
  75. edc_auth/constants.py +10 -0
  76. edc_auth/import_users.py +3 -3
  77. edc_auth/migrations/0001_squashed_0033_alter_userprofile_is_multisite_viewer.py +5 -5
  78. edc_auth/migrations/0012_auto_20191026_0034.py +3 -2
  79. edc_auth/migrations/0013_auto_20191026_0055.py +3 -2
  80. edc_auth/migrations/0025_permissions.py +3 -2
  81. edc_auth/migrations/0035_alter_edcpermissions_device_created_and_more.py +85 -0
  82. edc_auth/models/user_profile.py +14 -11
  83. edc_auth/post_migrate_signals.py +1 -1
  84. edc_auth/site_auths.py +80 -67
  85. edc_consent/actions.py +4 -5
  86. edc_consent/auths.py +18 -12
  87. edc_consent/migrations/0001_initial.py +3 -2
  88. edc_consent/migrations/0006_alter_edcpermissions_device_created_and_more.py +49 -0
  89. edc_consent/model_mixins/consent_extension_model_mixin.py +4 -5
  90. edc_consent/model_mixins/requires_consent_fields_model_mixin.py +2 -2
  91. edc_consent/utils.py +1 -1
  92. edc_constants/constants.py +1 -0
  93. edc_crf/auths.py +5 -0
  94. edc_crf/migrations/0001_initial.py +3 -2
  95. edc_crf/migrations/0009_alter_crfstatus_device_created_and_more.py +54 -0
  96. edc_dashboard/auths.py +10 -6
  97. edc_dashboard/migrations/0001_initial.py +3 -2
  98. edc_dashboard/migrations/0006_alter_edcpermissions_device_created_and_more.py +49 -0
  99. edc_dashboard/templatetags/edc_dashboard_extras.py +3 -2
  100. edc_dashboard/url_config.py +92 -83
  101. edc_dashboard/url_names.py +4 -4
  102. edc_dashboard/view_mixins/url_request_context_mixin.py +6 -5
  103. edc_data_manager/action_items.py +4 -6
  104. edc_data_manager/admin/actions.py +5 -5
  105. edc_data_manager/admin/data_query_admin.py +12 -11
  106. edc_data_manager/auths.py +37 -34
  107. edc_data_manager/handlers/handlers.py +6 -7
  108. edc_data_manager/migrations/0001_initial.py +19 -19
  109. edc_data_manager/migrations/0025_edcpermissions.py +3 -2
  110. edc_data_manager/migrations/0040_alter_datadictionary_device_created_and_more.py +327 -0
  111. edc_data_manager/migrations/0041_alter_dataquery_dm_user_and_more.py +164 -0
  112. edc_data_manager/models/data_query.py +5 -5
  113. edc_data_manager/models/model_mixins.py +8 -8
  114. edc_data_manager/rule/query_rule_wrapper.py +7 -7
  115. edc_data_manager/rule/rule_runner.py +4 -3
  116. edc_document_status/model_mixins.py +1 -1
  117. edc_egfr/egfr.py +11 -7
  118. edc_egfr/model_mixins/egfr_model_mixin.py +3 -7
  119. edc_export/admin/data_request_admin.py +2 -2
  120. edc_export/archive_exporter.py +7 -5
  121. edc_export/auths.py +32 -28
  122. edc_export/files_archiver.py +3 -2
  123. edc_export/migrations/0001_initial.py +26 -26
  124. edc_export/migrations/0004_auto_20190305_0123.py +25 -72
  125. edc_export/migrations/0013_edcpermissions.py +3 -2
  126. edc_export/migrations/0022_alter_datarequest_description_and_more.py +611 -0
  127. edc_export/migrations/0023_alter_datarequesthistory_archive_filename_and_more.py +29 -0
  128. edc_export/model_exporter/file_history_updater.py +10 -10
  129. edc_export/model_exporter/model_exporter.py +17 -15
  130. edc_export/model_exporter/object_history_helpers.py +2 -2
  131. edc_export/models/data_request_history.py +4 -4
  132. edc_export/utils.py +10 -15
  133. edc_facility/auths.py +8 -3
  134. edc_facility/facility.py +16 -22
  135. edc_facility/import_holidays.py +13 -13
  136. edc_facility/migrations/0005_healthfacility_healthfacilitytypes_and_more.py +8 -7
  137. edc_facility/migrations/0016_alter_healthfacility_device_created_and_more.py +139 -0
  138. edc_facility/migrations/0017_alter_healthfacility_gps_and_more.py +54 -0
  139. edc_facility/model_mixins.py +5 -5
  140. edc_form_label/custom_label_condition.py +11 -8
  141. edc_form_label/form_label.py +1 -1
  142. edc_form_runners/auths.py +11 -6
  143. edc_form_runners/form_runner.py +3 -5
  144. edc_form_runners/migrations/0001_initial.py +3 -2
  145. edc_form_runners/migrations/0005_alter_issue_device_created_and_more.py +49 -0
  146. edc_form_validators/applicable_field_validator.py +7 -6
  147. edc_form_validators/base_form_validator.py +8 -9
  148. edc_form_validators/other_specify_field_validator.py +2 -8
  149. edc_form_validators/required_field_validator.py +19 -16
  150. edc_identifier/migrations/0001_squashed_0018_auto_20180128_1054.py +3 -3
  151. edc_identifier/migrations/0002_auto_20190305_0123.py +3 -6
  152. edc_identifier/migrations/0006_auto_20161127_2226.py +15 -15
  153. edc_identifier/migrations/0007_auto_20161204_2227.py +3 -3
  154. edc_identifier/migrations/0009_auto_20161221_2323.py +3 -5
  155. edc_identifier/migrations/0010_auto_20170112_0602.py +17 -17
  156. edc_identifier/migrations/0011_alter_identifiermodel_device_created_and_more.py +79 -0
  157. edc_identifier/model_mixins.py +1 -1
  158. edc_identifier/models.py +9 -9
  159. edc_identifier/research_identifier.py +11 -10
  160. edc_identifier/simple_identifier.py +9 -5
  161. edc_identifier/subject_identifier.py +3 -2
  162. edc_lab/auths.py +26 -23
  163. edc_lab/lab/aliquot_creator.py +5 -8
  164. edc_lab/lab/primary_aliquot.py +14 -5
  165. edc_lab/migrations/0001_initial.py +29 -29
  166. edc_lab/migrations/0008_auto_20170921_0719.py +3 -3
  167. edc_lab/migrations/0010_auto_20171127_1541.py +15 -15
  168. edc_lab/migrations/0012_auto_20180114_1438.py +3 -3
  169. edc_lab/migrations/0019_auto_20190305_0123.py +43 -127
  170. edc_lab/migrations/0036_alter_aliquot_comment_alter_aliquot_device_created_and_more.py +1139 -0
  171. edc_lab/migrations/0037_alter_historicalorder_order_datetime_and_more.py +31 -0
  172. edc_lab/model_mixins/aliquot/aliquot_identifier_model_mixin.py +2 -2
  173. edc_lab/model_mixins/aliquot/aliquot_model_mixin.py +2 -2
  174. edc_lab/model_mixins/panel_model_mixin.py +18 -7
  175. edc_lab/model_mixins/requisition/crf_with_requisition_model_mixin.py +2 -4
  176. edc_lab/model_mixins/requisition/requisition_model_mixin.py +13 -16
  177. edc_lab/model_mixins/requisition/requisition_verify_model_mixin.py +1 -1
  178. edc_lab/model_mixins/result/result_item_model_mixin.py +4 -4
  179. edc_lab/model_mixins/shipping/manifest_model_mixin.py +4 -5
  180. edc_lab/model_mixins/shipping/verify_model_mixin.py +4 -6
  181. edc_lab/models/aliquot.py +2 -2
  182. edc_lab/models/box.py +1 -1
  183. edc_lab/models/box_item.py +6 -7
  184. edc_lab/models/manifest/manifest.py +3 -3
  185. edc_lab/models/manifest/manifest_item.py +7 -7
  186. edc_lab/models/manifest/shipper.py +3 -4
  187. edc_lab/models/order.py +6 -4
  188. edc_lab/models/panel.py +4 -4
  189. edc_lab/models/result.py +2 -2
  190. edc_lab/models/result_item.py +2 -2
  191. edc_lab_dashboard/auths.py +16 -11
  192. edc_lab_dashboard/migrations/0001_initial.py +3 -2
  193. edc_lab_dashboard/migrations/0005_alter_edcpermissions_device_created_and_more.py +49 -0
  194. edc_lab_dashboard/views/action_views/receive_view.py +2 -2
  195. edc_lab_dashboard/views/action_views/verify_box_item_view.py +2 -2
  196. edc_lab_results/calculate_missing.py +8 -8
  197. edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py +2 -2
  198. edc_lab_results/get_summary.py +26 -25
  199. edc_lab_results/model_mixins/blood_result_model_mixin.py +9 -7
  200. edc_lab_results/model_mixins/fbg_model_mixin.py +3 -3
  201. edc_lab_results/model_mixins/glucose_model_mixin.py +3 -3
  202. edc_lab_results/model_mixins/hba1c_model_mixin.py +3 -3
  203. edc_label/auths.py +6 -1
  204. edc_label/label_template.py +8 -8
  205. edc_label/migrations/0001_initial.py +3 -2
  206. edc_label/migrations/0007_alter_zpllabeltemplates_device_created_and_more.py +49 -0
  207. edc_list_data/list_model_maker.py +2 -2
  208. edc_list_data/load_list_data.py +11 -14
  209. edc_list_data/load_model_data.py +3 -3
  210. edc_list_data/model_mixins.py +12 -4
  211. edc_list_data/post_migrate_signals.py +1 -1
  212. edc_list_data/preload_data.py +4 -4
  213. edc_list_data/row.py +1 -1
  214. edc_list_data/site_list_data.py +6 -5
  215. edc_listboard/migrations/0001_initial.py +3 -2
  216. edc_listboard/migrations/0007_alter_listboard_device_created_and_more.py +49 -0
  217. edc_locator/auths.py +18 -13
  218. edc_locator/migrations/0001_initial.py +7 -7
  219. edc_locator/migrations/0018_auto_20190305_0123.py +5 -12
  220. edc_locator/migrations/0040_alter_historicalsubjectlocator_action_identifier_and_more.py +187 -0
  221. edc_locator/migrations/0041_alter_historicalsubjectlocator_report_datetime_and_more.py +27 -0
  222. edc_locator/model_mixins/locator_model_mixin.py +2 -2
  223. edc_locator/model_mixins/subject_contact_fields_mixin.py +1 -1
  224. edc_locator/modeladmin_mixins.py +5 -4
  225. edc_ltfu/model_mixins.py +7 -5
  226. edc_metadata/auths.py +11 -7
  227. edc_metadata/metadata/metadata.py +1 -1
  228. edc_metadata/metadata_rules/crf/crf_rule.py +1 -1
  229. edc_metadata/metadata_rules/decorators.py +1 -1
  230. edc_metadata/metadata_rules/metadata_rule_evaluator.py +5 -3
  231. edc_metadata/metadata_rules/rule.py +2 -3
  232. edc_metadata/metadata_rules/rule_evaluator.py +1 -1
  233. edc_metadata/migrations/0002_auto_20161127_2226.py +5 -5
  234. edc_metadata/migrations/0005_auto_20170112_0602.py +5 -5
  235. edc_metadata/migrations/0011_auto_20190305_0123.py +5 -12
  236. edc_metadata/migrations/0031_alter_crfmetadata_device_created_and_more.py +120 -0
  237. edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +7 -4
  238. edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +5 -2
  239. edc_metadata/models/crf_metadata.py +5 -5
  240. edc_metadata/models/crf_metadata_model_mixin.py +4 -5
  241. edc_metadata/models/requisition_metadata.py +6 -6
  242. edc_metadata/models/signals.py +10 -11
  243. edc_metadata/views/refresh_metadata_actions_view.py +3 -3
  244. edc_model/models/address_mixin.py +6 -6
  245. edc_model/models/fields/initials_field.py +2 -1
  246. edc_model/models/historical_records.py +2 -9
  247. edc_model/models/url_model_mixin.py +2 -2
  248. edc_model/validators/date.py +7 -8
  249. edc_model_admin/list_filters/future_date_list_filter.py +3 -3
  250. edc_model_admin/list_filters/past_date_list_filter.py +3 -3
  251. edc_model_admin/mixins/model_admin_protect_pii_mixin.py +4 -5
  252. edc_model_fields/fields/other_charfield.py +2 -2
  253. edc_navbar/auths.py +18 -13
  254. edc_navbar/migrations/0004_auto_20220825_0451.py +3 -2
  255. edc_navbar/migrations/0009_alter_edcpermissions_device_created_and_more.py +49 -0
  256. edc_notification/auths.py +9 -4
  257. edc_notification/migrations/0001_initial.py +3 -3
  258. edc_notification/migrations/0004_auto_20190305_0123.py +3 -6
  259. edc_notification/migrations/0011_alter_notification_device_created_and_more.py +59 -0
  260. edc_notification/models/notification.py +1 -1
  261. edc_notification/notification/graded_event_notification.py +2 -2
  262. edc_notification/notification/model_notification.py +3 -30
  263. edc_notification/notification/new_model_notification.py +1 -1
  264. edc_notification/notification/notification.py +12 -12
  265. edc_notification/notification/updated_model_notification.py +2 -2
  266. edc_offstudy/auths.py +12 -7
  267. edc_offstudy/migrations/0001_initial.py +4 -4
  268. edc_offstudy/migrations/0002_auto_20180921_0434.py +4 -4
  269. edc_offstudy/migrations/0005_auto_20190305_0123.py +5 -12
  270. edc_offstudy/migrations/0023_alter_historicalsubjectoffstudy_action_identifier_and_more.py +188 -0
  271. edc_offstudy/migrations/0024_alter_historicalsubjectoffstudy_offstudy_datetime_and_more.py +43 -0
  272. edc_offstudy/model_mixins/offstudy_model_mixin.py +3 -2
  273. edc_pdutils/df_exporters/csv_exporter.py +33 -37
  274. edc_pdutils/df_exporters/csv_model_exporter.py +7 -3
  275. edc_pdutils/migrations/0001_initial.py +8 -8
  276. edc_pharmacy/admin/prescription/rx_refill_admin.py +4 -3
  277. edc_pharmacy/admin/reports/stock_availability_admin.py +5 -6
  278. edc_pharmacy/auths.py +19 -15
  279. edc_pharmacy/migrations/0001_initial.py +64 -63
  280. edc_pharmacy/migrations/0015_auto_20220913_2139.py +26 -26
  281. edc_pharmacy/migrations/0024_allocation_assignment_containerunits_dispense_and_more.py +81 -86
  282. edc_pharmacy/migrations/0037_remove_historicalstock_confirmed_at_site_by_and_more.py +5 -5
  283. edc_pharmacy/migrations/0039_remove_dispense_registered_subject_and_more.py +7 -8
  284. edc_pharmacy/migrations/0049_remove_stocktransferconfirmation_stock_and_more.py +12 -12
  285. edc_pharmacy/migrations/0050_remove_stocktransferconfirmation2_location_and_more.py +5 -5
  286. edc_pharmacy/migrations/0051_alter_historicalstocktransferconfirmationitem_options_and_more.py +3 -4
  287. edc_pharmacy/migrations/0053_alter_location_managers_alter_historicalstock_lot_and_more.py +3 -3
  288. edc_pharmacy/migrations/0057_scanduplicates.py +3 -3
  289. edc_pharmacy/migrations/0060_alter_container_max_per_subject_and_more.py +13 -15
  290. edc_pharmacy/migrations/0068_stockout.py +2 -3
  291. edc_pharmacy/migrations/0076_historicalstockadjustment_stockadjustment.py +5 -5
  292. edc_pharmacy/migrations/0077_historicalstockadjustment_adjustment_datetime_and_more.py +3 -4
  293. edc_pharmacy/migrations/0081_historicalconfirmation_confirmation.py +8 -8
  294. edc_pharmacy/migrations/0084_confirmationatsiteitem_and_more.py +0 -2
  295. edc_pharmacy/migrations/0089_alter_allocation_allocated_by_and_more.py +3284 -0
  296. edc_pharmacy/migrations/0090_alter_allocation_allocation_datetime_and_more.py +227 -0
  297. edc_pharmacy/model_mixins/study_medication_refill_model_mixin.py +2 -2
  298. edc_pharmacy/models/medication/formulation.py +5 -7
  299. edc_pharmacy/models/medication/medication.py +3 -3
  300. edc_pharmacy/models/model_mixins.py +10 -11
  301. edc_pharmacy/models/prescription/rx.py +13 -12
  302. edc_pharmacy/models/prescription/rx_refill.py +0 -1
  303. edc_pharmacy/models/stock/allocation.py +3 -3
  304. edc_pharmacy/models/stock/confirmation.py +3 -3
  305. edc_pharmacy/models/stock/confirmation_at_site.py +3 -2
  306. edc_pharmacy/models/stock/confirmation_at_site_item.py +3 -4
  307. edc_pharmacy/models/stock/dispense.py +5 -6
  308. edc_pharmacy/models/stock/dispense_item.py +2 -2
  309. edc_pharmacy/models/stock/location.py +3 -4
  310. edc_pharmacy/models/stock/lot.py +6 -7
  311. edc_pharmacy/models/stock/order.py +1 -1
  312. edc_pharmacy/models/stock/receive.py +3 -3
  313. edc_pharmacy/models/stock/receive_item.py +4 -4
  314. edc_pharmacy/models/stock/repack_request.py +2 -2
  315. edc_pharmacy/models/stock/stock.py +12 -18
  316. edc_pharmacy/models/stock/stock_adjustment.py +4 -4
  317. edc_pharmacy/models/stock/stock_request.py +2 -2
  318. edc_pharmacy/models/stock/stock_request_item.py +2 -2
  319. edc_pharmacy/models/stock/stock_transfer.py +3 -3
  320. edc_pharmacy/models/stock/stock_transfer_item.py +2 -2
  321. edc_pharmacy/models/stock/storage_bin.py +3 -4
  322. edc_pharmacy/models/stock/storage_bin_item.py +2 -2
  323. edc_pharmacy/models/stock/supplier.py +3 -4
  324. edc_pharmacy/models/storage/box.py +2 -2
  325. edc_pharmacy/models/storage/items/container_model_mixin.py +3 -4
  326. edc_pharmacy/models/storage/items/pill_bottle_model_mixin.py +2 -2
  327. edc_pharmacy/models/storage/items/subject_pill_bottle.py +1 -1
  328. edc_pharmacy/models/storage/room.py +2 -2
  329. edc_pharmacy/models/storage/shelf.py +2 -2
  330. edc_pharmacy/models/storage/utils.py +1 -1
  331. edc_pharmacy/prescribe/create_prescription.py +3 -3
  332. edc_pharmacy/utils/allocate_stock.py +2 -3
  333. edc_pharmacy/utils/confirm_stock.py +3 -4
  334. edc_pharmacy/utils/confirm_stock_at_site.py +4 -5
  335. edc_pharmacy/utils/dispense.py +3 -4
  336. edc_pharmacy/utils/process_repack_request.py +4 -5
  337. edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py +2 -3
  338. edc_pharmacy/utils/transfer_stock.py +2 -3
  339. edc_pharmacy/views/add_to_storage_bin_view.py +2 -2
  340. edc_pharmacy/views/allocate_to_subject_view.py +2 -2
  341. edc_pharmacy/views/confirmation_at_site_view.py +6 -9
  342. edc_pharmacy/views/dispense_view.py +2 -2
  343. edc_pharmacy/views/move_to_storage_bin_view.py +2 -3
  344. edc_pharmacy/views/print_labels_view.py +2 -2
  345. edc_prn/admin_site.py +5 -0
  346. edc_prn/prn.py +10 -11
  347. edc_prn/urls.py +11 -0
  348. edc_protocol/research_protocol_config.py +2 -3
  349. edc_protocol_incident/action_items.py +4 -4
  350. edc_protocol_incident/auths.py +27 -20
  351. edc_protocol_incident/migrations/0001_initial.py +7 -7
  352. edc_protocol_incident/migrations/0001_squashed_0015_auto_20220927_0401.py +13 -13
  353. edc_protocol_incident/migrations/0005_protocolincident_historicalprotocolincident_and_more.py +7 -7
  354. edc_protocol_incident/migrations/0024_alter_actionsrequired_extra_value_and_more.py +625 -0
  355. edc_protocol_incident/migrations/0025_alter_historicalprotocoldeviationviolation_report_datetime_and_more.py +42 -0
  356. edc_protocol_incident/model_mixins/protocol_deviation_violation_model_mixin.py +18 -11
  357. edc_protocol_incident/model_mixins/protocol_incident_model_mixin.py +26 -11
  358. edc_pylabels/actions.py +3 -5
  359. edc_pylabels/auths.py +6 -1
  360. edc_pylabels/migrations/0002_alter_label_options_label_created_and_more.py +3 -7
  361. edc_pylabels/migrations/0005_labelconfiguration_delete_label_and_more.py +3 -3
  362. edc_pylabels/migrations/0013_alter_labelconfiguration_device_created_and_more.py +49 -0
  363. edc_qareports/auths.py +11 -7
  364. edc_qareports/migrations/0001_initial.py +4 -5
  365. edc_qareports/migrations/0005_edcpermissions.py +3 -3
  366. edc_qareports/migrations/0006_qareportlog.py +2 -3
  367. edc_qareports/migrations/0019_alter_edcpermissions_device_created_and_more.py +102 -0
  368. edc_qareports/migrations/0020_alter_note_report_datetime_and_more.py +24 -0
  369. edc_qareports/model_mixins/note_model_mixin.py +7 -4
  370. edc_qareports/model_mixins/on_study_missing_values_model_mixin.py +4 -4
  371. edc_qareports/model_mixins/qa_report_model_mixin.py +2 -3
  372. edc_qareports/models/note.py +4 -4
  373. edc_qareports/models/qa_reports_log.py +2 -3
  374. edc_randomization/admin.py +30 -24
  375. edc_randomization/auths.py +12 -7
  376. edc_randomization/decorators.py +1 -1
  377. edc_randomization/migrations/0001_initial.py +3 -2
  378. edc_randomization/migrations/0002_historicalrandomizationlist.py +3 -2
  379. edc_randomization/migrations/0009_edcpermissions.py +3 -2
  380. edc_randomization/migrations/0014_alter_edcpermissions_device_created_and_more.py +141 -0
  381. edc_randomization/model_mixins.py +9 -9
  382. edc_randomization/randomizer.py +22 -20
  383. edc_randomization/utils.py +19 -18
  384. edc_refusal/auths.py +7 -2
  385. edc_refusal/migrations/0001_initial.py +4 -4
  386. edc_refusal/migrations/0002_historicalsubjectrefusal.py +4 -4
  387. edc_refusal/migrations/0012_alter_historicalsubjectrefusal_comment_and_more.py +122 -0
  388. edc_refusal/migrations/0013_alter_historicalsubjectrefusal_report_datetime_and_more.py +28 -0
  389. edc_refusal/model_mixins.py +4 -4
  390. edc_registration/auths.py +28 -23
  391. edc_registration/migrations/0002_auto_20161127_2226.py +3 -4
  392. edc_registration/migrations/0005_auto_20170111_1809.py +4 -2
  393. edc_registration/migrations/0016_historicalregisteredsubject.py +3 -2
  394. edc_registration/migrations/0017_auto_20190305_0123.py +5 -12
  395. edc_registration/migrations/0033_alter_historicalregisteredsubject_additional_key_and_more.py +302 -0
  396. edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +15 -7
  397. edc_registration/models/registered_subject.py +28 -27
  398. edc_reportable/age_evaluator.py +4 -2
  399. edc_reportable/formula.py +5 -5
  400. edc_reportable/migrations/0001_initial.py +7 -7
  401. edc_reportable/migrations/0003_referencerangecollection_grade1_and_more.py +3 -3
  402. edc_reportable/migrations/0004_alter_referencerangecollection_grade3_and_more.py +5 -5
  403. edc_reportable/migrations/0006_alter_gradingdata_revision_and_more.py +5 -6
  404. edc_reportable/migrations/0007_alter_gradingdata_age_phrase_and_more.py +509 -0
  405. edc_reportable/models/normal_data.py +4 -5
  406. edc_reportable/models/reference_model_mixins.py +10 -11
  407. edc_reportable/models/reference_range_collection.py +12 -13
  408. edc_reportable/post_migrate_signals.py +1 -1
  409. edc_reportable/utils/convert_units.py +11 -45
  410. edc_reportable/utils/get_normal_data_or_raise.py +2 -2
  411. edc_reportable/utils/get_reference_range_collection.py +2 -3
  412. edc_reportable/utils/load_data.py +4 -4
  413. edc_reportable/utils/update_grading_data.py +5 -5
  414. edc_review_dashboard/auths.py +23 -18
  415. edc_review_dashboard/migrations/0001_initial.py +3 -2
  416. edc_review_dashboard/migrations/0006_alter_edcpermissions_device_created_and_more.py +49 -0
  417. edc_screening/age_evaluator.py +6 -6
  418. edc_screening/auths.py +35 -30
  419. edc_screening/eligibility.py +1 -1
  420. edc_screening/gender_evaluator.py +1 -1
  421. edc_screening/migrations/0001_initial.py +3 -2
  422. edc_screening/migrations/0005_alter_edcpermissions_device_created_and_more.py +49 -0
  423. edc_screening/model_mixins/eligibility_model_mixin.py +3 -6
  424. edc_screening/model_mixins/screening_fields_model_mixin.py +6 -8
  425. edc_screening/model_mixins/screening_methods_model_mixin.py +1 -1
  426. edc_screening/screening_eligibility.py +2 -4
  427. edc_screening/utils.py +9 -9
  428. edc_search/generate_slug.py +26 -0
  429. edc_search/model_mixins.py +10 -22
  430. edc_sites/auths.py +8 -3
  431. edc_sites/migrations/0007_edcpermissions.py +3 -2
  432. edc_sites/migrations/0010_alter_edcpermissions_device_created_and_more.py +69 -0
  433. edc_sites/model_mixins/site_model_mixin.py +2 -2
  434. edc_sites/site.py +5 -5
  435. edc_sites/system_checks.py +1 -1
  436. edc_subject_dashboard/auths.py +27 -22
  437. edc_subject_dashboard/migrations/0001_initial.py +3 -2
  438. edc_subject_dashboard/migrations/0005_alter_edcpermissions_device_created_and_more.py +49 -0
  439. edc_subject_dashboard/requisition_report.py +6 -4
  440. edc_subject_dashboard/requisition_verifier.py +6 -6
  441. edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +2 -2
  442. edc_subject_dashboard/view_mixins/subject_visit_view_mixin.py +7 -8
  443. edc_subject_dashboard/view_utils/subject_consent_listboard_button.py +3 -2
  444. edc_subject_dashboard/view_utils/timepoint_status_button.py +2 -2
  445. edc_timepoint/apps.py +0 -21
  446. edc_timepoint/model_mixins.py +22 -20
  447. edc_transfer/model_mixins.py +4 -4
  448. edc_transfer/modeladmin_mixins.py +1 -1
  449. edc_unblinding/auths.py +9 -4
  450. edc_unblinding/migrations/0001_initial.py +13 -13
  451. edc_unblinding/migrations/0014_alter_historicalunblindingrequest_action_identifier_and_more.py +291 -0
  452. edc_unblinding/migrations/0015_alter_historicalunblindingrequest_report_datetime_and_more.py +45 -0
  453. edc_unblinding/models/unblinding_request.py +2 -2
  454. edc_unblinding/models/unblinding_review.py +3 -3
  455. edc_utils/__init__.py +3 -1
  456. edc_utils/age.py +10 -16
  457. edc_utils/show_urls.py +29 -2
  458. edc_visit_schedule/auths.py +6 -1
  459. edc_visit_schedule/migrations/0001_initial.py +3 -3
  460. edc_visit_schedule/migrations/0002_auto_20190305_0123.py +3 -6
  461. edc_visit_schedule/migrations/0003_historicalvisitschedule_visitschedule.py +5 -4
  462. edc_visit_schedule/migrations/0015_historicalonschedule_offschedule_onschedule.py +10 -10
  463. edc_visit_schedule/migrations/0019_alter_historicalonschedule_device_created_and_more.py +229 -0
  464. edc_visit_schedule/migrations/0020_alter_historicalonschedule_onschedule_datetime_and_more.py +65 -0
  465. edc_visit_schedule/model_mixins/off_schedule_model_mixin.py +5 -4
  466. edc_visit_schedule/model_mixins/on_schedule_model_mixin.py +5 -4
  467. edc_visit_schedule/model_mixins/visit_schedule/visit_code_fields_model_mixin.py +1 -1
  468. edc_visit_schedule/models/subject_schedule_history.py +6 -6
  469. edc_visit_schedule/schedule/visit_collection.py +1 -1
  470. edc_visit_schedule/site_visit_schedules.py +2 -2
  471. edc_visit_schedule/subject_schedule.py +5 -4
  472. edc_visit_schedule/view_mixins.py +2 -3
  473. edc_visit_schedule/visit/visit.py +3 -2
  474. edc_visit_tracking/migrations/0004_subjectvisit_subjectvisitmissedreasons_extra_value_and_more.py +13 -13
  475. edc_visit_tracking/migrations/0009_alter_historicalsubjectvisit_comments_and_more.py +450 -0
  476. edc_visit_tracking/migrations/0010_alter_historicalsubjectvisit_report_datetime_and_more.py +68 -0
  477. edc_visit_tracking/model_mixins/base/visit_methods_model_mixin.py +3 -3
  478. edc_visit_tracking/model_mixins/crfs/visit_tracking_crf_model_mixin.py +2 -2
  479. edc_visit_tracking/model_mixins/requisitions/visit_tracking_requisition_model_mixin.py +2 -2
  480. edc_visit_tracking/model_mixins/subject_visit_missed_model_mixin.py +2 -2
  481. edc_visit_tracking/model_mixins/visit_model_mixin/caretaker_fields_mixin.py +2 -3
  482. edc_visit_tracking/model_mixins/visit_model_mixin/previous_visit_model_mixin.py +1 -1
  483. edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_fields_mixin.py +8 -11
  484. edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_mixin.py +1 -1
  485. edc_visit_tracking/models/signals.py +3 -3
  486. edc_visit_tracking/models/subject_visit.py +1 -1
  487. edc_vitals/calculators/bmi.py +7 -5
  488. edc_vitals/utils.py +1 -1
  489. edc_form_label/models.py +0 -0
  490. edc_search/constants.py +0 -1
  491. edc_search/models.py +0 -0
  492. edc_search/search_slug.py +0 -50
  493. edc_search/updater.py +0 -30
  494. edc_search/wsgi.py +0 -7
  495. {clinicedc-2.0.6.dist-info → clinicedc-2.0.8.dist-info}/licenses/LICENSE +0 -0
@@ -6,7 +6,7 @@ from django.db import migrations, models
6
6
 
7
7
  import edc_model_fields.fields.hostname_modification_field
8
8
  import edc_model_fields.fields.userfield
9
- import edc_utils
9
+ import django.utils.timezone
10
10
 
11
11
 
12
12
  class Migration(migrations.Migration):
@@ -16,7 +16,7 @@ class Migration(migrations.Migration):
16
16
  migrations.AlterField(
17
17
  model_name="appointment",
18
18
  name="created",
19
- field=models.DateTimeField(blank=True, default=edc_utils.date.get_utcnow),
19
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
20
20
  ),
21
21
  migrations.AlterField(
22
22
  model_name="appointment",
@@ -40,7 +40,7 @@ class Migration(migrations.Migration):
40
40
  migrations.AlterField(
41
41
  model_name="appointment",
42
42
  name="modified",
43
- field=models.DateTimeField(blank=True, default=edc_utils.date.get_utcnow),
43
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
44
44
  ),
45
45
  migrations.AlterField(
46
46
  model_name="appointment",
@@ -59,7 +59,7 @@ class Migration(migrations.Migration):
59
59
  migrations.AlterField(
60
60
  model_name="historicalappointment",
61
61
  name="created",
62
- field=models.DateTimeField(blank=True, default=edc_utils.date.get_utcnow),
62
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
63
63
  ),
64
64
  migrations.AlterField(
65
65
  model_name="historicalappointment",
@@ -83,7 +83,7 @@ class Migration(migrations.Migration):
83
83
  migrations.AlterField(
84
84
  model_name="historicalappointment",
85
85
  name="modified",
86
- field=models.DateTimeField(blank=True, default=edc_utils.date.get_utcnow),
86
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
87
87
  ),
88
88
  migrations.AlterField(
89
89
  model_name="historicalappointment",
@@ -4,6 +4,7 @@ import django_audit_fields.fields.hostname_modification_field
4
4
  import django_audit_fields.fields.userfield
5
5
  import django_audit_fields.models.audit_model_mixin
6
6
  from django.db import migrations, models
7
+ import django.utils.timezone
7
8
 
8
9
 
9
10
  class Migration(migrations.Migration):
@@ -13,9 +14,7 @@ class Migration(migrations.Migration):
13
14
  migrations.AlterField(
14
15
  model_name="appointment",
15
16
  name="created",
16
- field=models.DateTimeField(
17
- blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
18
- ),
17
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
19
18
  ),
20
19
  migrations.AlterField(
21
20
  model_name="appointment",
@@ -29,9 +28,7 @@ class Migration(migrations.Migration):
29
28
  migrations.AlterField(
30
29
  model_name="appointment",
31
30
  name="modified",
32
- field=models.DateTimeField(
33
- blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
34
- ),
31
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
35
32
  ),
36
33
  migrations.AlterField(
37
34
  model_name="appointment",
@@ -56,9 +53,7 @@ class Migration(migrations.Migration):
56
53
  migrations.AlterField(
57
54
  model_name="historicalappointment",
58
55
  name="created",
59
- field=models.DateTimeField(
60
- blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
61
- ),
56
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
62
57
  ),
63
58
  migrations.AlterField(
64
59
  model_name="historicalappointment",
@@ -72,9 +67,7 @@ class Migration(migrations.Migration):
72
67
  migrations.AlterField(
73
68
  model_name="historicalappointment",
74
69
  name="modified",
75
- field=models.DateTimeField(
76
- blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
77
- ),
70
+ field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
78
71
  ),
79
72
  migrations.AlterField(
80
73
  model_name="historicalappointment",
@@ -0,0 +1,220 @@
1
+ # Generated by Django 5.2.6 on 2025-09-17 16:53
2
+
3
+ import django.db.models.deletion
4
+ import edc_model_fields.fields.other_charfield
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ("edc_appointment", "0049_alter_appointment_revision_and_more"),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AlterField(
16
+ model_name="appointment",
17
+ name="appt_type",
18
+ field=models.ForeignKey(
19
+ null=True,
20
+ on_delete=django.db.models.deletion.PROTECT,
21
+ to="edc_appointment.appointmenttype",
22
+ verbose_name="Appointment type",
23
+ ),
24
+ ),
25
+ migrations.AlterField(
26
+ model_name="appointment",
27
+ name="appt_type_other",
28
+ field=edc_model_fields.fields.other_charfield.OtherCharField(
29
+ blank=True,
30
+ default="",
31
+ max_length=35,
32
+ verbose_name="If other appointment type, please specify ...",
33
+ ),
34
+ ),
35
+ migrations.AlterField(
36
+ model_name="appointment",
37
+ name="comment",
38
+ field=models.CharField(
39
+ blank=True, default="", max_length=250, verbose_name="Comment"
40
+ ),
41
+ ),
42
+ migrations.AlterField(
43
+ model_name="appointment",
44
+ name="device_created",
45
+ field=models.CharField(
46
+ blank=True, default="", max_length=10, verbose_name="Device created"
47
+ ),
48
+ ),
49
+ migrations.AlterField(
50
+ model_name="appointment",
51
+ name="device_modified",
52
+ field=models.CharField(
53
+ blank=True, default="", max_length=10, verbose_name="Device modified"
54
+ ),
55
+ ),
56
+ migrations.AlterField(
57
+ model_name="appointment",
58
+ name="document_status_comments",
59
+ field=models.TextField(
60
+ blank=True,
61
+ default="",
62
+ help_text="for example, why some data is still pending",
63
+ verbose_name="Any comments related to status of this document",
64
+ ),
65
+ ),
66
+ migrations.AlterField(
67
+ model_name="appointment",
68
+ name="locale_created",
69
+ field=models.CharField(
70
+ blank=True,
71
+ default="",
72
+ help_text="Auto-updated by Modeladmin",
73
+ max_length=10,
74
+ verbose_name="Locale created",
75
+ ),
76
+ ),
77
+ migrations.AlterField(
78
+ model_name="appointment",
79
+ name="locale_modified",
80
+ field=models.CharField(
81
+ blank=True,
82
+ default="",
83
+ help_text="Auto-updated by Modeladmin",
84
+ max_length=10,
85
+ verbose_name="Locale modified",
86
+ ),
87
+ ),
88
+ migrations.AlterField(
89
+ model_name="appointment",
90
+ name="visit_code",
91
+ field=models.CharField(default="", editable=False, max_length=25),
92
+ ),
93
+ migrations.AlterField(
94
+ model_name="appointmenttype",
95
+ name="extra_value",
96
+ field=models.CharField(default="", max_length=250),
97
+ ),
98
+ migrations.AlterField(
99
+ model_name="appointmenttype",
100
+ name="field_name",
101
+ field=models.CharField(
102
+ blank=True,
103
+ default="",
104
+ editable=False,
105
+ help_text="Not required",
106
+ max_length=25,
107
+ ),
108
+ ),
109
+ migrations.AlterField(
110
+ model_name="appointmenttype",
111
+ name="plural_name",
112
+ field=models.CharField(
113
+ default="", max_length=250, verbose_name="Plural name"
114
+ ),
115
+ ),
116
+ migrations.AlterField(
117
+ model_name="historicalappointment",
118
+ name="appt_type",
119
+ field=models.ForeignKey(
120
+ blank=True,
121
+ db_constraint=False,
122
+ null=True,
123
+ on_delete=django.db.models.deletion.DO_NOTHING,
124
+ related_name="+",
125
+ to="edc_appointment.appointmenttype",
126
+ verbose_name="Appointment type",
127
+ ),
128
+ ),
129
+ migrations.AlterField(
130
+ model_name="historicalappointment",
131
+ name="appt_type_other",
132
+ field=edc_model_fields.fields.other_charfield.OtherCharField(
133
+ blank=True,
134
+ default="",
135
+ max_length=35,
136
+ verbose_name="If other appointment type, please specify ...",
137
+ ),
138
+ ),
139
+ migrations.AlterField(
140
+ model_name="historicalappointment",
141
+ name="comment",
142
+ field=models.CharField(
143
+ blank=True, default="", max_length=250, verbose_name="Comment"
144
+ ),
145
+ ),
146
+ migrations.AlterField(
147
+ model_name="historicalappointment",
148
+ name="device_created",
149
+ field=models.CharField(
150
+ blank=True, default="", max_length=10, verbose_name="Device created"
151
+ ),
152
+ ),
153
+ migrations.AlterField(
154
+ model_name="historicalappointment",
155
+ name="device_modified",
156
+ field=models.CharField(
157
+ blank=True, default="", max_length=10, verbose_name="Device modified"
158
+ ),
159
+ ),
160
+ migrations.AlterField(
161
+ model_name="historicalappointment",
162
+ name="document_status_comments",
163
+ field=models.TextField(
164
+ blank=True,
165
+ default="",
166
+ help_text="for example, why some data is still pending",
167
+ verbose_name="Any comments related to status of this document",
168
+ ),
169
+ ),
170
+ migrations.AlterField(
171
+ model_name="historicalappointment",
172
+ name="locale_created",
173
+ field=models.CharField(
174
+ blank=True,
175
+ default="",
176
+ help_text="Auto-updated by Modeladmin",
177
+ max_length=10,
178
+ verbose_name="Locale created",
179
+ ),
180
+ ),
181
+ migrations.AlterField(
182
+ model_name="historicalappointment",
183
+ name="locale_modified",
184
+ field=models.CharField(
185
+ blank=True,
186
+ default="",
187
+ help_text="Auto-updated by Modeladmin",
188
+ max_length=10,
189
+ verbose_name="Locale modified",
190
+ ),
191
+ ),
192
+ migrations.AlterField(
193
+ model_name="historicalappointment",
194
+ name="visit_code",
195
+ field=models.CharField(default="", editable=False, max_length=25),
196
+ ),
197
+ migrations.AlterField(
198
+ model_name="infosources",
199
+ name="extra_value",
200
+ field=models.CharField(default="", max_length=250),
201
+ ),
202
+ migrations.AlterField(
203
+ model_name="infosources",
204
+ name="field_name",
205
+ field=models.CharField(
206
+ blank=True,
207
+ default="",
208
+ editable=False,
209
+ help_text="Not required",
210
+ max_length=25,
211
+ ),
212
+ ),
213
+ migrations.AlterField(
214
+ model_name="infosources",
215
+ name="plural_name",
216
+ field=models.CharField(
217
+ default="", max_length=250, verbose_name="Plural name"
218
+ ),
219
+ ),
220
+ ]
@@ -36,7 +36,6 @@ class AppointmentFieldsModelMixin(models.Model):
36
36
  "edc_appointment.AppointmentType",
37
37
  verbose_name="Appointment type",
38
38
  on_delete=models.PROTECT,
39
- default=None,
40
39
  null=True,
41
40
  blank=False,
42
41
  help_text="",
@@ -82,7 +81,7 @@ class AppointmentFieldsModelMixin(models.Model):
82
81
  ),
83
82
  )
84
83
 
85
- comment = models.CharField("Comment", max_length=250, blank=True, null=True)
84
+ comment = models.CharField("Comment", max_length=250, blank=True, default="")
86
85
 
87
86
  is_confirmed = models.BooleanField(default=False, editable=False)
88
87
 
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  from typing import TYPE_CHECKING, TypeVar
4
5
 
5
6
  from django.core.exceptions import ObjectDoesNotExist
@@ -154,10 +155,8 @@ class AppointmentMethodsModelMixin(models.Model):
154
155
  def related_visit(self: Appointment) -> VisitModel | None:
155
156
  """Returns the related visit model for the current instance."""
156
157
  related_visit = None
157
- try:
158
+ with contextlib.suppress(ObjectDoesNotExist):
158
159
  related_visit = getattr(self, self.related_visit_model_attr())
159
- except ObjectDoesNotExist:
160
- pass
161
160
  return related_visit
162
161
 
163
162
  @property
@@ -87,7 +87,7 @@ class AppointmentModelMixin(
87
87
  "Subject is not on a schedule. Using subject_identifier="
88
88
  f"`{self.subject_identifier}` and appt_datetime=`{dte_as_str}`."
89
89
  f"Got {e}"
90
- )
90
+ ) from e
91
91
  if self.appt_datetime > onschedule_obj.onschedule_datetime:
92
92
  # update appointment timepoints
93
93
  schedule.put_on_schedule(
@@ -122,33 +122,36 @@ class AppointmentModelMixin(
122
122
  return self.pk
123
123
 
124
124
  def validate_appt_datetime_not_before_previous(self) -> None:
125
- if self.appt_status != CANCELLED_APPT and self.appt_datetime:
126
- if (
127
- self.relative_previous
128
- and self.appt_datetime <= self.relative_previous.appt_datetime
129
- ):
130
- appt_datetime = formatted_datetime(self.appt_datetime)
131
- previous_appt_datetime = formatted_datetime(
132
- self.relative_previous.appt_datetime
133
- )
134
- raise AppointmentDatetimeError(
135
- "Datetime cannot be on or before previous appointment datetime. "
136
- f"Got {appt_datetime} <= {previous_appt_datetime}. "
137
- f"See appointment `{self}` and "
138
- f"`{self.relative_previous}`."
139
- )
125
+ if (
126
+ self.appt_status != CANCELLED_APPT
127
+ and self.appt_datetime
128
+ and self.relative_previous
129
+ and self.appt_datetime <= self.relative_previous.appt_datetime
130
+ ):
131
+ appt_datetime = formatted_datetime(self.appt_datetime)
132
+ previous_appt_datetime = formatted_datetime(self.relative_previous.appt_datetime)
133
+ raise AppointmentDatetimeError(
134
+ "Datetime cannot be on or before previous appointment datetime. "
135
+ f"Got {appt_datetime} <= {previous_appt_datetime}. "
136
+ f"See appointment `{self}` and "
137
+ f"`{self.relative_previous}`."
138
+ )
140
139
 
141
140
  def validate_appt_datetime_not_after_next(self) -> None:
142
- if self.appt_status != CANCELLED_APPT and self.appt_datetime and self.relative_next:
143
- if self.appt_datetime >= self.relative_next.appt_datetime:
144
- appt_datetime = formatted_datetime(self.appt_datetime)
145
- next_appt_datetime = formatted_datetime(self.relative_next.appt_datetime)
146
- raise AppointmentDatetimeError(
147
- "Datetime cannot be on or after next appointment datetime. "
148
- f"Got {appt_datetime} >= {next_appt_datetime}. "
149
- f"See appointment `{self}` and "
150
- f"`{self.relative_next}`."
151
- )
141
+ if (
142
+ self.appt_status != CANCELLED_APPT
143
+ and self.appt_datetime
144
+ and self.relative_next
145
+ and self.appt_datetime >= self.relative_next.appt_datetime
146
+ ):
147
+ appt_datetime = formatted_datetime(self.appt_datetime)
148
+ next_appt_datetime = formatted_datetime(self.relative_next.appt_datetime)
149
+ raise AppointmentDatetimeError(
150
+ "Datetime cannot be on or after next appointment datetime. "
151
+ f"Got {appt_datetime} >= {next_appt_datetime}. "
152
+ f"See appointment `{self}` and "
153
+ f"`{self.relative_next}`."
154
+ )
152
155
 
153
156
  @property
154
157
  def title(self: Appointment) -> str:
@@ -171,7 +174,7 @@ class AppointmentModelMixin(
171
174
 
172
175
  class Meta(NonUniqueSubjectIdentifierFieldMixin.Meta):
173
176
  abstract = True
174
- constraints = [
177
+ constraints = (
175
178
  UniqueConstraint(
176
179
  fields=[
177
180
  "subject_identifier",
@@ -192,7 +195,7 @@ class AppointmentModelMixin(
192
195
  ],
193
196
  name="unique_%(app_label)s_%(class)s_200",
194
197
  ),
195
- ]
198
+ )
196
199
  indexes = (
197
200
  models.Index(fields=["appt_datetime"]),
198
201
  models.Index(fields=["appt_status"]),
@@ -30,13 +30,12 @@ class WindowPeriodModelMixin(models.Model):
30
30
  and self.appt_status != CANCELLED_APPT
31
31
  and self.appt_datetime
32
32
  and self.timepoint_datetime
33
- ):
34
- if not self.ignore_window_period:
35
- try:
36
- raise_on_appt_datetime_not_in_window(self)
37
- except AppointmentWindowError as e:
38
- msg = f"{e} Perhaps catch this in the form"
39
- raise AppointmentWindowError(msg)
33
+ ) and not self.ignore_window_period:
34
+ try:
35
+ raise_on_appt_datetime_not_in_window(self)
36
+ except AppointmentWindowError as e:
37
+ msg = f"{e} Perhaps catch this in the form"
38
+ raise AppointmentWindowError(msg) from e
40
39
 
41
40
  class Meta:
42
41
  abstract = True
@@ -23,7 +23,7 @@ class Appointment(AppointmentModelMixin, SiteModelMixin, BaseUuidModel):
23
23
  )
24
24
 
25
25
  # noinspection PyTypeHints
26
- natural_key.dependencies = ["sites.Site"] # type: ignore
26
+ natural_key.dependencies = ("sites.Site",)
27
27
 
28
28
  class Meta(AppointmentModelMixin.Meta, SiteModelMixin.Meta, BaseUuidModel.Meta):
29
29
  indexes = (*AppointmentModelMixin.Meta.indexes, *BaseUuidModel.Meta.indexes)
@@ -1,9 +1,10 @@
1
1
  from django.core.exceptions import ObjectDoesNotExist
2
2
  from django.db.models.signals import post_delete, post_save, pre_delete
3
3
  from django.dispatch import receiver
4
+ from django.utils import timezone
4
5
 
5
6
  from edc_constants.constants import NO
6
- from edc_utils import formatted_datetime, get_utcnow
7
+ from edc_utils import formatted_datetime
7
8
  from edc_visit_schedule.site_visit_schedules import site_visit_schedules
8
9
  from edc_visit_tracking.utils import get_related_visit_model_cls
9
10
 
@@ -70,19 +71,23 @@ def create_appointments_on_post_save(sender, instance, raw, created, using, **kw
70
71
  def update_appt_status_on_related_visit_post_save(
71
72
  sender, instance, created, raw, update_fields, **kwargs
72
73
  ):
73
- if not raw and not update_fields and created:
74
- if isinstance(instance, (get_related_visit_model_cls(),)):
75
- try:
76
- AppointmentStatusUpdater(
77
- instance.appointment,
78
- change_to_in_progress=True,
79
- clear_others_in_progress=True,
80
- )
81
- except AttributeError as e:
82
- if "appointment" not in str(e):
83
- raise
84
- except AppointmentStatusUpdaterError:
85
- pass
74
+ if (
75
+ not raw
76
+ and not update_fields
77
+ and created
78
+ and isinstance(instance, (get_related_visit_model_cls(),))
79
+ ):
80
+ try:
81
+ AppointmentStatusUpdater(
82
+ instance.appointment,
83
+ change_to_in_progress=True,
84
+ clear_others_in_progress=True,
85
+ )
86
+ except AttributeError as e:
87
+ if "appointment" not in str(e):
88
+ raise
89
+ except AppointmentStatusUpdaterError:
90
+ pass
86
91
 
87
92
 
88
93
  @receiver(
@@ -127,7 +132,7 @@ def appointments_on_pre_delete(sender, instance, using, **kwargs):
127
132
  offschedule_datetime = schedule.offschedule_model_cls.objects.get(
128
133
  subject_identifier=instance.subject_identifier
129
134
  ).offschedule_datetime
130
- except ObjectDoesNotExist:
135
+ except ObjectDoesNotExist as e:
131
136
  raise AppointmentDeleteError(
132
137
  f"Appointment may not be deleted. "
133
138
  f"Subject {instance.subject_identifier} is on schedule "
@@ -138,7 +143,7 @@ def appointments_on_pre_delete(sender, instance, using, **kwargs):
138
143
  f"Perhaps complete off schedule model "
139
144
  f"'{instance.schedule.offschedule_model_cls().verbose_name.title()}' "
140
145
  f"first."
141
- )
146
+ ) from e
142
147
  else:
143
148
  if onschedule_datetime <= instance.appt_datetime <= offschedule_datetime:
144
149
  raise AppointmentDeleteError(
@@ -146,7 +151,7 @@ def appointments_on_pre_delete(sender, instance, using, **kwargs):
146
151
  f"Subject {instance.subject_identifier} is on schedule "
147
152
  f"'{instance.visit_schedule.verbose_name}.{instance.schedule_name}' "
148
153
  f"as of '{formatted_datetime(onschedule_datetime)}' "
149
- f"until '{formatted_datetime(get_utcnow())}'. "
154
+ f"until '{formatted_datetime(timezone.now())}'. "
150
155
  f"Got appointment datetime "
151
156
  f"{formatted_datetime(instance.appt_datetime)}. "
152
157
  )
@@ -177,16 +182,19 @@ def appointments_on_post_delete(sender, instance, using, **kwargs):
177
182
  dispatch_uid="update_appointments_to_next_on_post_save",
178
183
  )
179
184
  def update_appointments_to_next_on_post_save(sender, instance, raw, created, using, **kwargs):
180
- if not raw and not kwargs.get("update_fields"):
181
- if get_allow_skipped_appt_using().get(instance._meta.label_lower):
182
- skip_appt = SkipAppointments(instance)
183
- next_appointment_updated = skip_appt.update()
184
- allow_create_interim = getattr(instance, "allow_create_interim", False)
185
- if not next_appointment_updated and allow_create_interim:
186
- create_next_appointment_as_interim(
187
- next_appt_datetime=skip_appt.next_appt_datetime,
188
- appointment=skip_appt.appointment,
189
- )
185
+ if (
186
+ not raw
187
+ and not kwargs.get("update_fields")
188
+ and get_allow_skipped_appt_using().get(instance._meta.label_lower)
189
+ ):
190
+ skip_appt = SkipAppointments(instance)
191
+ next_appointment_updated = skip_appt.update()
192
+ allow_create_interim = getattr(instance, "allow_create_interim", False)
193
+ if not next_appointment_updated and allow_create_interim:
194
+ create_next_appointment_as_interim(
195
+ next_appt_datetime=skip_appt.next_appt_datetime,
196
+ appointment=skip_appt.appointment,
197
+ )
190
198
 
191
199
 
192
200
  @receiver(post_delete, weak=False, dispatch_uid="update_appointments_to_next_on_post_delete")
@@ -203,11 +211,12 @@ def update_appointments_to_next_on_post_delete(sender, instance, using, **kwargs
203
211
  def update_appointment_from_nextappointment_post_save(
204
212
  sender, instance, raw, created, using, **kwargs
205
213
  ):
206
- if not raw and not kwargs.get("update_fields"):
207
- if isinstance(instance, (NextAppointmentCrfModelMixin,)):
208
- if instance.offschedule_today == NO:
209
- appointment = Appointment.objects.get(
210
- pk=instance.related_visit.appointment.next.id
211
- )
212
- appointment.appt_datetime = instance.appt_datetime
213
- appointment.save()
214
+ if (
215
+ not raw
216
+ and not kwargs.get("update_fields")
217
+ and isinstance(instance, (NextAppointmentCrfModelMixin,))
218
+ and instance.offschedule_today == NO
219
+ ):
220
+ appointment = Appointment.objects.get(pk=instance.related_visit.appointment.next.id)
221
+ appointment.appt_datetime = instance.appt_datetime
222
+ appointment.save()