clinicedc 2.0.16__py3-none-any.whl → 2.0.17__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.16.dist-info → clinicedc-2.0.17.dist-info}/METADATA +1 -1
- {clinicedc-2.0.16.dist-info → clinicedc-2.0.17.dist-info}/RECORD +6 -6
- edc_model_to_dataframe/model_to_dataframe.py +46 -55
- edc_model_to_dataframe/read_frame_edc.py +1 -1
- {clinicedc-2.0.16.dist-info → clinicedc-2.0.17.dist-info}/WHEEL +0 -0
- {clinicedc-2.0.16.dist-info → clinicedc-2.0.17.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.17
|
|
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
|
|
@@ -1966,8 +1966,8 @@ edc_model_form/utils.py,sha256=gGe3etrp5YF4AKMGFIR3I9V1CfX4c5Wm6fK-8JkUeT4,538
|
|
|
1966
1966
|
edc_model_to_dataframe/__init__.py,sha256=iDR_F5oKkKDL9FfBXl0WKpwc2iOWbOlY-8wpCipBNnQ,115
|
|
1967
1967
|
edc_model_to_dataframe/apps.py,sha256=US1ehhJPgkvmIDZXiUPMoSjHTWIztUbYGtx_azTXPIQ,290
|
|
1968
1968
|
edc_model_to_dataframe/constants.py,sha256=xgCr2ZXUsjZqf4wfpY0JBhQoARzQEVS8tnkfBWwOMVQ,702
|
|
1969
|
-
edc_model_to_dataframe/model_to_dataframe.py,sha256=
|
|
1970
|
-
edc_model_to_dataframe/read_frame_edc.py,sha256=
|
|
1969
|
+
edc_model_to_dataframe/model_to_dataframe.py,sha256=Ut-Ixi7eoppBlnlgq4gBnhBh3ts13n1UjLMGQcKY7Bs,18684
|
|
1970
|
+
edc_model_to_dataframe/read_frame_edc.py,sha256=aOj3FUUvTCr-V-RmkBQtSGpg46zpWfX40HfOT3eRS4U,800
|
|
1971
1971
|
edc_model_to_dataframe/urls.py,sha256=KChLHeE6yfONC6IdMm3Q4U3u7tmy_Mb1w2lrbpFz96g,148
|
|
1972
1972
|
edc_navbar/__init__.py,sha256=Y95juevvOawGZmOV3Ofef7oGwYSWWttAYT0v0mehkJc,195
|
|
1973
1973
|
edc_navbar/apps.py,sha256=fiWHsCYaDnHXNfYUu6DuvlAcO2k45-zkdbuXwXI3ksc,367
|
|
@@ -3406,7 +3406,7 @@ edc_vitals/models/fields/waist_circumference.py,sha256=fZcHFDdEwWLjIVLktKrFCD9UU
|
|
|
3406
3406
|
edc_vitals/models/fields/weight.py,sha256=zo9_9e3Cpu0UqoRbWS-iDkcDo6fK80b1dDQy4x4MyxE,921
|
|
3407
3407
|
edc_vitals/utils.py,sha256=vXid44KUXxeaSyund_y5MNXc5DFJs052_PwUAjszE2k,1384
|
|
3408
3408
|
edc_vitals/validators.py,sha256=vNiElWMs0rRnHRNuVoPLRf0rW_C_0xcfUyep1Y_Si5s,156
|
|
3409
|
-
clinicedc-2.0.
|
|
3410
|
-
clinicedc-2.0.
|
|
3411
|
-
clinicedc-2.0.
|
|
3412
|
-
clinicedc-2.0.
|
|
3409
|
+
clinicedc-2.0.17.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
|
3410
|
+
clinicedc-2.0.17.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
|
|
3411
|
+
clinicedc-2.0.17.dist-info/METADATA,sha256=Vh9mgF060UWvEzXQ9O7I8XTXxKqDH9REwVJLEdtoDrs,15899
|
|
3412
|
+
clinicedc-2.0.17.dist-info/RECORD,,
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
from copy import copy
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import pandas as pd
|
|
8
8
|
from django.apps import apps as django_apps
|
|
9
|
+
from django.contrib.sites.models import Site
|
|
9
10
|
from django.core.exceptions import FieldError
|
|
10
11
|
from django.db import OperationalError
|
|
12
|
+
from django.db.models import QuerySet
|
|
11
13
|
from django_crypto_fields.utils import get_encrypted_fields, has_encrypted_fields
|
|
12
14
|
from django_pandas.io import read_frame
|
|
15
|
+
from pandas import Series
|
|
13
16
|
|
|
14
|
-
from .
|
|
17
|
+
from edc_lab.models import Panel
|
|
18
|
+
from edc_list_data.model_mixins import ListModelMixin, ListUuidModelMixin
|
|
19
|
+
from edc_model.models import BaseUuidModel
|
|
20
|
+
from edc_sites.model_mixins import SiteModelMixin
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
from django.db.models import QuerySet
|
|
22
|
+
from .constants import ACTION_ITEM_COLUMNS, SYSTEM_COLUMNS
|
|
18
23
|
|
|
19
|
-
from edc_model.models import BaseUuidModel
|
|
20
|
-
from edc_sites.model_mixins import SiteModelMixin
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
class MyModel(SiteModelMixin, BaseUuidModel):
|
|
26
|
+
class Meta(BaseUuidModel.Meta):
|
|
27
|
+
pass
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
__all__ = ["ModelToDataframe", "ModelToDataframeError"]
|
|
@@ -52,16 +55,16 @@ class ModelToDataframe:
|
|
|
52
55
|
See also: get_crf()
|
|
53
56
|
"""
|
|
54
57
|
|
|
55
|
-
sys_field_names:
|
|
58
|
+
sys_field_names: tuple[str, ...] = (
|
|
56
59
|
"_state",
|
|
57
60
|
"_user_container_instance",
|
|
58
61
|
"_domain_cache",
|
|
59
62
|
"using",
|
|
60
63
|
"slug",
|
|
61
|
-
|
|
62
|
-
edc_sys_columns:
|
|
63
|
-
action_item_columns:
|
|
64
|
-
illegal_chars: dict[str] = {
|
|
64
|
+
)
|
|
65
|
+
edc_sys_columns: tuple[str, ...] = SYSTEM_COLUMNS
|
|
66
|
+
action_item_columns: tuple[str, ...] = ACTION_ITEM_COLUMNS
|
|
67
|
+
illegal_chars: dict[str, str] = { # noqa: RUF012
|
|
65
68
|
"\u2019": "'",
|
|
66
69
|
"\u2018": "'",
|
|
67
70
|
"\u201d": '"',
|
|
@@ -72,7 +75,7 @@ class ModelToDataframe:
|
|
|
72
75
|
def __init__(
|
|
73
76
|
self,
|
|
74
77
|
model: str | None = None,
|
|
75
|
-
queryset: QuerySet | None = None,
|
|
78
|
+
queryset: [QuerySet] | None = None,
|
|
76
79
|
query_filter: dict | None = None,
|
|
77
80
|
decrypt: bool | None = None,
|
|
78
81
|
drop_sys_columns: bool | None = None,
|
|
@@ -104,7 +107,7 @@ class ModelToDataframe:
|
|
|
104
107
|
try:
|
|
105
108
|
self.model_cls = django_apps.get_model(self.model)
|
|
106
109
|
except LookupError as e:
|
|
107
|
-
raise LookupError(f"Model is {self.model}. Got `{e}`")
|
|
110
|
+
raise LookupError(f"Model is {self.model}. Got `{e}`") from e
|
|
108
111
|
if self.sites:
|
|
109
112
|
try:
|
|
110
113
|
if queryset:
|
|
@@ -151,7 +154,7 @@ class ModelToDataframe:
|
|
|
151
154
|
|
|
152
155
|
dataframe = self.merge_m2ms(dataframe)
|
|
153
156
|
|
|
154
|
-
dataframe.rename(columns=self.columns
|
|
157
|
+
dataframe = dataframe.rename(columns=self.columns)
|
|
155
158
|
|
|
156
159
|
# remove timezone if asked
|
|
157
160
|
if self.remove_timezone:
|
|
@@ -174,12 +177,12 @@ class ModelToDataframe:
|
|
|
174
177
|
dataframe[column] = dataframe[column].dt.total_seconds()
|
|
175
178
|
|
|
176
179
|
# fillna
|
|
177
|
-
dataframe.fillna(value=np.nan, axis=0
|
|
180
|
+
dataframe = dataframe.fillna(value=np.nan, axis=0)
|
|
178
181
|
|
|
179
182
|
# remove illegal chars
|
|
180
183
|
for column in list(dataframe.select_dtypes(include=["object"]).columns):
|
|
181
184
|
dataframe[column] = dataframe.apply(
|
|
182
|
-
lambda x: self._clean_chars(x[
|
|
185
|
+
lambda x, col=column: self._clean_chars(x[col]), axis=1
|
|
183
186
|
)
|
|
184
187
|
self._dataframe = dataframe
|
|
185
188
|
return self._dataframe
|
|
@@ -188,7 +191,7 @@ class ModelToDataframe:
|
|
|
188
191
|
queryset = self.queryset.values_list(*self.columns).filter(**self.query_filter)
|
|
189
192
|
return pd.DataFrame(list(queryset), columns=[v for v in self.columns])
|
|
190
193
|
|
|
191
|
-
def get_dataframe_with_encrypted_fields(self, row_count: int) -> pd.DataFrame:
|
|
194
|
+
def get_dataframe_with_encrypted_fields(self, row_count: int) -> pd.DataFrame: # noqa: ARG002
|
|
192
195
|
df = read_frame(
|
|
193
196
|
self.queryset.filter(**self.query_filter), verbose=self.read_frame_verbose
|
|
194
197
|
)
|
|
@@ -233,28 +236,27 @@ class ModelToDataframe:
|
|
|
233
236
|
dataframe = dataframe.merge(df_m2m, on="id", how="left")
|
|
234
237
|
return dataframe
|
|
235
238
|
|
|
236
|
-
def _clean_chars(self, s):
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
s = s.replace(k, v)
|
|
246
|
-
except (AttributeError, TypeError):
|
|
247
|
-
break
|
|
248
|
-
return s
|
|
239
|
+
def _clean_chars(self, s: Series) -> Series:
|
|
240
|
+
if not s.empty:
|
|
241
|
+
for k, v in self.illegal_chars.items():
|
|
242
|
+
try:
|
|
243
|
+
s = s.replace(k, v)
|
|
244
|
+
except (AttributeError, TypeError):
|
|
245
|
+
break
|
|
246
|
+
return s
|
|
247
|
+
return np.nan
|
|
249
248
|
|
|
250
249
|
def move_sys_columns_to_end(self, columns: dict[str, str]) -> dict[str, str]:
|
|
251
250
|
system_columns = [
|
|
252
251
|
f.name for f in self.model_cls._meta.get_fields() if f.name in SYSTEM_COLUMNS
|
|
253
252
|
]
|
|
254
253
|
new_columns = {k: v for k, v in columns.items() if k not in system_columns}
|
|
255
|
-
if
|
|
256
|
-
|
|
257
|
-
|
|
254
|
+
if (
|
|
255
|
+
system_columns
|
|
256
|
+
and len(new_columns.keys()) != len(columns.keys())
|
|
257
|
+
and not self.drop_sys_columns
|
|
258
|
+
):
|
|
259
|
+
new_columns.update({k: k for k in system_columns})
|
|
258
260
|
return new_columns
|
|
259
261
|
|
|
260
262
|
def move_action_item_columns(self, columns: dict[str, str]) -> dict[str, str]:
|
|
@@ -262,12 +264,11 @@ class ModelToDataframe:
|
|
|
262
264
|
f.name for f in self.model_cls._meta.get_fields() if f.name in ACTION_ITEM_COLUMNS
|
|
263
265
|
]
|
|
264
266
|
new_columns = {k: v for k, v in columns.items() if k not in ACTION_ITEM_COLUMNS}
|
|
265
|
-
if action_item_columns
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
)
|
|
270
|
-
new_columns.update({k: k for k in ACTION_ITEM_COLUMNS})
|
|
267
|
+
if action_item_columns and (
|
|
268
|
+
len(new_columns.keys()) != len(columns.keys())
|
|
269
|
+
and not self.drop_action_item_columns
|
|
270
|
+
):
|
|
271
|
+
new_columns.update({k: k for k in ACTION_ITEM_COLUMNS})
|
|
271
272
|
return new_columns
|
|
272
273
|
|
|
273
274
|
@property
|
|
@@ -285,12 +286,10 @@ class ModelToDataframe:
|
|
|
285
286
|
columns = {col: col for col in columns_list}
|
|
286
287
|
for column_name in columns_list:
|
|
287
288
|
if column_name.endswith("_visit_id"):
|
|
288
|
-
|
|
289
|
+
with contextlib.suppress(FieldError):
|
|
289
290
|
columns = self.add_columns_for_subject_visit(
|
|
290
291
|
column_name=column_name, columns=columns
|
|
291
292
|
)
|
|
292
|
-
except FieldError:
|
|
293
|
-
pass
|
|
294
293
|
if column_name.endswith("_requisition") or column_name.endswith(
|
|
295
294
|
"requisition_id"
|
|
296
295
|
):
|
|
@@ -316,10 +315,8 @@ class ModelToDataframe:
|
|
|
316
315
|
else:
|
|
317
316
|
raise
|
|
318
317
|
for name in self.sys_field_names:
|
|
319
|
-
|
|
318
|
+
with contextlib.suppress(ValueError):
|
|
320
319
|
columns_list.remove(name)
|
|
321
|
-
except ValueError:
|
|
322
|
-
pass
|
|
323
320
|
if not self.decrypt:
|
|
324
321
|
columns_list = [col for col in columns_list if col not in self.encrypted_columns]
|
|
325
322
|
return columns_list
|
|
@@ -338,7 +335,6 @@ class ModelToDataframe:
|
|
|
338
335
|
@property
|
|
339
336
|
def list_columns(self) -> list[str]:
|
|
340
337
|
"""Return a list of column names with fk to a list model."""
|
|
341
|
-
from edc_list_data.model_mixins import ListModelMixin, ListUuidModelMixin
|
|
342
338
|
|
|
343
339
|
if not self._list_columns:
|
|
344
340
|
list_columns = []
|
|
@@ -348,14 +344,13 @@ class ModelToDataframe:
|
|
|
348
344
|
and fld_cls.related_model
|
|
349
345
|
and issubclass(fld_cls.related_model, (ListModelMixin, ListUuidModelMixin))
|
|
350
346
|
):
|
|
351
|
-
list_columns.append(fld_cls.attname)
|
|
347
|
+
list_columns.append(fld_cls.attname) # noqa: PERF401
|
|
352
348
|
self._list_columns = list(set(list_columns))
|
|
353
349
|
return self._list_columns
|
|
354
350
|
|
|
355
351
|
@property
|
|
356
352
|
def site_columns(self) -> list[str]:
|
|
357
353
|
"""Return a list of column names with fk to a site model."""
|
|
358
|
-
from django.contrib.sites.models import Site
|
|
359
354
|
|
|
360
355
|
if not self._site_columns:
|
|
361
356
|
site_columns = []
|
|
@@ -372,10 +367,6 @@ class ModelToDataframe:
|
|
|
372
367
|
@property
|
|
373
368
|
def other_columns(self) -> list[str]:
|
|
374
369
|
"""Return other column names with fk to a common models."""
|
|
375
|
-
from django.contrib.sites.models import Site
|
|
376
|
-
|
|
377
|
-
from edc_lab.models import Panel
|
|
378
|
-
|
|
379
370
|
related_model = [Site, Panel]
|
|
380
371
|
if not self._list_columns:
|
|
381
372
|
list_columns = []
|
|
@@ -7,7 +7,7 @@ __all__ = ["read_frame_edc"]
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def read_frame_edc(
|
|
10
|
-
queryset: QuerySet | str = None,
|
|
10
|
+
queryset: [QuerySet] | str | None = None,
|
|
11
11
|
drop_sys_columns: bool | None = None,
|
|
12
12
|
drop_action_item_columns: bool | None = None,
|
|
13
13
|
read_frame_verbose: bool | None = None,
|
|
File without changes
|
|
File without changes
|