meta-edc 0.3.33__py3-none-any.whl → 0.3.35__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_auth/auth_objects.py +15 -0
- meta_auth/auths.py +5 -2
- meta_edc/settings/debug.py +2 -2
- meta_edc/settings/defaults.py +4 -0
- meta_edc/urls.py +1 -0
- {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/METADATA +6 -3
- {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/RECORD +43 -29
- {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/WHEEL +1 -1
- meta_pharmacy/admin.py +135 -0
- meta_pharmacy/admin_site.py +5 -0
- meta_pharmacy/apps.py +3 -0
- meta_pharmacy/constants.py +10 -0
- meta_pharmacy/forms.py +65 -0
- meta_pharmacy/migrations/0002_initial.py +693 -0
- meta_pharmacy/migrations/0003_auto_20240909_2335.py +64 -0
- meta_pharmacy/migrations/0004_alter_historicalsubstitutions_report_datetime_and_more.py +23 -0
- meta_pharmacy/migrations/0005_auto_20240911_0352.py +17 -0
- meta_pharmacy/models.py +102 -0
- meta_pharmacy/urls.py +8 -0
- meta_prn/action_items.py +9 -1
- meta_reports/admin/__init__.py +1 -0
- meta_reports/admin/dbviews/__init__.py +1 -0
- meta_reports/admin/dbviews/imp_substitutions_admin.py +101 -0
- meta_reports/migrations/0017_auto_20240819_1711.py +1 -0
- meta_reports/migrations/0047_impsubstitutions.py +56 -0
- meta_reports/migrations/0048_auto_20240909_2338.py +48 -0
- meta_reports/migrations/0049_auto_20240911_0327.py +54 -0
- meta_reports/models/__init__.py +1 -0
- meta_reports/models/dbviews/__init__.py +1 -0
- meta_reports/models/dbviews/imp_substitutions/__init__.py +1 -0
- meta_reports/models/dbviews/imp_substitutions/unmanaged_model.py +39 -0
- meta_reports/models/dbviews/imp_substitutions/view_definition.py +21 -0
- meta_reports/models/dbviews/on_study_missing_values/qa_cases.py +0 -1
- meta_sites/tests/test_sites.py +1 -1
- meta_subject/migrations/0107_auto_20220415_0043.py +28 -22
- meta_subject/migrations/0126_auto_20220719_2142.py +4 -4
- meta_subject/migrations/0131_auto_20220722_0411.py +28 -23
- meta_subject/migrations/0132_auto_20220722_1825.py +10 -6
- meta_subject/migrations/0135_auto_20220722_2212.py +39 -35
- meta_subject/migrations/0150_auto_20220914_0039.py +15 -11
- meta_analytics/notebooks/meta_endpoints.ipynb +0 -286
- {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/AUTHORS +0 -0
- {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/LICENSE +0 -0
- {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
# Generated by Django 5.1 on 2024-09-09 20:35
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
import pandas as pd
|
5
|
+
from django.apps import apps as django_apps
|
6
|
+
from django.conf import settings
|
7
|
+
from django.db import migrations
|
8
|
+
from edc_utils import get_utcnow
|
9
|
+
from tqdm import tqdm
|
10
|
+
|
11
|
+
|
12
|
+
def populate_substitutions(apps, schema_editor):
|
13
|
+
"""import CSV file into pandas dataframe and use DF to
|
14
|
+
populate the model.
|
15
|
+
|
16
|
+
CSV file was sent to ERIK on 2024-09-09 in relation to
|
17
|
+
a possible protocol violation where a subject was given the
|
18
|
+
incorrect IMP. The CSV file lists `sid` where a bottle was not ready
|
19
|
+
to dispense and a bottle of `dispensed_sid` was dispensed instead.
|
20
|
+
"""
|
21
|
+
path = Path(settings.META_PHARMACY_RX_SUBSTITUTION_FILE).expanduser()
|
22
|
+
df = pd.read_csv(path, header=1)
|
23
|
+
df = df.rename(
|
24
|
+
columns={"MISSING DRUG": "sid", "SUBSTITUTE": "dispensed_sid", "SITE": "site_id"}
|
25
|
+
)
|
26
|
+
df = df.rename(
|
27
|
+
columns={"MISSING DRUG": "sid", "SUBSTITUTE": "dispensed_sid", "SITE": "site_id"}
|
28
|
+
)
|
29
|
+
df["sid"] = df["sid"].apply(pd.to_numeric, errors="coerce")
|
30
|
+
df["dispensed_sid"] = df["dispensed_sid"].apply(pd.to_numeric, errors="coerce")
|
31
|
+
df["site_id"] = df["site_id"].apply(pd.to_numeric, errors="coerce")
|
32
|
+
now = get_utcnow()
|
33
|
+
model_cls = django_apps.get_model("meta_pharmacy.substitutions")
|
34
|
+
df_error = df[df["dispensed_sid"].isna()]
|
35
|
+
df_error = df_error.reset_index()
|
36
|
+
df_ok = df[df["dispensed_sid"].notna()]
|
37
|
+
df_ok = df_ok.reset_index()
|
38
|
+
data = [
|
39
|
+
model_cls(
|
40
|
+
row_index=row["index"],
|
41
|
+
report_datetime=now,
|
42
|
+
site_id=row["site_id"],
|
43
|
+
sid=row["sid"],
|
44
|
+
dispensed_sid=row["dispensed_sid"],
|
45
|
+
user_created="erikvw",
|
46
|
+
created=now,
|
47
|
+
locale_created="en-gb",
|
48
|
+
)
|
49
|
+
for _, row in df_ok.iterrows()
|
50
|
+
]
|
51
|
+
created = len(model_cls.objects.bulk_create(data))
|
52
|
+
for obj in tqdm(model_cls.objects.all(), total=created):
|
53
|
+
obj.save()
|
54
|
+
if not df_error.empty:
|
55
|
+
print(df_error[["index", "sid"]])
|
56
|
+
|
57
|
+
|
58
|
+
class Migration(migrations.Migration):
|
59
|
+
|
60
|
+
dependencies = [
|
61
|
+
("meta_pharmacy", "0002_initial"),
|
62
|
+
]
|
63
|
+
|
64
|
+
operations = [migrations.RunPython(populate_substitutions)]
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Generated by Django 5.1 on 2024-09-11 00:30
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
("meta_pharmacy", "0003_auto_20240909_2335"),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AlterField(
|
14
|
+
model_name="historicalsubstitutions",
|
15
|
+
name="report_datetime",
|
16
|
+
field=models.DateTimeField(null=True),
|
17
|
+
),
|
18
|
+
migrations.AlterField(
|
19
|
+
model_name="substitutions",
|
20
|
+
name="report_datetime",
|
21
|
+
field=models.DateTimeField(null=True),
|
22
|
+
),
|
23
|
+
]
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Generated by Django 5.1 on 2024-09-11 00:52
|
2
|
+
from django.apps import apps as django_apps
|
3
|
+
from django.db import migrations
|
4
|
+
|
5
|
+
|
6
|
+
def update_report_datetime_as_none(apps, schema_editor):
|
7
|
+
model_cls = django_apps.get_model("meta_pharmacy.substitutions")
|
8
|
+
model_cls.objects.update(report_datetime=None)
|
9
|
+
|
10
|
+
|
11
|
+
class Migration(migrations.Migration):
|
12
|
+
|
13
|
+
dependencies = [
|
14
|
+
("meta_pharmacy", "0004_alter_historicalsubstitutions_report_datetime_and_more"),
|
15
|
+
]
|
16
|
+
|
17
|
+
operations = [migrations.RunPython(update_report_datetime_as_none)]
|
meta_pharmacy/models.py
CHANGED
@@ -0,0 +1,102 @@
|
|
1
|
+
from django.core.exceptions import ObjectDoesNotExist
|
2
|
+
from django.db import models
|
3
|
+
from django.db.models import Index
|
4
|
+
from edc_constants.choices import YES_NO_NOT_EVALUATED
|
5
|
+
from edc_constants.constants import NO, NOT_EVALUATED, YES
|
6
|
+
from edc_identifier.model_mixins import NonUniqueSubjectIdentifierFieldMixin
|
7
|
+
from edc_model.models import BaseUuidModel, HistoricalRecords
|
8
|
+
from edc_pharmacy.models import Rx as BaseRx
|
9
|
+
from edc_sites.managers import CurrentSiteManager
|
10
|
+
from edc_sites.model_mixins import SiteModelMixin
|
11
|
+
|
12
|
+
from meta_pharmacy.constants import MISSING_SUBJECT_IDENTIFIER
|
13
|
+
from meta_rando.models import RandomizationList
|
14
|
+
|
15
|
+
|
16
|
+
class MyManager(models.Manager):
|
17
|
+
use_in_migrations = True
|
18
|
+
|
19
|
+
|
20
|
+
class Rx(BaseRx):
|
21
|
+
"""A proxy model of edc_pharmacy.Rx.
|
22
|
+
|
23
|
+
For autocomplete only.
|
24
|
+
"""
|
25
|
+
|
26
|
+
def save(self, *args, **kwargs):
|
27
|
+
raise NotImplementedError(
|
28
|
+
"This proxy model may not be saved. Permissions should be view only"
|
29
|
+
)
|
30
|
+
|
31
|
+
class Meta:
|
32
|
+
proxy = True
|
33
|
+
|
34
|
+
|
35
|
+
class Substitutions(NonUniqueSubjectIdentifierFieldMixin, SiteModelMixin, BaseUuidModel):
|
36
|
+
"""A model to capture a CSV file that lists IMP substitutions
|
37
|
+
made where a bottle of `sid` is substituted with a bottle of
|
38
|
+
`dispensed_sid`.
|
39
|
+
|
40
|
+
The IMP substitutions were not done by any approved procedure. SID
|
41
|
+
values are sequential and not designed for human transcription.
|
42
|
+
"""
|
43
|
+
|
44
|
+
report_datetime = models.DateTimeField(null=True, blank=False)
|
45
|
+
|
46
|
+
row_index = models.IntegerField(null=True)
|
47
|
+
|
48
|
+
rx = models.ForeignKey(Rx, on_delete=models.PROTECT, null=True, blank=True)
|
49
|
+
|
50
|
+
sid = models.IntegerField(verbose_name="SID")
|
51
|
+
|
52
|
+
visit_no = models.IntegerField("Visit Number", null=True, blank=True)
|
53
|
+
|
54
|
+
dispensed_sid = models.IntegerField(verbose_name="Dispensed SID")
|
55
|
+
|
56
|
+
updated_visit_no = models.IntegerField("Visit Number", null=True, blank=True)
|
57
|
+
|
58
|
+
updated_subject_identifier = models.CharField(
|
59
|
+
max_length=50, verbose_name="Taken from subject", null=True, blank=True
|
60
|
+
)
|
61
|
+
|
62
|
+
arm_match = models.CharField(
|
63
|
+
max_length=15, choices=YES_NO_NOT_EVALUATED, default=NOT_EVALUATED
|
64
|
+
)
|
65
|
+
|
66
|
+
notes = models.TextField(verbose_name="Notes")
|
67
|
+
|
68
|
+
objects = MyManager()
|
69
|
+
on_site = CurrentSiteManager()
|
70
|
+
history = HistoricalRecords(inherit=True)
|
71
|
+
|
72
|
+
def __str__(self):
|
73
|
+
if self.rx:
|
74
|
+
return self.rx.subject_identifier
|
75
|
+
return str(self.sid)
|
76
|
+
|
77
|
+
def save(self, *args, **kwargs):
|
78
|
+
if not self.rx:
|
79
|
+
try:
|
80
|
+
self.rx = Rx.objects.get(rando_sid=self.sid)
|
81
|
+
except ObjectDoesNotExist:
|
82
|
+
self.status = MISSING_SUBJECT_IDENTIFIER
|
83
|
+
else:
|
84
|
+
self.subject_identifier = self.rx.subject_identifier
|
85
|
+
else:
|
86
|
+
self.subject_identifier = self.rx.subject_identifier
|
87
|
+
rando_a = RandomizationList.objects.get(sid=self.sid)
|
88
|
+
rando_b = RandomizationList.objects.get(sid=self.dispensed_sid)
|
89
|
+
if rando_a.assignment != rando_b.assignment:
|
90
|
+
self.arm_match = NO
|
91
|
+
else:
|
92
|
+
self.arm_match = YES
|
93
|
+
|
94
|
+
super().save(*args, **kwargs)
|
95
|
+
|
96
|
+
class Meta(SiteModelMixin.Meta, BaseUuidModel.Meta):
|
97
|
+
verbose_name_plural = "IMP Substitutions"
|
98
|
+
verbose_name = "IMP Substitution"
|
99
|
+
indexes = [
|
100
|
+
Index(fields=["sid", "dispensed_sid", "subject_identifier"]),
|
101
|
+
Index(fields=["row_index"]),
|
102
|
+
]
|
meta_pharmacy/urls.py
ADDED
meta_prn/action_items.py
CHANGED
@@ -27,6 +27,7 @@ from .constants import (
|
|
27
27
|
OFFSCHEDULE_PREGNANCY_ACTION,
|
28
28
|
OFFSTUDY_MEDICATION_ACTION,
|
29
29
|
PREGNANCY_NOTIFICATION_ACTION,
|
30
|
+
REFERRAL,
|
30
31
|
UNBLINDING_REQUEST_ACTION,
|
31
32
|
UNBLINDING_REVIEW_ACTION,
|
32
33
|
)
|
@@ -136,7 +137,7 @@ class DmReferralAction(ActionWithNotification):
|
|
136
137
|
name = DM_REFFERAL_ACTION
|
137
138
|
display_name = "Diabetes referral"
|
138
139
|
notification_display_name = "Diabetes referral"
|
139
|
-
parent_action_names =
|
140
|
+
parent_action_names = [OFFSTUDY_MEDICATION_ACTION]
|
140
141
|
reference_model = "meta_prn.dmreferral"
|
141
142
|
show_link_to_changelist = True
|
142
143
|
show_link_to_add = True
|
@@ -182,6 +183,13 @@ class OffStudyMedicationAction(ActionWithNotification):
|
|
182
183
|
priority = HIGH_PRIORITY
|
183
184
|
singleton = True
|
184
185
|
|
186
|
+
def get_next_actions(self):
|
187
|
+
if self.reference_obj.reason == REFERRAL:
|
188
|
+
next_actions = [DM_REFFERAL_ACTION]
|
189
|
+
else:
|
190
|
+
next_actions = [END_OF_STUDY_ACTION]
|
191
|
+
return next_actions
|
192
|
+
|
185
193
|
|
186
194
|
class UnblindingRequestAction(ActionWithNotification):
|
187
195
|
name = UNBLINDING_REQUEST_ACTION
|
meta_reports/admin/__init__.py
CHANGED
@@ -0,0 +1,101 @@
|
|
1
|
+
from django.apps import apps as django_apps
|
2
|
+
from django.contrib import admin
|
3
|
+
from django.core.exceptions import ObjectDoesNotExist
|
4
|
+
from django.template.loader import render_to_string
|
5
|
+
from django.urls import reverse
|
6
|
+
from django.utils.translation import gettext_lazy as _
|
7
|
+
from edc_appointment.models import Appointment
|
8
|
+
from edc_model_admin.dashboard import ModelAdminDashboardMixin
|
9
|
+
from edc_model_admin.list_filters import ReportDateListFilter
|
10
|
+
from edc_model_admin.mixins import TemplatesModelAdminMixin
|
11
|
+
from edc_qareports.modeladmin_mixins import QaReportModelAdminMixin
|
12
|
+
from edc_sites.admin import SiteModelAdminMixin
|
13
|
+
from edc_visit_schedule.admin import ScheduleStatusListFilter
|
14
|
+
from edc_visit_schedule.constants import DAY1
|
15
|
+
|
16
|
+
from ...admin_site import meta_reports_admin
|
17
|
+
from ...models import ImpSubstitutions
|
18
|
+
|
19
|
+
|
20
|
+
@admin.register(ImpSubstitutions, site=meta_reports_admin)
|
21
|
+
class ImpSubstitutionsAdmin(
|
22
|
+
QaReportModelAdminMixin,
|
23
|
+
SiteModelAdminMixin,
|
24
|
+
ModelAdminDashboardMixin,
|
25
|
+
TemplatesModelAdminMixin,
|
26
|
+
admin.ModelAdmin,
|
27
|
+
):
|
28
|
+
ordering = ["site", "subject_identifier"]
|
29
|
+
list_display = [
|
30
|
+
"dashboard",
|
31
|
+
"render_button",
|
32
|
+
"subject",
|
33
|
+
"sid",
|
34
|
+
"dispensed_sid",
|
35
|
+
"report_date",
|
36
|
+
"arm_match",
|
37
|
+
"allocated_date",
|
38
|
+
"user_created",
|
39
|
+
"user_modified",
|
40
|
+
"modified",
|
41
|
+
]
|
42
|
+
|
43
|
+
list_filter = [
|
44
|
+
"arm_match",
|
45
|
+
ScheduleStatusListFilter,
|
46
|
+
ReportDateListFilter,
|
47
|
+
"allocated_datetime",
|
48
|
+
]
|
49
|
+
|
50
|
+
search_fields = ["subject_identifier", "sid", "dispensed_sid"]
|
51
|
+
|
52
|
+
def dashboard(self, obj=None, label=None) -> str:
|
53
|
+
kwargs = self.get_subject_dashboard_url_kwargs(obj)
|
54
|
+
try:
|
55
|
+
kwargs.update(
|
56
|
+
appointment=str(
|
57
|
+
Appointment.objects.get(
|
58
|
+
subject_identifier=obj.subject_identifier,
|
59
|
+
visit_code=DAY1,
|
60
|
+
visit_code_sequence=0,
|
61
|
+
).id
|
62
|
+
)
|
63
|
+
)
|
64
|
+
except ObjectDoesNotExist:
|
65
|
+
pass
|
66
|
+
url = reverse(self.get_subject_dashboard_url_name(obj=obj), kwargs=kwargs)
|
67
|
+
context = dict(title=_("Go to subject's dashboard@1000"), url=url, label=label)
|
68
|
+
return render_to_string("dashboard_button.html", context=context)
|
69
|
+
|
70
|
+
@admin.display(description="Subject", ordering="subject_identifier")
|
71
|
+
def subject(self, obj):
|
72
|
+
return obj.subject_identifier
|
73
|
+
|
74
|
+
@admin.display(description="Allocated", ordering="allocated_datetime")
|
75
|
+
def allocated_date(self, obj=None):
|
76
|
+
return obj.allocated_datetime.date() if obj.allocated_datetime else None
|
77
|
+
|
78
|
+
@admin.display(description="Update")
|
79
|
+
def render_button(self, obj=None):
|
80
|
+
crf_model_cls = django_apps.get_model("meta_pharmacy", "substitutions")
|
81
|
+
url = reverse(
|
82
|
+
f"meta_pharmacy_admin:{crf_model_cls._meta.label_lower.replace('.', '_')}_change",
|
83
|
+
args=(obj.original_id,),
|
84
|
+
)
|
85
|
+
url = (
|
86
|
+
f"{url}?next={self.admin_site.name}:"
|
87
|
+
f"{self.model._meta.label_lower.replace('.', '_')}_changelist"
|
88
|
+
)
|
89
|
+
title = _(f"View {crf_model_cls._meta.verbose_name}")
|
90
|
+
label = _("View")
|
91
|
+
crf_button = render_to_string(
|
92
|
+
"edc_qareports/columns/change_button.html",
|
93
|
+
context=dict(title=title, url=url, label=label),
|
94
|
+
)
|
95
|
+
return crf_button
|
96
|
+
|
97
|
+
@admin.display(description="Report date", ordering="report_datetime")
|
98
|
+
def report_date(self, obj) -> str | None:
|
99
|
+
if obj.report_datetime:
|
100
|
+
return obj.report_datetime.date()
|
101
|
+
return None
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Generated by Django 5.1 on 2024-09-09 20:37
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
("meta_reports", "0046_auto_20240829_0250"),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.CreateModel(
|
14
|
+
name="ImpSubstitutions",
|
15
|
+
fields=[
|
16
|
+
(
|
17
|
+
"id",
|
18
|
+
models.BigAutoField(
|
19
|
+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
20
|
+
),
|
21
|
+
),
|
22
|
+
("report_model", models.CharField(max_length=50)),
|
23
|
+
("subject_identifier", models.CharField(max_length=25)),
|
24
|
+
("created", models.DateTimeField()),
|
25
|
+
("original_id", models.UUIDField(null=True)),
|
26
|
+
("sid", models.IntegerField(null=True, verbose_name="Original SID")),
|
27
|
+
(
|
28
|
+
"dispensed_sid",
|
29
|
+
models.IntegerField(null=True, verbose_name="Dispensed SID"),
|
30
|
+
),
|
31
|
+
(
|
32
|
+
"arm_match",
|
33
|
+
models.CharField(
|
34
|
+
choices=[
|
35
|
+
("Yes", "Yes"),
|
36
|
+
("No", "No"),
|
37
|
+
("not_evaluated", "Not evaluated"),
|
38
|
+
],
|
39
|
+
default="not_evaluated",
|
40
|
+
max_length=15,
|
41
|
+
),
|
42
|
+
),
|
43
|
+
("allocated_datetime", models.DateTimeField(null=True)),
|
44
|
+
("user_created", models.CharField(max_length=25)),
|
45
|
+
("user_modified", models.CharField(max_length=25)),
|
46
|
+
("modified", models.DateTimeField(null=True)),
|
47
|
+
],
|
48
|
+
options={
|
49
|
+
"verbose_name": "IMP Substitutions",
|
50
|
+
"verbose_name_plural": "IMP Substitutions",
|
51
|
+
"db_table": "imp_subjectitutions_view",
|
52
|
+
"managed": False,
|
53
|
+
"default_permissions": ("view", "export", "viewallsites"),
|
54
|
+
},
|
55
|
+
),
|
56
|
+
]
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Generated by Django 5.1 on 2024-09-09 20:38
|
2
|
+
|
3
|
+
import django_db_views.migration_functions
|
4
|
+
import django_db_views.operations
|
5
|
+
from django.db import migrations
|
6
|
+
|
7
|
+
|
8
|
+
class Migration(migrations.Migration):
|
9
|
+
|
10
|
+
dependencies = [
|
11
|
+
("meta_reports", "0047_impsubstitutions"),
|
12
|
+
]
|
13
|
+
|
14
|
+
operations = [
|
15
|
+
django_db_views.operations.ViewRunPython(
|
16
|
+
code=django_db_views.migration_functions.ForwardViewMigration(
|
17
|
+
"select *, uuid() as id, now() as `created`, 'meta_reports.imp_subjectitutions_view' as `report_model` from (SELECT s.subject_identifier, s.sid, s.dispensed_sid, arm_match, r.allocated_datetime, s.site_id, s.user_created, s.user_modified, s.modified, s.id AS original_id FROM meta_pharmacy_substitutions AS s LEFT JOIN meta_rando_randomizationlist AS r ON r.subject_identifier = s.subject_identifier ORDER BY s.subject_identifier) as A ORDER BY subject_identifier, site_id",
|
18
|
+
"imp_subjectitutions_view",
|
19
|
+
engine="django.db.backends.mysql",
|
20
|
+
),
|
21
|
+
reverse_code=django_db_views.migration_functions.BackwardViewMigration(
|
22
|
+
"", "imp_subjectitutions_view", engine="django.db.backends.mysql"
|
23
|
+
),
|
24
|
+
atomic=False,
|
25
|
+
),
|
26
|
+
django_db_views.operations.ViewRunPython(
|
27
|
+
code=django_db_views.migration_functions.ForwardViewMigration(
|
28
|
+
"select *, get_random_uuid() as id, now() as created, 'meta_reports.imp_subjectitutions_view' as report_model from (SELECT s.subject_identifier, s.sid, s.dispensed_sid, arm_match, r.allocated_datetime, s.site_id, s.user_created, s.user_modified, s.modified, s.id AS original_id FROM meta_pharmacy_substitutions AS s LEFT JOIN meta_rando_randomizationlist AS r ON r.subject_identifier = s.subject_identifier ORDER BY s.subject_identifier NULLS FIRST) as A ORDER BY subject_identifier, site_id",
|
29
|
+
"imp_subjectitutions_view",
|
30
|
+
engine="django.db.backends.postgresql",
|
31
|
+
),
|
32
|
+
reverse_code=django_db_views.migration_functions.BackwardViewMigration(
|
33
|
+
"", "imp_subjectitutions_view", engine="django.db.backends.postgresql"
|
34
|
+
),
|
35
|
+
atomic=False,
|
36
|
+
),
|
37
|
+
django_db_views.operations.ViewRunPython(
|
38
|
+
code=django_db_views.migration_functions.ForwardViewMigration(
|
39
|
+
"select *, uuid() as id, datetime() as created, 'meta_reports.imp_subjectitutions_view' as report_model from (SELECT s.subject_identifier, s.sid, s.dispensed_sid, arm_match, r.allocated_datetime, s.site_id, s.user_created, s.user_modified, s.modified, s.id AS original_id FROM meta_pharmacy_substitutions AS s LEFT JOIN meta_rando_randomizationlist AS r ON r.subject_identifier = s.subject_identifier ORDER BY s.subject_identifier) as A ORDER BY subject_identifier, site_id",
|
40
|
+
"imp_subjectitutions_view",
|
41
|
+
engine="django.db.backends.sqlite3",
|
42
|
+
),
|
43
|
+
reverse_code=django_db_views.migration_functions.BackwardViewMigration(
|
44
|
+
"", "imp_subjectitutions_view", engine="django.db.backends.sqlite3"
|
45
|
+
),
|
46
|
+
atomic=False,
|
47
|
+
),
|
48
|
+
]
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Generated by Django 5.1 on 2024-09-11 00:27
|
2
|
+
|
3
|
+
import django_db_views.migration_functions
|
4
|
+
import django_db_views.operations
|
5
|
+
from django.db import migrations
|
6
|
+
|
7
|
+
|
8
|
+
class Migration(migrations.Migration):
|
9
|
+
|
10
|
+
dependencies = [
|
11
|
+
("meta_reports", "0048_auto_20240909_2338"),
|
12
|
+
]
|
13
|
+
|
14
|
+
operations = [
|
15
|
+
django_db_views.operations.ViewRunPython(
|
16
|
+
code=django_db_views.migration_functions.ForwardViewMigration(
|
17
|
+
"select *, uuid() as id, now() as `created`, 'meta_reports.imp_subjectitutions_view' as `report_model` from (SELECT s.subject_identifier, s.sid, s.dispensed_sid, arm_match, s.report_datetime, r.allocated_datetime, s.site_id, s.user_created, s.user_modified, s.modified, s.id AS original_id FROM meta_pharmacy_substitutions AS s LEFT JOIN meta_rando_randomizationlist AS r ON r.subject_identifier = s.subject_identifier ORDER BY s.subject_identifier) as A ORDER BY subject_identifier, site_id",
|
18
|
+
"imp_subjectitutions_view",
|
19
|
+
engine="django.db.backends.mysql",
|
20
|
+
),
|
21
|
+
reverse_code=django_db_views.migration_functions.BackwardViewMigration(
|
22
|
+
"select *, uuid() as id, now() as `created`, 'meta_reports.imp_subjectitutions_view' as `report_model` from (SELECT s.subject_identifier, s.sid, s.dispensed_sid, arm_match, r.allocated_datetime, s.site_id, s.user_created, s.user_modified, s.modified, s.id AS original_id FROM meta_pharmacy_substitutions AS s LEFT JOIN meta_rando_randomizationlist AS r ON r.subject_identifier = s.subject_identifier ORDER BY s.subject_identifier) as A ORDER BY subject_identifier, site_id",
|
23
|
+
"imp_subjectitutions_view",
|
24
|
+
engine="django.db.backends.mysql",
|
25
|
+
),
|
26
|
+
atomic=False,
|
27
|
+
),
|
28
|
+
django_db_views.operations.ViewRunPython(
|
29
|
+
code=django_db_views.migration_functions.ForwardViewMigration(
|
30
|
+
"select *, get_random_uuid() as id, now() as created, 'meta_reports.imp_subjectitutions_view' as report_model from (SELECT s.subject_identifier, s.sid, s.dispensed_sid, arm_match, s.report_datetime, r.allocated_datetime, s.site_id, s.user_created, s.user_modified, s.modified, s.id AS original_id FROM meta_pharmacy_substitutions AS s LEFT JOIN meta_rando_randomizationlist AS r ON r.subject_identifier = s.subject_identifier ORDER BY s.subject_identifier NULLS FIRST) as A ORDER BY subject_identifier, site_id",
|
31
|
+
"imp_subjectitutions_view",
|
32
|
+
engine="django.db.backends.postgresql",
|
33
|
+
),
|
34
|
+
reverse_code=django_db_views.migration_functions.BackwardViewMigration(
|
35
|
+
"select *, get_random_uuid() as id, now() as created, 'meta_reports.imp_subjectitutions_view' as report_model from (SELECT s.subject_identifier, s.sid, s.dispensed_sid, arm_match, r.allocated_datetime, s.site_id, s.user_created, s.user_modified, s.modified, s.id AS original_id FROM meta_pharmacy_substitutions AS s LEFT JOIN meta_rando_randomizationlist AS r ON r.subject_identifier = s.subject_identifier ORDER BY s.subject_identifier NULLS FIRST) as A ORDER BY subject_identifier, site_id",
|
36
|
+
"imp_subjectitutions_view",
|
37
|
+
engine="django.db.backends.postgresql",
|
38
|
+
),
|
39
|
+
atomic=False,
|
40
|
+
),
|
41
|
+
django_db_views.operations.ViewRunPython(
|
42
|
+
code=django_db_views.migration_functions.ForwardViewMigration(
|
43
|
+
"select *, uuid() as id, datetime() as created, 'meta_reports.imp_subjectitutions_view' as report_model from (SELECT s.subject_identifier, s.sid, s.dispensed_sid, arm_match, s.report_datetime, r.allocated_datetime, s.site_id, s.user_created, s.user_modified, s.modified, s.id AS original_id FROM meta_pharmacy_substitutions AS s LEFT JOIN meta_rando_randomizationlist AS r ON r.subject_identifier = s.subject_identifier ORDER BY s.subject_identifier) as A ORDER BY subject_identifier, site_id",
|
44
|
+
"imp_subjectitutions_view",
|
45
|
+
engine="django.db.backends.sqlite3",
|
46
|
+
),
|
47
|
+
reverse_code=django_db_views.migration_functions.BackwardViewMigration(
|
48
|
+
"select *, uuid() as id, datetime() as created, 'meta_reports.imp_subjectitutions_view' as report_model from (SELECT s.subject_identifier, s.sid, s.dispensed_sid, arm_match, r.allocated_datetime, s.site_id, s.user_created, s.user_modified, s.modified, s.id AS original_id FROM meta_pharmacy_substitutions AS s LEFT JOIN meta_rando_randomizationlist AS r ON r.subject_identifier = s.subject_identifier ORDER BY s.subject_identifier) as A ORDER BY subject_identifier, site_id",
|
49
|
+
"imp_subjectitutions_view",
|
50
|
+
engine="django.db.backends.sqlite3",
|
51
|
+
),
|
52
|
+
atomic=False,
|
53
|
+
),
|
54
|
+
]
|
meta_reports/models/__init__.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
from .glucose_summary import GlucoseSummary
|
2
|
+
from .imp_substitutions import ImpSubstitutions
|
2
3
|
from .missing_screening_ogtt import NOTE_STATUSES, MissingOgttNote, MissingScreeningOgtt
|
3
4
|
from .on_study_missing_lab_values import OnStudyMissingLabValues
|
4
5
|
from .on_study_missing_values import OnStudyMissingValues
|
@@ -0,0 +1 @@
|
|
1
|
+
from .unmanaged_model import ImpSubstitutions
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from django.db import models
|
2
|
+
from django_db_views.db_view import DBView
|
3
|
+
from edc_constants.choices import YES_NO_NOT_EVALUATED
|
4
|
+
from edc_constants.constants import NOT_EVALUATED
|
5
|
+
from edc_qareports.model_mixins import QaReportModelMixin, qa_reports_permissions
|
6
|
+
|
7
|
+
from .view_definition import get_view_definition
|
8
|
+
|
9
|
+
|
10
|
+
class ImpSubstitutions(QaReportModelMixin, DBView):
|
11
|
+
|
12
|
+
original_id = models.UUIDField(null=True)
|
13
|
+
|
14
|
+
sid = models.IntegerField(verbose_name="Original SID", null=True)
|
15
|
+
|
16
|
+
dispensed_sid = models.IntegerField(verbose_name="Dispensed SID", null=True)
|
17
|
+
|
18
|
+
arm_match = models.CharField(
|
19
|
+
max_length=15, choices=YES_NO_NOT_EVALUATED, default=NOT_EVALUATED
|
20
|
+
)
|
21
|
+
|
22
|
+
report_datetime = models.DateTimeField(null=True)
|
23
|
+
|
24
|
+
allocated_datetime = models.DateTimeField(null=True)
|
25
|
+
|
26
|
+
user_created = models.CharField(max_length=25)
|
27
|
+
|
28
|
+
user_modified = models.CharField(max_length=25)
|
29
|
+
|
30
|
+
modified = models.DateTimeField(null=True)
|
31
|
+
|
32
|
+
view_definition = get_view_definition()
|
33
|
+
|
34
|
+
class Meta:
|
35
|
+
managed = False
|
36
|
+
db_table = "imp_subjectitutions_view"
|
37
|
+
verbose_name = "IMP Substitutions"
|
38
|
+
verbose_name_plural = "IMP Substitutions"
|
39
|
+
default_permissions = qa_reports_permissions
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from edc_qareports.sql_generator import SqlViewGenerator
|
2
|
+
|
3
|
+
|
4
|
+
def get_view_definition() -> dict:
|
5
|
+
subquery = """
|
6
|
+
select s.subject_identifier, s.sid, s.dispensed_sid, arm_match,
|
7
|
+
s.report_datetime, r.allocated_datetime,
|
8
|
+
s.site_id, s.user_created, s.user_modified, s.modified, s.id as original_id
|
9
|
+
from meta_pharmacy_substitutions as s left join meta_rando_randomizationlist as r
|
10
|
+
on r.subject_identifier=s.subject_identifier
|
11
|
+
order by s.subject_identifier
|
12
|
+
""" # noqa
|
13
|
+
sql_view = SqlViewGenerator(
|
14
|
+
report_model="meta_reports.imp_subjectitutions_view",
|
15
|
+
ordering=["subject_identifier", "site_id"],
|
16
|
+
)
|
17
|
+
return {
|
18
|
+
"django.db.backends.mysql": sql_view.as_mysql(subquery),
|
19
|
+
"django.db.backends.postgresql": sql_view.as_postgres(subquery),
|
20
|
+
"django.db.backends.sqlite3": sql_view.as_sqlite(subquery),
|
21
|
+
}
|
meta_sites/tests/test_sites.py
CHANGED