clinicedc 2.0.9__py3-none-any.whl → 2.0.11__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.9.dist-info → clinicedc-2.0.11.dist-info}/METADATA +3 -2
- {clinicedc-2.0.9.dist-info → clinicedc-2.0.11.dist-info}/RECORD +21 -21
- {clinicedc-2.0.9.dist-info → clinicedc-2.0.11.dist-info}/WHEEL +1 -1
- edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_validation_mixin.py +0 -7
- edc_consent/models/signals.py +16 -13
- edc_consent/site_consents.py +15 -11
- edc_egfr/form_validator_mixins/egfr_form_validator_mixins.py +5 -4
- edc_form_validators/applicable_field_validator.py +1 -2
- edc_form_validators/required_field_validator.py +14 -15
- edc_glucose/model_mixins/hba1c_model_mixin.py +1 -1
- edc_glucose/utils.py +5 -5
- edc_lab/lab/requisition_panel.py +10 -10
- edc_lab_panel/panels.py +13 -13
- edc_lab_results/form_validator_mixins/blood_results_fbg_form_validator_mixin.py +3 -8
- edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py +35 -32
- edc_lab_results/get_summary.py +3 -3
- edc_listboard/view_mixins/search_form_view_mixin.py +2 -3
- edc_model_admin/mixins/model_admin_form_instructions_mixin.py +4 -4
- edc_visit_schedule/baseline.py +4 -5
- edc_visit_schedule/site_visit_schedules.py +7 -10
- {clinicedc-2.0.9.dist-info → clinicedc-2.0.11.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.11
|
|
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
|
|
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
20
|
Requires-Dist: argon2-cffi>=25.1.0
|
|
21
21
|
Requires-Dist: arrow>=1.3.0
|
|
22
|
+
Requires-Dist: beautifulsoup4>=4.13.5
|
|
22
23
|
Requires-Dist: boto3>=1.39.16
|
|
23
24
|
Requires-Dist: celery>=5.5.3
|
|
24
25
|
Requires-Dist: cryptography>=45.0.7
|
|
@@ -34,6 +35,7 @@ Requires-Dist: django-multisite2>=2.1.0
|
|
|
34
35
|
Requires-Dist: django-pandas>=0.6.7
|
|
35
36
|
Requires-Dist: django-pylabels>=1.0.1
|
|
36
37
|
Requires-Dist: django-redis>=6.0.0
|
|
38
|
+
Requires-Dist: django-revision>=2.0.1
|
|
37
39
|
Requires-Dist: django-sequences>=3.0
|
|
38
40
|
Requires-Dist: django-simple-history>=3.10.1
|
|
39
41
|
Requires-Dist: django-storages>=1.14.6
|
|
@@ -43,7 +45,6 @@ Requires-Dist: gunicorn>=23.0.0
|
|
|
43
45
|
Requires-Dist: inflect>=7.5.0
|
|
44
46
|
Requires-Dist: mempass>=0.1.2
|
|
45
47
|
Requires-Dist: mysqlclient>=2.2.7
|
|
46
|
-
Requires-Dist: psycopg2>=2.9.10
|
|
47
48
|
Requires-Dist: pycups>=2.0.4
|
|
48
49
|
Requires-Dist: pypdf>=5.9.0
|
|
49
50
|
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
@@ -559,14 +559,14 @@ edc_consent/modelform_mixins/__init__.py,sha256=Z5bXi8FIka147nV0VLjb2PHCzJDaI4lY
|
|
|
559
559
|
edc_consent/modelform_mixins/consent_modelform_mixin/__init__.py,sha256=qg__oajqZP9ktA4BvoevB-hoYfAXTKCmhQyO4g-Dk04,196
|
|
560
560
|
edc_consent/modelform_mixins/consent_modelform_mixin/clean_fields_modelform_validation_mixin.py,sha256=OcV-4y4LuN10ixAqVkbZcr5Ubuq9GKiMVMHDPIdrY3Q,1681
|
|
561
561
|
edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_mixin.py,sha256=ayVs-MIVfTiBJMPqaUFqivYFemNB_E1lHO1DNu_SItE,1170
|
|
562
|
-
edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_validation_mixin.py,sha256=
|
|
562
|
+
edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_validation_mixin.py,sha256=Wwo3l-pdFlLkK8q50GkaCyPUXtuPJXODqYfb3HF1jDw,9613
|
|
563
563
|
edc_consent/modelform_mixins/consent_modelform_mixin/review_fields_modelform_mixin.py,sha256=HOiWpf8SnPaGMtRbKM6uAglbV1HmQWdzxSoyQW1ab2U,1922
|
|
564
564
|
edc_consent/modelform_mixins/requires_consent_modelform_mixin.py,sha256=-rQAmOvl1fx8-lEUB6M4V7tqaTqDRxtid002EWYPIxM,2340
|
|
565
565
|
edc_consent/models/__init__.py,sha256=kTUrf6WlsKInX7jVuD3DyIqRW1OUaeSemflLLzWLV_I,278
|
|
566
566
|
edc_consent/models/edc_permissions.py,sha256=f9PWQ1ZFz4rKT7wFpfr8anQ7mV6sCdNaWNMimPLGae8,338
|
|
567
|
-
edc_consent/models/signals.py,sha256=
|
|
567
|
+
edc_consent/models/signals.py,sha256=3e_VTdCMlE0D1TteK-hk1YxNDbBgAr894SK2SD8a-cA,3148
|
|
568
568
|
edc_consent/navbars.py,sha256=Bqb2UfOCLL6yuT_fSvKYQymbBj7V6q9CwvmI-VdUmls,334
|
|
569
|
-
edc_consent/site_consents.py,sha256=
|
|
569
|
+
edc_consent/site_consents.py,sha256=jQYufDBievxoyOlC0Q1Llxv9xYqCEyk58Q1Fg_cu3mY,16882
|
|
570
570
|
edc_consent/stubs.py,sha256=ZXjNqT6nBHRFHCB5tCOzBC3ZdiiBR3IitygO3FldTZA,513
|
|
571
571
|
edc_consent/system_checks.py,sha256=SsBMKj9xP-9C-bntBfJLDFuYG1iGu1cBjvFDa8IGDJg,4642
|
|
572
572
|
edc_consent/templates/edc_consent/home.html,sha256=MKlGwcdiz224le0XHwXCsDIlTELnfc08-_VABEQl1Ag,1005
|
|
@@ -850,7 +850,7 @@ edc_egfr/calculators/percent_change.py,sha256=YVKho6PrqDdfdwV6Ndrzw_9fRJquCWxTlk
|
|
|
850
850
|
edc_egfr/constants.py,sha256=9L7NcsG-rbd-yg7TnW95XCF5vSL4BO7eaDQD4-mgD80,59
|
|
851
851
|
edc_egfr/egfr.py,sha256=1S8okz_ALH_hFfAA6BF77Oz8_f2RkrT9JK4Q4DqpWA8,9485
|
|
852
852
|
edc_egfr/form_validator_mixins/__init__.py,sha256=rWunR6OZW209YkY3dCjueiu19MSu4GNFVfmfVWT4imY,120
|
|
853
|
-
edc_egfr/form_validator_mixins/egfr_form_validator_mixins.py,sha256=
|
|
853
|
+
edc_egfr/form_validator_mixins/egfr_form_validator_mixins.py,sha256=PEagpNmiTNq70e3BrP7aMsg7cnCvib_MM7tOm72_XzY,1833
|
|
854
854
|
edc_egfr/get_drop_notification_model.py,sha256=E9_vSVtIXrjYau7nnS81HlRzpVWennH4vwcs19Mc42s,287
|
|
855
855
|
edc_egfr/get_egfr_for_subject.py,sha256=3ZAhuB8cfNOw1yW1NdMAsZe0l13aVTPYy335k-1MCUw,951
|
|
856
856
|
edc_egfr/model_mixins/__init__.py,sha256=a426yBWQvZro-ajuTfhRBh2-qJ1Mlb5VELDBw0cq4Cw,124
|
|
@@ -1050,7 +1050,7 @@ edc_form_runners/templatetags/form_runners_extras.py,sha256=yzFHwHI2yKaLB_5NGsQo
|
|
|
1050
1050
|
edc_form_runners/urls.py,sha256=kglpnYOGvkQv5CvIyDSZAVrnQfUS7n8nED_3BvNo7NE,215
|
|
1051
1051
|
edc_form_runners/utils.py,sha256=szAFGuRd8hQ-8iIp_gFVWdH-G341CN3FQ5xPXfhj9Jw,2956
|
|
1052
1052
|
edc_form_validators/__init__.py,sha256=ErtgXVQ_uSpLGhAJbJU858CoJ1HBaXmPU_7ZimP0thM,727
|
|
1053
|
-
edc_form_validators/applicable_field_validator.py,sha256=
|
|
1053
|
+
edc_form_validators/applicable_field_validator.py,sha256=T3bs1jH0Ul9NOXSPS4jNZRex7Z4kWma-PP8BwImKKqU,7702
|
|
1054
1054
|
edc_form_validators/apps.py,sha256=cIzFMQHoJS4M0j91oQqS1cQPPpFnnZ6dVBmgg1kVd5E,122
|
|
1055
1055
|
edc_form_validators/base_form_validator.py,sha256=DVUY-o2FlNfV66Krctn3Jnttg-UmkmH7cw8HHrSy70U,5363
|
|
1056
1056
|
edc_form_validators/current_site_validator.py,sha256=KIMqa5huICwX7EzvIbsZW2N0r1010BQNLpsBAfONLLI,283
|
|
@@ -1066,7 +1066,7 @@ edc_form_validators/locale_validator.py,sha256=MztrfUcAKTwL3HfBRYMQQoXy1Acc9374m
|
|
|
1066
1066
|
edc_form_validators/many_to_many_field_validator.py,sha256=fp6B8RrcEvhFuvsuqLjmtVBZSFNkwunLmlEwuxHe7nc,14030
|
|
1067
1067
|
edc_form_validators/other_specify_field_validator.py,sha256=jqoO5ETt4zWNOLfJ2MglqOo4iwdrU0NXpY43O58nuTQ,2300
|
|
1068
1068
|
edc_form_validators/range_field_validator.py,sha256=0FQqzoduPM79YBhaEN975sEl5dF8eMg0vzFGsnBvdIY,1561
|
|
1069
|
-
edc_form_validators/required_field_validator.py,sha256=
|
|
1069
|
+
edc_form_validators/required_field_validator.py,sha256=sc2xYdEM_1W9rDdQZ6wbF1b0Jgz3qbRamHhIzuYkvNM,12001
|
|
1070
1070
|
edc_form_validators/test_case_mixin.py,sha256=C-okM31TYGWz_5lWRX1HYc0xcPX09gTgO6bO-2gkVho,2442
|
|
1071
1071
|
edc_form_validators/urls.py,sha256=_sVCnQeiAFRYKhxga3_DjoKj_4bgs1s2gjcynDiapvA,111
|
|
1072
1072
|
edc_glucose/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -1091,10 +1091,10 @@ edc_glucose/model_mixins/__init__.py,sha256=BBRpyYsEimvbgjVFpQr10THbaeuqz-EGROUf
|
|
|
1091
1091
|
edc_glucose/model_mixins/fasting_model_mixin.py,sha256=ZKE4ugOrIYEwVg5aorWM3FdXOdSYlMjR7TdGJTZwPM8,354
|
|
1092
1092
|
edc_glucose/model_mixins/fbg_model_mixin.py,sha256=NLCMuvG0QHWCMMXcOU5CnGPlLp_7F9SjZ6AyG8uJ-v4,246
|
|
1093
1093
|
edc_glucose/model_mixins/glucose_model_mixin.py,sha256=6KaNtNH7Rt5I-XAxmgFThcJiAtHiGy1OY0Q8eiJRDOY,437
|
|
1094
|
-
edc_glucose/model_mixins/hba1c_model_mixin.py,sha256=
|
|
1094
|
+
edc_glucose/model_mixins/hba1c_model_mixin.py,sha256=JpNd5D-2ePFvELXfV7aq4kOXP1SPgl8E7UiW2Ni3bqQ,974
|
|
1095
1095
|
edc_glucose/model_mixins/ogtt_model_mixin.py,sha256=O4EwgHyVTGnA0POdD95CDWJ6Mi6ylcI-XOnZSglXca8,251
|
|
1096
1096
|
edc_glucose/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1097
|
-
edc_glucose/utils.py,sha256
|
|
1097
|
+
edc_glucose/utils.py,sha256=-v_0y24hSNKhcZ4wOK-2Qei4sgqVzPuJPXIs1Zo50sk,1420
|
|
1098
1098
|
edc_identifier/__init__.py,sha256=kkhaH8PAtHEohoVF2VENZfZmXeKyAnHWJHS1NEWuLyM,97
|
|
1099
1099
|
edc_identifier/admin.py,sha256=LaKWYyBGNcRJ7yMNajKvVtj3JbcqqLSrizL3sWDf82g,1765
|
|
1100
1100
|
edc_identifier/admin_site.py,sha256=Aev8xku-hX60RlijUe5IJrpxEdiceLbnc4kdLN7uX3g,173
|
|
@@ -1192,7 +1192,7 @@ edc_lab/lab/lab_profile.py,sha256=048sJ-heOe6r8Bvyu945Gm_Fncm8e01Nvw0KWJ2HHfg,18
|
|
|
1192
1192
|
edc_lab/lab/manifest.py,sha256=3ndwq0YM4tjwbhRZU-WgA3Zd-WnuRAAvU5KXsqSxSPc,4024
|
|
1193
1193
|
edc_lab/lab/primary_aliquot.py,sha256=vO6eVsLcmbkmkgGlrNF9N2shPsOIS8tLzN6nzA5Kh6Q,1640
|
|
1194
1194
|
edc_lab/lab/processing_profile.py,sha256=DaJiWx4MAnPkIVOLpm454FvBt_PpzbYwf1qyXiuGh1Y,2390
|
|
1195
|
-
edc_lab/lab/requisition_panel.py,sha256=
|
|
1195
|
+
edc_lab/lab/requisition_panel.py,sha256=8M12H8NpRJb5EJqINjFKmEyRdRQCl2JmEL3qURL2A7g,3763
|
|
1196
1196
|
edc_lab/lab/requisition_panel_group.py,sha256=2nlqt3HXe7t4G5d5ZuQLzbR5-h51HYh3ExqygkfymBg,2098
|
|
1197
1197
|
edc_lab/lab/specimen.py,sha256=rrQKZ1MtjAT1rIcBm2GwbYitdwVabKweubfOVZ-v4_8,2949
|
|
1198
1198
|
edc_lab/lab/specimen_processor.py,sha256=K9S4-USt5UIgToRoiZ27oX3Tr3IOg4JDRherV5xC4oY,1922
|
|
@@ -1403,7 +1403,7 @@ edc_lab_dashboard/wsgi.py,sha256=B308vdF9JvIhT5OFCQFvq8pslX8Bz5XWybEcAACYDz8,178
|
|
|
1403
1403
|
edc_lab_panel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1404
1404
|
edc_lab_panel/apps.py,sha256=wBQgzF8013iMzxN-oQyiT9a6bbhJtXN8F_3xvflRgIQ,197
|
|
1405
1405
|
edc_lab_panel/constants.py,sha256=1hcrrKuzDBvtDk5bHZYKUKUB91wYBRHzWJdYIzJDHmM,638
|
|
1406
|
-
edc_lab_panel/panels.py,sha256=
|
|
1406
|
+
edc_lab_panel/panels.py,sha256=qGC-NRfwV6fNobPD5xOUo8AVX_3BCtLGs6AHxggmxOQ,3087
|
|
1407
1407
|
edc_lab_panel/processing_profiles.py,sha256=uBUVicajUXum1QT10agRJviKYylchPZdklQSmVJ_ueM,910
|
|
1408
1408
|
edc_lab_results/__init__.py,sha256=LLcNECzNbJLM2UwhCpWao4IueS3AJVoIIMKBme54pkg,277
|
|
1409
1409
|
edc_lab_results/action_items.py,sha256=T1zQBA5pxDURAwdWqctG0CjuuvfAltq71kItMPgap-0,3864
|
|
@@ -1415,10 +1415,10 @@ edc_lab_results/calculate_missing.py,sha256=mnSCszRLetffXVkCcVZkF5sxsp_1WjHRMzWm
|
|
|
1415
1415
|
edc_lab_results/constants.py,sha256=ZpvT3JboYvywvw2hMn2loEsdUHVWlueP2IzvMZzAdgA,505
|
|
1416
1416
|
edc_lab_results/fieldsets.py,sha256=0wGFo1X7EXXY0zbJ1mozFhed2f_tQbCxVLafojrJhW4,3906
|
|
1417
1417
|
edc_lab_results/form_validator_mixins/__init__.py,sha256=GLCQS8ZO6m-OsuIysw9oqsrDxhGqk8wIcxYcb5VPBBg,251
|
|
1418
|
-
edc_lab_results/form_validator_mixins/blood_results_fbg_form_validator_mixin.py,sha256=
|
|
1419
|
-
edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py,sha256
|
|
1418
|
+
edc_lab_results/form_validator_mixins/blood_results_fbg_form_validator_mixin.py,sha256=DsDoc4BvJE89LBcTf9sckJ9LXSdgw54jHJlIxvP_VMM,735
|
|
1419
|
+
edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py,sha256=VO0iDEAte_3cRjuwcVaslVuT6gSDotTZxxQUeOUa7M8,4509
|
|
1420
1420
|
edc_lab_results/form_validator_mixins/blood_results_glu_form_validator_mixin.py,sha256=vojao9i1l5DH4t99GM_d76LAeiaAb1l2VA-i2O0gtDI,173
|
|
1421
|
-
edc_lab_results/get_summary.py,sha256=
|
|
1421
|
+
edc_lab_results/get_summary.py,sha256=GOmNys-FIvuyEwhoTm8emSKczHJ-q_ycRytEtOW6AD0,2116
|
|
1422
1422
|
edc_lab_results/model_mixin_factories/__init__.py,sha256=zueOK5TVko7qbmQN5xsI7D-WjIp8mx5LLI9M2ZdbC-g,408
|
|
1423
1423
|
edc_lab_results/model_mixin_factories/field_attrs.py,sha256=mc2mXMc3Jr9cDu8_4bS13eAMfAdIuzKfeuI_hdWgMlQ,2696
|
|
1424
1424
|
edc_lab_results/model_mixin_factories/reportable_result_model_mixin_factory.py,sha256=09SmRdI1ak1MfwH3aVHIBrV7qp3Lik2rXdj00nTFfUg,1095
|
|
@@ -1526,7 +1526,7 @@ edc_listboard/urls.py,sha256=0Pd5FbB3DBCwEtzZxLwDmvNx_yxTYbcHRPGNex8lrS4,174
|
|
|
1526
1526
|
edc_listboard/view_mixins/__init__.py,sha256=aDbmIyoHkifzkK5DAfMJ-365NCnNGs-OcwkPW8M6mNw,241
|
|
1527
1527
|
edc_listboard/view_mixins/listboard_filter_view_mixin.py,sha256=pApr0k8a6FKzJXTjpejO1QluV-7_p6CT_QWqEdLnPbE,2811
|
|
1528
1528
|
edc_listboard/view_mixins/querystring_view_mixin.py,sha256=T4O3i6-A2-jZdK863fjf0NFWv2mCXICb6YMQ2qJTfSk,938
|
|
1529
|
-
edc_listboard/view_mixins/search_form_view_mixin.py,sha256=
|
|
1529
|
+
edc_listboard/view_mixins/search_form_view_mixin.py,sha256=6uitlsgTtUhVCH33a4gxrqBckiCLOt3N8sPY8yGB_jo,1154
|
|
1530
1530
|
edc_listboard/view_mixins/search_listboard_view_mixin.py,sha256=VNbP-p3L2Bkgzyp5exzvE-p4tBl96YF3b_RVpGC4ejg,2952
|
|
1531
1531
|
edc_listboard/views/__init__.py,sha256=IqobRNiACZF8HApUabAHhCUqoGJhzmHxApaTqFxVkPY,127
|
|
1532
1532
|
edc_listboard/views/listboard_view.py,sha256=-3g3umgXCTw0u07TWSptQJpIaUfGfqLxB3WLrzErSus,8360
|
|
@@ -1805,7 +1805,7 @@ edc_model_admin/mixins/inlines/stacked_inline_modeladmin_mixin.py,sha256=mtSEOgR
|
|
|
1805
1805
|
edc_model_admin/mixins/inlines/tabular_inline_mixin.py,sha256=GY83Zm0NzdAAz7aDNKZLmwHc6pujEbl6G4BaUi26P4U,529
|
|
1806
1806
|
edc_model_admin/mixins/model_admin_bypass_default_form_cls_mixin.py,sha256=VNjKt87aoOrEYEuGDHhIJRAlSiitxp6TH4MQEs-ex4s,5914
|
|
1807
1807
|
edc_model_admin/mixins/model_admin_form_auto_number_mixin.py,sha256=zDMoUAi2FQHygPjbXqnQuTQkBQ3Vgo3lATbrV7Bdgc4,1499
|
|
1808
|
-
edc_model_admin/mixins/model_admin_form_instructions_mixin.py,sha256=
|
|
1808
|
+
edc_model_admin/mixins/model_admin_form_instructions_mixin.py,sha256=KDMbIvmtRQbf7jczmyMTS68A9Gkosjj3IqnKSq9WUhI,2889
|
|
1809
1809
|
edc_model_admin/mixins/model_admin_hide_delete_button_on_condition.py,sha256=KHZjtAsL_gvBvmj46ZFvz-loRnEofOD256g-iercoxo,884
|
|
1810
1810
|
edc_model_admin/mixins/model_admin_institution_mixin.py,sha256=iqWFcHqZVyTICbfL_SLXRTC1NI97lfcLonK_hV_eQdM,150
|
|
1811
1811
|
edc_model_admin/mixins/model_admin_limit_to_selected_foreignkey.py,sha256=I2BnfcJfNa-UosdLObFMcD451-lmwbzwcZ_iAdjvA3s,1245
|
|
@@ -3119,7 +3119,7 @@ edc_visit_schedule/admin/visit_schedule_admin.py,sha256=3E3QF4cNtiS9dgf4b9A911ZA
|
|
|
3119
3119
|
edc_visit_schedule/admin_site.py,sha256=y_1rTzAGI1aG9W8LK_s77axiXRwffdqwVltP09XwAhs,187
|
|
3120
3120
|
edc_visit_schedule/apps.py,sha256=mibLBV26LU352_ELqBNnafobUyRKhq2UFFRV_rabTyA,317
|
|
3121
3121
|
edc_visit_schedule/auths.py,sha256=F498GY111anDlK2g4-5_q7qF5MEcJ8bE5OCnimQZhSU,233
|
|
3122
|
-
edc_visit_schedule/baseline.py,sha256=
|
|
3122
|
+
edc_visit_schedule/baseline.py,sha256=ZXwB8xdT1xFtoyFZ9k_anGUq1mJC6fTOOBu4vu5L8iY,3346
|
|
3123
3123
|
edc_visit_schedule/choices.py,sha256=feq0GogDxFn_MyrnNcynlWeq5QTrXeJOEHA23cRiccQ,304
|
|
3124
3124
|
edc_visit_schedule/constants.py,sha256=4vqZ7MI-4bu9wQ7n7R5hzX2uVwO0ZJZ34uUA3W8yypY,714
|
|
3125
3125
|
edc_visit_schedule/exceptions.py,sha256=ELecFg9kl435cLw354tL90ifQBLAXVclWuhKlqSj5k8,947
|
|
@@ -3179,7 +3179,7 @@ edc_visit_schedule/schedule/schedule.py,sha256=daKuzIx0sa9c4RjedXQtFFzx79bqdEvAJ
|
|
|
3179
3179
|
edc_visit_schedule/schedule/visit_collection.py,sha256=9OJKrLtx-H0eL9oo0dsRqYGPICiAMqEF4xIhhlinx0A,2033
|
|
3180
3180
|
edc_visit_schedule/schedule/window.py,sha256=P_hNhAXBmRxED_0UrlpQoYHF5r_nTEpTK7U2JSAnpCs,5222
|
|
3181
3181
|
edc_visit_schedule/simple_model_validator.py,sha256=pE2QCSI1VhSr9Dd-NGd-72u6e479_vzbS8iL17eg4Yc,771
|
|
3182
|
-
edc_visit_schedule/site_visit_schedules.py,sha256=
|
|
3182
|
+
edc_visit_schedule/site_visit_schedules.py,sha256=nFeazdSDJNSvv0RWl9uTs9ZBiC7kFrGKiZY2w-HRfMY,13207
|
|
3183
3183
|
edc_visit_schedule/subject_schedule.py,sha256=WCLzZquQ0bI3O_eMoHAg94adQ07rYt812gNuiemLZwU,16520
|
|
3184
3184
|
edc_visit_schedule/system_checks.py,sha256=mh38YzVxisDe-aY6liqsu1w9F9yhJkWghKpV04JLH4U,9255
|
|
3185
3185
|
edc_visit_schedule/templates/edc_visit_schedule/home.html,sha256=RqtGsq6-zXi43jU0iSv24-xsIXXHkuiHjJvF-yqDyjA,1112
|
|
@@ -3293,7 +3293,7 @@ edc_vitals/models/fields/waist_circumference.py,sha256=fZcHFDdEwWLjIVLktKrFCD9UU
|
|
|
3293
3293
|
edc_vitals/models/fields/weight.py,sha256=zo9_9e3Cpu0UqoRbWS-iDkcDo6fK80b1dDQy4x4MyxE,921
|
|
3294
3294
|
edc_vitals/utils.py,sha256=vXid44KUXxeaSyund_y5MNXc5DFJs052_PwUAjszE2k,1384
|
|
3295
3295
|
edc_vitals/validators.py,sha256=vNiElWMs0rRnHRNuVoPLRf0rW_C_0xcfUyep1Y_Si5s,156
|
|
3296
|
-
clinicedc-2.0.
|
|
3297
|
-
clinicedc-2.0.
|
|
3298
|
-
clinicedc-2.0.
|
|
3299
|
-
clinicedc-2.0.
|
|
3296
|
+
clinicedc-2.0.11.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
|
3297
|
+
clinicedc-2.0.11.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
|
|
3298
|
+
clinicedc-2.0.11.dist-info/METADATA,sha256=fYAbtZ8ZcUaWG3yebbSu73i13Z7EtPyYDGf3k-nStZc,15892
|
|
3299
|
+
clinicedc-2.0.11.dist-info/RECORD,,
|
|
@@ -74,13 +74,6 @@ class ConsentModelFormValidationMixin:
|
|
|
74
74
|
raise forms.ValidationError(str(e))
|
|
75
75
|
return None
|
|
76
76
|
|
|
77
|
-
# def validate_last_allowed_visitcode(self, visit_code: str) -> str:
|
|
78
|
-
# name = "last_allowed_visitcode"
|
|
79
|
-
# value = self.cleaned_data.get(name, getattr(self.instance, name, None))
|
|
80
|
-
# if value and int(visit_code) > int(value):
|
|
81
|
-
# raise forms.ValidationError({"__all__": msg})
|
|
82
|
-
# return value
|
|
83
|
-
|
|
84
77
|
def validate_min_age(self) -> None:
|
|
85
78
|
"""Raises if age is below the age of consent"""
|
|
86
79
|
if self.age_delta:
|
edc_consent/models/signals.py
CHANGED
|
@@ -60,16 +60,19 @@ def requires_consent_on_pre_save(instance, raw, using, update_fields, **kwargs):
|
|
|
60
60
|
def update_appointment_from_consentext_post_save(
|
|
61
61
|
sender, instance, raw, created, using, **kwargs
|
|
62
62
|
):
|
|
63
|
-
if
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
63
|
+
if (
|
|
64
|
+
not raw
|
|
65
|
+
and not kwargs.get("update_fields")
|
|
66
|
+
and isinstance(instance, (ConsentExtensionModelMixin,))
|
|
67
|
+
):
|
|
68
|
+
cdef = site_consents.get_consent_definition(
|
|
69
|
+
model=instance.subject_consent._meta.label_lower,
|
|
70
|
+
version=instance.subject_consent.version,
|
|
71
|
+
)
|
|
72
|
+
visit_schedule, schedule = site_visit_schedules.get_by_consent_definition(cdef)
|
|
73
|
+
subject_schedule = SubjectSchedule(
|
|
74
|
+
instance.subject_consent.subject_identifier,
|
|
75
|
+
visit_schedule=visit_schedule,
|
|
76
|
+
schedule=schedule,
|
|
77
|
+
)
|
|
78
|
+
subject_schedule.refresh_appointments()
|
edc_consent/site_consents.py
CHANGED
|
@@ -182,7 +182,7 @@ class SiteConsents:
|
|
|
182
182
|
|
|
183
183
|
def get_consent_definition(
|
|
184
184
|
self,
|
|
185
|
-
model: str = None,
|
|
185
|
+
model: str | None = None,
|
|
186
186
|
report_datetime: datetime | None = None,
|
|
187
187
|
version: str | None = None,
|
|
188
188
|
site: SingleSite | None = None,
|
|
@@ -222,7 +222,7 @@ class SiteConsents:
|
|
|
222
222
|
|
|
223
223
|
def get_consent_definitions(
|
|
224
224
|
self,
|
|
225
|
-
model: str = None,
|
|
225
|
+
model: str | None = None,
|
|
226
226
|
report_datetime: datetime | None = None,
|
|
227
227
|
version: str | None = None,
|
|
228
228
|
site: SingleSite | None = None,
|
|
@@ -268,10 +268,11 @@ class SiteConsents:
|
|
|
268
268
|
def _filter_cdefs_by_model_or_raise(
|
|
269
269
|
model: str | None,
|
|
270
270
|
consent_definitions: list[ConsentDefinition],
|
|
271
|
-
errror_messages: list[str] = None,
|
|
271
|
+
errror_messages: list[str] | None = None,
|
|
272
272
|
attrname: str | None = None,
|
|
273
273
|
) -> tuple[list[ConsentDefinition], list[str]]:
|
|
274
274
|
attrname = attrname or "model"
|
|
275
|
+
errror_messages = errror_messages or []
|
|
275
276
|
cdefs = consent_definitions
|
|
276
277
|
if model:
|
|
277
278
|
cdefs = [
|
|
@@ -291,17 +292,17 @@ class SiteConsents:
|
|
|
291
292
|
def _filter_cdefs_by_screening_model_or_raise(
|
|
292
293
|
model: str | None,
|
|
293
294
|
consent_definitions: list[ConsentDefinition],
|
|
294
|
-
errror_messages: list[str] = None,
|
|
295
|
+
errror_messages: list[str] | None = None,
|
|
295
296
|
) -> tuple[list[ConsentDefinition], list[str]]:
|
|
297
|
+
errror_messages = errror_messages or []
|
|
296
298
|
cdefs = consent_definitions
|
|
297
299
|
if model:
|
|
298
300
|
cdefs = []
|
|
299
301
|
for cdef in consent_definitions:
|
|
300
302
|
if isinstance(cdef.screening_model, list):
|
|
301
303
|
for screening_model in cdef.screening_model:
|
|
302
|
-
if model == screening_model:
|
|
303
|
-
|
|
304
|
-
cdefs.append(cdef)
|
|
304
|
+
if model == screening_model and cdef not in cdefs:
|
|
305
|
+
cdefs.append(cdef)
|
|
305
306
|
elif model == cdef.screening_model:
|
|
306
307
|
cdefs.append(cdef)
|
|
307
308
|
if not cdefs:
|
|
@@ -311,12 +312,13 @@ class SiteConsents:
|
|
|
311
312
|
errror_messages.append(f"model={model}")
|
|
312
313
|
return cdefs, errror_messages
|
|
313
314
|
|
|
315
|
+
@staticmethod
|
|
314
316
|
def _filter_cdefs_by_report_datetime_or_raise(
|
|
315
|
-
self,
|
|
316
317
|
report_datetime: datetime | None,
|
|
317
318
|
consent_definitions: list[ConsentDefinition],
|
|
318
|
-
errror_messages: list[str] = None,
|
|
319
|
+
errror_messages: list[str] | None = None,
|
|
319
320
|
) -> tuple[list[ConsentDefinition], list[str]]:
|
|
321
|
+
errror_messages = errror_messages or []
|
|
320
322
|
cdefs = deepcopy(consent_definitions)
|
|
321
323
|
if report_datetime:
|
|
322
324
|
cdefs = [
|
|
@@ -340,8 +342,9 @@ class SiteConsents:
|
|
|
340
342
|
self,
|
|
341
343
|
version: str | None,
|
|
342
344
|
consent_definitions: list[ConsentDefinition],
|
|
343
|
-
errror_messages: list[str] = None,
|
|
345
|
+
errror_messages: list[str] | None = None,
|
|
344
346
|
) -> tuple[list[ConsentDefinition], list[str]]:
|
|
347
|
+
errror_messages = errror_messages or []
|
|
345
348
|
cdefs = consent_definitions
|
|
346
349
|
if version:
|
|
347
350
|
cdefs = [cdef for cdef in cdefs if cdef.version == version]
|
|
@@ -359,8 +362,9 @@ class SiteConsents:
|
|
|
359
362
|
self,
|
|
360
363
|
site: SingleSite | None,
|
|
361
364
|
consent_definitions: list[ConsentDefinition],
|
|
362
|
-
errror_messages: list[str] = None,
|
|
365
|
+
errror_messages: list[str] | None = None,
|
|
363
366
|
) -> list[ConsentDefinition]:
|
|
367
|
+
errror_messages = errror_messages or []
|
|
364
368
|
cdefs = consent_definitions
|
|
365
369
|
if site:
|
|
366
370
|
cdefs_copy = [cdef for cdef in consent_definitions]
|
|
@@ -35,10 +35,11 @@ class EgfrCkdEpiFormValidatorMixin:
|
|
|
35
35
|
class EgfrCockcroftGaultFormValidatorMixin:
|
|
36
36
|
def validate_egfr(
|
|
37
37
|
self,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
*,
|
|
39
|
+
gender: str,
|
|
40
|
+
age_in_years: int,
|
|
41
|
+
weight_in_kgs: float,
|
|
42
|
+
ethnicity: str,
|
|
42
43
|
):
|
|
43
44
|
opts = dict(
|
|
44
45
|
gender=gender,
|
|
@@ -117,8 +117,7 @@ class ApplicableFieldValidator(BaseFormValidator):
|
|
|
117
117
|
field_applicable_value = self.get(field_applicable)
|
|
118
118
|
|
|
119
119
|
if field_value in responses and (
|
|
120
|
-
field_applicable_value in (NULL_STRING, not_applicable)
|
|
121
|
-
or field_applicable_value is None
|
|
120
|
+
field_applicable_value in (None, NULL_STRING, not_applicable)
|
|
122
121
|
):
|
|
123
122
|
self.raise_applicable(field_applicable, msg=msg, applicable_msg=applicable_msg)
|
|
124
123
|
elif (
|
|
@@ -3,7 +3,7 @@ from copy import copy
|
|
|
3
3
|
from django.db.models import QuerySet
|
|
4
4
|
from django.utils.translation import gettext_lazy as _
|
|
5
5
|
|
|
6
|
-
from edc_constants.constants import DWTA, NOT_APPLICABLE
|
|
6
|
+
from edc_constants.constants import DWTA, NOT_APPLICABLE, NULL_STRING
|
|
7
7
|
|
|
8
8
|
from .base_form_validator import (
|
|
9
9
|
NOT_REQUIRED_ERROR,
|
|
@@ -128,16 +128,13 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
128
128
|
raise InvalidModelFormFieldValidator(errmsg)
|
|
129
129
|
if self.cleaned_data and field_required in self.cleaned_data:
|
|
130
130
|
if condition and (
|
|
131
|
-
self.cleaned_data.get(field_required)
|
|
132
|
-
or self.cleaned_data.get(field_required) == ""
|
|
133
|
-
or self.cleaned_data.get(field_required) == NOT_APPLICABLE
|
|
131
|
+
self.cleaned_data.get(field_required) in [None, NULL_STRING, NOT_APPLICABLE]
|
|
134
132
|
):
|
|
135
133
|
self.raise_required(field=field_required, msg=required_msg)
|
|
136
134
|
elif inverse and (
|
|
137
135
|
not condition
|
|
138
|
-
and self.cleaned_data.get(field_required)
|
|
139
|
-
|
|
140
|
-
and self.cleaned_data.get(field_required) != NOT_APPLICABLE
|
|
136
|
+
and self.cleaned_data.get(field_required)
|
|
137
|
+
not in [None, NULL_STRING, NOT_APPLICABLE]
|
|
141
138
|
):
|
|
142
139
|
self.raise_not_required(field=field_required, msg=not_required_msg)
|
|
143
140
|
return False
|
|
@@ -198,14 +195,17 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
198
195
|
field_value = self.cleaned_data.get(field)
|
|
199
196
|
|
|
200
197
|
if field_required_evaluate_as_int:
|
|
201
|
-
field_required_has_value = self.cleaned_data.get(field_required)
|
|
198
|
+
field_required_has_value = self.cleaned_data.get(field_required) not in [
|
|
199
|
+
None,
|
|
200
|
+
NULL_STRING,
|
|
201
|
+
]
|
|
202
202
|
else:
|
|
203
203
|
field_required_has_value = bool(self.cleaned_data.get(field_required))
|
|
204
204
|
|
|
205
|
-
if field_value
|
|
205
|
+
if field_value not in [None, NULL_STRING] and not field_required_has_value:
|
|
206
206
|
self.raise_required(field=field_required, msg=required_msg)
|
|
207
207
|
elif (
|
|
208
|
-
field_value
|
|
208
|
+
field_value in [None, NULL_STRING]
|
|
209
209
|
and field_required_has_value
|
|
210
210
|
and self.cleaned_data.get(field_required) != NOT_APPLICABLE
|
|
211
211
|
and inverse
|
|
@@ -270,13 +270,12 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
270
270
|
"""Required `b` if `a`; do not require `b` if not `a`"""
|
|
271
271
|
if is_instance_field:
|
|
272
272
|
self.update_cleaned_data_from_instance(field)
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
):
|
|
273
|
+
if self.cleaned_data.get(field) is not None and self.cleaned_data.get(
|
|
274
|
+
field_required
|
|
275
|
+
) in [None, NULL_STRING]:
|
|
277
276
|
self.raise_required(field=field_required, msg=required_msg)
|
|
278
277
|
elif (
|
|
279
|
-
self.cleaned_data.get(field)
|
|
278
|
+
self.cleaned_data.get(field) in [None, NULL_STRING]
|
|
280
279
|
and self.cleaned_data.get(field_required) is not None
|
|
281
280
|
):
|
|
282
281
|
self.raise_not_required(field=field_required, msg=required_msg)
|
edc_glucose/utils.py
CHANGED
|
@@ -10,15 +10,15 @@ from .constants import GLUCOSE_HIGH_READING
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def validate_glucose_as_millimoles_per_liter(
|
|
13
|
-
prefix: str
|
|
13
|
+
prefix: str, cleaned_data: dict
|
|
14
14
|
) -> None | Decimal:
|
|
15
15
|
converted_value = None
|
|
16
16
|
min_val = Decimal("0.00")
|
|
17
17
|
max_val = Decimal("30.00")
|
|
18
18
|
high_value = Decimal(f"{GLUCOSE_HIGH_READING}")
|
|
19
|
-
value
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
if (value := cleaned_data.get(f"{prefix}_value")) and (
|
|
20
|
+
units := cleaned_data.get(f"{prefix}_units")
|
|
21
|
+
):
|
|
22
22
|
try:
|
|
23
23
|
converted_value = convert_units(
|
|
24
24
|
label=prefix,
|
|
@@ -27,7 +27,7 @@ def validate_glucose_as_millimoles_per_liter(
|
|
|
27
27
|
units_to=MILLIMOLES_PER_LITER,
|
|
28
28
|
)
|
|
29
29
|
except ConversionNotHandled as e:
|
|
30
|
-
raise forms.ValidationError({f"{prefix}_units": str(e)})
|
|
30
|
+
raise forms.ValidationError({f"{prefix}_units": str(e)}) from e
|
|
31
31
|
if (
|
|
32
32
|
not (min_val <= round_half_away_from_zero(converted_value, 2) <= max_val)
|
|
33
33
|
and round_half_away_from_zero(converted_value, 2) != high_value
|
edc_lab/lab/requisition_panel.py
CHANGED
|
@@ -14,14 +14,14 @@ class RequisitionPanelLookupError(Exception):
|
|
|
14
14
|
pass
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class InvalidProcessingProfile(Exception):
|
|
17
|
+
class InvalidProcessingProfile(Exception): # noqa: N818
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class PanelAttrs:
|
|
22
22
|
""" "A simple class of panel name attributes."""
|
|
23
23
|
|
|
24
|
-
def __init__(self, name: str
|
|
24
|
+
def __init__(self, name: str, alpha_code: str | None = None) -> None:
|
|
25
25
|
title = " ".join(name.split("_")).title()
|
|
26
26
|
alpha_code = alpha_code or ""
|
|
27
27
|
self.abbreviation = f"{name[0:2]}{name[-1:]}".upper()
|
|
@@ -38,13 +38,13 @@ class RequisitionPanel:
|
|
|
38
38
|
|
|
39
39
|
def __init__(
|
|
40
40
|
self,
|
|
41
|
-
name: str = None,
|
|
42
|
-
processing_profile: ProcessingProfile = None,
|
|
43
|
-
verbose_name: str = None,
|
|
44
|
-
abbreviation: str = None,
|
|
45
|
-
utest_ids:
|
|
46
|
-
is_poc=None,
|
|
47
|
-
reference_range_collection_name=None,
|
|
41
|
+
name: str | None = None,
|
|
42
|
+
processing_profile: ProcessingProfile | None = None,
|
|
43
|
+
verbose_name: str | None = None,
|
|
44
|
+
abbreviation: str | None = None,
|
|
45
|
+
utest_ids: tuple[str | tuple[str, ...], ...] | None = None,
|
|
46
|
+
is_poc: bool | str | None = None,
|
|
47
|
+
reference_range_collection_name: str | None = None,
|
|
48
48
|
) -> None:
|
|
49
49
|
self._panel_model_obj = None
|
|
50
50
|
self.name = name
|
|
@@ -95,7 +95,7 @@ class RequisitionPanel:
|
|
|
95
95
|
f"'{self.requisition_model}'. "
|
|
96
96
|
f"See {self!r} or the lab profile {self.lab_profile_name}."
|
|
97
97
|
f"Got {e}"
|
|
98
|
-
)
|
|
98
|
+
) from e
|
|
99
99
|
return requisition_model_cls
|
|
100
100
|
|
|
101
101
|
@property
|
edc_lab_panel/panels.py
CHANGED
|
@@ -40,7 +40,7 @@ hba1c_panel = RequisitionPanel(
|
|
|
40
40
|
verbose_name="HbA1c (Venous)",
|
|
41
41
|
processing_profile=hba1c_processing,
|
|
42
42
|
abbreviation="HBA1C",
|
|
43
|
-
utest_ids=
|
|
43
|
+
utest_ids=(("hba1c", "HbA1c"),),
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
hba1c_poc_panel = RequisitionPanel(
|
|
@@ -48,7 +48,7 @@ hba1c_poc_panel = RequisitionPanel(
|
|
|
48
48
|
verbose_name="HbA1c (POC)",
|
|
49
49
|
abbreviation="HBA1C_POC",
|
|
50
50
|
processing_profile=poc_processing,
|
|
51
|
-
utest_ids=
|
|
51
|
+
utest_ids=(("hba1c", "HbA1c"),),
|
|
52
52
|
)
|
|
53
53
|
|
|
54
54
|
|
|
@@ -57,7 +57,7 @@ fbc_panel = RequisitionPanel(
|
|
|
57
57
|
verbose_name="Full Blood Count",
|
|
58
58
|
processing_profile=fbc_processing,
|
|
59
59
|
abbreviation="FBC",
|
|
60
|
-
utest_ids=
|
|
60
|
+
utest_ids=(
|
|
61
61
|
("haemoglobin", "Haemoglobin"),
|
|
62
62
|
"hct",
|
|
63
63
|
"rbc",
|
|
@@ -66,7 +66,7 @@ fbc_panel = RequisitionPanel(
|
|
|
66
66
|
"mcv",
|
|
67
67
|
"mch",
|
|
68
68
|
"mchc",
|
|
69
|
-
|
|
69
|
+
),
|
|
70
70
|
)
|
|
71
71
|
|
|
72
72
|
blood_glucose_panel = RequisitionPanel(
|
|
@@ -74,7 +74,7 @@ blood_glucose_panel = RequisitionPanel(
|
|
|
74
74
|
verbose_name="Blood Glucose (Venous)",
|
|
75
75
|
abbreviation="BGL",
|
|
76
76
|
processing_profile=blood_glucose_processing,
|
|
77
|
-
utest_ids=
|
|
77
|
+
utest_ids=(("glucose", "Glucose"),),
|
|
78
78
|
)
|
|
79
79
|
|
|
80
80
|
blood_glucose_poc_panel = RequisitionPanel(
|
|
@@ -82,7 +82,7 @@ blood_glucose_poc_panel = RequisitionPanel(
|
|
|
82
82
|
verbose_name="Blood Glucose (POC)",
|
|
83
83
|
abbreviation="BGL-POC",
|
|
84
84
|
processing_profile=poc_processing,
|
|
85
|
-
utest_ids=
|
|
85
|
+
utest_ids=(("glucose", "Glucose"),),
|
|
86
86
|
)
|
|
87
87
|
|
|
88
88
|
cd4_panel = RequisitionPanel(
|
|
@@ -90,14 +90,14 @@ cd4_panel = RequisitionPanel(
|
|
|
90
90
|
verbose_name="CD4",
|
|
91
91
|
abbreviation="CD4",
|
|
92
92
|
processing_profile=cd4_processing,
|
|
93
|
-
utest_ids=
|
|
93
|
+
utest_ids=("cd4",),
|
|
94
94
|
)
|
|
95
95
|
vl_panel = RequisitionPanel(
|
|
96
96
|
name=VL,
|
|
97
97
|
verbose_name="Viral Load",
|
|
98
98
|
abbreviation="VL",
|
|
99
99
|
processing_profile=vl_processing,
|
|
100
|
-
utest_ids=
|
|
100
|
+
utest_ids=("vl",),
|
|
101
101
|
)
|
|
102
102
|
|
|
103
103
|
|
|
@@ -106,7 +106,7 @@ rft_panel = RequisitionPanel(
|
|
|
106
106
|
verbose_name="Chemistry: Renal Function Tests",
|
|
107
107
|
abbreviation=RFT,
|
|
108
108
|
processing_profile=rft_processing,
|
|
109
|
-
utest_ids=
|
|
109
|
+
utest_ids=("urea", "creatinine", "uric_acid", "egfr", "egfr_drop"),
|
|
110
110
|
)
|
|
111
111
|
|
|
112
112
|
lipids_panel = RequisitionPanel(
|
|
@@ -114,7 +114,7 @@ lipids_panel = RequisitionPanel(
|
|
|
114
114
|
verbose_name="Chemistry: Lipids",
|
|
115
115
|
abbreviation=LIPIDS,
|
|
116
116
|
processing_profile=lipids_processing,
|
|
117
|
-
utest_ids=
|
|
117
|
+
utest_ids=(LDL, HDL, TRIG, CHOL),
|
|
118
118
|
)
|
|
119
119
|
|
|
120
120
|
lft_panel = RequisitionPanel(
|
|
@@ -122,7 +122,7 @@ lft_panel = RequisitionPanel(
|
|
|
122
122
|
verbose_name="Chemistry: Liver Function Tests",
|
|
123
123
|
abbreviation=LFT,
|
|
124
124
|
processing_profile=lft_processing,
|
|
125
|
-
utest_ids=
|
|
125
|
+
utest_ids=("ast", "alt", "alp", "amylase", "ggt", "albumin"),
|
|
126
126
|
)
|
|
127
127
|
|
|
128
128
|
insulin_panel = RequisitionPanel(
|
|
@@ -130,7 +130,7 @@ insulin_panel = RequisitionPanel(
|
|
|
130
130
|
verbose_name="Insulin",
|
|
131
131
|
abbreviation="INS",
|
|
132
132
|
processing_profile=insulin_processing,
|
|
133
|
-
utest_ids=
|
|
133
|
+
utest_ids=("ins",),
|
|
134
134
|
)
|
|
135
135
|
|
|
136
136
|
sputum_panel = RequisitionPanel(
|
|
@@ -138,5 +138,5 @@ sputum_panel = RequisitionPanel(
|
|
|
138
138
|
verbose_name="Sputum",
|
|
139
139
|
abbreviation="SPM",
|
|
140
140
|
processing_profile=sputum_processing,
|
|
141
|
-
utest_ids=
|
|
141
|
+
utest_ids=(),
|
|
142
142
|
)
|
|
@@ -7,18 +7,13 @@ from edc_glucose.utils import validate_glucose_as_millimoles_per_liter
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class BloodResultsFbgFormValidatorMixin:
|
|
10
|
-
|
|
11
10
|
@property
|
|
12
11
|
def reportables_evaluator_options(self: Any):
|
|
13
12
|
if not self.cleaned_data.get("fasting"):
|
|
14
13
|
raise forms.ValidationError({"fasting": "This field is required."})
|
|
15
|
-
fasting = (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
(self.cleaned_data.get("fasting") == FASTING)
|
|
19
|
-
or (self.cleaned_data.get("fasting") == YES)
|
|
20
|
-
)
|
|
21
|
-
else False
|
|
14
|
+
fasting = bool(
|
|
15
|
+
self.cleaned_data.get("fasting") == FASTING
|
|
16
|
+
or self.cleaned_data.get("fasting") == YES
|
|
22
17
|
)
|
|
23
18
|
return dict(fasting=fasting)
|
|
24
19
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
from collections import namedtuple
|
|
2
3
|
from typing import Any
|
|
3
4
|
|
|
4
|
-
from edc_constants.constants import NO, YES
|
|
5
|
+
from edc_constants.constants import NO, NULL_STRING, YES
|
|
6
|
+
from edc_lab import RequisitionPanel
|
|
5
7
|
from edc_lab.form_validators import CrfRequisitionFormValidatorMixin
|
|
6
8
|
from edc_reportable.forms import ReportablesFormValidatorMixin
|
|
7
9
|
|
|
@@ -15,25 +17,26 @@ class BloodResultsFormValidatorMixin(
|
|
|
15
17
|
CrfRequisitionFormValidatorMixin,
|
|
16
18
|
):
|
|
17
19
|
value_field_suffix = "_value"
|
|
18
|
-
panel = None
|
|
19
|
-
panels =
|
|
20
|
+
panel: RequisitionPanel = None
|
|
21
|
+
panels: tuple[RequisitionPanel, ...] = ()
|
|
20
22
|
is_poc_field: str = "is_poc"
|
|
21
|
-
# egfr_percent_drop_threshold: float = 20.0000
|
|
22
|
-
# egfr_value_threshold: float = 45.0000
|
|
23
|
-
# egfr_formula: str = "ckd-epi"
|
|
24
|
-
# reference_range_collection_name: str = None
|
|
25
23
|
|
|
26
24
|
def evaluate_value(self, **kwargs):
|
|
27
25
|
"""A hook to evaluate a field value"""
|
|
28
26
|
pass
|
|
29
27
|
|
|
30
28
|
def clean(self: Any) -> None:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
if self.cleaned_data.get(self.is_poc_field) not in [None, NULL_STRING]:
|
|
30
|
+
# do not require requisition if poc == YES
|
|
31
|
+
self.required_if(NO, field=self.is_poc_field, field_required="requisition")
|
|
32
|
+
else:
|
|
33
|
+
# requires requisition if any `value` fields have value but inverse not True.
|
|
34
|
+
# It is OK to submit the requisition without any `value` fields data.
|
|
35
|
+
self.required_if_true(
|
|
36
|
+
any(self.fields_names_with_values),
|
|
37
|
+
field_required=self.requisition_field,
|
|
38
|
+
inverse=False,
|
|
39
|
+
)
|
|
37
40
|
|
|
38
41
|
if self.requisition:
|
|
39
42
|
for fields_name in self.fields_names_with_values:
|
|
@@ -43,19 +46,19 @@ class BloodResultsFormValidatorMixin(
|
|
|
43
46
|
utest_id = fields_name
|
|
44
47
|
if f"{utest_id}_units" in self.cleaned_data:
|
|
45
48
|
self.required_if_not_none(
|
|
46
|
-
field=f"{utest_id}{self.value_field_suffix or
|
|
49
|
+
field=f"{utest_id}{self.value_field_suffix or NULL_STRING}",
|
|
47
50
|
field_required=f"{utest_id}_units",
|
|
48
51
|
field_required_evaluate_as_int=True,
|
|
49
52
|
)
|
|
50
53
|
if f"{utest_id}_abnormal" in self.cleaned_data:
|
|
51
54
|
self.required_if_not_none(
|
|
52
|
-
field=f"{utest_id}{self.value_field_suffix or
|
|
55
|
+
field=f"{utest_id}{self.value_field_suffix or NULL_STRING}",
|
|
53
56
|
field_required=f"{utest_id}_abnormal",
|
|
54
57
|
field_required_evaluate_as_int=False,
|
|
55
58
|
)
|
|
56
59
|
if f"{utest_id}_reportable" in self.cleaned_data:
|
|
57
60
|
self.required_if_not_none(
|
|
58
|
-
field=f"{utest_id}{self.value_field_suffix or
|
|
61
|
+
field=f"{utest_id}{self.value_field_suffix or NULL_STRING}",
|
|
59
62
|
field_required=f"{utest_id}_reportable",
|
|
60
63
|
field_required_evaluate_as_int=False,
|
|
61
64
|
)
|
|
@@ -80,34 +83,34 @@ class BloodResultsFormValidatorMixin(
|
|
|
80
83
|
|
|
81
84
|
@property
|
|
82
85
|
def is_poc(self: Any) -> bool:
|
|
83
|
-
if self.cleaned_data.get(self.is_poc_field):
|
|
86
|
+
if self.is_poc_field and self.cleaned_data.get(self.is_poc_field):
|
|
84
87
|
return self.cleaned_data.get(self.is_poc_field) == YES
|
|
85
88
|
return False
|
|
86
89
|
|
|
87
90
|
@property
|
|
88
|
-
def fields_names_with_values(self: Any) ->
|
|
89
|
-
"""Returns a list result `value`
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
def fields_names_with_values(self: Any) -> tuple[str, ...]:
|
|
92
|
+
"""Returns a list result `value` field names that are not None."""
|
|
93
|
+
field_names = (f"{utest_id}{self.value_field_suffix}" for utest_id in self.utest_ids)
|
|
94
|
+
return tuple(
|
|
95
|
+
[
|
|
96
|
+
field_name
|
|
97
|
+
for field_name in field_names
|
|
98
|
+
if self.cleaned_data.get(field_name) not in [None, NULL_STRING]
|
|
99
|
+
]
|
|
100
|
+
)
|
|
96
101
|
|
|
97
102
|
@property
|
|
98
|
-
def utest_ids(self: Any) ->
|
|
103
|
+
def utest_ids(self: Any) -> tuple:
|
|
99
104
|
utest_ids = []
|
|
100
105
|
for panel in self.panel_list:
|
|
101
106
|
for utest_id in panel.utest_ids:
|
|
102
|
-
|
|
107
|
+
with contextlib.suppress(ValueError):
|
|
103
108
|
utest_id, _ = utest_id
|
|
104
|
-
except ValueError:
|
|
105
|
-
pass
|
|
106
109
|
utest_ids.append(utest_id)
|
|
107
|
-
return utest_ids
|
|
110
|
+
return tuple(utest_ids)
|
|
108
111
|
|
|
109
112
|
@property
|
|
110
|
-
def panel_list(self: Any) ->
|
|
113
|
+
def panel_list(self: Any) -> tuple[RequisitionPanel]:
|
|
111
114
|
if self.panel:
|
|
112
|
-
return
|
|
115
|
+
return (self.panel,)
|
|
113
116
|
return self.panels
|
edc_lab_results/get_summary.py
CHANGED
|
@@ -18,9 +18,9 @@ def get_summary(obj) -> tuple[list[str], list[str], list[str]]:
|
|
|
18
18
|
except ValueError:
|
|
19
19
|
utest_id = field_name
|
|
20
20
|
reference_range_collection = get_reference_range_collection(obj)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
units = getattr(obj, f"{utest_id}_units", None)
|
|
22
|
+
value = getattr(obj, field_name)
|
|
23
|
+
if units and value:
|
|
24
24
|
opts.update(units=units, label=utest_id)
|
|
25
25
|
try:
|
|
26
26
|
grading_data, grading_eval_phrase = reference_range_collection.get_grade(
|
|
@@ -28,9 +28,8 @@ class SearchFormViewMixin:
|
|
|
28
28
|
)
|
|
29
29
|
except NoReverseMatch as e:
|
|
30
30
|
raise SearchFormViewError(
|
|
31
|
-
f"{e}. Expected one of {url_names.registry}. "
|
|
32
|
-
|
|
33
|
-
)
|
|
31
|
+
f"{e}. Expected one of {url_names.registry}. See attribute 'search_form_url'."
|
|
32
|
+
) from e
|
|
34
33
|
return f"{url}{self.querystring}"
|
|
35
34
|
|
|
36
35
|
@property
|
|
@@ -38,12 +38,12 @@ class ModelAdminFormInstructionsMixin:
|
|
|
38
38
|
"additional questions may be "
|
|
39
39
|
"required or some answers may need to be corrected."
|
|
40
40
|
)
|
|
41
|
-
add_instructions =
|
|
42
|
-
change_instructions =
|
|
41
|
+
add_instructions = ()
|
|
42
|
+
change_instructions = ()
|
|
43
43
|
|
|
44
|
-
additional_instructions =
|
|
44
|
+
additional_instructions = ()
|
|
45
45
|
add_additional_instructions = None
|
|
46
|
-
change_additional_instructions =
|
|
46
|
+
change_additional_instructions = ()
|
|
47
47
|
|
|
48
48
|
def get_add_instructions(
|
|
49
49
|
self, extra_context: dict | None, request: WSGIRequest | None = None
|
edc_visit_schedule/baseline.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
from typing import TYPE_CHECKING, Any
|
|
4
5
|
|
|
5
6
|
from .exceptions import SiteVisitScheduleError, VisitScheduleBaselineError
|
|
@@ -28,10 +29,8 @@ class Baseline:
|
|
|
28
29
|
try:
|
|
29
30
|
instance = instance.appointment
|
|
30
31
|
except AttributeError:
|
|
31
|
-
|
|
32
|
+
with contextlib.suppress(AttributeError):
|
|
32
33
|
instance = instance.subject_visit.appointment
|
|
33
|
-
except AttributeError:
|
|
34
|
-
pass
|
|
35
34
|
self.visit_schedule_name = instance.visit_schedule_name
|
|
36
35
|
self.schedule_name = instance.schedule_name
|
|
37
36
|
self.visit_code_sequence = instance.visit_code_sequence
|
|
@@ -59,7 +58,7 @@ class Baseline:
|
|
|
59
58
|
try:
|
|
60
59
|
visit_schedule = site_visit_schedules.get_visit_schedule(self.visit_schedule_name)
|
|
61
60
|
except SiteVisitScheduleError as e:
|
|
62
|
-
raise VisitScheduleBaselineError(str(e))
|
|
61
|
+
raise VisitScheduleBaselineError(str(e)) from e
|
|
63
62
|
return visit_schedule
|
|
64
63
|
|
|
65
64
|
@property
|
|
@@ -67,7 +66,7 @@ class Baseline:
|
|
|
67
66
|
try:
|
|
68
67
|
schedule = self.visit_schedule.schedules.get(self.schedule_name)
|
|
69
68
|
except SiteVisitScheduleError as e:
|
|
70
|
-
raise VisitScheduleBaselineError(str(e))
|
|
69
|
+
raise VisitScheduleBaselineError(str(e)) from e
|
|
71
70
|
return schedule
|
|
72
71
|
|
|
73
72
|
@property
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import copy
|
|
4
5
|
import sys
|
|
5
6
|
from typing import TYPE_CHECKING
|
|
@@ -68,10 +69,8 @@ class SiteVisitSchedules:
|
|
|
68
69
|
|
|
69
70
|
def get_visit_schedule(self, visit_schedule_name=None) -> VisitSchedule:
|
|
70
71
|
"""Returns a visit schedule instance or raises."""
|
|
71
|
-
|
|
72
|
+
with contextlib.suppress(AttributeError):
|
|
72
73
|
visit_schedule_name = visit_schedule_name.split(".")[0]
|
|
73
|
-
except AttributeError:
|
|
74
|
-
pass
|
|
75
74
|
visit_schedule = self.registry.get(visit_schedule_name)
|
|
76
75
|
if not visit_schedule:
|
|
77
76
|
visit_schedule_names = "', '".join(self.registry.keys())
|
|
@@ -88,10 +87,8 @@ class SiteVisitSchedules:
|
|
|
88
87
|
"""
|
|
89
88
|
visit_schedules = {}
|
|
90
89
|
for visit_schedule_name in visit_schedule_names:
|
|
91
|
-
|
|
92
|
-
visit_schedule_name = visit_schedule_name.split(".")[0]
|
|
93
|
-
except AttributeError:
|
|
94
|
-
pass
|
|
90
|
+
with contextlib.suppress(AttributeError):
|
|
91
|
+
visit_schedule_name = visit_schedule_name.split(".")[0] # noqa: PLW2901
|
|
95
92
|
visit_schedules[visit_schedule_name] = self.get_visit_schedule(visit_schedule_name)
|
|
96
93
|
return visit_schedules or self.registry
|
|
97
94
|
|
|
@@ -105,13 +102,13 @@ class SiteVisitSchedules:
|
|
|
105
102
|
for schedule in visit_schedule.schedules.values():
|
|
106
103
|
try:
|
|
107
104
|
consent_definitions = getattr(schedule, attr)
|
|
108
|
-
except (AttributeError, TypeError):
|
|
105
|
+
except (AttributeError, TypeError) as e:
|
|
109
106
|
raise SiteVisitScheduleError(
|
|
110
107
|
f"Invalid attr for Schedule. See {schedule}. Got `{attr}`."
|
|
111
|
-
)
|
|
108
|
+
) from e
|
|
112
109
|
for _cdef in consent_definitions:
|
|
113
110
|
if _cdef == cdef:
|
|
114
|
-
ret.append([visit_schedule, schedule])
|
|
111
|
+
ret.append([visit_schedule, schedule]) # noqa: PERF401
|
|
115
112
|
if not ret:
|
|
116
113
|
raise SiteVisitScheduleError(
|
|
117
114
|
f"Schedule not found. No schedule exists for {attr}={cdef}."
|
|
File without changes
|