clinicedc 2.0.13__py3-none-any.whl → 2.0.15__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.
- {clinicedc-2.0.13.dist-info → clinicedc-2.0.15.dist-info}/METADATA +2 -3
- {clinicedc-2.0.13.dist-info → clinicedc-2.0.15.dist-info}/RECORD +58 -58
- edc_analytics/custom_tables/age.py +1 -0
- edc_analytics/custom_tables/art.py +1 -0
- edc_analytics/custom_tables/bmi.py +1 -0
- edc_analytics/custom_tables/bp.py +1 -0
- edc_analytics/custom_tables/fasting.py +1 -0
- edc_analytics/custom_tables/fbg.py +1 -0
- edc_analytics/custom_tables/fbg_ogtt.py +1 -0
- edc_analytics/custom_tables/hba1c.py +1 -0
- edc_analytics/custom_tables/ogtt.py +1 -0
- edc_analytics/custom_tables/waist.py +1 -0
- edc_analytics/row/row_definitions.py +1 -1
- edc_analytics/row/row_statistics_with_gender.py +1 -0
- edc_analytics/stata/get_stata_labels_from_model.py +5 -6
- edc_analytics/table.py +1 -0
- edc_appointment/creators/appointments_creator.py +5 -9
- edc_dx/diagnoses.py +2 -2
- edc_dx/form_validators/diagnosis_form_validator_mixin.py +1 -0
- edc_dx/form_validators/result_form_validator_mixin.py +14 -16
- edc_dx_review/form_mixins/clinical_review_baseline_required_form_mixin.py +1 -0
- edc_dx_review/medical_date.py +2 -1
- edc_dx_review/model_mixins/clinical_review_baseline_model_mixin.py +1 -0
- edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_chol_model_mixin.py +1 -0
- edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_dm_model_mixin.py +1 -0
- edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_hiv_model_mixin.py +1 -0
- edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_htn_model_mixin.py +1 -0
- edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_model_mixin.py +1 -0
- edc_dx_review/model_mixins/dx_location_model_mixin.py +1 -0
- edc_dx_review/model_mixins/factory/baseline_review_model_mixin_factory.py +1 -0
- edc_dx_review/model_mixins/factory/calculate_date.py +1 -0
- edc_dx_review/model_mixins/factory/dx_initial_review_model_mixin_factory.py +2 -1
- edc_dx_review/model_mixins/factory/followup_review_model_mixin_factory.py +1 -0
- edc_dx_review/model_mixins/factory/rx_initial_review_model_mixin_factory.py +1 -0
- edc_dx_review/model_mixins/followup_review/followup_review_model_mixin.py +1 -0
- edc_dx_review/model_mixins/followup_review/hiv_followup_review_model_mixin.py +1 -0
- edc_dx_review/model_mixins/initial_review/chol_initial_review_model_mixin.py +1 -0
- edc_dx_review/model_mixins/initial_review/hiv_initial_model_mixins.py +1 -0
- edc_dx_review/model_mixins/initial_review/ncd_initial_review_model_mixin.py +1 -0
- edc_dx_review/radio_fields.py +3 -3
- edc_dx_review/utils.py +2 -1
- edc_randomization/randomizer.py +2 -5
- edc_randomization/utils.py +10 -0
- edc_visit_tracking/action_items.py +1 -2
- edc_visit_tracking/apps.py +1 -1
- edc_visit_tracking/context_processors.py +1 -2
- edc_visit_tracking/crf_date_validator.py +4 -4
- edc_visit_tracking/form_validators/visit_form_validator.py +51 -45
- edc_visit_tracking/form_validators/visit_missed_form_validator.py +6 -1
- edc_visit_tracking/model_mixins/utils.py +1 -1
- edc_visit_tracking/modelform_mixins/crf/visit_tracking_crf_modelform_mixin.py +4 -4
- edc_visit_tracking/modelform_mixins/utils.py +2 -2
- edc_visit_tracking/models/signals.py +1 -1
- edc_visit_tracking/typing_stubs.py +3 -3
- edc_visit_tracking/utils.py +1 -1
- edc_visit_tracking/visit_sequence.py +3 -4
- {clinicedc-2.0.13.dist-info → clinicedc-2.0.15.dist-info}/WHEEL +0 -0
- {clinicedc-2.0.13.dist-info → clinicedc-2.0.15.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clinicedc
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.15
|
|
4
4
|
Summary: A clinical trials data management framework built on Django
|
|
5
5
|
Keywords: django,clinicedc,edc,clinical trials,research,data management,esource
|
|
6
6
|
Author: Erik van Widenfelt, Jonathan Willitts
|
|
@@ -45,7 +45,6 @@ Requires-Dist: gunicorn>=23.0.0
|
|
|
45
45
|
Requires-Dist: inflect>=7.5.0
|
|
46
46
|
Requires-Dist: mempass>=0.1.2
|
|
47
47
|
Requires-Dist: mysqlclient>=2.2.7
|
|
48
|
-
Requires-Dist: psycopg2>=2.9.10
|
|
49
48
|
Requires-Dist: pycups>=2.0.4
|
|
50
49
|
Requires-Dist: pypdf>=5.9.0
|
|
51
50
|
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
@@ -57,7 +56,7 @@ Requires-Dist: tqdm>=4.67.1
|
|
|
57
56
|
Requires-Dist: twilio>=9.7.0
|
|
58
57
|
Maintainer: Erik van Widenfelt
|
|
59
58
|
Maintainer-email: Erik van Widenfelt <ew2789@gmail.com>
|
|
60
|
-
Requires-Python: >=3.12
|
|
59
|
+
Requires-Python: >=3.12, <3.14
|
|
61
60
|
Project-URL: Changelog, https://github.com/clinicedc/clinicedc/blob/main/CHANGES
|
|
62
61
|
Project-URL: Documentation, https://github.com/clinicedc/clinicedc/docs
|
|
63
62
|
Project-URL: Homepage, https://github.com/clinicedc/clinicedc
|
|
@@ -296,27 +296,27 @@ edc_analytics/__init__.py,sha256=Tne7Oyy6wpFVNcFoEpurXJ4_I3Tz93jvqOMmTTXW_X0,123
|
|
|
296
296
|
edc_analytics/apps.py,sha256=bZ9Gtr1YdBX2Q1nkrzOi7wG9_Ri3vUj8ZOATve3hDzo,229
|
|
297
297
|
edc_analytics/constants.py,sha256=a8aUKrhSWKFfLBm9e7CFQk0mEdkFNbGbAxpUrqaTXjw,481
|
|
298
298
|
edc_analytics/custom_tables/__init__.py,sha256=TVI12TnJ5aMqTW2liyS4AjfLAIgg5rY8W_4WBsWfNw8,330
|
|
299
|
-
edc_analytics/custom_tables/age.py,sha256=
|
|
300
|
-
edc_analytics/custom_tables/art.py,sha256=
|
|
301
|
-
edc_analytics/custom_tables/bmi.py,sha256=
|
|
302
|
-
edc_analytics/custom_tables/bp.py,sha256=
|
|
303
|
-
edc_analytics/custom_tables/fasting.py,sha256=
|
|
304
|
-
edc_analytics/custom_tables/fbg.py,sha256=
|
|
305
|
-
edc_analytics/custom_tables/fbg_ogtt.py,sha256=
|
|
299
|
+
edc_analytics/custom_tables/age.py,sha256=Jx78ka_AZ3qskHEAPeR1nQwENM6-MY12gEsM6rcZwY4,2615
|
|
300
|
+
edc_analytics/custom_tables/art.py,sha256=pgT7w2t6vzE5WFos4pe9E2XIEynFBBKFzryL1NV2abQ,2717
|
|
301
|
+
edc_analytics/custom_tables/bmi.py,sha256=5Ww5438pCAV_ApJ9k-_TIoJ805PibG-zAkvbv-1XUew,3844
|
|
302
|
+
edc_analytics/custom_tables/bp.py,sha256=THkAu7CKd9eZVlYDPtUrRFZg8zbO49U66kgEwtwRYQA,3189
|
|
303
|
+
edc_analytics/custom_tables/fasting.py,sha256=_r9_yyfleq99iNscgXYPNuP-cvyel1pLsEAU_I8JSJM,3993
|
|
304
|
+
edc_analytics/custom_tables/fbg.py,sha256=EX1PMVmjJ6r1RBeHzaFdpyEdYy2aJIhz1yB4I3JB-Pg,2967
|
|
305
|
+
edc_analytics/custom_tables/fbg_ogtt.py,sha256=Z22yMyKEu6PxDaD4BWAw5VAWj-dcSqNXHjPtC_IdXgY,12128
|
|
306
306
|
edc_analytics/custom_tables/gender.py,sha256=lLPggxFYv-G0hnY_Bkl3rAdAitCNJ6KEud2FOGfaubQ,252
|
|
307
|
-
edc_analytics/custom_tables/hba1c.py,sha256=
|
|
308
|
-
edc_analytics/custom_tables/ogtt.py,sha256=
|
|
309
|
-
edc_analytics/custom_tables/waist.py,sha256
|
|
307
|
+
edc_analytics/custom_tables/hba1c.py,sha256=tyK6EaxKfVeHbrZXfPV8s1QaiqPEC4_S1sSb3hFPsgw,2621
|
|
308
|
+
edc_analytics/custom_tables/ogtt.py,sha256=_WuSBG0_bj4SH0LELOkLPdAnOzWHLhLB0x43uPc-U-s,2888
|
|
309
|
+
edc_analytics/custom_tables/waist.py,sha256=tWQtmwn0AwV3JFmlbTSvcUq9TkTM19ewwHSTLHeun3s,3145
|
|
310
310
|
edc_analytics/data.py,sha256=yaa7Hi9bQMhFKt_7wWDFZYsfU-tAKoE-gsm97_5vSco,1032
|
|
311
311
|
edc_analytics/row/__init__.py,sha256=oBRBcrTeWr77ZfvsSnCGKbrHS0-qY26yt4EDn_hxllA,192
|
|
312
312
|
edc_analytics/row/row_definition.py,sha256=me9QGvDb7aijLPoRD_jey1cLkJ3TuBLkF6IpzoyV_Y4,1434
|
|
313
|
-
edc_analytics/row/row_definitions.py,sha256=
|
|
313
|
+
edc_analytics/row/row_definitions.py,sha256=7PW5wd-wiYaOeIJ_lqCi6bAzOQUWG_gyykKeqK0VZ-k,962
|
|
314
314
|
edc_analytics/row/row_statistics.py,sha256=M5zjBCbguVOxOp2Lmla9JUkZA9bG3V8aanY6zswDRL0,2897
|
|
315
|
-
edc_analytics/row/row_statistics_with_gender.py,sha256=
|
|
315
|
+
edc_analytics/row/row_statistics_with_gender.py,sha256=ti16XFqDgjrMcd-1v5kMxc5X1R6UqHYVKQ8khKvFPAI,3211
|
|
316
316
|
edc_analytics/stata/__init__.py,sha256=fzENxSjaoxaXbSeafnaNXOPO85aXWbObQBnYfhAJI6w,69
|
|
317
|
-
edc_analytics/stata/get_stata_labels_from_model.py,sha256=
|
|
317
|
+
edc_analytics/stata/get_stata_labels_from_model.py,sha256=Dsu0635K2F0xK3zrU-BmnuLV2ZD51mDqbTxJZWfgL4I,1431
|
|
318
318
|
edc_analytics/styler.py,sha256=IvuKalHPSJLZTjzOKAt7wK2kCRRvZGL9_PxM70Vo68Q,3086
|
|
319
|
-
edc_analytics/table.py,sha256=
|
|
319
|
+
edc_analytics/table.py,sha256=PJwlnURui-BXOqc9s0STA1Fs0FqbdAtNLKLnGFV_kS4,3753
|
|
320
320
|
edc_analytics/urls.py,sha256=NR4c2ybRQNjbtJwi-PTsYB9oacVuVZsIRHo7ZnA0WIc,118
|
|
321
321
|
edc_appconfig/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
322
322
|
edc_appconfig/apps.py,sha256=eMQEa-pQzqWyuBGbpWWNJwgmbc7oSZ3d0FNFdd_08ik,11211
|
|
@@ -342,7 +342,7 @@ edc_appointment/constants.py,sha256=tsv0MV8Zo4e1aLnx00VYsM0EmyUUuTupeEFb2cko1N8,
|
|
|
342
342
|
edc_appointment/context_processors.py,sha256=LTdhNLGDhsJmdobhnM4VwhZns-67jm5Culz8vxsErzI,387
|
|
343
343
|
edc_appointment/creators/__init__.py,sha256=yv_eSyS8ogQjSe-Y3elxUMa3unIbgTMH3Tg9QSRLdho,425
|
|
344
344
|
edc_appointment/creators/appointment_creator.py,sha256=HXB7tDyPHZ29MNqpDHuYXmJTRaWCxAtvU4KOypEc0o0,9757
|
|
345
|
-
edc_appointment/creators/appointments_creator.py,sha256=
|
|
345
|
+
edc_appointment/creators/appointments_creator.py,sha256=XH-pYfxtl5lS917mEGSMFWrwAVuOmjdB2Efw9ONo0jU,5638
|
|
346
346
|
edc_appointment/creators/unscheduled_appointment_creator.py,sha256=m7yLmW2xcNGf5PhhTUc1PleI8JKlhaYSoMWVCBZDvis,10590
|
|
347
347
|
edc_appointment/creators/utils.py,sha256=H4Q2_BVFqTX5ra8Aov061ItHLlusyGY2eOFoHC4taag,2568
|
|
348
348
|
edc_appointment/exceptions.py,sha256=07KyrUsorB4ke1TToWx2BcRAAsJYMKkOAt8cjn93B3A,1214
|
|
@@ -874,10 +874,10 @@ edc_document_status/models/signals.py,sha256=yZmZiYD3c2QDqUUTdTN087Fc3_B2-Vi-983
|
|
|
874
874
|
edc_document_status/urls.py,sha256=Hgg7_Kjo7c1aFcXBB8kuOJIvW5IY7Do7z5vkeBAWEDQ,213
|
|
875
875
|
edc_dx/__init__.py,sha256=im7QJgvIrK5LLffGaTEL5v_TT81Io51SjF3cjPwPHnk,156
|
|
876
876
|
edc_dx/apps.py,sha256=L9jTcNHWXli9vYd5FPnyc7SXOj-qs_3ntVJ549nJ0XE,109
|
|
877
|
-
edc_dx/diagnoses.py,sha256=
|
|
877
|
+
edc_dx/diagnoses.py,sha256=TWP5MaCGqBhZKvB_l-dFyMttL0mFV-2mUCYIE1sdO1U,9048
|
|
878
878
|
edc_dx/form_validators/__init__.py,sha256=0Xdntahcc_2lYyYIdigkkcZMtfGTa1qX3C1zo0NW-eo,138
|
|
879
|
-
edc_dx/form_validators/diagnosis_form_validator_mixin.py,sha256=
|
|
880
|
-
edc_dx/form_validators/result_form_validator_mixin.py,sha256=
|
|
879
|
+
edc_dx/form_validators/diagnosis_form_validator_mixin.py,sha256=7XVTPPkVx-X3BW8XiBKRYGTeuPIYnt9Xsbcno8Q5ZOU,1926
|
|
880
|
+
edc_dx/form_validators/result_form_validator_mixin.py,sha256=Us_ZJrLc8HB1xw6Bu_ZM6gQhHJDEj3eusv2M7mlUUPs,2405
|
|
881
881
|
edc_dx/utils.py,sha256=tHhSJgXYz_DwP9Ujhi4MVLFc2Sz-oxcOQEpDO9elz5w,1223
|
|
882
882
|
edc_dx_review/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
883
883
|
edc_dx_review/apps.py,sha256=kgCI6K_HKHiKb1MfblBJSr-naZk7bABgMWhtpmIl1wg,116
|
|
@@ -887,42 +887,42 @@ edc_dx_review/choices.py,sha256=AufGpbPViQ6eA0TJxfod3g9Y8T-aLvr6tulAHL3AQtc,643
|
|
|
887
887
|
edc_dx_review/constants.py,sha256=1l1n556hBzL5M_fbnQkxK4dXzYH_35pem42QZVWHslk,204
|
|
888
888
|
edc_dx_review/fieldsets.py,sha256=aLgxJqfz8XsoK9dXI4wa8v9qS_GD1KQbO0b2pVg_ClA,1276
|
|
889
889
|
edc_dx_review/form_mixins/__init__.py,sha256=BT941i0F_ahGzpZQtM-Jhd911XVFrxKDj87L0w-hCSM,112
|
|
890
|
-
edc_dx_review/form_mixins/clinical_review_baseline_required_form_mixin.py,sha256=
|
|
890
|
+
edc_dx_review/form_mixins/clinical_review_baseline_required_form_mixin.py,sha256=Goxl-DVRfAv2qtZlquN9-GJ31slylRPkoI07zyrYO3k,943
|
|
891
891
|
edc_dx_review/form_validator_mixins/__init__.py,sha256=qlmi4uKl-Dqtliyw42ZfldMkYhdgAs4xOwK9isYGEMA,218
|
|
892
892
|
edc_dx_review/form_validator_mixins/clinical_review_baseline_form_validator_mixin.py,sha256=KFc4PwH2__fo4OvHrBtNTuYApliVuiD-VT1RRnmqcCw,226
|
|
893
893
|
edc_dx_review/form_validator_mixins/clinical_review_followup_form_validator_mixin.py,sha256=fNZF9TJrak9e9yUb8VJNLElLThWLGma3Wun5-DP6OtA,1097
|
|
894
894
|
edc_dx_review/list_data.py,sha256=Igsmks2rDC95PDMWZmWLynhvQ6dBL2K0qazwtEDDXOc,711
|
|
895
|
-
edc_dx_review/medical_date.py,sha256=
|
|
895
|
+
edc_dx_review/medical_date.py,sha256=I_TJ5tEY8Mkqgy3_z_pjKlHszNE5LWYSXiRMEXxpQMw,6896
|
|
896
896
|
edc_dx_review/migrations/0001_initial.py,sha256=0BmL_Pj5j-lRlR_IBzwDqGH96pIOT8mlf3huCK43Jlk,11691
|
|
897
897
|
edc_dx_review/migrations/0002_diagnosislocations_extra_value_and_more.py,sha256=I8wCv3MXFzDufDC9fksSfKjDNi5TK_whkWN0Jc6a4Pg,965
|
|
898
898
|
edc_dx_review/migrations/0003_alter_diagnosislocations_options_and_more.py,sha256=xeaCnYnmqFnVemeqNhuIc8-o9SAKZpguZHd4QTinr7c,5011
|
|
899
899
|
edc_dx_review/migrations/0004_remove_diagnosislocations_edc_dx_revi_name_a39b40_idx_and_more.py,sha256=a0yE-oHCphIt9-PjfMa5qLb1FNBoMbcq1qliyw6MGVQ,524
|
|
900
900
|
edc_dx_review/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
901
901
|
edc_dx_review/model_mixins/__init__.py,sha256=-Yga4EAzAyBO9Q6Wmtj9bfKujnVBFPxklp98pkhvijw,700
|
|
902
|
-
edc_dx_review/model_mixins/clinical_review_baseline_model_mixin.py,sha256=
|
|
902
|
+
edc_dx_review/model_mixins/clinical_review_baseline_model_mixin.py,sha256=E_1UuQlJmBbiQnTAm5LugRplHRWe6rRVa26teCnlOtA,927
|
|
903
903
|
edc_dx_review/model_mixins/clinical_review_followup/__init__.py,sha256=vhLjTnrXT89BYohT18JHRmxN8ua6I_KSBO-bkmJiP34,403
|
|
904
|
-
edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_chol_model_mixin.py,sha256=
|
|
905
|
-
edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_dm_model_mixin.py,sha256=
|
|
906
|
-
edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_hiv_model_mixin.py,sha256=
|
|
907
|
-
edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_htn_model_mixin.py,sha256=
|
|
908
|
-
edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_model_mixin.py,sha256=
|
|
909
|
-
edc_dx_review/model_mixins/dx_location_model_mixin.py,sha256=
|
|
904
|
+
edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_chol_model_mixin.py,sha256=fWV8YCXki24r-o5a2Zs2VGMgWeqXW7rd5ayzffwo82o,1678
|
|
905
|
+
edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_dm_model_mixin.py,sha256=ICoAmdtNpiGyuNN2-P0M9WyI2HsIW-tilwU3d3bnKrY,1654
|
|
906
|
+
edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_hiv_model_mixin.py,sha256=L0_RiaJWrKANwfFz7pU9ZvkVIRzek3_1ckOhk4BNPDo,1681
|
|
907
|
+
edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_htn_model_mixin.py,sha256=uC8W37NO0slc8a1xwRGsemdgbsM2VkYNRgyDZUTqU48,1739
|
|
908
|
+
edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_model_mixin.py,sha256=8SzfY9qBxQhc2RKPyhH-LLAS90FnDZbHoz_rTnw0Nkk,893
|
|
909
|
+
edc_dx_review/model_mixins/dx_location_model_mixin.py,sha256=XJZOh7ZJhJRM99DjhHSkipoGwfgKQqdhzJZRmUPDqjk,426
|
|
910
910
|
edc_dx_review/model_mixins/factory/__init__.py,sha256=IdBPEgN-mJm9v0vjSDLGc10v12pIp-NFpBi-E0y5Zmk,348
|
|
911
|
-
edc_dx_review/model_mixins/factory/baseline_review_model_mixin_factory.py,sha256=
|
|
912
|
-
edc_dx_review/model_mixins/factory/calculate_date.py,sha256=
|
|
913
|
-
edc_dx_review/model_mixins/factory/dx_initial_review_model_mixin_factory.py,sha256=
|
|
914
|
-
edc_dx_review/model_mixins/factory/followup_review_model_mixin_factory.py,sha256=
|
|
915
|
-
edc_dx_review/model_mixins/factory/rx_initial_review_model_mixin_factory.py,sha256=
|
|
911
|
+
edc_dx_review/model_mixins/factory/baseline_review_model_mixin_factory.py,sha256=5h8wWOlcB-CZqanTwk8LfCZdq9y3DaFSmOoCLv87j5g,1747
|
|
912
|
+
edc_dx_review/model_mixins/factory/calculate_date.py,sha256=Ytjlk2R7CsNWTYWVwzl17UuUHai8ZBKa4A0cB58XsqE,1527
|
|
913
|
+
edc_dx_review/model_mixins/factory/dx_initial_review_model_mixin_factory.py,sha256=fiq649IUg28cbi9KGGHtWQ6lntQpSy53mWJtXm1q7_o,3211
|
|
914
|
+
edc_dx_review/model_mixins/factory/followup_review_model_mixin_factory.py,sha256=QUq2cMo9IPWLnhXeyHg1qj6iRUuSAWY2m9oyHSh8cOQ,1247
|
|
915
|
+
edc_dx_review/model_mixins/factory/rx_initial_review_model_mixin_factory.py,sha256=DTO3NAN26VoZLQ0JQwF-N5pPo-Gxsw89m-iAkg7Wu3c,2377
|
|
916
916
|
edc_dx_review/model_mixins/followup_review/__init__.py,sha256=2vTnIxKhFrmxXDVdngv5RumwycrpnWh7FaMU8CA99LU,139
|
|
917
|
-
edc_dx_review/model_mixins/followup_review/followup_review_model_mixin.py,sha256=
|
|
918
|
-
edc_dx_review/model_mixins/followup_review/hiv_followup_review_model_mixin.py,sha256=
|
|
917
|
+
edc_dx_review/model_mixins/followup_review/followup_review_model_mixin.py,sha256=HJJSqRxDj8wxxCRmOTsA_mz6l9Q5Fp5gpZYHHthMu0k,674
|
|
918
|
+
edc_dx_review/model_mixins/followup_review/hiv_followup_review_model_mixin.py,sha256=y0Ux_v55bfDtOIQO4MmLu-E00tRfSLmP5QMPixuUVtg,1035
|
|
919
919
|
edc_dx_review/model_mixins/initial_review/__init__.py,sha256=nSRBcgZcJnbACdazzI_1oJ9B5YHjAbvpCO3KlFsn510,250
|
|
920
|
-
edc_dx_review/model_mixins/initial_review/chol_initial_review_model_mixin.py,sha256=
|
|
921
|
-
edc_dx_review/model_mixins/initial_review/hiv_initial_model_mixins.py,sha256=
|
|
922
|
-
edc_dx_review/model_mixins/initial_review/ncd_initial_review_model_mixin.py,sha256=
|
|
920
|
+
edc_dx_review/model_mixins/initial_review/chol_initial_review_model_mixin.py,sha256=sj3nBYKY4nY3arX6TcChN1KvjlKFr5216ijT4_b2Z6M,1001
|
|
921
|
+
edc_dx_review/model_mixins/initial_review/hiv_initial_model_mixins.py,sha256=ayOxv6D_sFC_5ttGYQigEMq2c-hC5823H7pPlDXVkJU,3642
|
|
922
|
+
edc_dx_review/model_mixins/initial_review/ncd_initial_review_model_mixin.py,sha256=UsE-z9qZzNnBj7v7dwaBqzIj24h63l_ywnYjvMzrT4c,1193
|
|
923
923
|
edc_dx_review/models.py,sha256=j270tH76Fb-WYMc7nR_4JfIT9An5QQ-FSTiiGer4YUE,610
|
|
924
|
-
edc_dx_review/radio_fields.py,sha256=
|
|
925
|
-
edc_dx_review/utils.py,sha256=
|
|
924
|
+
edc_dx_review/radio_fields.py,sha256=sb8njIokL0v_kA-7_HZJtiUXdW2ILeOYOXMqd2XLsLU,810
|
|
925
|
+
edc_dx_review/utils.py,sha256=bTh1zcmSiEq27RlEaDd0Cp4NZ6jhg7gr_dZapsLk28U,6974
|
|
926
926
|
edc_egfr/__init__.py,sha256=q9hW6cas07gJxgElFaN7AtUGzwTwHuXvexxGFej3_pQ,56
|
|
927
927
|
edc_egfr/admin/__init__.py,sha256=X4SsX1UIcGwxzNHH2uG4rnLxLQiYcLJeGLi4agxnkEQ,79
|
|
928
928
|
edc_egfr/admin/egfr_drop_notification_admin_mixin.py,sha256=Chet4mYDfZI4ohYrAyyf4CymZE6jCkYUB7y7uSAP-rs,2431
|
|
@@ -2795,12 +2795,12 @@ edc_randomization/models/edc_permissions.py,sha256=f6P4zXxLQdTAbn2KnFlwfE86Lmzj7
|
|
|
2795
2795
|
edc_randomization/models/randomization_list.py,sha256=Hd5yUSZ5HBKhNzYdlbJufab8U2x3Dzw8n4WXnYzJxyc,253
|
|
2796
2796
|
edc_randomization/randomization_list_importer.py,sha256=v8d1klIfAsf9CwR_4HLLzifDnJvFD2bXhDOe0XqznjM,12366
|
|
2797
2797
|
edc_randomization/randomization_list_verifier.py,sha256=0JqlbVCBZzx18Vxp2gID4zHjZMyO1LaHzqe4zTzRUpY,6481
|
|
2798
|
-
edc_randomization/randomizer.py,sha256=
|
|
2798
|
+
edc_randomization/randomizer.py,sha256=N57PvCGjsjlw4r4V-zE5K7uWnLUw4QwZREu-mQ-O_4U,12939
|
|
2799
2799
|
edc_randomization/randomizers.py,sha256=JvMDAnsgvRZkl50Bawm2iLsyw3bfWfGwBafn54K5g44,297
|
|
2800
2800
|
edc_randomization/site_randomizers.py,sha256=T6u1cNPZtoh5ajdsBciSVWzZYZyOPbiTqS9VBGKJ7H8,4077
|
|
2801
2801
|
edc_randomization/system_checks.py,sha256=S_2Vkq3J3DQVEXOV3JNNlCzuZcl1l34-tTnaBaSZaBk,3025
|
|
2802
2802
|
edc_randomization/urls.py,sha256=yQnOoy5U5uL00f7_JJcZk7toskmZunG_kJdl-epCV9U,223
|
|
2803
|
-
edc_randomization/utils.py,sha256=
|
|
2803
|
+
edc_randomization/utils.py,sha256=uRTtoyAuWo5D6y65ZHBWMSody8IMJBK6cS3mFmJGIMw,5730
|
|
2804
2804
|
edc_randomization/views.py,sha256=mQnAoYzIbqXFXablJbPY3NdJc4K3fD4le-GOF2kpMmE,340
|
|
2805
2805
|
edc_refusal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2806
2806
|
edc_refusal/admin.py,sha256=upce4HEPyPYKTe_R_krQ9TaLufcJN50c0lw4JrzbTwE,2151
|
|
@@ -3319,18 +3319,18 @@ edc_visit_schedule/visit_schedule/schedules_collection.py,sha256=yujoCASDUWyFsqa
|
|
|
3319
3319
|
edc_visit_schedule/visit_schedule/visit_schedule.py,sha256=s4pzIUMSDyGgj2wINI53qO3V_UFU_vhEX7lTBtz9UmE,5734
|
|
3320
3320
|
edc_visit_schedule/wsgi.py,sha256=ToL_Ox7yQ30uUMBj5Ev2q_im2dl6mWKdqsUyJWeyFWU,397
|
|
3321
3321
|
edc_visit_tracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3322
|
-
edc_visit_tracking/action_items.py,sha256=
|
|
3322
|
+
edc_visit_tracking/action_items.py,sha256=lWf2-lNgz3QxkvbHW3j7YpZFbwqZwUvI_qDaAR4SWWI,1216
|
|
3323
3323
|
edc_visit_tracking/admin.py,sha256=YQ6gNY7oMgzfR-HAgMfeZ8DsH0Yhr8GvmUeQUb11eqQ,554
|
|
3324
3324
|
edc_visit_tracking/admin_site.py,sha256=f_MgAkWy84hY48SOb5vT21IcSkebPm0j5b6ZmcuCjcw,187
|
|
3325
|
-
edc_visit_tracking/apps.py,sha256=
|
|
3325
|
+
edc_visit_tracking/apps.py,sha256=WD1WUsqsYoH0arcRdx6eDyEU3pB4cwpjY3wvYvlpg7U,545
|
|
3326
3326
|
edc_visit_tracking/choices.py,sha256=HVpd_Wr31NAG2kTKaF7TkPAgBk5PB_8hfQN3sAiNisA,2322
|
|
3327
3327
|
edc_visit_tracking/constants.py,sha256=-XXCR2VzMxpZrCdGp4CB958j3E0llrPd5PKdrblsHNs,444
|
|
3328
|
-
edc_visit_tracking/context_processors.py,sha256=
|
|
3329
|
-
edc_visit_tracking/crf_date_validator.py,sha256=
|
|
3328
|
+
edc_visit_tracking/context_processors.py,sha256=U4zznNUdTAxFNjr56nl13GXFQ2yM9VIochpGujf9bb0,312
|
|
3329
|
+
edc_visit_tracking/crf_date_validator.py,sha256=_UhYtNvzAyVdechjEmR-g9-SKBGb4aCW0DlK4WaY7rg,3737
|
|
3330
3330
|
edc_visit_tracking/exceptions.py,sha256=trVxjsip2_Fl6y2e4gWts9XVVPcOqmJXvthuJSSFTMk,155
|
|
3331
3331
|
edc_visit_tracking/form_validators/__init__.py,sha256=BQcVQ_BBlMb_x5unFYm2sy5fTGiI3pBnyxsgrebjIfU,119
|
|
3332
|
-
edc_visit_tracking/form_validators/visit_form_validator.py,sha256=
|
|
3333
|
-
edc_visit_tracking/form_validators/visit_missed_form_validator.py,sha256=
|
|
3332
|
+
edc_visit_tracking/form_validators/visit_form_validator.py,sha256=lRc1IA3ujgWqJL0VQbjPzJa8G6njXKogbOVoN_EzTGg,11972
|
|
3333
|
+
edc_visit_tracking/form_validators/visit_missed_form_validator.py,sha256=xJRVbAGcxXSvfb6ZkxGHfI01G1K7afuxF8wXa-smAMg,1449
|
|
3334
3334
|
edc_visit_tracking/locale/sw/LC_MESSAGES/django.po,sha256=lRGiikykQlMhoS-SgN-Hzqgm3NpBuAoR9H_vsEt4-aI,3719
|
|
3335
3335
|
edc_visit_tracking/managers.py,sha256=oQyaMAnUYYLqY9qwSU8rT2-KUvqu4M9t9J1YzIIdwFQ,4008
|
|
3336
3336
|
edc_visit_tracking/migrations/0001_initial.py,sha256=R3MUszHP_y2zQAO9JXu16td3ZTkJreh6jCBOc6biALs,5655
|
|
@@ -3354,7 +3354,7 @@ edc_visit_tracking/model_mixins/crfs/visit_tracking_crf_model_mixin.py,sha256=5p
|
|
|
3354
3354
|
edc_visit_tracking/model_mixins/requisitions/__init__.py,sha256=JZHxR8XADoba21z1JJsJyQattiRheoLj4mkCDk_HEKc,87
|
|
3355
3355
|
edc_visit_tracking/model_mixins/requisitions/visit_tracking_requisition_model_mixin.py,sha256=I72j8p5SkYLDpxAAeRx76z_TMi-lIH0JxcbKY6OOALg,1186
|
|
3356
3356
|
edc_visit_tracking/model_mixins/subject_visit_missed_model_mixin.py,sha256=BnPnpbNSVyJgbzxCyWAnoOXkjRUyciifFIR6kWNp_yA,3593
|
|
3357
|
-
edc_visit_tracking/model_mixins/utils.py,sha256=
|
|
3357
|
+
edc_visit_tracking/model_mixins/utils.py,sha256=yL3DsZbW5mG8kOo3r3D1J_dGhvYTpYb9x6FY0fQMrts,1554
|
|
3358
3358
|
edc_visit_tracking/model_mixins/visit_model_mixin/__init__.py,sha256=YpThykSaGcXNz7rOH-aOOodhLFFhnR3vDwn40gsaxpc,248
|
|
3359
3359
|
edc_visit_tracking/model_mixins/visit_model_mixin/caretaker_fields_mixin.py,sha256=4GDRDe6It-b1UNKatsdI_oyu1_47bpnJsuKv6yLQWZ8,1024
|
|
3360
3360
|
edc_visit_tracking/model_mixins/visit_model_mixin/previous_visit_model_mixin.py,sha256=NZ5UDkZltDmqYu6uHOL4BWB5-tnwEyWmIHI_VYNY3Is,1875
|
|
@@ -3365,23 +3365,23 @@ edc_visit_tracking/modeladmin_mixins/crf_model_admin_mixin.py,sha256=KaShntUo5g0
|
|
|
3365
3365
|
edc_visit_tracking/modeladmin_mixins/visit_model_admin_mixin.py,sha256=bnBzchsrRYZzq8-AGV7veWzXsLPHOx_bwFCHduUBLRM,5156
|
|
3366
3366
|
edc_visit_tracking/modelform_mixins/__init__.py,sha256=YSmMaMAWmggeIoftggG_ij8H2kX-H3P5T8w5E0TCCZ0,194
|
|
3367
3367
|
edc_visit_tracking/modelform_mixins/crf/__init__.py,sha256=bPOdhr4KLEBFo-SK1F8DWD6us7kFolU4qphjg5oTnTs,129
|
|
3368
|
-
edc_visit_tracking/modelform_mixins/crf/visit_tracking_crf_modelform_mixin.py,sha256=
|
|
3369
|
-
edc_visit_tracking/modelform_mixins/utils.py,sha256=
|
|
3368
|
+
edc_visit_tracking/modelform_mixins/crf/visit_tracking_crf_modelform_mixin.py,sha256=Ynl8z-rL1LwmYGtWMb-XbYwdcM7p_rX6tA5Fx6uSbfc,4373
|
|
3369
|
+
edc_visit_tracking/modelform_mixins/utils.py,sha256=PlmJw4enAaESEGkzooH0iOX9LtGZL7jasDKaHZ9IOdM,1669
|
|
3370
3370
|
edc_visit_tracking/modelform_mixins/visit_tracking_modelform_mixin.py,sha256=pik_qYv2vJLd3HtYslZEn65_Cy0iHSs-rqkryoyjZg8,2580
|
|
3371
3371
|
edc_visit_tracking/models/__init__.py,sha256=aB-hnevvK3ttednU3e3c5Fb8i2GIRK5p2zRRkgoSXSc,318
|
|
3372
|
-
edc_visit_tracking/models/signals.py,sha256=
|
|
3372
|
+
edc_visit_tracking/models/signals.py,sha256=AgdyrUfya4obxnOjoTl62IIRHMfuP-wMtMm6skwes5E,2800
|
|
3373
3373
|
edc_visit_tracking/models/subject_visit.py,sha256=yrSV_pH-W_tw3S9Px2ELSTeFM82haNf0YaekWVaTaRI,1732
|
|
3374
3374
|
edc_visit_tracking/models/subject_visit_missed.py,sha256=VsVF0eALA-UHTeWkaJT5xGCo8FYXqBiRxv-t61wIFtk,963
|
|
3375
3375
|
edc_visit_tracking/models/subject_visit_missed_reasons.py,sha256=dGEUkZvD3wqyUk3joSmfdsEPEswZ6dsCQJAUZ69cewI,257
|
|
3376
3376
|
edc_visit_tracking/models/visit_reasons.py,sha256=OWAL3iksKQlkp9zcD3OXDnjg03kS97hATMRjIZMVsU0,214
|
|
3377
3377
|
edc_visit_tracking/stubs.py,sha256=4lGFKGJEckmnXbNdnQQwc_HcdQFSQxE1H2oQpDircBs,1164
|
|
3378
3378
|
edc_visit_tracking/system_checks.py,sha256=rK1yGfp0H9nuQJBl1PA_RhvorBXMG-ve_Wc6jKXBmdQ,435
|
|
3379
|
-
edc_visit_tracking/typing_stubs.py,sha256=
|
|
3379
|
+
edc_visit_tracking/typing_stubs.py,sha256=7LMpFxcJICBFY7fBFZ1z0JSwSBfYGE6K1whAE7BVb-U,2106
|
|
3380
3380
|
edc_visit_tracking/urls.py,sha256=X9Wiy0mlDaCeiiOKNytfSNfVY6oeeJbaBPtHol81HD0,181
|
|
3381
|
-
edc_visit_tracking/utils.py,sha256=
|
|
3381
|
+
edc_visit_tracking/utils.py,sha256=3Bamx6fWOISqN5_JhXU3R1eHwcBZ4f1KDdNu0t_aHEg,3258
|
|
3382
3382
|
edc_visit_tracking/view_utils/__init__.py,sha256=ABuaZK3UEKHbLjaJv-0e0tovLI0xKG6wrxgElloTeZ4,87
|
|
3383
3383
|
edc_visit_tracking/view_utils/related_visit_button.py,sha256=D0y6mxtY8mW5sQsczyC-GEl2rzc_n5cgTVCTKF-tdIQ,3385
|
|
3384
|
-
edc_visit_tracking/visit_sequence.py,sha256=
|
|
3384
|
+
edc_visit_tracking/visit_sequence.py,sha256=ppK53IVuFD1ZcDHSEeP_P1YfqCNSveznf9s_Rqg5CVY,4300
|
|
3385
3385
|
edc_visit_tracking/wsgi.py,sha256=_CGuos4-Wxsv6M7sc_3MnHKbWlGEfqzJDUrO6JUHzIo,179
|
|
3386
3386
|
edc_vitals/__init__.py,sha256=k7pm4XmS32dJU4XRYKHJrzEkA7CuQ75_Jj7nLkvUiDY,117
|
|
3387
3387
|
edc_vitals/apps.py,sha256=AvV-Dwgo6KRe5VhriAP8CYM6OJG6kGh2_1BwQ6Wmc8o,145
|
|
@@ -3406,7 +3406,7 @@ edc_vitals/models/fields/waist_circumference.py,sha256=fZcHFDdEwWLjIVLktKrFCD9UU
|
|
|
3406
3406
|
edc_vitals/models/fields/weight.py,sha256=zo9_9e3Cpu0UqoRbWS-iDkcDo6fK80b1dDQy4x4MyxE,921
|
|
3407
3407
|
edc_vitals/utils.py,sha256=vXid44KUXxeaSyund_y5MNXc5DFJs052_PwUAjszE2k,1384
|
|
3408
3408
|
edc_vitals/validators.py,sha256=vNiElWMs0rRnHRNuVoPLRf0rW_C_0xcfUyep1Y_Si5s,156
|
|
3409
|
-
clinicedc-2.0.
|
|
3410
|
-
clinicedc-2.0.
|
|
3411
|
-
clinicedc-2.0.
|
|
3412
|
-
clinicedc-2.0.
|
|
3409
|
+
clinicedc-2.0.15.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
|
3410
|
+
clinicedc-2.0.15.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
|
|
3411
|
+
clinicedc-2.0.15.dist-info/METADATA,sha256=OYiw0Dnv4nYU6PEi6gKTj-xbM-7sc6meDbengt5P2zY,15899
|
|
3412
|
+
clinicedc-2.0.15.dist-info/RECORD,,
|
|
@@ -35,10 +35,9 @@ def get_stata_labels_from_model(
|
|
|
35
35
|
if suffix:
|
|
36
36
|
if f"{fld.name}_{suffix}" in df.columns:
|
|
37
37
|
labels.update({f"{fld.name}_{suffix}": strip_html(str(fld.verbose_name)[:80])})
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
pass
|
|
38
|
+
elif f"{fld.name}_{suffix}" in df.columns:
|
|
39
|
+
try:
|
|
40
|
+
labels.update({fld.name: strip_html(str(fld.verbose_name)[:80])})
|
|
41
|
+
except AttributeError:
|
|
42
|
+
pass
|
|
44
43
|
return labels
|
edc_analytics/table.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
from datetime import datetime
|
|
4
5
|
from typing import TYPE_CHECKING
|
|
5
6
|
from zoneinfo import ZoneInfo
|
|
@@ -39,7 +40,7 @@ class AppointmentsCreator:
|
|
|
39
40
|
visit_schedule: VisitSchedule | None = None,
|
|
40
41
|
schedule: Schedule | None = None,
|
|
41
42
|
report_datetime: datetime | None = None,
|
|
42
|
-
appointment_model: str = None,
|
|
43
|
+
appointment_model: str | None = None,
|
|
43
44
|
site_id: int | None = None,
|
|
44
45
|
skip_baseline: bool | None = None,
|
|
45
46
|
):
|
|
@@ -103,13 +104,12 @@ class AppointmentsCreator:
|
|
|
103
104
|
else:
|
|
104
105
|
self.delete_appointments_after_timepoint(last_timepoint)
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
return self.appointment_model_cls.objects.filter(
|
|
107
108
|
subject_identifier=self.subject_identifier,
|
|
108
109
|
site_id=self.site_id,
|
|
109
110
|
visit_schedule_name=self.visit_schedule.name,
|
|
110
111
|
schedule_name=self.schedule.name,
|
|
111
112
|
).order_by("timepoint")
|
|
112
|
-
return appointments
|
|
113
113
|
|
|
114
114
|
def update_or_create_appointment(self, **kwargs) -> Appointment:
|
|
115
115
|
"""Updates or creates an appointment for this subject
|
|
@@ -133,10 +133,8 @@ class AppointmentsCreator:
|
|
|
133
133
|
schedule_name=self.schedule.name,
|
|
134
134
|
)
|
|
135
135
|
for appointment in appointments:
|
|
136
|
-
|
|
136
|
+
with contextlib.suppress(ProtectedError):
|
|
137
137
|
appointment.delete()
|
|
138
|
-
except ProtectedError:
|
|
139
|
-
pass
|
|
140
138
|
|
|
141
139
|
def delete_appointments_after_timepoint(self, last_timepoint) -> None:
|
|
142
140
|
"""Delete appointments after a given timepoint.
|
|
@@ -151,7 +149,5 @@ class AppointmentsCreator:
|
|
|
151
149
|
visit_schedule_name=self.visit_schedule.name,
|
|
152
150
|
schedule_name=self.schedule.name,
|
|
153
151
|
):
|
|
154
|
-
|
|
152
|
+
with contextlib.suppress(ProtectedError):
|
|
155
153
|
appointment.delete()
|
|
156
|
-
except ProtectedError:
|
|
157
|
-
pass
|
edc_dx/diagnoses.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from datetime import date, datetime
|
|
4
|
-
from typing import Dict
|
|
5
4
|
|
|
6
5
|
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
|
|
6
|
+
|
|
7
7
|
from edc_constants.constants import YES
|
|
8
8
|
from edc_dx_review.utils import (
|
|
9
9
|
get_clinical_review_baseline_model_cls,
|
|
@@ -130,7 +130,7 @@ class Diagnoses:
|
|
|
130
130
|
|
|
131
131
|
def report_datetime_opts(
|
|
132
132
|
self, prefix: str = None, lte: bool = None
|
|
133
|
-
) ->
|
|
133
|
+
) -> dict[str, datetime]:
|
|
134
134
|
opts = {}
|
|
135
135
|
prefix = prefix.lower() or ""
|
|
136
136
|
if self.report_datetime:
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Tuple
|
|
4
|
-
|
|
5
3
|
from django import forms
|
|
6
4
|
from django.conf import settings
|
|
5
|
+
|
|
7
6
|
from edc_dx_review.utils import raise_if_clinical_review_does_not_exist
|
|
8
7
|
from edc_form_validators import INVALID_ERROR, FormValidator
|
|
9
8
|
from edc_utils import convert_php_dateformat
|
|
@@ -13,7 +12,7 @@ from ..diagnoses import ClinicalReviewBaselineRequired, Diagnoses, InitialReview
|
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class ResultFormValidatorMixin(FormValidator):
|
|
16
|
-
dx:
|
|
15
|
+
dx: tuple[str, str] # e.g. (HIV, "HIV Infection")
|
|
17
16
|
|
|
18
17
|
def clean(self):
|
|
19
18
|
raise_if_baseline(self.cleaned_data.get("subject_visit"))
|
|
@@ -50,16 +49,15 @@ class ResultFormValidatorMixin(FormValidator):
|
|
|
50
49
|
raise forms.ValidationError(
|
|
51
50
|
f"A {dx_msg_label} diagnosis has not been reported for this subject."
|
|
52
51
|
)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
52
|
+
if dx_date > self.cleaned_data.get(drawn_date_fld):
|
|
53
|
+
formatted_date = dx_date.strftime(
|
|
54
|
+
convert_php_dateformat(settings.SHORT_DATE_FORMAT)
|
|
55
|
+
)
|
|
56
|
+
raise forms.ValidationError(
|
|
57
|
+
{
|
|
58
|
+
"drawn_date": (
|
|
59
|
+
"Invalid. Subject was diagnosed with "
|
|
60
|
+
f"{dx_msg_label} on {formatted_date}."
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
)
|
edc_dx_review/medical_date.py
CHANGED
|
@@ -4,6 +4,7 @@ from datetime import date, datetime
|
|
|
4
4
|
|
|
5
5
|
from django.conf import settings
|
|
6
6
|
from django.core.exceptions import ValidationError
|
|
7
|
+
|
|
7
8
|
from edc_constants.constants import GT, GTE, LT, LTE
|
|
8
9
|
from edc_model import estimated_date_from_ago
|
|
9
10
|
from edc_utils import convert_php_dateformat
|
|
@@ -23,7 +24,7 @@ class MedicalDateError(ValidationError):
|
|
|
23
24
|
|
|
24
25
|
@property
|
|
25
26
|
def message_dict(self):
|
|
26
|
-
|
|
27
|
+
self.error_dict
|
|
27
28
|
return {k: v[0] for k, v in dict(self).items()}
|
|
28
29
|
|
|
29
30
|
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from datetime import date
|
|
4
4
|
|
|
5
5
|
from django.db import models
|
|
6
|
+
|
|
6
7
|
from edc_constants.choices import YES_NO
|
|
7
8
|
from edc_constants.constants import NOT_APPLICABLE, YES
|
|
8
9
|
from edc_dx import Diagnoses
|
|
@@ -36,7 +37,7 @@ def dx_initial_review_methods_model_mixin_factory():
|
|
|
36
37
|
|
|
37
38
|
@property
|
|
38
39
|
def diagnoses(self):
|
|
39
|
-
subject_identifier =
|
|
40
|
+
subject_identifier = self.subject_identifier
|
|
40
41
|
return Diagnoses(
|
|
41
42
|
subject_identifier=subject_identifier,
|
|
42
43
|
report_datetime=getattr(self, get_report_datetime_field_name()),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
|
2
2
|
from django.db import models
|
|
3
|
+
|
|
3
4
|
from edc_constants.choices import YES_NO, YES_NO_NA, YES_NO_PENDING_NA
|
|
4
5
|
from edc_constants.constants import NOT_APPLICABLE, YES
|
|
5
6
|
from edc_lab.choices import VL_QUANTIFIER_NA
|
edc_dx_review/radio_fields.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
from typing import Dict
|
|
2
1
|
|
|
3
2
|
from django.contrib import admin
|
|
3
|
+
|
|
4
4
|
from edc_dx import get_diagnosis_labels_prefixes
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def get_clinical_review_cond_radio_fields() ->
|
|
7
|
+
def get_clinical_review_cond_radio_fields() -> dict[str, int]:
|
|
8
8
|
radio_fields = {}
|
|
9
9
|
for prefix in get_diagnosis_labels_prefixes():
|
|
10
10
|
cond = prefix.lower()
|
|
@@ -17,7 +17,7 @@ def get_clinical_review_cond_radio_fields() -> Dict[str, int]:
|
|
|
17
17
|
return radio_fields
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def get_clinical_review_baseline_cond_radio_fields() ->
|
|
20
|
+
def get_clinical_review_baseline_cond_radio_fields() -> dict[str, int]:
|
|
21
21
|
radio_fields = {}
|
|
22
22
|
for prefix in get_diagnosis_labels_prefixes():
|
|
23
23
|
cond = prefix.lower()
|
edc_dx_review/utils.py
CHANGED
|
@@ -7,6 +7,7 @@ from django import forms
|
|
|
7
7
|
from django.apps import apps as django_apps
|
|
8
8
|
from django.conf import settings
|
|
9
9
|
from django.core.exceptions import ObjectDoesNotExist
|
|
10
|
+
|
|
10
11
|
from edc_constants.constants import HIV, YES
|
|
11
12
|
from edc_model.utils import model_exists_or_raise
|
|
12
13
|
from edc_visit_schedule.baseline import VisitScheduleBaselineError
|
|
@@ -78,7 +79,7 @@ def get_extra_attrs():
|
|
|
78
79
|
"review": "review",
|
|
79
80
|
}
|
|
80
81
|
try:
|
|
81
|
-
data =
|
|
82
|
+
data = settings.EDC_DX_REVIEW_EXTRA_ATTRS
|
|
82
83
|
except AttributeError:
|
|
83
84
|
pass
|
|
84
85
|
else:
|
edc_randomization/randomizer.py
CHANGED
|
@@ -22,6 +22,7 @@ from .randomization_list_importer import (
|
|
|
22
22
|
RandomizationListAlreadyImported,
|
|
23
23
|
RandomizationListImporter,
|
|
24
24
|
)
|
|
25
|
+
from .utils import get_randomization_list_path
|
|
25
26
|
|
|
26
27
|
if TYPE_CHECKING:
|
|
27
28
|
from edc_registration.models import RegisteredSubject
|
|
@@ -99,11 +100,7 @@ class Randomizer:
|
|
|
99
100
|
DEFAULT_ASSIGNMENT_DESCRIPTION_MAP,
|
|
100
101
|
)
|
|
101
102
|
filename: str = "randomization_list.csv"
|
|
102
|
-
randomizationlist_folder: Path | str =
|
|
103
|
-
settings,
|
|
104
|
-
"EDC_RANDOMIZATION_LIST_PATH",
|
|
105
|
-
Path(settings.BASE_DIR).expanduser() / ".etc",
|
|
106
|
-
)
|
|
103
|
+
randomizationlist_folder: Path | str = get_randomization_list_path()
|
|
107
104
|
extra_csv_fieldnames: list[str] | None = None
|
|
108
105
|
trial_is_blinded: bool = True
|
|
109
106
|
importer_cls: Any = RandomizationListImporter
|
edc_randomization/utils.py
CHANGED
|
@@ -25,6 +25,16 @@ class SubjectNotRandomization(Exception): # noqa: N818
|
|
|
25
25
|
pass
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
def get_randomization_list_path() -> Path:
|
|
29
|
+
return Path(
|
|
30
|
+
getattr(
|
|
31
|
+
settings,
|
|
32
|
+
"EDC_RANDOMIZATION_LIST_PATH",
|
|
33
|
+
settings.ETC_DIR,
|
|
34
|
+
)
|
|
35
|
+
).expanduser()
|
|
36
|
+
|
|
37
|
+
|
|
28
38
|
def get_assignment_for_subject(
|
|
29
39
|
subject_identifier: str,
|
|
30
40
|
randomizer_name: str,
|
|
@@ -26,12 +26,11 @@ class VisitMissedAction(ActionWithNotification):
|
|
|
26
26
|
|
|
27
27
|
def get_next_actions(self) -> list[Action]:
|
|
28
28
|
next_actions: list[Action] = []
|
|
29
|
-
|
|
29
|
+
return self.append_to_next_if_required(
|
|
30
30
|
next_actions=next_actions,
|
|
31
31
|
action_name=self.get_loss_to_followup_action_name(),
|
|
32
32
|
required=self.is_ltfu(),
|
|
33
33
|
)
|
|
34
|
-
return next_actions
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
class MissedVisitAction(VisitMissedAction):
|
edc_visit_tracking/apps.py
CHANGED
|
@@ -12,7 +12,7 @@ class AppConfig(DjangoAppConfig):
|
|
|
12
12
|
verbose_name = "Edc Visit Tracking"
|
|
13
13
|
report_datetime_allowance: int = 30
|
|
14
14
|
allow_crf_report_datetime_before_visit: bool = False
|
|
15
|
-
reason_field: dict = {}
|
|
15
|
+
reason_field: dict = {} # noqa: RUF012
|
|
16
16
|
|
|
17
17
|
def ready(self):
|
|
18
18
|
register(context_processors_check)
|
|
@@ -2,11 +2,10 @@ from .constants import DEFERRED_VISIT, LOST_VISIT, MISSED_VISIT, SCHEDULED, UNSC
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def constants(request) -> dict:
|
|
5
|
-
|
|
5
|
+
return dict(
|
|
6
6
|
DEFERRED_VISIT=DEFERRED_VISIT,
|
|
7
7
|
LOST_VISIT=LOST_VISIT,
|
|
8
8
|
MISSED_VISIT=MISSED_VISIT,
|
|
9
9
|
SCHEDULED=SCHEDULED,
|
|
10
10
|
UNSCHEDULED=UNSCHEDULED,
|
|
11
11
|
)
|
|
12
|
-
return dct
|
|
@@ -14,11 +14,11 @@ class CrfReportDateAllowanceError(Exception):
|
|
|
14
14
|
pass
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class CrfReportDateBeforeStudyStart(Exception):
|
|
17
|
+
class CrfReportDateBeforeStudyStart(Exception): # noqa: N818
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class CrfReportDateIsFuture(Exception):
|
|
21
|
+
class CrfReportDateIsFuture(Exception): # noqa: N818
|
|
22
22
|
pass
|
|
23
23
|
|
|
24
24
|
|
|
@@ -58,13 +58,13 @@ class CrfDateValidator:
|
|
|
58
58
|
datetime_not_before_study_start(self.report_datetime)
|
|
59
59
|
except ValidationError as e:
|
|
60
60
|
message = e.message if hasattr(e, "message") else str(e)
|
|
61
|
-
raise CrfReportDateBeforeStudyStart(message)
|
|
61
|
+
raise CrfReportDateBeforeStudyStart(message) from e
|
|
62
62
|
# datetime_not_future
|
|
63
63
|
try:
|
|
64
64
|
datetime_not_future(self.report_datetime)
|
|
65
65
|
except ValidationError as e:
|
|
66
66
|
message = e.message if hasattr(e, "message") else str(e)
|
|
67
|
-
raise CrfReportDateIsFuture(message)
|
|
67
|
+
raise CrfReportDateIsFuture(message) from e
|
|
68
68
|
|
|
69
69
|
formatted_visit_datetime = self.visit_report_datetime.strftime(
|
|
70
70
|
convert_php_dateformat(settings.SHORT_DATE_FORMAT)
|
|
@@ -168,41 +168,44 @@ class VisitFormValidator(WindowPeriodFormValidatorMixin, FormValidator):
|
|
|
168
168
|
"""Asserts the report_datetime is not before the
|
|
169
169
|
appt_datetime.
|
|
170
170
|
"""
|
|
171
|
-
if report_datetime_local := self.report_datetime
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
171
|
+
if (report_datetime_local := self.report_datetime) and (
|
|
172
|
+
report_datetime_local.date() < self.appt_datetime_local.date()
|
|
173
|
+
):
|
|
174
|
+
appt_datetime_str = formatted_datetime(
|
|
175
|
+
self.appt_datetime_local, format_as_date=True
|
|
176
|
+
)
|
|
177
|
+
self.raise_validation_error(
|
|
178
|
+
{
|
|
179
|
+
"report_datetime": (
|
|
180
|
+
"Invalid. Cannot be before appointment date. "
|
|
181
|
+
f"Got appointment date {appt_datetime_str}"
|
|
182
|
+
)
|
|
183
|
+
},
|
|
184
|
+
INVALID_ERROR,
|
|
185
|
+
)
|
|
185
186
|
|
|
186
187
|
def validate_visit_datetime_matches_appt_datetime_at_baseline(self) -> None:
|
|
187
188
|
"""Asserts the report_datetime matches the appt_datetime
|
|
188
189
|
as baseline.
|
|
189
190
|
"""
|
|
190
|
-
if
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
INVALID_ERROR,
|
|
191
|
+
if (
|
|
192
|
+
(is_baseline(instance=self.appointment))
|
|
193
|
+
and (report_datetime_local := self.report_datetime)
|
|
194
|
+
and (report_datetime_local.date() != self.appt_datetime_local.date())
|
|
195
|
+
):
|
|
196
|
+
appt_datetime_str = formatted_datetime(
|
|
197
|
+
self.appt_datetime_local, format_as_date=True
|
|
198
|
+
)
|
|
199
|
+
self.raise_validation_error(
|
|
200
|
+
{
|
|
201
|
+
"report_datetime": (
|
|
202
|
+
"Invalid. Must match appointment date at baseline. "
|
|
203
|
+
"If necessary, change the appointment date and "
|
|
204
|
+
f"try again. Got appointment date {appt_datetime_str}"
|
|
205
205
|
)
|
|
206
|
+
},
|
|
207
|
+
INVALID_ERROR,
|
|
208
|
+
)
|
|
206
209
|
|
|
207
210
|
def validate_visits_completed_in_order(self) -> None:
|
|
208
211
|
"""Asserts visits are completed in order."""
|
|
@@ -210,7 +213,7 @@ class VisitFormValidator(WindowPeriodFormValidatorMixin, FormValidator):
|
|
|
210
213
|
try:
|
|
211
214
|
visit_sequence.enforce_sequence()
|
|
212
215
|
except VisitSequenceError as e:
|
|
213
|
-
raise forms.ValidationError(e, code=INVALID_ERROR)
|
|
216
|
+
raise forms.ValidationError(e, code=INVALID_ERROR) from e
|
|
214
217
|
|
|
215
218
|
def validate_visit_code_sequence_and_reason(self) -> None:
|
|
216
219
|
"""Asserts the `reason` makes sense relative to the
|
|
@@ -234,7 +237,7 @@ class VisitFormValidator(WindowPeriodFormValidatorMixin, FormValidator):
|
|
|
234
237
|
and EDC_VISIT_TRACKING_ALLOW_MISSED_UNSCHEDULED is False
|
|
235
238
|
):
|
|
236
239
|
raise forms.ValidationError(
|
|
237
|
-
{"reason":
|
|
240
|
+
{"reason": "Invalid. This is an unscheduled visit. See appointment."},
|
|
238
241
|
code=INVALID_ERROR,
|
|
239
242
|
)
|
|
240
243
|
# raise if CRF metadata exist
|
|
@@ -267,23 +270,25 @@ class VisitFormValidator(WindowPeriodFormValidatorMixin, FormValidator):
|
|
|
267
270
|
field_required="reason_missed_other",
|
|
268
271
|
)
|
|
269
272
|
|
|
270
|
-
if
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
273
|
+
if (
|
|
274
|
+
self.validate_unscheduled_visit_reason
|
|
275
|
+
and "reason_unscheduled" in self.cleaned_data
|
|
276
|
+
):
|
|
277
|
+
self.applicable_if(
|
|
278
|
+
UNSCHEDULED,
|
|
279
|
+
field="reason",
|
|
280
|
+
field_applicable="reason_unscheduled",
|
|
281
|
+
)
|
|
277
282
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
+
self.required_if(
|
|
284
|
+
OTHER,
|
|
285
|
+
field="reason_unscheduled",
|
|
286
|
+
field_required="reason_unscheduled_other",
|
|
287
|
+
)
|
|
283
288
|
|
|
284
289
|
def metadata_exists_for(
|
|
285
290
|
self,
|
|
286
|
-
entry_status: str = None,
|
|
291
|
+
entry_status: str | None = None,
|
|
287
292
|
filter_models: list[str] | None = None,
|
|
288
293
|
exclude_models: list[str] | None = None,
|
|
289
294
|
) -> int:
|
|
@@ -291,8 +296,9 @@ class VisitFormValidator(WindowPeriodFormValidatorMixin, FormValidator):
|
|
|
291
296
|
the given entry_status.
|
|
292
297
|
"""
|
|
293
298
|
exclude_opts: dict = {}
|
|
299
|
+
entry_status = entry_status or KEYED
|
|
294
300
|
filter_opts = deepcopy(self.crf_filter_options)
|
|
295
|
-
filter_opts.update(entry_status=entry_status
|
|
301
|
+
filter_opts.update(entry_status=entry_status)
|
|
296
302
|
if filter_models:
|
|
297
303
|
filter_opts.update(model__in=filter_models)
|
|
298
304
|
if exclude_models:
|
|
@@ -3,6 +3,8 @@ from edc_form_validators import FormValidator
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class VisitMissedFormValidator(FormValidator):
|
|
6
|
+
min_contact_attempts_count = 3
|
|
7
|
+
|
|
6
8
|
def clean(self) -> None:
|
|
7
9
|
self.applicable_if(YES, field="contact_attempted", field_applicable="contact_made")
|
|
8
10
|
self.required_if(
|
|
@@ -16,7 +18,10 @@ class VisitMissedFormValidator(FormValidator):
|
|
|
16
18
|
if self.cleaned_data.get("contact_attempts_count") is None
|
|
17
19
|
else self.cleaned_data.get("contact_attempts_count")
|
|
18
20
|
)
|
|
19
|
-
cond =
|
|
21
|
+
cond = (
|
|
22
|
+
self.cleaned_data.get("contact_made") == NO
|
|
23
|
+
and contact_attempts_count < self.min_contact_attempts_count
|
|
24
|
+
)
|
|
20
25
|
self.required_if_true(
|
|
21
26
|
cond,
|
|
22
27
|
field_required="contact_attempts_explained",
|
|
@@ -25,7 +25,7 @@ def get_related_visit_model_attr(model_cls) -> str:
|
|
|
25
25
|
fld_cls.related_model is not None
|
|
26
26
|
and fld_cls.related_model == related_visit_model_cls
|
|
27
27
|
):
|
|
28
|
-
attrs.append(fld_cls.name)
|
|
28
|
+
attrs.append(fld_cls.name) # noqa: PERF401
|
|
29
29
|
if len(attrs) > 1:
|
|
30
30
|
raise RelatedVisitFieldError(
|
|
31
31
|
f"More than one field is related to the visit model. See {model_cls}. "
|
|
@@ -76,11 +76,11 @@ class VisitTrackingCrfModelFormMixin:
|
|
|
76
76
|
def related_visit_model_attr(self) -> str:
|
|
77
77
|
try:
|
|
78
78
|
return self._meta.model.related_visit_model_attr()
|
|
79
|
-
except AttributeError:
|
|
79
|
+
except AttributeError as e:
|
|
80
80
|
raise VisitTrackingCrfModelFormMixinError(
|
|
81
81
|
"Expected method `related_visit_model_attr`. Is this a CRF? "
|
|
82
82
|
f"See model {self._meta.model}"
|
|
83
|
-
)
|
|
83
|
+
) from e
|
|
84
84
|
|
|
85
85
|
def validate_visit_tracking(self: Any) -> None:
|
|
86
86
|
# trigger a validation error if visit field is None
|
|
@@ -104,7 +104,7 @@ class VisitTrackingCrfModelFormMixin:
|
|
|
104
104
|
CrfReportDateBeforeStudyStart,
|
|
105
105
|
CrfReportDateIsFuture,
|
|
106
106
|
) as e:
|
|
107
|
-
raise forms.ValidationError({self.report_datetime_field_attr: str(e)})
|
|
107
|
+
raise forms.ValidationError({self.report_datetime_field_attr: str(e)}) from e
|
|
108
108
|
|
|
109
109
|
def validate_visits_completed_in_order(self) -> None:
|
|
110
110
|
"""Asserts visits are completed in order."""
|
|
@@ -115,4 +115,4 @@ class VisitTrackingCrfModelFormMixin:
|
|
|
115
115
|
try:
|
|
116
116
|
visit_sequence.enforce_sequence(document_type="CRF")
|
|
117
117
|
except VisitSequenceError as e:
|
|
118
|
-
raise forms.ValidationError(str(e), code=INVALID_ERROR)
|
|
118
|
+
raise forms.ValidationError(str(e), code=INVALID_ERROR) from e
|
|
@@ -29,9 +29,9 @@ def get_related_visit(
|
|
|
29
29
|
VisitTrackingCrfModelFormMixin
|
|
30
30
|
| InlineCrfModelFormMixin
|
|
31
31
|
| CrfFormValidator
|
|
32
|
-
| CrfFormValidatorMixin
|
|
32
|
+
| CrfFormValidatorMixin
|
|
33
33
|
),
|
|
34
|
-
related_visit_model_attr: str = None,
|
|
34
|
+
related_visit_model_attr: str | None = None,
|
|
35
35
|
) -> RelatedVisitModel | None:
|
|
36
36
|
"""Returns the related visit model instance or None.
|
|
37
37
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import Any,
|
|
4
|
+
from typing import Any, Protocol, TypeVar
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
7
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
@@ -33,10 +33,10 @@ class ModelBase(type):
|
|
|
33
33
|
def _base_manager(cls: type[_Self]) -> BaseManager[_Self]: ...
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
class Options(
|
|
36
|
+
class Options([_M]):
|
|
37
37
|
def label_lower(self) -> str: ...
|
|
38
38
|
|
|
39
|
-
def fields(self) ->
|
|
39
|
+
def fields(self) -> tuple[Field]: ...
|
|
40
40
|
|
|
41
41
|
def get_fields(
|
|
42
42
|
self, include_parents: bool = ..., include_hidden: bool = ...
|
edc_visit_tracking/utils.py
CHANGED
|
@@ -63,7 +63,7 @@ def get_subject_visit_missed_model() -> str:
|
|
|
63
63
|
try:
|
|
64
64
|
model = settings.SUBJECT_VISIT_MISSED_MODEL
|
|
65
65
|
except AttributeError as e:
|
|
66
|
-
raise ImproperlyConfigured(f"{error_msg} Got {e}.")
|
|
66
|
+
raise ImproperlyConfigured(f"{error_msg} Got {e}.") from e
|
|
67
67
|
else:
|
|
68
68
|
if not model:
|
|
69
69
|
raise ImproperlyConfigured(f"{error_msg} Got None.")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
from typing import TYPE_CHECKING
|
|
4
5
|
|
|
5
6
|
from django.utils.translation import gettext_lazy as _
|
|
@@ -39,7 +40,7 @@ class VisitSequence:
|
|
|
39
40
|
self.visit_code = self.appointment.visit_code
|
|
40
41
|
self.visit_code_sequence = self.appointment.visit_code_sequence
|
|
41
42
|
|
|
42
|
-
def enforce_sequence(self, document_type: str = None) -> None:
|
|
43
|
+
def enforce_sequence(self, document_type: str | None = None) -> None:
|
|
43
44
|
"""Raises an exception if sequence is not adhered to; that is,
|
|
44
45
|
the visit reports are not completed in order.
|
|
45
46
|
|
|
@@ -80,10 +81,8 @@ class VisitSequence:
|
|
|
80
81
|
previous_visit_code = self.visit_code
|
|
81
82
|
else:
|
|
82
83
|
previous = self.appointment.schedule.visits.previous(self.visit_code)
|
|
83
|
-
|
|
84
|
+
with contextlib.suppress(AttributeError):
|
|
84
85
|
previous_visit_code = previous.code
|
|
85
|
-
except AttributeError:
|
|
86
|
-
pass
|
|
87
86
|
return previous_visit_code
|
|
88
87
|
|
|
89
88
|
@property
|
|
File without changes
|
|
File without changes
|