meta-edc 0.3.39__py3-none-any.whl → 1.4.0__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.
Files changed (637) hide show
  1. meta_ae/action_items.py +38 -27
  2. meta_ae/admin/__init__.py +11 -0
  3. meta_ae/admin/ae_initial_admin.py +5 -2
  4. meta_ae/admin/ae_susar_admin.py +1 -1
  5. meta_ae/admin/death_report_admin.py +1 -1
  6. meta_ae/admin/modeladmin_mixins.py +14 -15
  7. meta_ae/baker_recipes.py +4 -6
  8. meta_ae/choices.py +1 -1
  9. meta_ae/forms/__init__.py +13 -0
  10. meta_ae/forms/death_report_form.py +1 -1
  11. meta_ae/forms/modelform_mixins.py +2 -2
  12. meta_ae/list_data.py +1 -1
  13. meta_ae/migrations/0001_initial.py +27 -27
  14. meta_ae/migrations/0006_aelocalreview_aesponsorreview.py +5 -5
  15. meta_ae/migrations/0017_auto_20221130_2257.py +1 -1
  16. meta_ae/migrations/0022_historicalhospitalization_hospitalization.py +5 -13
  17. meta_ae/migrations/0023_alter_aefollowup_action_identifier_and_more.py +2017 -0
  18. meta_ae/model_mixins/__init__.py +2 -0
  19. meta_ae/model_mixins/ae_review_model_mixin.py +6 -6
  20. meta_ae/model_mixins/death_report_model_mixin.py +3 -3
  21. meta_ae/models/__init__.py +13 -0
  22. meta_ae/models/hospitalization.py +3 -3
  23. meta_ae/pdf_reports/__init__.py +2 -0
  24. meta_ae/templatetags/meta_ae_extras.py +4 -6
  25. meta_analytics/.DS_Store +0 -0
  26. meta_analytics/README.rst +1 -2
  27. meta_analytics/dataframes/__init__.py +27 -0
  28. meta_analytics/dataframes/constants.py +5 -2
  29. meta_analytics/dataframes/get_eos_df.py +16 -4
  30. meta_analytics/dataframes/get_glucose_df.py +166 -0
  31. meta_analytics/dataframes/get_glucose_fbg_df.py +26 -0
  32. meta_analytics/dataframes/get_glucose_fbg_ogtt_df.py +21 -0
  33. meta_analytics/dataframes/get_last_imp_visits_df.py +12 -10
  34. meta_analytics/dataframes/glucose_endpoints/__init__.py +2 -0
  35. meta_analytics/dataframes/glucose_endpoints/endpoint_by_date.py +125 -124
  36. meta_analytics/dataframes/glucose_endpoints/glucose_endpoints_by_date.py +111 -235
  37. meta_analytics/dataframes/screening/__init__.py +2 -0
  38. meta_analytics/dataframes/screening/get_glucose_tested_only_df.py +1 -2
  39. meta_analytics/dataframes/screening/get_screening_df.py +9 -15
  40. meta_analytics/dataframes/utils.py +21 -12
  41. meta_analytics/get_tables.py +1 -2
  42. meta_analytics/tables/__init__.py +2 -0
  43. meta_analytics/tables/enrolled/glucose.py +2 -1
  44. meta_analytics/utils.py +81 -0
  45. meta_auth/auths.py +1 -1
  46. meta_consent/action_items.py +22 -3
  47. meta_consent/admin/__init__.py +7 -0
  48. meta_consent/admin/actions/__init__.py +2 -0
  49. meta_consent/admin/actions/create_missing_prescriptions.py +2 -2
  50. meta_consent/admin/list_filters.py +22 -0
  51. meta_consent/admin/modeladmin_mixins.py +4 -5
  52. meta_consent/admin/subject_consent_v1_ext_admin.py +93 -0
  53. meta_consent/baker_recipes.py +7 -7
  54. meta_consent/consents.py +15 -2
  55. meta_consent/constants.py +1 -0
  56. meta_consent/form_validators/__init__.py +2 -0
  57. meta_consent/forms/__init__.py +8 -0
  58. meta_consent/forms/subject_consent_v1_ext_form.py +47 -0
  59. meta_consent/forms/subject_reconsent_form.py +4 -4
  60. meta_consent/management/commands/create_missing_prescriptions.py +5 -3
  61. meta_consent/migrations/0001_initial.py +9 -9
  62. meta_consent/migrations/0024_historicalsubjectconsentv1.py +3 -8
  63. meta_consent/migrations/0026_historicalsubjectconsentv1ext_subjectconsentv1ext.py +535 -0
  64. meta_consent/migrations/0027_auto_20250111_0344.py +30 -0
  65. meta_consent/migrations/0028_historicalsubjectconsentv1ext_assessment_score_and_more.py +162 -0
  66. meta_consent/migrations/0029_alter_historicalsubjectconsentv1ext_agrees_to_extension_and_more.py +33 -0
  67. meta_consent/migrations/0030_auto_20250120_2114.py +40 -0
  68. meta_consent/migrations/0031_alter_historicalsubjectconsent_guardian_name_and_more.py +124 -0
  69. meta_consent/migrations/0032_alter_historicalsubjectconsent_device_created_and_more.py +678 -0
  70. meta_consent/migrations/0033_historicalsubjectconsentspfq_subjectconsentspfq.py +615 -0
  71. meta_consent/migrations/0034_remove_subjectconsentspfq_site_and_more.py +23 -0
  72. meta_consent/migrations/0035_alter_historicalsubjectconsent_consent_definition_name_and_more.py +43 -0
  73. meta_consent/models/__init__.py +10 -0
  74. meta_consent/models/model_mixins.py +1 -2
  75. meta_consent/models/signals.py +25 -11
  76. meta_consent/models/subject_consent.py +2 -2
  77. meta_consent/models/subject_consent_v1.py +0 -1
  78. meta_consent/models/subject_consent_v1_ext.py +34 -0
  79. meta_consent/models/subject_reconsent.py +4 -4
  80. meta_dashboard/navbars.py +2 -6
  81. meta_dashboard/patterns.py +1 -1
  82. meta_dashboard/templates/meta_dashboard/{bootstrap3/buttons → buttons}/eligibility_button.html +1 -1
  83. meta_dashboard/templates/meta_dashboard/{bootstrap3/buttons → buttons}/screening_button.html +1 -1
  84. meta_dashboard/templates/meta_dashboard/subject/dashboard/sidebar.html +24 -0
  85. meta_dashboard/templates/meta_dashboard/{bootstrap3/subject → subject}/dashboard/top_bar.html +1 -1
  86. meta_dashboard/templates/meta_dashboard/subject/dashboard.html +14 -0
  87. meta_dashboard/templatetags/meta_dashboard_extras.py +9 -14
  88. meta_dashboard/urls.py +5 -5
  89. meta_dashboard/view_utils/__init__.py +13 -0
  90. meta_dashboard/view_utils/subject_screening_button.py +13 -20
  91. meta_dashboard/views/__init__.py +8 -0
  92. meta_dashboard/views/ae/__init__.py +2 -0
  93. meta_dashboard/views/ae/ae_listboard_view.py +1 -1
  94. meta_dashboard/views/ae/death_report_listboard_view.py +1 -1
  95. meta_dashboard/views/screening/__init__.py +2 -0
  96. meta_dashboard/views/screening/listboard_view.py +2 -3
  97. meta_dashboard/views/subject/__init__.py +2 -0
  98. meta_dashboard/views/subject/dashboard/__init__.py +2 -0
  99. meta_dashboard/views/subject/dashboard/dashboard_view.py +19 -8
  100. meta_dashboard/views/subject/listboard/__init__.py +2 -0
  101. meta_dashboard/views/subject/listboard/listboard_view.py +2 -3
  102. meta_edc/__init__.py +5 -9
  103. meta_edc/admin.py +3 -4
  104. meta_edc/celery.py +2 -2
  105. meta_edc/celery_live.py +19 -0
  106. meta_edc/celery_uat.py +25 -0
  107. meta_edc/management/commands/update_forms_reference.py +16 -14
  108. meta_edc/meta_version.py +2 -2
  109. meta_edc/navbars.py +7 -5
  110. meta_edc/settings/debug.py +13 -4
  111. meta_edc/settings/defaults.py +55 -18
  112. meta_edc/settings/live.py +4 -1
  113. meta_edc/settings/logging.py +9 -4
  114. meta_edc/settings/minimal.py +4 -5
  115. meta_edc/settings/uat.py +3 -1
  116. meta_edc/templates/meta_edc/{bootstrap3/base.html → base.html} +1 -1
  117. meta_edc/templates/meta_edc/{bootstrap3/home.html → home.html} +2 -2
  118. meta_edc/urls.py +3 -1
  119. meta_edc/utils.py +3 -1
  120. meta_edc/views/__init__.py +2 -0
  121. meta_edc/views/home_view.py +1 -2
  122. meta_edc-1.4.0.dist-info/METADATA +174 -0
  123. {meta_edc-0.3.39.dist-info → meta_edc-1.4.0.dist-info}/RECORD +578 -586
  124. meta_edc-1.4.0.dist-info/WHEEL +4 -0
  125. meta_labs/list_data.py +2 -2
  126. meta_labs/reportables.py +80 -11
  127. meta_lists/list_data.py +13 -4
  128. meta_lists/migrations/0008_auto_20200528_1517.py +2 -2
  129. meta_lists/migrations/0019_auto_20250128_0143.py +48 -0
  130. meta_lists/migrations/0020_alter_abnormalfootappearanceobservations_extra_value_and_more.py +404 -0
  131. meta_pharmacy/admin/__init__.py +5 -0
  132. meta_pharmacy/admin/rx_admin.py +1 -0
  133. meta_pharmacy/admin/substitutions_admin.py +3 -3
  134. meta_pharmacy/constants.py +1 -1
  135. meta_pharmacy/forms/__init__.py +2 -0
  136. meta_pharmacy/forms/rx_form.py +0 -1
  137. meta_pharmacy/forms/substitutions_form.py +6 -4
  138. meta_pharmacy/labels/__init__.py +5 -2
  139. meta_pharmacy/labels/draw_label_for_subject_with_barcode.py +4 -1
  140. meta_pharmacy/labels/draw_label_with_test_data.py +2 -2
  141. meta_pharmacy/labels/label_data.py +1 -2
  142. meta_pharmacy/labels/print_sheets.py +4 -6
  143. meta_pharmacy/management/commands/update_initial_pharmacy_data.py +4 -3
  144. meta_pharmacy/migrations/0002_initial.py +7 -20
  145. meta_pharmacy/migrations/0003_auto_20240909_2335.py +3 -2
  146. meta_pharmacy/migrations/0006_lotnumber_label.py +5 -14
  147. meta_pharmacy/migrations/0008_remove_lotnumber_medication_and_more.py +5 -6
  148. meta_pharmacy/migrations/0010_alter_historicallabeldata_device_created_and_more.py +382 -0
  149. meta_pharmacy/models/__init__.py +7 -0
  150. meta_pharmacy/models/label_data.py +4 -5
  151. meta_pharmacy/models/rx_label.py +0 -1
  152. meta_pharmacy/models/substitutions.py +4 -4
  153. meta_pharmacy/prepare_meta_pharmacy.py +1 -1
  154. meta_pharmacy/utils/__init__.py +2 -0
  155. meta_pharmacy/utils/update_initial_pharmacy_data.py +1 -1
  156. meta_prn/action_items.py +44 -45
  157. meta_prn/admin/__init__.py +16 -0
  158. meta_prn/admin/dm_referral_admin.py +2 -1
  159. meta_prn/admin/end_of_study_admin.py +26 -15
  160. meta_prn/admin/loss_to_followup_admin.py +3 -2
  161. meta_prn/admin/off_study_medication_admin.py +5 -6
  162. meta_prn/admin/offschedule_admin.py +12 -11
  163. meta_prn/admin/offschedule_dm_referral_admin.py +11 -10
  164. meta_prn/admin/offschedule_postnatal_admin.py +15 -7
  165. meta_prn/admin/offschedule_pregnancy_admin.py +18 -9
  166. meta_prn/admin/onschedule_admin.py +7 -8
  167. meta_prn/admin/onschedule_dm_referral_admin.py +11 -12
  168. meta_prn/admin/pregnancy_notification_admin.py +5 -6
  169. meta_prn/admin/protocol_incident_admin.py +1 -1
  170. meta_prn/admin/subject_transfer_admin.py +1 -1
  171. meta_prn/baker_recipes.py +10 -10
  172. meta_prn/choices.py +11 -7
  173. meta_prn/constants.py +1 -0
  174. meta_prn/form_validators/__init__.py +5 -0
  175. meta_prn/form_validators/end_of_study.py +65 -20
  176. meta_prn/form_validators/protocol_incident.py +1 -1
  177. meta_prn/forms/__init__.py +13 -0
  178. meta_prn/forms/dm_referral_form.py +2 -8
  179. meta_prn/forms/end_of_study_form.py +1 -1
  180. meta_prn/forms/loss_to_followup_form.py +1 -1
  181. meta_prn/forms/off_study_medication_form.py +2 -2
  182. meta_prn/forms/offschedule_form.py +25 -0
  183. meta_prn/forms/pregnancy_notification_form.py +15 -17
  184. meta_prn/list_data.py +3 -3
  185. meta_prn/migrations/0001_initial.py +25 -25
  186. meta_prn/migrations/0017_auto_20220307_1929.py +5 -5
  187. meta_prn/migrations/0018_auto_20220309_2106.py +9 -9
  188. meta_prn/migrations/0021_auto_20220316_2147.py +13 -13
  189. meta_prn/migrations/0022_auto_20220318_0133.py +9 -9
  190. meta_prn/migrations/0032_historicalegfrnotification_egfrnotification.py +5 -13
  191. meta_prn/migrations/0034_auto_20220630_1110.py +1 -1
  192. meta_prn/migrations/0035_auto_20220630_1140.py +1 -1
  193. meta_prn/migrations/0038_alter_endofstudy_delivery_date_and_more.py +5 -13
  194. meta_prn/migrations/0041_endofstudy_transfer_date_and_more.py +5 -13
  195. meta_prn/migrations/0057_historicalonscheduledmreferral_and_more.py +13 -38
  196. meta_prn/migrations/0060_alter_onschedule_managers_and_more.py +55 -0
  197. meta_prn/migrations/0061_auto_20250115_2025.py +56 -0
  198. meta_prn/migrations/0062_alter_endofstudy_offstudy_reason_and_more.py +72 -0
  199. meta_prn/migrations/0063_historicaloffstudymedication_singleton_field_and_more.py +37 -0
  200. meta_prn/migrations/0064_auto_20250602_2143.py +18 -0
  201. meta_prn/migrations/0065_alter_historicaloffstudymedication_subject_identifier_and_more.py +23 -0
  202. meta_prn/migrations/0066_alter_historicallosstofollowup_subject_identifier_and_more.py +23 -0
  203. meta_prn/migrations/0067_alter_offschedule_managers_and_more.py +2557 -0
  204. meta_prn/migrations/0068_alter_dmreferral_referral_note_and_more.py +235 -0
  205. meta_prn/migrations/0069_alter_historicaloffstudymedication_singleton_field_and_more.py +37 -0
  206. meta_prn/models/__init__.py +20 -0
  207. meta_prn/models/dm_referral.py +2 -2
  208. meta_prn/models/end_of_study.py +28 -23
  209. meta_prn/models/loss_to_followup.py +8 -10
  210. meta_prn/models/off_study_medication.py +7 -4
  211. meta_prn/models/offschedule.py +4 -4
  212. meta_prn/models/pregnancy_notification.py +3 -5
  213. meta_prn/models/protocol_incident.py +5 -2
  214. meta_prn/models/signals.py +16 -14
  215. meta_prn/models/subject_transfer.py +7 -0
  216. meta_prn/templates/meta_prn/eos/additional_instructions.html +3 -0
  217. meta_prn/templates/meta_prn/offschedule/additional_instructions.html +2 -0
  218. meta_rando/migrations/0001_initial.py +5 -5
  219. meta_rando/migrations/0006_alter_historicalrandomizationlist_allocated_user_and_more.py +130 -0
  220. meta_rando/migrations/0007_spfqlist.py +197 -0
  221. meta_rando/migrations/0008_delete_spfqlist.py +16 -0
  222. meta_rando/models/__init__.py +1 -0
  223. meta_rando/{models.py → models/randomization_list.py} +3 -3
  224. meta_rando/randomizers.py +2 -2
  225. meta_reports/__init__.py +2 -0
  226. meta_reports/admin/__init__.py +16 -0
  227. meta_reports/admin/dbviews/__init__.py +13 -0
  228. meta_reports/admin/dbviews/glucose_summary_admin.py +7 -7
  229. meta_reports/admin/dbviews/imp_substitutions_admin.py +14 -13
  230. meta_reports/admin/dbviews/missing_screening_ogtt_admin/__init__.py +5 -0
  231. meta_reports/admin/dbviews/missing_screening_ogtt_admin/note_model_admin.py +27 -3
  232. meta_reports/admin/dbviews/missing_screening_ogtt_admin/unmanaged_model_admin.py +7 -7
  233. meta_reports/admin/dbviews/on_study_missing_lab_values_admin/__init__.py +2 -0
  234. meta_reports/admin/dbviews/on_study_missing_lab_values_admin/unmanaged_model_admin.py +0 -3
  235. meta_reports/admin/dbviews/on_study_missing_values_admin/__init__.py +2 -0
  236. meta_reports/admin/dbviews/on_study_missing_values_admin/unmanaged_model_admin.py +0 -1
  237. meta_reports/admin/dbviews/patient_history_missing_baseline_cd4_admin.py +9 -9
  238. meta_reports/admin/dbviews/unattended_three_in_row2_admin.py +6 -6
  239. meta_reports/admin/dbviews/unattended_three_in_row_admin.py +5 -5
  240. meta_reports/admin/dbviews/unattended_two_in_row_admin.py +5 -5
  241. meta_reports/admin/endpoints_admin.py +1 -1
  242. meta_reports/admin/endpoints_all_admin.py +0 -1
  243. meta_reports/admin/last_imp_refill_admin.py +33 -36
  244. meta_reports/admin/list_filters.py +3 -3
  245. meta_reports/admin/modeladmin_mixins.py +10 -17
  246. meta_reports/death_report.py +2 -2
  247. meta_reports/forms/__init__.py +2 -0
  248. meta_reports/forms/missing_ogtt_note_form.py +3 -4
  249. meta_reports/management/commands/generate_endpoints.py +5 -4
  250. meta_reports/migrations/0035_historicalmissingogttnote_missingogttnote.py +5 -14
  251. meta_reports/migrations/0054_auto_20250422_2003.py +81 -0
  252. meta_reports/migrations/0055_alter_glucosesummary_table.py +17 -0
  253. meta_reports/migrations/0056_auto_20250422_2214.py +54 -0
  254. meta_reports/migrations/0057_auto_20250422_2224.py +54 -0
  255. meta_reports/migrations/0058_auto_20250422_2232.py +54 -0
  256. meta_reports/migrations/0059_alter_endpoints_created_and_more.py +161 -0
  257. meta_reports/migrations/0060_auto_20250926_0242.py +366 -0
  258. meta_reports/migrations/0061_auto_20251004_0043.py +21 -0
  259. meta_reports/migrations/0062_auto_20251004_0106.py +21 -0
  260. meta_reports/models/__init__.py +17 -0
  261. meta_reports/models/dbviews/__init__.py +14 -0
  262. meta_reports/models/dbviews/glucose_summary/__init__.py +2 -0
  263. meta_reports/models/dbviews/glucose_summary/unmanaged_model.py +15 -3
  264. meta_reports/models/dbviews/glucose_summary/view_definition.py +8 -5
  265. meta_reports/models/dbviews/imp_substitutions/__init__.py +2 -0
  266. meta_reports/models/dbviews/imp_substitutions/unmanaged_model.py +1 -2
  267. meta_reports/models/dbviews/imp_substitutions/view_definition.py +1 -1
  268. meta_reports/models/dbviews/missing_screening_ogtt/__init__.py +2 -0
  269. meta_reports/models/dbviews/missing_screening_ogtt/note_model.py +2 -2
  270. meta_reports/models/dbviews/missing_screening_ogtt/unmanaged_model.py +4 -3
  271. meta_reports/models/dbviews/on_study_missing_lab_values/__init__.py +2 -0
  272. meta_reports/models/dbviews/on_study_missing_lab_values/qa_cases.py +13 -11
  273. meta_reports/models/dbviews/on_study_missing_lab_values/unmanged_model.py +0 -1
  274. meta_reports/models/dbviews/on_study_missing_values/__init__.py +2 -0
  275. meta_reports/models/dbviews/on_study_missing_values/qa_cases.py +19 -1
  276. meta_reports/models/dbviews/on_study_missing_values/unmanged_model.py +0 -1
  277. meta_reports/models/dbviews/patient_history_missing_baseline_cd4/__init__.py +2 -0
  278. meta_reports/models/dbviews/patient_history_missing_baseline_cd4/unmanaged_model.py +0 -1
  279. meta_reports/models/dbviews/unattended_three_in_row/__init__.py +2 -0
  280. meta_reports/models/dbviews/unattended_three_in_row/unmanaged_model.py +0 -1
  281. meta_reports/models/dbviews/unattended_three_in_row2/__init__.py +2 -0
  282. meta_reports/models/dbviews/unattended_three_in_row2/unmanaged_model.py +0 -1
  283. meta_reports/models/dbviews/unattended_two_in_row/__init__.py +2 -0
  284. meta_reports/models/dbviews/unattended_two_in_row/unmanaged_model.py +0 -1
  285. meta_reports/models/endpoints.py +8 -4
  286. meta_reports/models/last_imp_refill.py +2 -3
  287. meta_reports/pdf_report.py +2 -2
  288. meta_reports/tasks.py +4 -3
  289. meta_reports/templates/meta_reports/columns/subject_identifier_column.html +1 -1
  290. meta_reports/templates/meta_reports/endpoints_all_change_list_note.html +1 -1
  291. meta_reports/templates/meta_reports/endpoints_change_list_note.html +1 -1
  292. meta_reports/templates/meta_reports/last_imp_refill/changelist_note.html +13 -0
  293. meta_screening/admin/__init__.py +8 -0
  294. meta_screening/admin/fieldsets.py +13 -15
  295. meta_screening/admin/list_filters.py +7 -5
  296. meta_screening/admin/screening_part_one_admin.py +1 -3
  297. meta_screening/admin/screening_part_three_admin.py +2 -3
  298. meta_screening/admin/screening_part_two_admin.py +7 -10
  299. meta_screening/admin/subject_refusal_admin.py +5 -3
  300. meta_screening/admin/subject_screening_admin.py +21 -10
  301. meta_screening/baker_recipes.py +18 -10
  302. meta_screening/calculators.py +1 -1
  303. meta_screening/choices.py +1 -1
  304. meta_screening/constants.py +1 -1
  305. meta_screening/eligibility/__init__.py +9 -0
  306. meta_screening/eligibility/eligibility.py +21 -14
  307. meta_screening/eligibility/eligibility_part_one.py +1 -1
  308. meta_screening/eligibility/eligibility_part_three/__init__.py +2 -0
  309. meta_screening/eligibility/eligibility_part_three/base_eligibility_part_three.py +68 -49
  310. meta_screening/eligibility/eligibility_part_three/eligibility_part_three_phase_three.py +14 -15
  311. meta_screening/eligibility/eligibility_part_two.py +1 -1
  312. meta_screening/form_validators/__init__.py +8 -0
  313. meta_screening/form_validators/screening_part_one.py +1 -1
  314. meta_screening/form_validators/screening_part_three.py +6 -2
  315. meta_screening/form_validators/screening_part_two.py +1 -1
  316. meta_screening/form_validators/subject_refusal.py +1 -1
  317. meta_screening/forms/__init__.py +20 -0
  318. meta_screening/forms/field_lists.py +16 -18
  319. meta_screening/forms/screening_part_one_form.py +2 -2
  320. meta_screening/forms/screening_part_three_form.py +5 -3
  321. meta_screening/forms/screening_part_two_form.py +1 -5
  322. meta_screening/forms/subject_refusal_form.py +0 -4
  323. meta_screening/forms/subject_screening_form.py +0 -4
  324. meta_screening/migrations/0001_initial.py +15 -15
  325. meta_screening/migrations/0010_auto_20191106_0828.py +5 -5
  326. meta_screening/migrations/0012_auto_20191107_0427.py +1 -1
  327. meta_screening/migrations/0068_alter_historicalscreeningpartone_acute_condition_and_more.py +1579 -0
  328. meta_screening/model_mixins/__init__.py +8 -0
  329. meta_screening/model_mixins/calculated_model_mixin.py +2 -2
  330. meta_screening/model_mixins/creatinine_fields_model_mixin.py +1 -1
  331. meta_screening/model_mixins/eligibility_model_mixin.py +6 -4
  332. meta_screening/model_mixins/part_one_fields_model_mixin.py +16 -19
  333. meta_screening/model_mixins/part_three_fields_model_mixin.py +6 -7
  334. meta_screening/model_mixins/part_two_fields_model_mixin.py +19 -17
  335. meta_screening/models/__init__.py +9 -0
  336. meta_screening/models/icp_referral.py +5 -5
  337. meta_screening/models/proxy_models.py +1 -1
  338. meta_screening/models/signals.py +10 -11
  339. meta_screening/models/subject_refusal.py +1 -1
  340. meta_screening/models/subject_screening.py +2 -4
  341. meta_subject/action_items.py +18 -14
  342. meta_subject/admin/__init__.py +42 -0
  343. meta_subject/admin/birth_outcome_admin.py +7 -9
  344. meta_subject/admin/blood_results/__init__.py +9 -0
  345. meta_subject/admin/blood_results/blood_results_fbc_admin.py +1 -1
  346. meta_subject/admin/blood_results/blood_results_hba1c_admin.py +1 -1
  347. meta_subject/admin/blood_results/blood_results_ins_admin.py +1 -1
  348. meta_subject/admin/blood_results/blood_results_lft_admin.py +1 -1
  349. meta_subject/admin/blood_results/blood_results_lipids_admin.py +1 -1
  350. meta_subject/admin/blood_results/blood_results_rft_admin.py +3 -5
  351. meta_subject/admin/complications_glycemia_admin.py +1 -1
  352. meta_subject/admin/delivery_admin.py +8 -11
  353. meta_subject/admin/diabetes/__init__.py +2 -0
  354. meta_subject/admin/diabetes/dm_endpoint_admin.py +2 -2
  355. meta_subject/admin/diabetes/dm_followup_admin.py +3 -2
  356. meta_subject/admin/egfr_drop_notification_admin.py +1 -1
  357. meta_subject/admin/fields.py +5 -5
  358. meta_subject/admin/fieldsets.py +5 -5
  359. meta_subject/admin/followup_examination_admin.py +11 -9
  360. meta_subject/admin/followup_vitals_admin.py +31 -6
  361. meta_subject/admin/glucose_admin.py +4 -8
  362. meta_subject/admin/glucose_fbg_admin.py +18 -11
  363. meta_subject/admin/health_economics/__init__.py +2 -0
  364. meta_subject/admin/health_economics/health_economics_simple_admin.py +1 -1
  365. meta_subject/admin/health_economics/health_economics_update_admin.py +1 -1
  366. meta_subject/admin/hepatitis_test_admin.py +1 -1
  367. meta_subject/admin/hiv_exit_review_admin.py +55 -0
  368. meta_subject/admin/list_filters.py +5 -5
  369. meta_subject/admin/mnsi_admin.py +7 -5
  370. meta_subject/admin/next_appointment_admin.py +19 -0
  371. meta_subject/admin/other_arv_regimens_admin.py +3 -3
  372. meta_subject/admin/patient_history_admin.py +4 -4
  373. meta_subject/admin/physical_exam_admin.py +1 -1
  374. meta_subject/admin/pregnancy_update_admin.py +1 -1
  375. meta_subject/admin/study_medication_admin.py +8 -15
  376. meta_subject/admin/subject_requisition_admin.py +2 -2
  377. meta_subject/admin/subject_visit_admin.py +1 -1
  378. meta_subject/admin/subject_visit_missed_admin.py +1 -1
  379. meta_subject/admin/urine_dipstick_test_admin.py +1 -1
  380. meta_subject/admin/urine_pregnancy_admin.py +1 -1
  381. meta_subject/baker_recipes.py +15 -15
  382. meta_subject/choices.py +4 -3
  383. meta_subject/form_validators/__init__.py +15 -0
  384. meta_subject/form_validators/delivery_form_validator.py +3 -3
  385. meta_subject/form_validators/dm_endpoint_form_validator.py +3 -1
  386. meta_subject/form_validators/dm_followup_form_validator.py +8 -7
  387. meta_subject/form_validators/egfr_drop_notification_form_validator.py +1 -1
  388. meta_subject/form_validators/followup_examination_form_validator.py +1 -1
  389. meta_subject/form_validators/glucose_fbg_form_validator.py +76 -0
  390. meta_subject/form_validators/glucose_form_validator.py +10 -67
  391. meta_subject/form_validators/hiv_exit_review_form_validator.py +18 -0
  392. meta_subject/form_validators/mixins.py +95 -0
  393. meta_subject/forms/__init__.py +44 -0
  394. meta_subject/forms/blood_results/__init__.py +9 -0
  395. meta_subject/forms/blood_results/blood_results_hba1c_form.py +1 -1
  396. meta_subject/forms/blood_results/blood_results_rft_form.py +60 -4
  397. meta_subject/forms/delivery_form.py +2 -0
  398. meta_subject/forms/diabetes/__init__.py +2 -0
  399. meta_subject/forms/diabetes/dm_followup_form.py +2 -2
  400. meta_subject/forms/followup_vitals_form.py +27 -1
  401. meta_subject/forms/glucose_fbg_form.py +1 -46
  402. meta_subject/forms/health_economics/__init__.py +2 -0
  403. meta_subject/forms/hepatitis_test_form.py +1 -1
  404. meta_subject/forms/hiv_exit_review_form.py +13 -0
  405. meta_subject/forms/mixins.py +1 -1
  406. meta_subject/forms/next_appointment_form.py +36 -0
  407. meta_subject/forms/other_arv_regimens_form.py +1 -1
  408. meta_subject/forms/patient_history_form.py +1 -1
  409. meta_subject/forms/physical_exam_form.py +1 -1
  410. meta_subject/forms/pregnancy_update_form.py +1 -1
  411. meta_subject/forms/slider_widget.py +1 -1
  412. meta_subject/forms/study_medication_form.py +18 -12
  413. meta_subject/forms/subject_requisition_form.py +1 -1
  414. meta_subject/forms/subject_visit_missed_form.py +1 -1
  415. meta_subject/forms/urine_dipstick_test_form.py +1 -1
  416. meta_subject/forms/urine_pregnancy_form.py +1 -1
  417. meta_subject/management/commands/create_missing_refills.py +3 -3
  418. meta_subject/management/commands/create_missing_rx.py +2 -2
  419. meta_subject/management/commands/missed.py +20 -23
  420. meta_subject/metadata_rules/__init__.py +2 -0
  421. meta_subject/metadata_rules/metadata_rules.py +14 -0
  422. meta_subject/metadata_rules/predicates.py +44 -25
  423. meta_subject/migrations/0001_initial.py +61 -61
  424. meta_subject/migrations/0002_auto_20191021_0353.py +5 -5
  425. meta_subject/migrations/0012_auto_20200118_2334.py +5 -5
  426. meta_subject/migrations/0014_auto_20200120_1622.py +5 -5
  427. meta_subject/migrations/0018_coronakap_historicalcoronakap.py +5 -5
  428. meta_subject/migrations/0033_auto_20200516_2356.py +5 -5
  429. meta_subject/migrations/0038_auto_20200520_0020.py +5 -5
  430. meta_subject/migrations/0040_auto_20200527_2155.py +1 -1
  431. meta_subject/migrations/0045_auto_20200530_1801.py +1 -1
  432. meta_subject/migrations/0051_auto_20200617_2117.py +5 -5
  433. meta_subject/migrations/0063_auto_20210715_0337.py +5 -5
  434. meta_subject/migrations/0066_auto_20210721_0335.py +9 -9
  435. meta_subject/migrations/0067_auto_20210726_0340.py +5 -5
  436. meta_subject/migrations/0068_auto_20210728_1809.py +5 -5
  437. meta_subject/migrations/0072_auto_20210805_1545.py +7 -7
  438. meta_subject/migrations/0073_auto_20210809_0055.py +5 -5
  439. meta_subject/migrations/0077_auto_20210809_2323.py +3 -3
  440. meta_subject/migrations/0082_auto_20210823_1612.py +3 -3
  441. meta_subject/migrations/0083_auto_20210823_1620.py +3 -3
  442. meta_subject/migrations/0088_auto_20210924_0027.py +5 -5
  443. meta_subject/migrations/0090_auto_20210924_0424.py +5 -5
  444. meta_subject/migrations/0093_auto_20211117_0352.py +5 -5
  445. meta_subject/migrations/0095_auto_20220128_1719.py +5 -5
  446. meta_subject/migrations/0098_auto_20220309_2106.py +5 -5
  447. meta_subject/migrations/0101_auto_20220316_2147.py +13 -13
  448. meta_subject/migrations/0107_auto_20220415_0043.py +1 -1
  449. meta_subject/migrations/0115_historicalegfrnotification_egfrnotification.py +5 -13
  450. meta_subject/migrations/0128_auto_20220720_0055.py +1 -1
  451. meta_subject/migrations/0131_auto_20220722_0411.py +1 -1
  452. meta_subject/migrations/0135_auto_20220722_2212.py +2 -1
  453. meta_subject/migrations/0164_dmreferralfollowup_historicaldmreferralfollowup.py +5 -5
  454. meta_subject/migrations/0172_remove_historicalbloodresultsglu_action_item_and_more.py +1 -2
  455. meta_subject/migrations/0177_alter_bloodresultslft_alp_value_and_more.py +1 -2
  456. meta_subject/migrations/0178_historicalhealtheconomicsupdate_and_more.py +5 -14
  457. meta_subject/migrations/0186_healtheconomicsupdate_singleton_field_and_more.py +1 -2
  458. meta_subject/migrations/0187_dmdiagnosis_historicaldmdiagnosis_dmdxresult_and_more.py +5 -14
  459. meta_subject/migrations/0188_historicaldmdxresult_dmdxresult.py +5 -14
  460. meta_subject/migrations/0199_auto_20240516_0247.py +1 -1
  461. meta_subject/migrations/0209_remove_historicaldmdxresult_dm_diagnosis_and_more.py +1 -2
  462. meta_subject/migrations/0212_auto_20240827_2222.py +1 -1
  463. meta_subject/migrations/0216_historicalnextappointment_nextappointment.py +553 -0
  464. meta_subject/migrations/0217_alter_historicalnextappointment_appt_datetime_and_more.py +42 -0
  465. meta_subject/migrations/0218_alter_historicalnextappointment_appt_date_and_more.py +53 -0
  466. meta_subject/migrations/0219_remove_historicalnextappointment_allow_create_interim_and_more.py +92 -0
  467. meta_subject/migrations/0220_historicalbloodresultsgludummy_bloodresultsgludummy.py +825 -0
  468. meta_subject/migrations/0221_auto_20250402_1913.py +42 -0
  469. meta_subject/migrations/0222_alter_historicalstudymedication_stock_codes_and_more.py +46 -0
  470. meta_subject/migrations/0223_bloodresultsfbc_errors_bloodresultsgludummy_errors_and_more.py +83 -0
  471. meta_subject/migrations/0224_bloodresultsfbc_abnormal_summary_and_more.py +153 -0
  472. meta_subject/migrations/0225_followupvitals_waist_circumference_and_more.py +46 -0
  473. meta_subject/migrations/0226_followupvitals_waist_circumference_comment_and_more.py +97 -0
  474. meta_subject/migrations/0227_alter_followupvitals_waist_circumference_comment_and_more.py +97 -0
  475. meta_subject/migrations/0228_bloodresultshba1c_hba1c_datetime_and_more.py +2518 -0
  476. meta_subject/migrations/0229_alter_glucosefbg_consent_model_and_more.py +1918 -0
  477. meta_subject/migrations/0230_alter_historicaldelivery_action_identifier_and_more.py +1733 -0
  478. meta_subject/migrations/0231_alter_historicalmedicationadherence_consent_model_and_more.py +2054 -0
  479. meta_subject/migrations/0232_alter_patienthistory_concomitant_conditions_and_more.py +1170 -0
  480. meta_subject/migrations/0233_historicalspfq_spfq.py +1066 -0
  481. meta_subject/migrations/0234_remove_spfq_site_remove_spfq_subject_visit_and_more.py +27 -0
  482. meta_subject/migrations/0235_glucosefbg_endpoint_today_and_more.py +606 -0
  483. meta_subject/migrations/0236_alter_historicalhivexitreview_other_current_arv_regimen_and_more.py +58 -0
  484. meta_subject/migrations/0237_historicalhivexitreview_singleton_field_and_more.py +68 -0
  485. meta_subject/migrations/0238_historicalhivexitreview_available_and_more.py +88 -0
  486. meta_subject/model_mixins/__init__.py +10 -0
  487. meta_subject/model_mixins/arv_history_model_mixin.py +3 -44
  488. meta_subject/model_mixins/arv_review_model_mixin.py +53 -0
  489. meta_subject/model_mixins/search_slug_model_mixin.py +1 -2
  490. meta_subject/model_mixins/vitals_fields_model_mixin.py +33 -0
  491. meta_subject/models/__init__.py +52 -0
  492. meta_subject/models/birth_outcomes.py +3 -3
  493. meta_subject/models/blood_results/__init__.py +11 -0
  494. meta_subject/models/blood_results/blood_results_glu.py +29 -0
  495. meta_subject/models/blood_results/blood_results_hba1c.py +0 -1
  496. meta_subject/models/blood_results/blood_results_ins.py +0 -1
  497. meta_subject/models/blood_results/blood_results_lft.py +0 -1
  498. meta_subject/models/delivery.py +4 -6
  499. meta_subject/models/diabetes/__init__.py +2 -0
  500. meta_subject/models/diabetes/dm_endpoint.py +5 -5
  501. meta_subject/models/diabetes/dm_followup.py +7 -8
  502. meta_subject/models/diet_and_lifestyle.py +2 -2
  503. meta_subject/models/followup_examination.py +12 -14
  504. meta_subject/models/glucose.py +5 -5
  505. meta_subject/models/glucose_fbg.py +17 -4
  506. meta_subject/models/health_economics/__init__.py +2 -0
  507. meta_subject/models/health_economics/health_economics.py +8 -8
  508. meta_subject/models/health_economics/health_economics_simple.py +2 -2
  509. meta_subject/models/health_economics/health_economics_update.py +3 -2
  510. meta_subject/models/hepatitis_test.py +2 -2
  511. meta_subject/models/hiv_exit_review.py +44 -0
  512. meta_subject/models/next_appointment.py +15 -0
  513. meta_subject/models/other_arv_regimens_detail.py +1 -1
  514. meta_subject/models/patient_history.py +6 -7
  515. meta_subject/models/physical_exam.py +2 -2
  516. meta_subject/models/pregnancy_update.py +1 -1
  517. meta_subject/models/signals.py +15 -13
  518. meta_subject/models/subject_visit.py +2 -2
  519. meta_subject/models/todo.txt +1 -1
  520. meta_subject/models/urine_dipstick_test.py +2 -2
  521. meta_subject/models/urine_pregnancy.py +1 -1
  522. meta_subject/static/meta_subject/slider.css +1 -1
  523. meta_subject/templates/meta_subject/endpoint_review_instructions.html +1 -1
  524. meta_subject/templates/meta_subject/widgets/slider.html +0 -1
  525. meta_visit_schedule/visit_schedules/__init__.py +2 -0
  526. meta_visit_schedule/visit_schedules/phase_three/__init__.py +2 -0
  527. meta_visit_schedule/visit_schedules/phase_three/crfs.py +34 -1
  528. meta_visit_schedule/visit_schedules/phase_three/schedule.py +4 -4
  529. meta_visit_schedule/visit_schedules/phase_three/schedule_dm_referral.py +1 -2
  530. meta_visit_schedule/visit_schedules/phase_three/schedule_pregnancy.py +2 -3
  531. meta_ae/tests/holidays.csv +0 -15
  532. meta_ae/tests/tests/test_actions.py +0 -127
  533. meta_ae/tests/urls.py +0 -10
  534. meta_analytics/dataframes/enrolled/__init__.py +0 -1
  535. meta_analytics/dataframes/enrolled/get_glucose_df.py +0 -122
  536. meta_analytics/tests/__init__.py +0 -0
  537. meta_analytics/tests/test_endpoints_by_date.py +0 -94
  538. meta_consent/tests/__init__.py +0 -0
  539. meta_consent/tests/holidays.csv +0 -15
  540. meta_consent/tests/tests/__init__.py +0 -0
  541. meta_consent/tests/tests/test_form_validators.py +0 -110
  542. meta_consent/tests/tests/test_subject_consent.py +0 -10
  543. meta_consent/tests/urls.py +0 -17
  544. meta_dashboard/templates/meta_dashboard/bootstrap3/subject/dashboard.html +0 -11
  545. meta_dashboard/tests/__init__.py +0 -0
  546. meta_dashboard/tests/admin.py +0 -22
  547. meta_dashboard/tests/holidays.csv +0 -15
  548. meta_dashboard/tests/tests/__init__.py +0 -0
  549. meta_dashboard/tests/urls.py +0 -55
  550. meta_edc/tests/__init__.py +0 -0
  551. meta_edc/tests/tests/__init__.py +0 -0
  552. meta_edc/tests/tests/test_endpoints.py +0 -554
  553. meta_edc-0.3.39.dist-info/AUTHORS +0 -0
  554. meta_edc-0.3.39.dist-info/METADATA +0 -766
  555. meta_edc-0.3.39.dist-info/WHEEL +0 -5
  556. meta_edc-0.3.39.dist-info/top_level.txt +0 -20
  557. meta_labs/tests/__init__.py +0 -0
  558. meta_labs/tests/test_labs.py +0 -27
  559. meta_labs/tests/test_reportables.py +0 -16
  560. meta_labs/tests/urls.py +0 -4
  561. meta_lists/tests/__init__.py +0 -0
  562. meta_lists/tests/test_lists.py +0 -8
  563. meta_pharmacy/admin/actions.py +0 -38
  564. meta_prn/tests/__init__.py +0 -0
  565. meta_prn/tests/tests/__init__.py +0 -0
  566. meta_prn/tests/tests/test_actions.py +0 -97
  567. meta_prn/tests/tests/test_dm_referral.py +0 -206
  568. meta_prn/tests/tests/test_manager_order.py +0 -14
  569. meta_prn/tests/tests/test_pregnancy_notification.py +0 -93
  570. meta_prn/tests/urls.py +0 -10
  571. meta_rando/tests/__init__.py +0 -0
  572. meta_rando/tests/tests/__init__.py +0 -0
  573. meta_rando/tests/tests/test_randomizers.py +0 -57
  574. meta_reports/tests/__init__.py +0 -0
  575. meta_reports/tests/test_reports.py +0 -35
  576. meta_reports/tests/test_sql_gen.py +0 -5
  577. meta_reports/tests/urls.py +0 -4
  578. meta_screening/offline_models.py +0 -3
  579. meta_screening/tests/__init__.py +0 -0
  580. meta_screening/tests/holidays.csv +0 -15
  581. meta_screening/tests/meta_test_case_mixin.py +0 -216
  582. meta_screening/tests/options.py +0 -127
  583. meta_screening/tests/tests/__init__.py +0 -0
  584. meta_screening/tests/tests/test_forms.py +0 -397
  585. meta_screening/tests/tests/test_screening_part_one.py +0 -108
  586. meta_screening/tests/tests/test_screening_part_three.py +0 -436
  587. meta_screening/tests/tests/test_screening_part_two.py +0 -84
  588. meta_sites/tests/__init__.py +0 -0
  589. meta_sites/tests/test_sites.py +0 -12
  590. meta_sites/tests/urls.py +0 -4
  591. meta_stats/__init__.py +0 -0
  592. meta_stats/incidence.py +0 -16
  593. meta_stats/models.py +0 -0
  594. meta_stats/tests/__init__.py +0 -0
  595. meta_stats/tests/tests/__init__.py +0 -0
  596. meta_stats/tests/tests/test_incidence.py +0 -10
  597. meta_subject/tests/__init__.py +0 -0
  598. meta_subject/tests/holidays.csv +0 -15
  599. meta_subject/tests/tests/__init__.py +0 -0
  600. meta_subject/tests/tests/test_egfr.py +0 -234
  601. meta_subject/tests/tests/test_fixes.py +0 -64
  602. meta_subject/tests/tests/test_followup.py +0 -52
  603. meta_subject/tests/tests/test_manager_order.py +0 -11
  604. meta_subject/tests/tests/test_medication_adherence.py +0 -75
  605. meta_subject/tests/tests/test_metadata_rules.py +0 -135
  606. meta_subject/tests/tests/test_mnsi.py +0 -317
  607. meta_subject/tests/tests/test_patient_history_form.py +0 -74
  608. meta_subject/tests/tests/test_physical_exam.py +0 -84
  609. meta_subject/tests/tests/test_sf12.py +0 -173
  610. meta_subject/tests/tests/test_study_medication.py +0 -230
  611. meta_subject/tests/urls.py +0 -24
  612. meta_visit_schedule/tests/__init__.py +0 -0
  613. meta_visit_schedule/tests/tests/__init__.py +0 -0
  614. meta_visit_schedule/tests/tests/test_schedule.py +0 -181
  615. meta_visit_schedule/tests/urls.py +0 -4
  616. tests/__init__.py +0 -0
  617. tests/etc/randomization_list.csv +0 -241
  618. tests/etc/randomization_list_phase_three.csv +0 -241
  619. tests/etc/user-aes-local.key +0 -0
  620. tests/etc/user-aes-restricted.key +0 -1
  621. tests/etc/user-rsa-local-private.pem +0 -27
  622. tests/etc/user-rsa-local-public.pem +0 -9
  623. tests/etc/user-rsa-restricted-private.pem +0 -27
  624. tests/etc/user-rsa-restricted-public.pem +0 -9
  625. tests/etc/user-salt-local.key +0 -0
  626. tests/etc/user-salt-restricted.key +0 -0
  627. tests/holidays.csv +0 -15
  628. tests/test_settings.py +0 -186
  629. /meta_ae/templates/meta_ae/{bootstrap3/ae_initial_description.html → aeinitial_description.html} +0 -0
  630. /meta_dashboard/templates/meta_dashboard/{bootstrap3/buttons → buttons}/add_consent_button.html +0 -0
  631. /meta_dashboard/templates/meta_dashboard/{bootstrap3/buttons → buttons}/dashboard_button.html +0 -0
  632. /meta_dashboard/templates/meta_dashboard/{bootstrap3/buttons → buttons}/refusal_button.html +0 -0
  633. /meta_dashboard/templates/meta_dashboard/{bootstrap3/screening → screening}/listboard.html +0 -0
  634. /meta_dashboard/templates/meta_dashboard/{bootstrap3/subject → subject}/listboard.html +0 -0
  635. {meta_edc-0.3.39.dist-info → meta_edc-1.4.0.dist-info/licenses}/LICENSE +0 -0
  636. /meta_ae/tests/__init__.py → /meta_subject/management/__init__py.py +0 -0
  637. /meta_ae/tests/tests/__init__.py → /meta_subject/management/commands/__init__py.py +0 -0
@@ -1,29 +1,32 @@
1
1
  from urllib import parse
2
2
 
3
+ from clinicedc_constants import DEAD, DELIVERY, LTFU, PREGNANCY, TOXICITY
3
4
  from django import forms
4
5
  from django.apps import apps as django_apps
5
6
  from django.core.exceptions import ObjectDoesNotExist
6
7
  from django.urls import reverse
7
8
  from django.utils.html import format_html
9
+ from django.utils.translation import gettext as _
8
10
  from edc_action_item.models import ActionType
9
11
  from edc_adverse_event.form_validator_mixins import (
10
12
  RequiresDeathReportFormValidatorMixin,
11
13
  )
12
14
  from edc_consent.constants import CONSENT_WITHDRAWAL
13
- from edc_constants.constants import DEAD, DELIVERY, PREGNANCY, TOXICITY
14
15
  from edc_form_validators import INVALID_ERROR, FormValidator
15
- from edc_ltfu.constants import LTFU
16
16
  from edc_ltfu.modelform_mixins import RequiresLtfuFormValidatorMixin
17
17
  from edc_offstudy.constants import COMPLETED_FOLLOWUP
18
18
  from edc_offstudy.utils import OffstudyError
19
19
  from edc_prn.modelform_mixins import PrnFormValidatorMixin
20
20
  from edc_transfer.constants import TRANSFERRED
21
21
  from edc_utils import formatted_date
22
- from edc_visit_schedule.constants import MONTH36
22
+ from edc_visit_schedule.constants import MONTH36, MONTH48
23
+ from edc_visit_schedule.exceptions import OffScheduleError
24
+ from edc_visit_schedule.site_visit_schedules import site_visit_schedules
23
25
  from edc_visit_schedule.utils import off_all_schedules_or_raise
24
26
 
25
27
  from ..constants import (
26
28
  CLINICAL_WITHDRAWAL,
29
+ COMPLETED_FOLLOWUP_48,
27
30
  INVESTIGATOR_DECISION,
28
31
  OFFSTUDY_MEDICATION_ACTION,
29
32
  )
@@ -44,6 +47,7 @@ class EndOfStudyFormValidator(
44
47
  self.validate_offstudy_datetime_against_last_seen_date()
45
48
 
46
49
  self.validate_completed_36m()
50
+ self.validate_completed_48m()
47
51
 
48
52
  self.required_if(DEAD, field="offstudy_reason", field_required="death_date")
49
53
  self.validate_death_report_if_deceased()
@@ -108,6 +112,41 @@ class EndOfStudyFormValidator(
108
112
  {"offstudy_reason": "Invalid. 36 month visit has not been submitted."},
109
113
  INVALID_ERROR,
110
114
  )
115
+ schedule = site_visit_schedules.get_visit_schedule(
116
+ visit_schedule_name="visit_schedule"
117
+ ).schedules.get("schedule")
118
+ if subject_visit_model_cls.objects.filter(
119
+ subject_identifier=self.subject_identifier,
120
+ visit_code_sequence=0,
121
+ appointment__timepoint__gt=schedule.visits.get(MONTH36).timepoint,
122
+ appointment__schedule_name=schedule.name,
123
+ ):
124
+ self.raise_validation_error(
125
+ {
126
+ "offstudy_reason": (
127
+ "Invalid. Participant has attended visits past 36 months."
128
+ )
129
+ },
130
+ INVALID_ERROR,
131
+ )
132
+
133
+ def validate_completed_48m(self):
134
+ if (
135
+ self.cleaned_data.get("offstudy_reason")
136
+ and self.cleaned_data.get("offstudy_reason").name == COMPLETED_FOLLOWUP_48
137
+ ):
138
+ subject_visit_model_cls = django_apps.get_model("meta_subject.subjectvisit")
139
+ try:
140
+ subject_visit_model_cls.objects.get(
141
+ subject_identifier=self.subject_identifier,
142
+ visit_code=MONTH48,
143
+ visit_code_sequence=0,
144
+ )
145
+ except ObjectDoesNotExist:
146
+ self.raise_validation_error(
147
+ {"offstudy_reason": "Invalid. 48 month visit has not been submitted."},
148
+ INVALID_ERROR,
149
+ )
111
150
 
112
151
  def validate_pregnancy(self):
113
152
  if (
@@ -152,7 +191,7 @@ class EndOfStudyFormValidator(
152
191
  if (obj.bhcg_date or obj.report_datetime.date()) != self.cleaned_data.get(
153
192
  "pregnancy_date"
154
193
  ):
155
- dt = formatted_date((obj.bhcg_date or obj.report_datetime.date()))
194
+ dt = formatted_date(obj.bhcg_date or obj.report_datetime.date())
156
195
  self.raise_validation_error(
157
196
  {
158
197
  "pregnancy_date": (
@@ -189,7 +228,7 @@ class EndOfStudyFormValidator(
189
228
  obj.delivery_datetime.date() or obj.report_datetime.date()
190
229
  ) != self.cleaned_data.get("delivery_date"):
191
230
  dt = formatted_date(
192
- (obj.delivery_datetime.date() or obj.report_datetime.date())
231
+ obj.delivery_datetime.date() or obj.report_datetime.date()
193
232
  )
194
233
  self.raise_validation_error(
195
234
  {
@@ -224,10 +263,15 @@ class EndOfStudyFormValidator(
224
263
 
225
264
  self.raise_validation_error(
226
265
  format_html(
227
- "Participant is reported to be on study medication. Complete "
228
- f'<a href="{url}?{query_string}" title="Add new form">'
229
- f"{off_study_medication_model_cls._meta.verbose_name}</A> "
230
- "PRN action and try again."
266
+ _(
267
+ "Participant is reported to be on study medication. Complete "
268
+ '<a href="{url}?{query_string}" title="Add new form">'
269
+ "{verbose_name}</A> "
270
+ "PRN action and try again."
271
+ ),
272
+ url=url,
273
+ query_string=query_string,
274
+ verbose_name=off_study_medication_model_cls._meta.verbose_name,
231
275
  ),
232
276
  INVALID_ERROR,
233
277
  )
@@ -237,20 +281,21 @@ class EndOfStudyFormValidator(
237
281
  off_all_schedules_or_raise(
238
282
  subject_identifier=self.cleaned_data.get("subject_identifier"),
239
283
  )
240
- except OffstudyError as e:
284
+ except (OffstudyError, OffScheduleError) as e:
241
285
  self.raise_validation_error(str(e), INVALID_ERROR)
242
286
 
243
287
  def validate_offstudy_datetime_against_last_seen_date(self):
244
- if self.cleaned_data.get("offstudy_datetime") and self.cleaned_data.get(
245
- "last_seen_date"
246
- ):
247
- if (
288
+ if (
289
+ self.cleaned_data.get("offstudy_datetime")
290
+ and self.cleaned_data.get("last_seen_date")
291
+ and (
248
292
  self.cleaned_data.get("last_seen_date")
249
293
  > self.cleaned_data.get("offstudy_datetime").date()
250
- ):
251
- raise forms.ValidationError(
252
- {"last_seen_date": "Invalid. May not be after termination date"}
253
- )
294
+ )
295
+ ):
296
+ raise forms.ValidationError(
297
+ {"last_seen_date": "Invalid. May not be after termination date"}
298
+ )
254
299
 
255
300
  def validate_transfer(self):
256
301
  if (
@@ -260,7 +305,7 @@ class EndOfStudyFormValidator(
260
305
  transfer_model_cls = django_apps.get_model("meta_prn.subjecttransfer")
261
306
  try:
262
307
  obj = transfer_model_cls.objects.get(
263
- subject_visit__subject_identifier=self.subject_identifier,
308
+ subject_identifier=self.subject_identifier,
264
309
  )
265
310
  except ObjectDoesNotExist:
266
311
  self.raise_validation_error(
@@ -273,7 +318,7 @@ class EndOfStudyFormValidator(
273
318
  INVALID_ERROR,
274
319
  )
275
320
  else:
276
- if obj.transfer_date.date() != self.cleaned_data.get("transfer_date"):
321
+ if obj.transfer_date != self.cleaned_data.get("transfer_date"):
277
322
  dt = formatted_date(obj.transfer_date)
278
323
  self.raise_validation_error(
279
324
  {
@@ -1,4 +1,4 @@
1
- from edc_constants.constants import CLOSED, OTHER, YES
1
+ from clinicedc_constants import CLOSED, OTHER, YES
2
2
  from edc_form_validators import FormValidator
3
3
  from edc_prn.modelform_mixins import PrnFormValidatorMixin
4
4
 
@@ -8,3 +8,16 @@ from .offschedule_pregnancy_form import OffSchedulePregnancyForm
8
8
  from .pregnancy_notification_form import PregnancyNotificationForm
9
9
  from .protocol_incident_form import ProtocolIncidentForm
10
10
  from .subject_transfer_form import SubjectTransferForm
11
+
12
+ __all__ = [
13
+ "DmReferralForm",
14
+ "EndOfStudyForm",
15
+ "LossToFollowupForm",
16
+ "OffScheduleDmReferralForm",
17
+ "OffScheduleForm",
18
+ "OffSchedulePregnancyForm",
19
+ "OffStudyMedicationForm",
20
+ "PregnancyNotificationForm",
21
+ "ProtocolIncidentForm",
22
+ "SubjectTransferForm",
23
+ ]
@@ -24,17 +24,11 @@ class DmReferralForm(
24
24
  ):
25
25
  form_validator_cls = DmReferralFormValidator
26
26
 
27
- # subject_identifier = forms.CharField(
28
- # label="Subject Identifier",
29
- # required=False,
30
- # widget=forms.TextInput(attrs={"readonly": "readonly"}),
31
- # )
32
-
33
27
  class Meta:
34
28
  model = DmReferral
35
29
  fields = "__all__"
36
- help_text = {"subject_identifier": "(read-only)", "action_identifier": "(read-only)"}
37
- widgets = {
30
+ help_text = {"subject_identifier": "(read-only)", "action_identifier": "(read-only)"} # noqa: RUF012
31
+ widgets = { # noqa: RUF012
38
32
  "subject_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
39
33
  "action_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
40
34
  }
@@ -28,4 +28,4 @@ class EndOfStudyForm(
28
28
  class Meta:
29
29
  model = EndOfStudy
30
30
  fields = "__all__"
31
- labels = {"offstudy_datetime": "Date patient terminated from study:"}
31
+ labels = {"offstudy_datetime": "Date patient terminated from study:"} # noqa: RUF012
@@ -1,6 +1,6 @@
1
+ from clinicedc_constants import YES
1
2
  from django import forms
2
3
  from edc_action_item.forms.action_item_form_mixin import ActionItemFormMixin
3
- from edc_constants.constants import YES
4
4
  from edc_form_validators.form_validator import FormValidator
5
5
  from edc_form_validators.form_validator_mixins import FormValidatorMixin
6
6
  from edc_model_form.mixins import BaseModelFormMixin
@@ -52,8 +52,8 @@ class OffStudyMedicationForm(
52
52
  class Meta:
53
53
  model = OffStudyMedication
54
54
  fields = "__all__"
55
- help_text = {"subject_identifier": "(read-only)", "action_identifier": "(read-only)"}
56
- widgets = {
55
+ help_text = {"subject_identifier": "(read-only)", "action_identifier": "(read-only)"} # noqa: RUF012
56
+ widgets = { # noqa: RUF012
57
57
  "subject_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
58
58
  "action_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
59
59
  }
@@ -1,5 +1,8 @@
1
1
  from django import forms
2
+ from django.core.exceptions import ObjectDoesNotExist
2
3
  from edc_action_item.forms.action_item_form_mixin import ActionItemFormMixin
4
+ from edc_appointment.constants import MISSED_APPT
5
+ from edc_appointment.models import Appointment
3
6
  from edc_form_validators.form_validator import FormValidator
4
7
  from edc_form_validators.form_validator_mixins import FormValidatorMixin
5
8
  from edc_model_form.mixins import BaseModelFormMixin
@@ -7,6 +10,8 @@ from edc_prn.modelform_mixins import PrnFormValidatorMixin
7
10
  from edc_sites.forms import SiteModelFormMixin
8
11
  from edc_visit_schedule.modelform_mixins import OffScheduleModelFormMixin
9
12
 
13
+ from meta_subject.models import HivExitReview
14
+
10
15
  from ..models import OffSchedule
11
16
 
12
17
 
@@ -31,6 +36,26 @@ class OffScheduleForm(
31
36
  widget=forms.TextInput(attrs={"readonly": "readonly"}),
32
37
  )
33
38
 
39
+ def clean(self):
40
+ try:
41
+ HivExitReview.objects.get(
42
+ subject_visit__subject_identifier=self.get_subject_identifier()
43
+ )
44
+ except ObjectDoesNotExist as e:
45
+ # if last visit was missed do not require
46
+ last_appointment = (
47
+ Appointment.objects.filter(
48
+ subject_identifier=self.get_subject_identifier(),
49
+ subjectvisit__isnull=False,
50
+ )
51
+ .order_by("appt_datetime")
52
+ .last()
53
+ )
54
+ if last_appointment.appt_timing != MISSED_APPT:
55
+ raise forms.ValidationError(
56
+ "Hiv Exit Review CRF is required before taking subject off schedule."
57
+ ) from e
58
+
34
59
  class Meta:
35
60
  model = OffSchedule
36
61
  fields = "__all__"
@@ -1,8 +1,8 @@
1
+ from clinicedc_constants import FEMALE, NO, YES
1
2
  from dateutil.relativedelta import relativedelta
2
3
  from django import forms
3
4
  from django.core.exceptions import ObjectDoesNotExist
4
5
  from edc_action_item.forms.action_item_form_mixin import ActionItemFormMixin
5
- from edc_constants.constants import FEMALE, NO, YES
6
6
  from edc_form_validators import INVALID_ERROR
7
7
  from edc_form_validators.form_validator import FormValidator
8
8
  from edc_form_validators.form_validator_mixins import FormValidatorMixin
@@ -47,23 +47,21 @@ class PregnancyNotificationFormValidator(PrnFormValidatorMixin, FormValidator):
47
47
  self.instance.id is None
48
48
  and self.cleaned_data.get("bhcg_confirmed") == YES
49
49
  and self.cleaned_data.get("bhcg_date")
50
+ and not UrinePregnancy.objects.filter(
51
+ subject_visit__subject_identifier=self.subject_identifier,
52
+ notified=False,
53
+ assay_date=self.cleaned_data.get("bhcg_date"),
54
+ )
55
+ .exclude(subject_visit__visit_code=DAY1, subject_visit__visit_code_sequence=0)
56
+ .exists()
50
57
  ):
51
- if (
52
- not UrinePregnancy.objects.filter(
53
- subject_visit__subject_identifier=self.subject_identifier,
54
- notified=False,
55
- assay_date=self.cleaned_data.get("bhcg_date"),
56
- )
57
- .exclude(subject_visit__visit_code=DAY1, subject_visit__visit_code_sequence=0)
58
- .exists()
59
- ):
60
- self.raise_validation_error(
61
- "Invalid. A positive Urine βhCG cannot be found. "
62
- "Ensure the UPT has been entered, the date matches, and the "
63
- "UPT is not a baseline UPT. "
64
- f"See also PRN CRF {UrinePregnancy._meta.verbose_name}",
65
- INVALID_ERROR,
66
- )
58
+ self.raise_validation_error(
59
+ "Invalid. A positive Urine βhCG cannot be found. "
60
+ "Ensure the UPT has been entered, the date matches, and the "
61
+ "UPT is not a baseline UPT. "
62
+ f"See also PRN CRF {UrinePregnancy._meta.verbose_name}",
63
+ INVALID_ERROR,
64
+ )
67
65
 
68
66
  def validate_edd(self):
69
67
  if (
meta_prn/list_data.py CHANGED
@@ -1,8 +1,8 @@
1
- from edc_constants.constants import NOT_APPLICABLE, OTHER
1
+ from clinicedc_constants import NOT_APPLICABLE, OTHER
2
2
 
3
3
  list_data = {
4
4
  "edc_protocol_incident.protocolviolations": [
5
- ("failure_to_obtain_informed_consent", "Failure to obtain informed " "consent"),
5
+ ("failure_to_obtain_informed_consent", "Failure to obtain informed consent"),
6
6
  ("enrollment_of_ineligible_patient", "Enrollment of ineligible patient"),
7
7
  (
8
8
  "screening_procedure_not_done",
@@ -31,7 +31,7 @@ list_data = {
31
31
  (NOT_APPLICABLE, "Not applicable"),
32
32
  ],
33
33
  "edc_protocol_incident.protocolincidents": [
34
- ("failure_to_obtain_informed_consent", "Failure to obtain informed " "consent"),
34
+ ("failure_to_obtain_informed_consent", "Failure to obtain informed consent"),
35
35
  ("enrollment_of_ineligible_patient", "Enrollment of ineligible patient"),
36
36
  (
37
37
  "screening_procedure_not_done",
@@ -9,7 +9,7 @@ import django.db.models.deletion
9
9
  import django_audit_fields.fields.hostname_modification_field
10
10
  import django_audit_fields.fields.userfield
11
11
  import django_audit_fields.fields.uuid_auto_field
12
- import django_audit_fields.models.audit_model_mixin
12
+ import django.utils.timezone
13
13
  import django_revision.revision_field
14
14
  import edc_action_item.managers
15
15
  import edc_identifier.managers
@@ -80,14 +80,14 @@ class Migration(migrations.Migration):
80
80
  "created",
81
81
  models.DateTimeField(
82
82
  blank=True,
83
- default=django_audit_fields.models.audit_model_mixin.utcnow,
83
+ default=django.utils.timezone.now,
84
84
  ),
85
85
  ),
86
86
  (
87
87
  "modified",
88
88
  models.DateTimeField(
89
89
  blank=True,
90
- default=django_audit_fields.models.audit_model_mixin.utcnow,
90
+ default=django.utils.timezone.now,
91
91
  ),
92
92
  ),
93
93
  (
@@ -250,14 +250,14 @@ class Migration(migrations.Migration):
250
250
  "created",
251
251
  models.DateTimeField(
252
252
  blank=True,
253
- default=django_audit_fields.models.audit_model_mixin.utcnow,
253
+ default=django.utils.timezone.now,
254
254
  ),
255
255
  ),
256
256
  (
257
257
  "modified",
258
258
  models.DateTimeField(
259
259
  blank=True,
260
- default=django_audit_fields.models.audit_model_mixin.utcnow,
260
+ default=django.utils.timezone.now,
261
261
  ),
262
262
  ),
263
263
  (
@@ -439,14 +439,14 @@ class Migration(migrations.Migration):
439
439
  "created",
440
440
  models.DateTimeField(
441
441
  blank=True,
442
- default=django_audit_fields.models.audit_model_mixin.utcnow,
442
+ default=django.utils.timezone.now,
443
443
  ),
444
444
  ),
445
445
  (
446
446
  "modified",
447
447
  models.DateTimeField(
448
448
  blank=True,
449
- default=django_audit_fields.models.audit_model_mixin.utcnow,
449
+ default=django.utils.timezone.now,
450
450
  ),
451
451
  ),
452
452
  (
@@ -779,14 +779,14 @@ class Migration(migrations.Migration):
779
779
  "created",
780
780
  models.DateTimeField(
781
781
  blank=True,
782
- default=django_audit_fields.models.audit_model_mixin.utcnow,
782
+ default=django.utils.timezone.now,
783
783
  ),
784
784
  ),
785
785
  (
786
786
  "modified",
787
787
  models.DateTimeField(
788
788
  blank=True,
789
- default=django_audit_fields.models.audit_model_mixin.utcnow,
789
+ default=django.utils.timezone.now,
790
790
  ),
791
791
  ),
792
792
  (
@@ -885,14 +885,14 @@ class Migration(migrations.Migration):
885
885
  "created",
886
886
  models.DateTimeField(
887
887
  blank=True,
888
- default=django_audit_fields.models.audit_model_mixin.utcnow,
888
+ default=django.utils.timezone.now,
889
889
  ),
890
890
  ),
891
891
  (
892
892
  "modified",
893
893
  models.DateTimeField(
894
894
  blank=True,
895
- default=django_audit_fields.models.audit_model_mixin.utcnow,
895
+ default=django.utils.timezone.now,
896
896
  ),
897
897
  ),
898
898
  (
@@ -1063,14 +1063,14 @@ class Migration(migrations.Migration):
1063
1063
  "created",
1064
1064
  models.DateTimeField(
1065
1065
  blank=True,
1066
- default=django_audit_fields.models.audit_model_mixin.utcnow,
1066
+ default=django.utils.timezone.now,
1067
1067
  ),
1068
1068
  ),
1069
1069
  (
1070
1070
  "modified",
1071
1071
  models.DateTimeField(
1072
1072
  blank=True,
1073
- default=django_audit_fields.models.audit_model_mixin.utcnow,
1073
+ default=django.utils.timezone.now,
1074
1074
  ),
1075
1075
  ),
1076
1076
  (
@@ -1270,14 +1270,14 @@ class Migration(migrations.Migration):
1270
1270
  "created",
1271
1271
  models.DateTimeField(
1272
1272
  blank=True,
1273
- default=django_audit_fields.models.audit_model_mixin.utcnow,
1273
+ default=django.utils.timezone.now,
1274
1274
  ),
1275
1275
  ),
1276
1276
  (
1277
1277
  "modified",
1278
1278
  models.DateTimeField(
1279
1279
  blank=True,
1280
- default=django_audit_fields.models.audit_model_mixin.utcnow,
1280
+ default=django.utils.timezone.now,
1281
1281
  ),
1282
1282
  ),
1283
1283
  (
@@ -1496,14 +1496,14 @@ class Migration(migrations.Migration):
1496
1496
  "created",
1497
1497
  models.DateTimeField(
1498
1498
  blank=True,
1499
- default=django_audit_fields.models.audit_model_mixin.utcnow,
1499
+ default=django.utils.timezone.now,
1500
1500
  ),
1501
1501
  ),
1502
1502
  (
1503
1503
  "modified",
1504
1504
  models.DateTimeField(
1505
1505
  blank=True,
1506
- default=django_audit_fields.models.audit_model_mixin.utcnow,
1506
+ default=django.utils.timezone.now,
1507
1507
  ),
1508
1508
  ),
1509
1509
  (
@@ -1870,14 +1870,14 @@ class Migration(migrations.Migration):
1870
1870
  "created",
1871
1871
  models.DateTimeField(
1872
1872
  blank=True,
1873
- default=django_audit_fields.models.audit_model_mixin.utcnow,
1873
+ default=django.utils.timezone.now,
1874
1874
  ),
1875
1875
  ),
1876
1876
  (
1877
1877
  "modified",
1878
1878
  models.DateTimeField(
1879
1879
  blank=True,
1880
- default=django_audit_fields.models.audit_model_mixin.utcnow,
1880
+ default=django.utils.timezone.now,
1881
1881
  ),
1882
1882
  ),
1883
1883
  (
@@ -2003,14 +2003,14 @@ class Migration(migrations.Migration):
2003
2003
  "created",
2004
2004
  models.DateTimeField(
2005
2005
  blank=True,
2006
- default=django_audit_fields.models.audit_model_mixin.utcnow,
2006
+ default=django.utils.timezone.now,
2007
2007
  ),
2008
2008
  ),
2009
2009
  (
2010
2010
  "modified",
2011
2011
  models.DateTimeField(
2012
2012
  blank=True,
2013
- default=django_audit_fields.models.audit_model_mixin.utcnow,
2013
+ default=django.utils.timezone.now,
2014
2014
  ),
2015
2015
  ),
2016
2016
  (
@@ -2222,14 +2222,14 @@ class Migration(migrations.Migration):
2222
2222
  "created",
2223
2223
  models.DateTimeField(
2224
2224
  blank=True,
2225
- default=django_audit_fields.models.audit_model_mixin.utcnow,
2225
+ default=django.utils.timezone.now,
2226
2226
  ),
2227
2227
  ),
2228
2228
  (
2229
2229
  "modified",
2230
2230
  models.DateTimeField(
2231
2231
  blank=True,
2232
- default=django_audit_fields.models.audit_model_mixin.utcnow,
2232
+ default=django.utils.timezone.now,
2233
2233
  ),
2234
2234
  ),
2235
2235
  (
@@ -2448,14 +2448,14 @@ class Migration(migrations.Migration):
2448
2448
  "created",
2449
2449
  models.DateTimeField(
2450
2450
  blank=True,
2451
- default=django_audit_fields.models.audit_model_mixin.utcnow,
2451
+ default=django.utils.timezone.now,
2452
2452
  ),
2453
2453
  ),
2454
2454
  (
2455
2455
  "modified",
2456
2456
  models.DateTimeField(
2457
2457
  blank=True,
2458
- default=django_audit_fields.models.audit_model_mixin.utcnow,
2458
+ default=django.utils.timezone.now,
2459
2459
  ),
2460
2460
  ),
2461
2461
  (
@@ -8,7 +8,7 @@ import django.db.models.deletion
8
8
  import django_audit_fields.fields.hostname_modification_field
9
9
  import django_audit_fields.fields.userfield
10
10
  import django_audit_fields.fields.uuid_auto_field
11
- import django_audit_fields.models.audit_model_mixin
11
+ import django.utils.timezone
12
12
  import django_crypto_fields.fields.encrypted_text_field
13
13
  import django_revision.revision_field
14
14
  import edc_model.validators.date
@@ -64,14 +64,14 @@ class Migration(migrations.Migration):
64
64
  "created",
65
65
  models.DateTimeField(
66
66
  blank=True,
67
- default=django_audit_fields.models.audit_model_mixin.utcnow,
67
+ default=django.utils.timezone.now,
68
68
  ),
69
69
  ),
70
70
  (
71
71
  "modified",
72
72
  models.DateTimeField(
73
73
  blank=True,
74
- default=django_audit_fields.models.audit_model_mixin.utcnow,
74
+ default=django.utils.timezone.now,
75
75
  ),
76
76
  ),
77
77
  (
@@ -383,14 +383,14 @@ class Migration(migrations.Migration):
383
383
  "created",
384
384
  models.DateTimeField(
385
385
  blank=True,
386
- default=django_audit_fields.models.audit_model_mixin.utcnow,
386
+ default=django.utils.timezone.now,
387
387
  ),
388
388
  ),
389
389
  (
390
390
  "modified",
391
391
  models.DateTimeField(
392
392
  blank=True,
393
- default=django_audit_fields.models.audit_model_mixin.utcnow,
393
+ default=django.utils.timezone.now,
394
394
  ),
395
395
  ),
396
396
  (