meta-edc 1.0.6__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. meta_ae/action_items.py +10 -2
  2. meta_ae/baker_recipes.py +1 -2
  3. meta_ae/tests/tests/test_actions.py +1 -2
  4. meta_analytics/dataframes/__init__.py +3 -0
  5. meta_analytics/dataframes/constants.py +1 -1
  6. meta_analytics/dataframes/get_eos_df.py +15 -2
  7. meta_analytics/dataframes/get_glucose_df.py +149 -0
  8. meta_analytics/dataframes/get_glucose_fbg_df.py +27 -0
  9. meta_analytics/dataframes/get_glucose_fbg_ogtt_df.py +22 -0
  10. meta_analytics/dataframes/glucose_endpoints/endpoint_by_date.py +106 -120
  11. meta_analytics/dataframes/glucose_endpoints/glucose_endpoints_by_date.py +36 -227
  12. meta_analytics/dataframes/utils.py +18 -4
  13. meta_analytics/notebooks/anu.ipynb +95 -0
  14. meta_analytics/notebooks/appointment_planning.ipynb +329 -0
  15. meta_analytics/notebooks/arvs.ipynb +103 -0
  16. meta_analytics/notebooks/cleaning/consent_v1_ext.ipynb +227 -0
  17. meta_analytics/notebooks/cleaning/offschedule_eos.ipynb +353 -0
  18. meta_analytics/notebooks/dsmc/renal_dysfunction.ipynb +435 -0
  19. meta_analytics/notebooks/endpoints/meta_endpoints_by_date.ipynb +664 -0
  20. meta_analytics/notebooks/followup_examination.ipynb +141 -0
  21. meta_analytics/notebooks/hba1c.ipynb +136 -0
  22. meta_analytics/notebooks/hiv_regimens.ipynb +429 -0
  23. meta_analytics/notebooks/incidence.ipynb +232 -0
  24. meta_analytics/notebooks/liver.ipynb +389 -0
  25. meta_analytics/notebooks/magreth.ipynb +645 -0
  26. meta_analytics/notebooks/monitoring_report.ipynb +1751 -0
  27. meta_analytics/notebooks/pharmacy.ipynb +1070 -0
  28. meta_analytics/notebooks/pharmacy_stock_202410.ipynb +306 -0
  29. meta_analytics/notebooks/steering.ipynb +61 -0
  30. meta_analytics/notebooks/undiagnosed/meta3_screening_consort_chart.ipynb +1176 -0
  31. meta_analytics/notebooks/undiagnosed/meta3_screening_undiagnosed.ipynb +519 -0
  32. meta_analytics/notebooks/undiagnosed/meta_screening_table2.ipynb +964 -0
  33. meta_analytics/notebooks/undiagnosed/screen_undiagnosed_or.ipynb +296 -0
  34. meta_analytics/notebooks/undiagnosed/screening.ipynb +273 -0
  35. meta_analytics/notebooks/undiagnosed/screening2.ipynb +958 -0
  36. meta_analytics/notebooks/undiagnosed/screening_undiagnosed_20241002.ipynb +958 -0
  37. meta_analytics/notebooks/ven.ipynb +191 -0
  38. meta_analytics/notebooks/vitals.ipynb +263 -0
  39. meta_analytics/utils.py +81 -0
  40. meta_edc/settings/debug.py +3 -2
  41. meta_edc/urls.py +1 -0
  42. {meta_edc-1.0.6.dist-info → meta_edc-1.1.0.dist-info}/METADATA +6 -5
  43. {meta_edc-1.0.6.dist-info → meta_edc-1.1.0.dist-info}/RECORD +77 -36
  44. {meta_edc-1.0.6.dist-info → meta_edc-1.1.0.dist-info}/WHEEL +1 -1
  45. meta_edc-1.1.0.dist-info/licenses/AUTHORS.rst +8 -0
  46. meta_labs/reportables.py +14 -11
  47. meta_labs/tests/test_reportables.py +33 -12
  48. meta_pharmacy/notebooks/pharmacy.ipynb +41 -0
  49. meta_prn/migrations/0063_historicaloffstudymedication_singleton_field_and_more.py +37 -0
  50. meta_prn/migrations/0064_auto_20250602_2143.py +18 -0
  51. meta_prn/models/end_of_study.py +2 -0
  52. meta_prn/models/off_study_medication.py +2 -0
  53. meta_reports/migrations/0054_auto_20250422_2003.py +81 -0
  54. meta_reports/migrations/0055_alter_glucosesummary_table.py +17 -0
  55. meta_reports/migrations/0056_auto_20250422_2214.py +54 -0
  56. meta_reports/migrations/0057_auto_20250422_2224.py +54 -0
  57. meta_reports/migrations/0058_auto_20250422_2232.py +54 -0
  58. meta_reports/models/dbviews/glucose_summary/unmanaged_model.py +13 -1
  59. meta_reports/models/dbviews/glucose_summary/view_definition.py +8 -5
  60. meta_screening/eligibility/eligibility_part_three/base_eligibility_part_three.py +59 -47
  61. meta_screening/form_validators/screening_part_three.py +6 -1
  62. meta_screening/tests/meta_test_case_mixin.py +3 -0
  63. meta_screening/tests/tests/test_forms.py +9 -2
  64. meta_screening/tests/tests/test_screening_part_three.py +11 -14
  65. meta_subject/action_items.py +1 -2
  66. meta_subject/choices.py +2 -1
  67. meta_subject/form_validators/glucose_form_validator.py +16 -1
  68. meta_subject/forms/blood_results/blood_results_rft_form.py +60 -3
  69. meta_subject/forms/study_medication_form.py +5 -3
  70. meta_subject/migrations/0221_auto_20250402_1913.py +42 -0
  71. meta_subject/migrations/0222_alter_historicalstudymedication_stock_codes_and_more.py +46 -0
  72. meta_subject/migrations/0223_bloodresultsfbc_errors_bloodresultsgludummy_errors_and_more.py +83 -0
  73. meta_subject/migrations/0224_bloodresultsfbc_abnormal_summary_and_more.py +153 -0
  74. meta_subject/tests/tests/test_egfr.py +5 -5
  75. meta_analytics/dataframes/enrolled/__init__.py +0 -1
  76. meta_analytics/dataframes/enrolled/get_glucose_df.py +0 -122
  77. /meta_edc-1.0.6.dist-info/AUTHORS → /meta_analytics/dataframes/glucose_endpoints/utils.py +0 -0
  78. {meta_edc-1.0.6.dist-info → meta_edc-1.1.0.dist-info/licenses}/LICENSE +0 -0
  79. {meta_edc-1.0.6.dist-info → meta_edc-1.1.0.dist-info}/top_level.txt +0 -0
@@ -3,21 +3,78 @@ from django.utils.safestring import mark_safe
3
3
  from edc_action_item.forms import ActionItemCrfFormMixin
4
4
  from edc_crf.crf_form_validator import CrfFormValidator
5
5
  from edc_crf.modelform_mixins import CrfModelFormMixin
6
+ from edc_egfr.form_validator_mixins import EgfrCkdEpiFormValidatorMixin
6
7
  from edc_lab_panel.panels import rft_panel
7
8
  from edc_lab_results.form_validator_mixins import BloodResultsFormValidatorMixin
8
- from edc_reportable import BmiFormValidatorMixin
9
+ from edc_registration.models import RegisteredSubject
10
+ from edc_utils import age
11
+ from edc_visit_schedule.utils import is_baseline
12
+ from edc_vitals.form_validators import BmiFormValidatorMixin
9
13
 
10
- from ...models import BloodResultsRft
14
+ from meta_visit_schedule.constants import DAY1
15
+
16
+ from ...models import BloodResultsRft, FollowupVitals, PhysicalExam, SubjectVisit
11
17
 
12
18
 
13
19
  class BloodResultsRftFormValidator(
14
- BloodResultsFormValidatorMixin, BmiFormValidatorMixin, CrfFormValidator
20
+ BloodResultsFormValidatorMixin,
21
+ EgfrCkdEpiFormValidatorMixin,
22
+ BmiFormValidatorMixin,
23
+ CrfFormValidator,
15
24
  ):
16
25
  panel = rft_panel
17
26
 
18
27
  def clean(self):
19
28
  super().clean()
20
29
  self.validate_bmi()
30
+ if self.cleaned_data.get("creatinine_value") and self.cleaned_data.get(
31
+ "creatinine_units"
32
+ ):
33
+
34
+ if is_baseline(self.related_visit):
35
+ baseline_egfr_value = None
36
+ else:
37
+ baseline_visit = SubjectVisit.objects.get(
38
+ subject_identifier=self.related_visit.subject_identifier,
39
+ visit_code=DAY1,
40
+ visit_code_sequence=0,
41
+ )
42
+ baseline_egfr_value = BloodResultsRft.objects.get(
43
+ subject_visit=baseline_visit
44
+ ).egfr_value
45
+ rs = RegisteredSubject.objects.get(
46
+ subject_identifier=self.related_visit.subject_identifier
47
+ )
48
+ age_in_years = age(rs.dob, self.report_datetime).years
49
+
50
+ self.validate_egfr(
51
+ gender=rs.gender,
52
+ age_in_years=age_in_years,
53
+ ethnicity=rs.ethnicity,
54
+ weight_in_kgs=self.get_weight_in_kgs(),
55
+ baseline_egfr_value=baseline_egfr_value,
56
+ )
57
+
58
+ def get_weight_in_kgs(self) -> float | None:
59
+ if is_baseline(self.related_visit):
60
+ obj = (
61
+ PhysicalExam.objects.filter(
62
+ subject_visit=self.related_visit, weight__isnull=False
63
+ )
64
+ .order_by("report_datetime")
65
+ .last()
66
+ )
67
+ else:
68
+ obj = (
69
+ FollowupVitals.objects.filter(
70
+ subject_visit=self.related_visit, weight__isnull=False
71
+ )
72
+ .order_by("report_datetime")
73
+ .last()
74
+ )
75
+ if obj:
76
+ return obj.weight
77
+ return None
21
78
 
22
79
 
23
80
  class BloodResultsRftForm(ActionItemCrfFormMixin, CrfModelFormMixin, forms.ModelForm):
@@ -38,16 +38,18 @@ class StudyMedicationFormValidator(BaseStudyMedicationFormValidator):
38
38
 
39
39
  def validate_stock_codes_are_dispensed(self):
40
40
  if self.cleaned_data.get("stock_codes"):
41
- pattern = re.compile("^([A-Z0-9]{6})(,[A-Z0-9]{6})*$")
41
+ # pattern = re.compile("^([A-Z0-9]{6})(,[A-Z0-9]{6})*$")
42
+ pattern = re.compile("^([A-Z0-9]{6})(\r\n[A-Z0-9]{6})*$")
42
43
  if not pattern.match(self.cleaned_data.get("stock_codes")):
43
44
  raise forms.ValidationError(
44
45
  {
45
46
  "stock_codes": (
46
- "Invalid format. Enter one or more valid codes separated by comma"
47
+ "Invalid format. Enter one or more valid codes. "
48
+ "One code per line only. No commas, spaces, etc"
47
49
  )
48
50
  }
49
51
  )
50
- for stock_code in self.cleaned_data.get("stock_codes").split(","):
52
+ for stock_code in self.cleaned_data.get("stock_codes").split("\r\n"):
51
53
  try:
52
54
  Stock.objects.get(
53
55
  code=stock_code,
@@ -0,0 +1,42 @@
1
+ # Generated by Django 6.0 on 2025-04-02 16:13
2
+ from django.apps import apps as django_apps
3
+ from django.db import migrations
4
+ from django.db.migrations import RunPython
5
+ from edc_appointment.constants import MISSED_APPT
6
+ from edc_pdutils.dataframes import get_crf
7
+ from edc_utils import get_utcnow
8
+ from tqdm import tqdm
9
+
10
+
11
+ def update(apps, schema_editor):
12
+ """Correct missed appointments incorrectly set to
13
+ appt_timing=ONTIME_APPT.
14
+ """
15
+ appointment_model_cls = django_apps.get_model("edc_appointment.appointment")
16
+ df_missedvisit = get_crf(
17
+ "meta_subject.subjectvisitmissed",
18
+ subject_visit_model="meta_subject.subjectvisit",
19
+ )
20
+ df_subjects = df_missedvisit[df_missedvisit.appt_timing != MISSED_APPT][
21
+ ["subject_identifier", "visit_code_str", "visit_code_sequence"]
22
+ ].copy()
23
+ for tpl in tqdm(df_subjects.itertuples(), total=len(df_subjects)):
24
+ obj = appointment_model_cls.objects.get(
25
+ subject_identifier=tpl.subject_identifier,
26
+ visit_code=tpl.visit_code_str,
27
+ visit_code_sequence=tpl.visit_code_sequence,
28
+ )
29
+ obj.appt_timing = MISSED_APPT
30
+ obj.modified = get_utcnow()
31
+ obj.user_modified = "erikvw"
32
+
33
+ obj.save(update_fields=["appt_timing", "user_modified", "modified"])
34
+
35
+
36
+ class Migration(migrations.Migration):
37
+
38
+ dependencies = [
39
+ ("meta_subject", "0220_historicalbloodresultsgludummy_bloodresultsgludummy"),
40
+ ]
41
+
42
+ operations = [RunPython(update)]
@@ -0,0 +1,46 @@
1
+ # Generated by Django 6.0 on 2025-04-15 22:06
2
+
3
+ import django.core.validators
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ("meta_subject", "0221_auto_20250402_1913"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterField(
15
+ model_name="historicalstudymedication",
16
+ name="stock_codes",
17
+ field=models.TextField(
18
+ blank=True,
19
+ help_text="Enter the medication bottle barcode or barcodes. Type one code per line",
20
+ max_length=30,
21
+ null=True,
22
+ validators=[
23
+ django.core.validators.RegexValidator(
24
+ message="Enter one or more valid codes, one code per line",
25
+ regex="^([A-Z0-9]{6})(\r\n[A-Z0-9]{6})*$",
26
+ )
27
+ ],
28
+ ),
29
+ ),
30
+ migrations.AlterField(
31
+ model_name="studymedication",
32
+ name="stock_codes",
33
+ field=models.TextField(
34
+ blank=True,
35
+ help_text="Enter the medication bottle barcode or barcodes. Type one code per line",
36
+ max_length=30,
37
+ null=True,
38
+ validators=[
39
+ django.core.validators.RegexValidator(
40
+ message="Enter one or more valid codes, one code per line",
41
+ regex="^([A-Z0-9]{6})(\r\n[A-Z0-9]{6})*$",
42
+ )
43
+ ],
44
+ ),
45
+ ),
46
+ ]
@@ -0,0 +1,83 @@
1
+ # Generated by Django 5.2.3 on 2025-06-25 20:55
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("meta_subject", "0222_alter_historicalstudymedication_stock_codes_and_more"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="bloodresultsfbc",
15
+ name="errors",
16
+ field=models.TextField(blank=True, null=True),
17
+ ),
18
+ migrations.AddField(
19
+ model_name="bloodresultsgludummy",
20
+ name="errors",
21
+ field=models.TextField(blank=True, null=True),
22
+ ),
23
+ migrations.AddField(
24
+ model_name="bloodresultshba1c",
25
+ name="errors",
26
+ field=models.TextField(blank=True, null=True),
27
+ ),
28
+ migrations.AddField(
29
+ model_name="bloodresultsins",
30
+ name="errors",
31
+ field=models.TextField(blank=True, null=True),
32
+ ),
33
+ migrations.AddField(
34
+ model_name="bloodresultslft",
35
+ name="errors",
36
+ field=models.TextField(blank=True, null=True),
37
+ ),
38
+ migrations.AddField(
39
+ model_name="bloodresultslipids",
40
+ name="errors",
41
+ field=models.TextField(blank=True, null=True),
42
+ ),
43
+ migrations.AddField(
44
+ model_name="bloodresultsrft",
45
+ name="errors",
46
+ field=models.TextField(blank=True, null=True),
47
+ ),
48
+ migrations.AddField(
49
+ model_name="historicalbloodresultsfbc",
50
+ name="errors",
51
+ field=models.TextField(blank=True, null=True),
52
+ ),
53
+ migrations.AddField(
54
+ model_name="historicalbloodresultsgludummy",
55
+ name="errors",
56
+ field=models.TextField(blank=True, null=True),
57
+ ),
58
+ migrations.AddField(
59
+ model_name="historicalbloodresultshba1c",
60
+ name="errors",
61
+ field=models.TextField(blank=True, null=True),
62
+ ),
63
+ migrations.AddField(
64
+ model_name="historicalbloodresultsins",
65
+ name="errors",
66
+ field=models.TextField(blank=True, null=True),
67
+ ),
68
+ migrations.AddField(
69
+ model_name="historicalbloodresultslft",
70
+ name="errors",
71
+ field=models.TextField(blank=True, null=True),
72
+ ),
73
+ migrations.AddField(
74
+ model_name="historicalbloodresultslipids",
75
+ name="errors",
76
+ field=models.TextField(blank=True, null=True),
77
+ ),
78
+ migrations.AddField(
79
+ model_name="historicalbloodresultsrft",
80
+ name="errors",
81
+ field=models.TextField(blank=True, null=True),
82
+ ),
83
+ ]
@@ -0,0 +1,153 @@
1
+ # Generated by Django 5.2.3 on 2025-06-25 23:57
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("meta_subject", "0223_bloodresultsfbc_errors_bloodresultsgludummy_errors_and_more"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="bloodresultsfbc",
15
+ name="abnormal_summary",
16
+ field=models.TextField(blank=True, null=True),
17
+ ),
18
+ migrations.AddField(
19
+ model_name="bloodresultsfbc",
20
+ name="reportable_summary",
21
+ field=models.TextField(blank=True, null=True),
22
+ ),
23
+ migrations.AddField(
24
+ model_name="bloodresultsgludummy",
25
+ name="abnormal_summary",
26
+ field=models.TextField(blank=True, null=True),
27
+ ),
28
+ migrations.AddField(
29
+ model_name="bloodresultsgludummy",
30
+ name="reportable_summary",
31
+ field=models.TextField(blank=True, null=True),
32
+ ),
33
+ migrations.AddField(
34
+ model_name="bloodresultshba1c",
35
+ name="abnormal_summary",
36
+ field=models.TextField(blank=True, null=True),
37
+ ),
38
+ migrations.AddField(
39
+ model_name="bloodresultshba1c",
40
+ name="reportable_summary",
41
+ field=models.TextField(blank=True, null=True),
42
+ ),
43
+ migrations.AddField(
44
+ model_name="bloodresultsins",
45
+ name="abnormal_summary",
46
+ field=models.TextField(blank=True, null=True),
47
+ ),
48
+ migrations.AddField(
49
+ model_name="bloodresultsins",
50
+ name="reportable_summary",
51
+ field=models.TextField(blank=True, null=True),
52
+ ),
53
+ migrations.AddField(
54
+ model_name="bloodresultslft",
55
+ name="abnormal_summary",
56
+ field=models.TextField(blank=True, null=True),
57
+ ),
58
+ migrations.AddField(
59
+ model_name="bloodresultslft",
60
+ name="reportable_summary",
61
+ field=models.TextField(blank=True, null=True),
62
+ ),
63
+ migrations.AddField(
64
+ model_name="bloodresultslipids",
65
+ name="abnormal_summary",
66
+ field=models.TextField(blank=True, null=True),
67
+ ),
68
+ migrations.AddField(
69
+ model_name="bloodresultslipids",
70
+ name="reportable_summary",
71
+ field=models.TextField(blank=True, null=True),
72
+ ),
73
+ migrations.AddField(
74
+ model_name="bloodresultsrft",
75
+ name="abnormal_summary",
76
+ field=models.TextField(blank=True, null=True),
77
+ ),
78
+ migrations.AddField(
79
+ model_name="bloodresultsrft",
80
+ name="reportable_summary",
81
+ field=models.TextField(blank=True, null=True),
82
+ ),
83
+ migrations.AddField(
84
+ model_name="historicalbloodresultsfbc",
85
+ name="abnormal_summary",
86
+ field=models.TextField(blank=True, null=True),
87
+ ),
88
+ migrations.AddField(
89
+ model_name="historicalbloodresultsfbc",
90
+ name="reportable_summary",
91
+ field=models.TextField(blank=True, null=True),
92
+ ),
93
+ migrations.AddField(
94
+ model_name="historicalbloodresultsgludummy",
95
+ name="abnormal_summary",
96
+ field=models.TextField(blank=True, null=True),
97
+ ),
98
+ migrations.AddField(
99
+ model_name="historicalbloodresultsgludummy",
100
+ name="reportable_summary",
101
+ field=models.TextField(blank=True, null=True),
102
+ ),
103
+ migrations.AddField(
104
+ model_name="historicalbloodresultshba1c",
105
+ name="abnormal_summary",
106
+ field=models.TextField(blank=True, null=True),
107
+ ),
108
+ migrations.AddField(
109
+ model_name="historicalbloodresultshba1c",
110
+ name="reportable_summary",
111
+ field=models.TextField(blank=True, null=True),
112
+ ),
113
+ migrations.AddField(
114
+ model_name="historicalbloodresultsins",
115
+ name="abnormal_summary",
116
+ field=models.TextField(blank=True, null=True),
117
+ ),
118
+ migrations.AddField(
119
+ model_name="historicalbloodresultsins",
120
+ name="reportable_summary",
121
+ field=models.TextField(blank=True, null=True),
122
+ ),
123
+ migrations.AddField(
124
+ model_name="historicalbloodresultslft",
125
+ name="abnormal_summary",
126
+ field=models.TextField(blank=True, null=True),
127
+ ),
128
+ migrations.AddField(
129
+ model_name="historicalbloodresultslft",
130
+ name="reportable_summary",
131
+ field=models.TextField(blank=True, null=True),
132
+ ),
133
+ migrations.AddField(
134
+ model_name="historicalbloodresultslipids",
135
+ name="abnormal_summary",
136
+ field=models.TextField(blank=True, null=True),
137
+ ),
138
+ migrations.AddField(
139
+ model_name="historicalbloodresultslipids",
140
+ name="reportable_summary",
141
+ field=models.TextField(blank=True, null=True),
142
+ ),
143
+ migrations.AddField(
144
+ model_name="historicalbloodresultsrft",
145
+ name="abnormal_summary",
146
+ field=models.TextField(blank=True, null=True),
147
+ ),
148
+ migrations.AddField(
149
+ model_name="historicalbloodresultsrft",
150
+ name="reportable_summary",
151
+ field=models.TextField(blank=True, null=True),
152
+ ),
153
+ ]
@@ -205,8 +205,8 @@ class TestEgfr(MetaTestCaseMixin, TestCase):
205
205
  data.update(creatinine_value=152.0, creatinine_units=MICROMOLES_PER_LITER)
206
206
  obj_1000 = BloodResultsRft.objects.create(**data)
207
207
  obj_1000.refresh_from_db()
208
- self.assertEqual(round_half_away_from_zero(obj_1000.egfr_value, 3), Decimal("49.030"))
209
- self.assertEqual(round_half_away_from_zero(obj_1000.egfr_value, 2), Decimal("49.03"))
208
+ self.assertEqual(round_half_away_from_zero(obj_1000.egfr_value, 3), Decimal("49.019"))
209
+ self.assertEqual(round_half_away_from_zero(obj_1000.egfr_value, 2), Decimal("49.02"))
210
210
  self.assertEqual(round_half_away_from_zero(obj_1000.egfr_value, 1), Decimal("49.0"))
211
211
  self.assertEqual(round_half_away_from_zero(obj_1000.egfr_value), Decimal("49"))
212
212
 
@@ -228,7 +228,7 @@ class TestEgfr(MetaTestCaseMixin, TestCase):
228
228
  data.update(creatinine_value=150.8, creatinine_units=MICROMOLES_PER_LITER)
229
229
  obj_2000 = BloodResultsRft.objects.create(**data)
230
230
  obj_2000.refresh_from_db()
231
- self.assertEqual(round_half_away_from_zero(obj_2000.egfr_value, 3), Decimal("49.503"))
232
- self.assertEqual(round_half_away_from_zero(obj_2000.egfr_value, 2), Decimal("49.50"))
231
+ self.assertEqual(round_half_away_from_zero(obj_2000.egfr_value, 3), Decimal("49.492"))
232
+ self.assertEqual(round_half_away_from_zero(obj_2000.egfr_value, 2), Decimal("49.49"))
233
233
  self.assertEqual(round_half_away_from_zero(obj_2000.egfr_value, 1), Decimal("49.5"))
234
- self.assertEqual(round_half_away_from_zero(obj_2000.egfr_value), Decimal("50"))
234
+ self.assertEqual(round_half_away_from_zero(obj_2000.egfr_value), Decimal("49"))
@@ -1 +0,0 @@
1
- from .get_glucose_df import get_glucose_df
@@ -1,122 +0,0 @@
1
- import numpy as np
2
- import pandas as pd
3
- from django_pandas.io import read_frame
4
- from edc_pdutils.dataframes import get_eos, get_subject_consent, get_subject_visit
5
-
6
- from meta_subject.models import Glucose, GlucoseFbg
7
-
8
-
9
- def get_glucose_df() -> pd.DataFrame:
10
- qs_glucose_fbg = GlucoseFbg.objects.all()
11
- df_glucose_fbg = read_frame(qs_glucose_fbg)
12
- df_glucose_fbg.rename(
13
- columns={"fbg_fasting": "fasting", "subject_visit": "subject_visit_id"},
14
- inplace=True,
15
- )
16
-
17
- df_glucose_fbg.loc[(df_glucose_fbg["fasting"] == "fasting"), "fasting"] = "Yes"
18
- df_glucose_fbg.loc[(df_glucose_fbg["fasting"] == "non_fasting"), "fasting"] = "No"
19
- df_glucose_fbg["fasting_hrs"] = np.nan
20
- df_glucose_fbg["fasting_hrs"] = df_glucose_fbg["fasting_duration_delta"].apply(
21
- lambda x: x.total_seconds() / 3600
22
- )
23
- df_glucose_fbg["fasting_hrs"] = df_glucose_fbg["fasting_hrs"].apply(
24
- lambda x: 8.05 if not x else x
25
- )
26
- # df_glucose_fbg = df_glucose_fbg.loc[df_glucose_fbg["fasting_hrs"] >= 8.0]
27
- # df_glucose_fbg.reset_index(drop=True, inplace=True)
28
- df_glucose_fbg.loc[
29
- :,
30
- ["ogtt_value", "ogtt_units", "ogtt_datetime"],
31
- ] = [np.nan, None, pd.NaT]
32
- df_glucose_fbg["source"] = "meta_subject.glucosefbg"
33
-
34
- qs_glucose = Glucose.objects.all()
35
- df_glucose = read_frame(qs_glucose)
36
- df_glucose.rename(columns={"subject_visit": "subject_visit_id"}, inplace=True)
37
- df_glucose.loc[(df_glucose["fasting"] == "fasting"), "fasting"] = "Yes"
38
- df_glucose.loc[(df_glucose["fasting"] == "non_fasting"), "fasting"] = "No"
39
- df_glucose["fasting_hrs"] = np.nan
40
- df_glucose["fasting_hrs"] = df_glucose[df_glucose["fasting"] == "Yes"][
41
- "fasting_duration_delta"
42
- ].apply(lambda x: x.total_seconds() / 3600)
43
- df_glucose["fasting_hrs"] = df_glucose["fasting_hrs"].apply(lambda x: 8.05 if not x else x)
44
- # df_glucose = df_glucose.loc[df_glucose["fasting_hrs"] >= 8.0]
45
- # df_glucose.reset_index(drop=True, inplace=True)
46
- df_glucose["source"] = "meta_subject.glucose"
47
-
48
- keep_cols = [
49
- "subject_visit_id",
50
- "fasting",
51
- "fasting_hrs",
52
- "fbg_value",
53
- "fbg_units",
54
- "fbg_datetime",
55
- "ogtt_value",
56
- "ogtt_units",
57
- "ogtt_datetime",
58
- "source",
59
- "revision",
60
- "report_datetime",
61
- ]
62
- df_glucose = df_glucose[keep_cols]
63
- df_glucose_fbg = df_glucose_fbg[keep_cols]
64
- # df = pd.concat([df_glucose_fbg, df_glucose])
65
- df = pd.merge(
66
- df_glucose,
67
- df_glucose_fbg,
68
- on="subject_visit_id",
69
- how="outer",
70
- indicator=True,
71
- suffixes=("", "_2"),
72
- )
73
-
74
- for suffix in ["", "_2"]:
75
- cols = [f"fasting_hrs{suffix}", f"fbg_value{suffix}", f"ogtt_value{suffix}"]
76
- df[cols] = df[cols].apply(pd.to_numeric)
77
- cols = [f"fbg_datetime{suffix}", f"ogtt_datetime{suffix}"]
78
- df[cols] = df[cols].apply(pd.to_datetime)
79
- df.loc[
80
- (df[f"fbg_units{suffix}"] != "mmol/L (millimoles/L)")
81
- & (df[f"fbg_value{suffix}"] >= 0),
82
- f"fbg_units{suffix}",
83
- ] = "mmol/L (millimoles/L)"
84
- df.loc[
85
- (df[f"ogtt_units{suffix}"] != "mmol/L (millimoles/L)")
86
- & (df[f"ogtt_value{suffix}"] >= 0),
87
- f"ogtt_units{suffix}",
88
- ] = "mmol/L (millimoles/L)"
89
- # remove values if not fasted
90
- # df.loc[(df[f"fasting{suffix}"] != YES), f"fbg_value{suffix}"] = np.nan
91
- # df.loc[(df[f"fasting{suffix}"] != YES), f"ogtt_value{suffix}"] = np.nan
92
-
93
- # reconcile all to single column
94
- df.loc[(df["fbg_value"].isna()) & (df["fbg_value_2"].notna()), "fbg_value"] = df[
95
- "fbg_value_2"
96
- ]
97
- df.loc[(df["ogtt_value"].isna()) & (df["ogtt_value_2"].notna()), "ogtt_value"] = df[
98
- "ogtt_value_2"
99
- ]
100
- cols = [col for col in list(df.columns) if col.endswith("_2")]
101
- df.drop(columns=cols, inplace=True)
102
- cols = [col for col in list(df.columns) if col.endswith("_3")]
103
- df.drop(columns=cols, inplace=True)
104
-
105
- df_subject_visit = get_subject_visit("meta_subject.subjectvisit")
106
- df_consent = get_subject_consent("meta_consent.subjectconsent")
107
- df_eos = get_eos("meta_prn.endofstudy")
108
-
109
- df = pd.merge(df_subject_visit, df, on="subject_visit_id", how="left")
110
- df = pd.merge(df, df_consent, on="subject_identifier", how="left")
111
- df = pd.merge(df, df_eos, on="subject_identifier", how="left")
112
-
113
- df["visit_days"] = df["baseline_datetime"].rsub(df["visit_datetime"]).dt.days
114
- df["fgb_days"] = df["baseline_datetime"].rsub(df["fbg_datetime"]).dt.days
115
- df["ogtt_days"] = df["baseline_datetime"].rsub(df["ogtt_datetime"]).dt.days
116
- df["visit_days"] = pd.to_numeric(df["visit_days"], downcast="integer")
117
- df["fgb_days"] = pd.to_numeric(df["fgb_days"], downcast="integer")
118
- df["ogtt_days"] = pd.to_numeric(df["ogtt_days"], downcast="integer")
119
-
120
- df = df.sort_values(by=["subject_identifier", "visit_code"])
121
- df.reset_index(drop=True, inplace=True)
122
- return df