meta-edc 1.0.7__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 (56) 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/notebooks/anu.ipynb +95 -0
  5. meta_analytics/notebooks/appointment_planning.ipynb +329 -0
  6. meta_analytics/notebooks/arvs.ipynb +103 -0
  7. meta_analytics/notebooks/cleaning/consent_v1_ext.ipynb +227 -0
  8. meta_analytics/notebooks/cleaning/offschedule_eos.ipynb +353 -0
  9. meta_analytics/notebooks/dsmc/renal_dysfunction.ipynb +435 -0
  10. meta_analytics/notebooks/endpoints/meta_endpoints_by_date.ipynb +664 -0
  11. meta_analytics/notebooks/followup_examination.ipynb +141 -0
  12. meta_analytics/notebooks/hba1c.ipynb +136 -0
  13. meta_analytics/notebooks/hiv_regimens.ipynb +122 -118
  14. meta_analytics/notebooks/incidence.ipynb +232 -0
  15. meta_analytics/notebooks/liver.ipynb +389 -0
  16. meta_analytics/notebooks/magreth.ipynb +645 -0
  17. meta_analytics/notebooks/monitoring_report.ipynb +435 -245
  18. meta_analytics/notebooks/pharmacy.ipynb +405 -306
  19. meta_analytics/notebooks/pharmacy_stock_202410.ipynb +306 -0
  20. meta_analytics/notebooks/steering.ipynb +61 -0
  21. meta_analytics/notebooks/undiagnosed/meta3_screening_consort_chart.ipynb +1176 -0
  22. meta_analytics/notebooks/undiagnosed/meta3_screening_undiagnosed.ipynb +519 -0
  23. meta_analytics/notebooks/undiagnosed/meta_screening_table2.ipynb +964 -0
  24. meta_analytics/notebooks/undiagnosed/screen_undiagnosed_or.ipynb +296 -0
  25. meta_analytics/notebooks/undiagnosed/screening.ipynb +273 -0
  26. meta_analytics/notebooks/undiagnosed/screening2.ipynb +958 -0
  27. meta_analytics/notebooks/undiagnosed/screening_undiagnosed_20241002.ipynb +958 -0
  28. meta_analytics/notebooks/ven.ipynb +191 -0
  29. meta_analytics/notebooks/vitals.ipynb +263 -0
  30. meta_edc/settings/debug.py +3 -2
  31. meta_edc/urls.py +1 -0
  32. {meta_edc-1.0.7.dist-info → meta_edc-1.1.0.dist-info}/METADATA +3 -3
  33. {meta_edc-1.0.7.dist-info → meta_edc-1.1.0.dist-info}/RECORD +55 -28
  34. {meta_edc-1.0.7.dist-info → meta_edc-1.1.0.dist-info}/WHEEL +1 -1
  35. meta_labs/reportables.py +14 -11
  36. meta_labs/tests/test_reportables.py +33 -12
  37. meta_pharmacy/notebooks/pharmacy.ipynb +41 -0
  38. meta_prn/migrations/0063_historicaloffstudymedication_singleton_field_and_more.py +37 -0
  39. meta_prn/migrations/0064_auto_20250602_2143.py +18 -0
  40. meta_prn/models/end_of_study.py +2 -0
  41. meta_prn/models/off_study_medication.py +2 -0
  42. meta_screening/eligibility/eligibility_part_three/base_eligibility_part_three.py +59 -47
  43. meta_screening/form_validators/screening_part_three.py +6 -1
  44. meta_screening/tests/meta_test_case_mixin.py +3 -0
  45. meta_screening/tests/tests/test_forms.py +9 -2
  46. meta_screening/tests/tests/test_screening_part_three.py +11 -14
  47. meta_subject/action_items.py +1 -2
  48. meta_subject/choices.py +2 -1
  49. meta_subject/forms/blood_results/blood_results_rft_form.py +60 -3
  50. meta_subject/migrations/0223_bloodresultsfbc_errors_bloodresultsgludummy_errors_and_more.py +83 -0
  51. meta_subject/migrations/0224_bloodresultsfbc_abnormal_summary_and_more.py +153 -0
  52. meta_subject/tests/tests/test_egfr.py +5 -5
  53. meta_analytics/dataframes/enrolled/__init__.py +0 -0
  54. {meta_edc-1.0.7.dist-info → meta_edc-1.1.0.dist-info}/licenses/AUTHORS.rst +0 -0
  55. {meta_edc-1.0.7.dist-info → meta_edc-1.1.0.dist-info}/licenses/LICENSE +0 -0
  56. {meta_edc-1.0.7.dist-info → meta_edc-1.1.0.dist-info}/top_level.txt +0 -0
@@ -4,11 +4,8 @@ from decimal import Decimal
4
4
  from dateutil.relativedelta import relativedelta
5
5
  from django.test import TestCase
6
6
  from edc_constants.constants import FEMALE, NO, NOT_APPLICABLE, POS, TBD, YES
7
- from edc_reportable import (
8
- MICROMOLES_PER_LITER,
9
- MILLIMOLES_PER_LITER,
10
- ConversionNotHandled,
11
- )
7
+ from edc_reportable import MICROMOLES_PER_LITER, MILLIMOLES_PER_LITER
8
+ from edc_reportable.utils import convert_units
12
9
  from edc_utils.date import get_utcnow
13
10
 
14
11
  from meta_screening.constants import (
@@ -292,20 +289,20 @@ class TestScreeningPartThree(TestCase):
292
289
  obj.ogtt_value = 3.0
293
290
  obj.ogtt_units = MICROMOLES_PER_LITER
294
291
  obj.ogtt_datetime = get_utcnow()
295
- try:
296
- obj.save()
297
- except ConversionNotHandled:
298
- pass
299
- else:
300
- self.fail("ConversionNotHandled unexpectedly not raised.")
292
+ obj.save()
301
293
 
302
294
  obj.refresh_from_db()
295
+ obj.ogtt_value = convert_units(
296
+ label="glucose",
297
+ value=3.0,
298
+ units_from=MICROMOLES_PER_LITER,
299
+ units_to=MILLIMOLES_PER_LITER,
300
+ )
303
301
  obj.ogtt_units = MILLIMOLES_PER_LITER
304
302
  obj.save()
305
303
 
306
- self.assertIn(ineligible_reason, obj.reasons_ineligible_part_three)
307
- self.assertEqual(obj.eligible_part_three, TBD)
308
- self.assertFalse(obj.eligible)
304
+ self.assertEqual(obj.eligible_part_three, YES)
305
+ self.assertTrue(obj.eligible)
309
306
  self.assertFalse(obj.consented)
310
307
 
311
308
  obj.ogtt_base_datetime = get_utcnow()
@@ -2,7 +2,7 @@ from django.apps import apps as django_apps
2
2
  from edc_action_item import Action, ActionWithNotification, site_action_items
3
3
  from edc_action_item.site_action_items import AlreadyRegistered
4
4
  from edc_adverse_event.constants import AE_INITIAL_ACTION
5
- from edc_constants.constants import HIGH_PRIORITY, NEW, NONE, POS, YES
5
+ from edc_constants.constants import GRADE3, GRADE4, HIGH_PRIORITY, NEW, NONE, POS, YES
6
6
  from edc_egfr.constants import EGFR_DROP_NOTIFICATION_ACTION
7
7
  from edc_lab_results.action_items import BloodResultsFbcAction
8
8
  from edc_lab_results.action_items import (
@@ -17,7 +17,6 @@ from edc_lab_results.action_items import (
17
17
  BloodResultsRftAction as BaseBloodResultsRftAction,
18
18
  )
19
19
  from edc_ltfu.constants import LTFU_ACTION
20
- from edc_reportable import GRADE3, GRADE4
21
20
  from edc_visit_schedule.constants import OFFSCHEDULE_ACTION
22
21
  from edc_visit_schedule.utils import is_baseline
23
22
  from edc_visit_tracking.constants import MISSED_VISIT
meta_subject/choices.py CHANGED
@@ -3,6 +3,8 @@ from edc_constants.constants import (
3
3
  ABSENT,
4
4
  CLOSED,
5
5
  DEAD,
6
+ GRADE3,
7
+ GRADE4,
6
8
  NEW,
7
9
  NO,
8
10
  NO_EXAM,
@@ -15,7 +17,6 @@ from edc_constants.constants import (
15
17
  PRESENT_WITH_REINFORCEMENT,
16
18
  YES,
17
19
  )
18
- from edc_reportable.constants import GRADE3, GRADE4
19
20
  from edc_visit_tracking.constants import MISSED_VISIT, SCHEDULED, UNSCHEDULED
20
21
 
21
22
  from meta_ae.choices import INFORMANT_RELATIONSHIP
@@ -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):
@@ -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"))
File without changes