meta-edc 0.3.22__py3-none-any.whl → 0.3.24__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.
- meta_analytics/dataframes/glucose_endpoints/glucose_endpoints_by_date.py +156 -124
- meta_edc/__init__.py +11 -4
- meta_edc/celery/__init__.py +2 -0
- meta_edc/wsgi.py +1 -1
- meta_edc/wsgi_live.py +1 -1
- meta_edc/wsgi_uat.py +1 -1
- {meta_edc-0.3.22.dist-info → meta_edc-0.3.24.dist-info}/METADATA +2 -2
- {meta_edc-0.3.22.dist-info → meta_edc-0.3.24.dist-info}/RECORD +49 -37
- meta_prn/tests/tests/test_dm_referral.py +2 -2
- meta_reports/__init__.py +1 -1
- meta_reports/admin/__init__.py +1 -0
- meta_reports/admin/endpoints_admin.py +17 -7
- meta_reports/admin/list_filters.py +30 -0
- meta_reports/admin/unmanaged/__init__.py +1 -0
- meta_reports/admin/unmanaged/glucose_summary_admin.py +76 -0
- meta_reports/migrations/0024_glucosesummary.py +38 -0
- meta_reports/migrations/0025_auto_20240822_0115.py +87 -0
- meta_reports/migrations/0026_auto_20240822_0120.py +54 -0
- meta_reports/migrations/0027_auto_20240822_0140.py +54 -0
- meta_reports/migrations/0028_alter_glucosesummary_options.py +22 -0
- meta_reports/migrations/0029_auto_20240822_0149.py +54 -0
- meta_reports/models/__init__.py +1 -0
- meta_reports/models/dbviews/__init__.py +1 -0
- meta_reports/models/dbviews/glucose_summary/__init__.py +2 -0
- meta_reports/models/dbviews/glucose_summary/unmanaged_model.py +31 -0
- meta_reports/models/dbviews/glucose_summary/view_definition.py +58 -0
- meta_reports/models/endpoints.py +3 -0
- meta_reports/tasks.py +1 -1
- meta_reports/templates/meta_reports/columns/subject_identifier_column.html +1 -0
- meta_subject/admin/__init__.py +1 -1
- meta_subject/admin/diabetes/__init__.py +1 -1
- meta_subject/admin/diabetes/dm_endpoint_admin.py +35 -0
- meta_subject/form_validators/__init__.py +1 -1
- meta_subject/form_validators/dm_endpoint_form_validator.py +35 -0
- meta_subject/forms/__init__.py +1 -1
- meta_subject/forms/diabetes/__init__.py +1 -2
- meta_subject/forms/diabetes/dm_endpoint_form.py +13 -0
- meta_subject/migrations/0209_remove_historicaldmdxresult_dm_diagnosis_and_more.py +37 -0
- meta_subject/migrations/0210_remove_dmdxresult_dm_diagnosis_and_more.py +123 -0
- meta_subject/migrations/0211_dmendpoint_endpoint_reached_and_more.py +45 -0
- meta_subject/models/__init__.py +1 -1
- meta_subject/models/diabetes/__init__.py +1 -2
- meta_subject/models/diabetes/dm_endpoint.py +61 -0
- meta_subject/models/glucose.py +4 -1
- meta_visit_schedule/visit_schedules/phase_three/schedule_dm_referral.py +1 -1
- meta_subject/admin/diabetes/dm_diagnosis_admin.py +0 -89
- meta_subject/form_validators/dm_diagnosis_form_validator.py +0 -38
- meta_subject/form_validators/dm_dx_result_form_validator.py +0 -7
- meta_subject/forms/diabetes/dm_diagnosis_form.py +0 -13
- meta_subject/forms/diabetes/dm_dx_result_form.py +0 -11
- meta_subject/models/diabetes/dm_diagnosis.py +0 -50
- meta_subject/models/diabetes/dm_dx_result.py +0 -70
- {meta_edc-0.3.22.dist-info → meta_edc-0.3.24.dist-info}/AUTHORS +0 -0
- {meta_edc-0.3.22.dist-info → meta_edc-0.3.24.dist-info}/LICENSE +0 -0
- {meta_edc-0.3.22.dist-info → meta_edc-0.3.24.dist-info}/WHEEL +0 -0
- {meta_edc-0.3.22.dist-info → meta_edc-0.3.24.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,8 @@ from edc_pdutils.dataframes import (
|
|
10
10
|
)
|
11
11
|
from edc_utils import get_utcnow
|
12
12
|
|
13
|
+
from meta_reports.models import Endpoints
|
14
|
+
|
13
15
|
from .constants import (
|
14
16
|
CASE_EOS,
|
15
17
|
CASE_FBG_ONLY,
|
@@ -31,68 +33,57 @@ class GlucoseEndpointsByDate:
|
|
31
33
|
fbg_threshhold = 7.0
|
32
34
|
ogtt_threshhold = 11.1
|
33
35
|
endpoint_cls = EndpointByDate
|
36
|
+
keep_cols = [
|
37
|
+
"subject_visit_id",
|
38
|
+
"fasting",
|
39
|
+
"fasting_hrs",
|
40
|
+
"fbg_value",
|
41
|
+
"fbg_units",
|
42
|
+
"fbg_datetime",
|
43
|
+
"ogtt_value",
|
44
|
+
"ogtt_units",
|
45
|
+
"ogtt_datetime",
|
46
|
+
"source",
|
47
|
+
"report_datetime",
|
48
|
+
]
|
49
|
+
|
50
|
+
visit_cols = [
|
51
|
+
"subject_visit_id",
|
52
|
+
"subject_identifier",
|
53
|
+
"visit_code",
|
54
|
+
"visit_datetime",
|
55
|
+
"site",
|
56
|
+
"baseline_datetime",
|
57
|
+
]
|
34
58
|
|
35
59
|
def __init__(
|
36
60
|
self, subject_identifiers: list[str] | None = None, case_list: list[int] | None = None
|
37
61
|
):
|
62
|
+
self.subject_identifiers = subject_identifiers or []
|
63
|
+
if len(self.subject_identifiers) == Endpoints.objects.all().count():
|
64
|
+
self.subject_identifiers = []
|
38
65
|
self.case_list = case_list or [CASE_OGTT, 2, 3, CASE_EOS]
|
39
66
|
self.endpoint_cases = {k: v for k, v in endpoint_cases.items() if k in self.case_list}
|
40
|
-
self.endpoint_only_df =
|
41
|
-
|
42
|
-
|
43
|
-
)
|
44
|
-
self.fbg_only_df["source"] = "meta_subject.glucosefbg"
|
45
|
-
self.fbg_only_df.rename(
|
46
|
-
columns={"fbg_fasting": "fasting", "subject_visit": "subject_visit_id"},
|
47
|
-
inplace=True,
|
48
|
-
)
|
49
|
-
self.fbg_only_df.loc[(self.fbg_only_df["fasting"] == "fasting"), "fasting"] = YES
|
50
|
-
self.fbg_only_df.loc[(self.fbg_only_df["fasting"] == "non_fasting"), "fasting"] = NO
|
67
|
+
self.endpoint_only_df = pd.DataFrame()
|
68
|
+
|
69
|
+
self.fbg_only_df = self.get_fbg_only_df()
|
51
70
|
|
52
71
|
self.df = get_crf(
|
53
|
-
model="meta_subject.glucose",
|
72
|
+
model="meta_subject.glucose",
|
73
|
+
subject_identifiers=self.subject_identifiers,
|
54
74
|
)
|
55
75
|
self.df["source"] = "meta_subject.glucose"
|
56
76
|
|
57
|
-
|
58
|
-
dftmp.loc[(dftmp["fasting"] == NO), "fasting_duration_delta"] = pd.NaT
|
59
|
-
if dftmp.empty:
|
60
|
-
dftmp["fasting_hrs"] = np.nan
|
61
|
-
else:
|
62
|
-
dftmp["fasting_hrs"] = (
|
63
|
-
dftmp["fasting_duration_delta"].dt.total_seconds() / 3600
|
64
|
-
)
|
77
|
+
self.calculate_fasting_hrs()
|
65
78
|
|
66
|
-
keep_cols = [
|
67
|
-
"subject_visit_id",
|
68
|
-
"fasting",
|
69
|
-
"fasting_hrs",
|
70
|
-
"fbg_value",
|
71
|
-
"fbg_units",
|
72
|
-
"fbg_datetime",
|
73
|
-
"ogtt_value",
|
74
|
-
"ogtt_units",
|
75
|
-
"ogtt_datetime",
|
76
|
-
"source",
|
77
|
-
"report_datetime",
|
78
|
-
]
|
79
79
|
self.fbg_only_df = self.fbg_only_df[
|
80
|
-
[col for col in keep_cols if not col.startswith("ogtt")]
|
80
|
+
[col for col in self.keep_cols if not col.startswith("ogtt")]
|
81
81
|
]
|
82
|
-
self.df = self.df[keep_cols]
|
82
|
+
self.df = self.df[self.keep_cols]
|
83
83
|
self.df.reset_index(drop=True)
|
84
84
|
self.df = self.df.copy()
|
85
85
|
|
86
|
-
|
87
|
-
for col in ["fbg_datetime", "report_datetime"]:
|
88
|
-
if not self.fbg_only_df[col].empty:
|
89
|
-
self.fbg_only_df[col] = self.fbg_only_df[col].dt.floor("d")
|
90
|
-
if not self.df[col].empty:
|
91
|
-
self.df[col] = self.df[col].dt.floor("d")
|
92
|
-
if not self.df["ogtt_datetime"].empty:
|
93
|
-
self.df["ogtt_datetime"] = self.df["ogtt_datetime"].dt.floor("d")
|
94
|
-
else:
|
95
|
-
self.df["ogtt_datetime"] = pd.NaT
|
86
|
+
self.normalize_dates()
|
96
87
|
|
97
88
|
# same shape but fbg_only_df ogtt columns are null
|
98
89
|
self.df = pd.merge(
|
@@ -104,7 +95,6 @@ class GlucoseEndpointsByDate:
|
|
104
95
|
suffixes=("", "2"),
|
105
96
|
)
|
106
97
|
self.df.reset_index(drop=True, inplace=True)
|
107
|
-
|
108
98
|
self.df_merged = self.df.copy()
|
109
99
|
|
110
100
|
# right_only
|
@@ -121,16 +111,8 @@ class GlucoseEndpointsByDate:
|
|
121
111
|
df_subject_visit = get_subject_visit(
|
122
112
|
"meta_subject.subjectvisit", subject_identifiers=subject_identifiers
|
123
113
|
)
|
124
|
-
visit_cols = [
|
125
|
-
"subject_visit_id",
|
126
|
-
"subject_identifier",
|
127
|
-
"visit_code",
|
128
|
-
"visit_datetime",
|
129
|
-
"site",
|
130
|
-
"baseline_datetime",
|
131
|
-
]
|
132
114
|
self.df = pd.merge(
|
133
|
-
df_subject_visit[visit_cols], self.df, on="subject_visit_id", how="left"
|
115
|
+
df_subject_visit[self.visit_cols], self.df, on="subject_visit_id", how="left"
|
134
116
|
)
|
135
117
|
self.df = self.df.sort_values(by=["subject_identifier", "fbg_datetime"])
|
136
118
|
self.df.reset_index(drop=True, inplace=True)
|
@@ -147,9 +129,10 @@ class GlucoseEndpointsByDate:
|
|
147
129
|
self.df = self.df.sort_values(by=["subject_identifier", "fbg_datetime"])
|
148
130
|
self.df.reset_index(drop=True, inplace=True)
|
149
131
|
|
150
|
-
self.df["
|
151
|
-
self.df["
|
152
|
-
|
132
|
+
if not self.df.loc[self.df["visit_datetime"].notna()].empty:
|
133
|
+
self.df["visit_days"] = (
|
134
|
+
self.df["baseline_datetime"].rsub(self.df["visit_datetime"]).dt.days
|
135
|
+
)
|
153
136
|
if not self.df.loc[self.df["fbg_datetime"].notna()].empty:
|
154
137
|
self.df["fgb_days"] = (
|
155
138
|
self.df.loc[self.df["fbg_datetime"].notna()]["baseline_datetime"]
|
@@ -167,12 +150,18 @@ class GlucoseEndpointsByDate:
|
|
167
150
|
else:
|
168
151
|
self.df["ogtt_days"] = np.nan
|
169
152
|
|
170
|
-
self.df
|
171
|
-
|
172
|
-
|
153
|
+
if self.df.empty:
|
154
|
+
self.df["visit_days"] = np.nan
|
155
|
+
self.df["fgb_days"] = np.nan
|
156
|
+
self.df["ogtt_days"] = np.nan
|
157
|
+
self.df["test"] = np.nan
|
158
|
+
else:
|
159
|
+
self.df["visit_days"] = pd.to_numeric(self.df["visit_days"], downcast="integer")
|
160
|
+
self.df["fgb_days"] = pd.to_numeric(self.df["fgb_days"], downcast="integer")
|
161
|
+
self.df["ogtt_days"] = pd.to_numeric(self.df["ogtt_days"], downcast="integer")
|
173
162
|
|
174
|
-
|
175
|
-
|
163
|
+
# label rows by type of glu tests (ones with value)
|
164
|
+
self.df["test"] = self.df.apply(get_test_string, axis=1)
|
176
165
|
|
177
166
|
self.df = self.df.sort_values(by=["subject_identifier", "visit_code"])
|
178
167
|
self.df = self.df.reset_index(drop=True)
|
@@ -232,6 +221,41 @@ class GlucoseEndpointsByDate:
|
|
232
221
|
].index
|
233
222
|
)
|
234
223
|
|
224
|
+
def get_fbg_only_df(self) -> pd.DataFrame:
|
225
|
+
fbg_only_df = get_crf(
|
226
|
+
model="meta_subject.glucosefbg", subject_identifiers=self.subject_identifiers
|
227
|
+
)
|
228
|
+
fbg_only_df["source"] = "meta_subject.glucosefbg"
|
229
|
+
fbg_only_df.rename(
|
230
|
+
columns={"fbg_fasting": "fasting", "subject_visit": "subject_visit_id"},
|
231
|
+
inplace=True,
|
232
|
+
)
|
233
|
+
fbg_only_df.loc[(fbg_only_df["fasting"] == "fasting"), "fasting"] = YES
|
234
|
+
fbg_only_df.loc[(fbg_only_df["fasting"] == "non_fasting"), "fasting"] = NO
|
235
|
+
return fbg_only_df
|
236
|
+
|
237
|
+
def normalize_dates(self):
|
238
|
+
"""Normalize dates"""
|
239
|
+
for col in ["fbg_datetime", "report_datetime"]:
|
240
|
+
if not self.fbg_only_df[col].empty:
|
241
|
+
self.fbg_only_df[col] = self.fbg_only_df[col].dt.floor("d")
|
242
|
+
if not self.df[col].empty:
|
243
|
+
self.df[col] = self.df[col].dt.floor("d")
|
244
|
+
if not self.df["ogtt_datetime"].empty:
|
245
|
+
self.df["ogtt_datetime"] = self.df["ogtt_datetime"].dt.floor("d")
|
246
|
+
else:
|
247
|
+
self.df["ogtt_datetime"] = pd.NaT
|
248
|
+
|
249
|
+
def calculate_fasting_hrs(self):
|
250
|
+
for dftmp in [self.fbg_only_df, self.df]:
|
251
|
+
dftmp.loc[(dftmp["fasting"] == NO), "fasting_duration_delta"] = pd.NaT
|
252
|
+
if dftmp.empty:
|
253
|
+
dftmp["fasting_hrs"] = np.nan
|
254
|
+
else:
|
255
|
+
dftmp["fasting_hrs"] = (
|
256
|
+
dftmp["fasting_duration_delta"].dt.total_seconds() / 3600
|
257
|
+
)
|
258
|
+
|
235
259
|
def append_subject_to_endpoint_df(self, subject_df: pd.DataFrame) -> None:
|
236
260
|
if self.endpoint_df.empty:
|
237
261
|
self.endpoint_df = subject_df.copy()
|
@@ -303,40 +327,43 @@ class GlucoseEndpointsByDate:
|
|
303
327
|
|
304
328
|
def merge_with_final_endpoints(self):
|
305
329
|
# merge endpoint_df with original df
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
self.
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
330
|
+
if self.endpoint_df.empty:
|
331
|
+
self.df = self.df[~(self.df["subject_identifier"].isin(self.subject_identifiers))]
|
332
|
+
else:
|
333
|
+
self.endpoint_df["test"] = self.endpoint_df.apply(get_test_string, axis=1)
|
334
|
+
self.endpoint_df.loc[self.endpoint_df["endpoint"] == 1, "days_to_endpoint"] = (
|
335
|
+
self.endpoint_df["fbg_datetime"] - self.endpoint_df["baseline_datetime"]
|
336
|
+
).dt.days
|
337
|
+
|
338
|
+
# print(f"Before dedup = {len(self.endpoint_df)}")
|
339
|
+
|
340
|
+
df1 = self.endpoint_df.copy()
|
341
|
+
df1 = df1[(df1["endpoint_type"] == CASE_EOS) & (df1["endpoint"] == 1)]
|
342
|
+
df1 = df1.sort_values(["subject_identifier", "fbg_datetime"])
|
343
|
+
df1 = df1.reset_index(drop=True)
|
344
|
+
df1 = df1.set_index(["subject_identifier"])
|
345
|
+
df1 = df1[~df1.index.duplicated(keep="last")]
|
346
|
+
df1 = df1.reset_index(drop=False)
|
347
|
+
|
348
|
+
df2 = self.endpoint_df.copy()
|
349
|
+
df2 = df2[(df2["endpoint_type"] != CASE_EOS) & (df2["endpoint"] == 1)]
|
350
|
+
df2 = df2.sort_values(["subject_identifier", "fbg_datetime"])
|
351
|
+
df2 = df2.reset_index(drop=True)
|
352
|
+
df2 = df2.set_index(["subject_identifier"])
|
353
|
+
df2 = df2[~df2.index.duplicated(keep="first")]
|
354
|
+
df2 = df2.reset_index(drop=False)
|
355
|
+
|
356
|
+
self.endpoint_only_df = pd.concat([df1, df2])
|
357
|
+
self.endpoint_only_df = self.endpoint_only_df.reset_index(drop=True)
|
358
|
+
# print(f"After dedup = {len(self.endpoint_df)}")
|
359
|
+
|
360
|
+
self.df = pd.merge(
|
361
|
+
self.df,
|
362
|
+
self.endpoint_only_df[["subject_identifier", "visit_code", "endpoint"]],
|
363
|
+
on=["subject_identifier", "visit_code"],
|
364
|
+
how="left",
|
365
|
+
suffixes=("", "_y"),
|
366
|
+
)
|
340
367
|
self.df = self.df.sort_values(by=["subject_identifier", "fbg_datetime"])
|
341
368
|
self.df = self.df.reset_index(drop=True)
|
342
369
|
|
@@ -394,30 +421,35 @@ class GlucoseEndpointsByDate:
|
|
394
421
|
model_cls.objects.filter(subject_identifier__in=subject_identifiers).delete()
|
395
422
|
else:
|
396
423
|
model_cls.objects.all().delete()
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
+
created = 0
|
425
|
+
if not df.empty:
|
426
|
+
data = [
|
427
|
+
model_cls(
|
428
|
+
subject_identifier=row["subject_identifier"],
|
429
|
+
site_id=row["site"],
|
430
|
+
baseline_datetime=(
|
431
|
+
None if pd.isna(row["baseline_datetime"]) else row["baseline_datetime"]
|
432
|
+
),
|
433
|
+
visit_code=None if pd.isna(row["visit_code"]) else row["visit_code"],
|
434
|
+
fbg_value=(None if pd.isna(row["fbg_value"]) else row["fbg_value"]),
|
435
|
+
ogtt_value=None if pd.isna(row["ogtt_value"]) else row["ogtt_value"],
|
436
|
+
fbg_datetime=(
|
437
|
+
None if pd.isna(row["fbg_datetime"]) else row["fbg_datetime"]
|
438
|
+
),
|
439
|
+
fasting=(None if pd.isna(row["fasting"]) else row["fasting"]),
|
440
|
+
endpoint_label=(
|
441
|
+
None if pd.isna(row["endpoint_label"]) else row["endpoint_label"]
|
442
|
+
),
|
443
|
+
offstudy_datetime=(
|
444
|
+
None if pd.isna(row["offstudy_datetime"]) else row["offstudy_datetime"]
|
445
|
+
),
|
446
|
+
offstudy_reason=(
|
447
|
+
None if pd.isna(row["offstudy_reason"]) else row["offstudy_reason"]
|
448
|
+
),
|
449
|
+
report_model=model,
|
450
|
+
created=now,
|
451
|
+
)
|
452
|
+
for _, row in df.iterrows()
|
453
|
+
]
|
454
|
+
created = len(model_cls.objects.bulk_create(data))
|
455
|
+
return created
|
meta_edc/__init__.py
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
import os
|
2
|
+
import sys
|
2
3
|
from importlib.metadata import PackageNotFoundError, version
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
# celery_app loads settings before Django, so ensure
|
6
|
+
# it will load the correct settings module coming from
|
7
|
+
# systemd service
|
8
|
+
if "meta_edc.celery.live:app" in sys.argv:
|
9
|
+
from .celery.live import app as celery_app
|
10
|
+
elif "meta_edc.celery.uat:app" in sys.argv:
|
11
|
+
from .celery.uat import app as celery_app
|
12
|
+
else:
|
13
|
+
from .celery.debug import app as celery_app
|
7
14
|
|
8
15
|
try:
|
9
16
|
__version__ = version(os.getcwd().split(os.sep)[-1])
|
@@ -11,4 +18,4 @@ except PackageNotFoundError:
|
|
11
18
|
__version__ = None
|
12
19
|
|
13
20
|
|
14
|
-
__all__ = ["
|
21
|
+
__all__ = ["celery_app", "__version__"]
|
meta_edc/celery/__init__.py
CHANGED
meta_edc/wsgi.py
CHANGED
meta_edc/wsgi_live.py
CHANGED
meta_edc/wsgi_uat.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: meta-edc
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.24
|
4
4
|
Summary: META Trial EDC (http://www.isrctn.com/ISRCTN76157257)
|
5
5
|
Home-page: https://github.com/meta-trial/meta-edc
|
6
6
|
Author: Erik van Widenfelt
|
@@ -19,7 +19,7 @@ Requires-Python: >=3.12
|
|
19
19
|
Description-Content-Type: text/x-rst
|
20
20
|
License-File: LICENSE
|
21
21
|
License-File: AUTHORS
|
22
|
-
Requires-Dist: edc ==0.6.
|
22
|
+
Requires-Dist: edc ==0.6.2
|
23
23
|
Requires-Dist: edc-microscopy
|
24
24
|
Requires-Dist: beautifulsoup4
|
25
25
|
Requires-Dist: edc-analytics
|