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.
Files changed (44) hide show
  1. meta_auth/auth_objects.py +15 -0
  2. meta_auth/auths.py +5 -2
  3. meta_edc/settings/debug.py +2 -2
  4. meta_edc/settings/defaults.py +4 -0
  5. meta_edc/urls.py +1 -0
  6. {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/METADATA +6 -3
  7. {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/RECORD +43 -29
  8. {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/WHEEL +1 -1
  9. meta_pharmacy/admin.py +135 -0
  10. meta_pharmacy/admin_site.py +5 -0
  11. meta_pharmacy/apps.py +3 -0
  12. meta_pharmacy/constants.py +10 -0
  13. meta_pharmacy/forms.py +65 -0
  14. meta_pharmacy/migrations/0002_initial.py +693 -0
  15. meta_pharmacy/migrations/0003_auto_20240909_2335.py +64 -0
  16. meta_pharmacy/migrations/0004_alter_historicalsubstitutions_report_datetime_and_more.py +23 -0
  17. meta_pharmacy/migrations/0005_auto_20240911_0352.py +17 -0
  18. meta_pharmacy/models.py +102 -0
  19. meta_pharmacy/urls.py +8 -0
  20. meta_prn/action_items.py +9 -1
  21. meta_reports/admin/__init__.py +1 -0
  22. meta_reports/admin/dbviews/__init__.py +1 -0
  23. meta_reports/admin/dbviews/imp_substitutions_admin.py +101 -0
  24. meta_reports/migrations/0017_auto_20240819_1711.py +1 -0
  25. meta_reports/migrations/0047_impsubstitutions.py +56 -0
  26. meta_reports/migrations/0048_auto_20240909_2338.py +48 -0
  27. meta_reports/migrations/0049_auto_20240911_0327.py +54 -0
  28. meta_reports/models/__init__.py +1 -0
  29. meta_reports/models/dbviews/__init__.py +1 -0
  30. meta_reports/models/dbviews/imp_substitutions/__init__.py +1 -0
  31. meta_reports/models/dbviews/imp_substitutions/unmanaged_model.py +39 -0
  32. meta_reports/models/dbviews/imp_substitutions/view_definition.py +21 -0
  33. meta_reports/models/dbviews/on_study_missing_values/qa_cases.py +0 -1
  34. meta_sites/tests/test_sites.py +1 -1
  35. meta_subject/migrations/0107_auto_20220415_0043.py +28 -22
  36. meta_subject/migrations/0126_auto_20220719_2142.py +4 -4
  37. meta_subject/migrations/0131_auto_20220722_0411.py +28 -23
  38. meta_subject/migrations/0132_auto_20220722_1825.py +10 -6
  39. meta_subject/migrations/0135_auto_20220722_2212.py +39 -35
  40. meta_subject/migrations/0150_auto_20220914_0039.py +15 -11
  41. meta_analytics/notebooks/meta_endpoints.ipynb +0 -286
  42. {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/AUTHORS +0 -0
  43. {meta_edc-0.3.33.dist-info → meta_edc-0.3.35.dist-info}/LICENSE +0 -0
  44. {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
@@ -0,0 +1,8 @@
1
+ from django.urls.conf import path
2
+ from django.views.generic import RedirectView
3
+
4
+ app_name = "meta_pharmacy"
5
+
6
+ urlpatterns = [
7
+ path("", RedirectView.as_view(url=f"/{app_name}/admin/"), name="home_url"),
8
+ ]
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 = None
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
@@ -1,5 +1,6 @@
1
1
  from .dbviews import (
2
2
  GlucoseSummaryAdmin,
3
+ ImpSubstitutionsAdmin,
3
4
  MissingOgttNoteModelAdmin,
4
5
  MissingScreeningOgttAdmin,
5
6
  OnStudyMissingLabValuesAdmin,
@@ -1,4 +1,5 @@
1
1
  from .glucose_summary_admin import GlucoseSummaryAdmin
2
+ from .imp_substitutions_admin import ImpSubstitutionsAdmin
2
3
  from .missing_screening_ogtt_admin import (
3
4
  MissingOgttNoteModelAdmin,
4
5
  MissingScreeningOgttAdmin,
@@ -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
@@ -9,6 +9,7 @@ class Migration(migrations.Migration):
9
9
 
10
10
  dependencies = [
11
11
  ("meta_reports", "0016_missingscreeningogtt"),
12
+ ("meta_screening", "0067_alter_historicalscreeningpartone_report_datetime_and_more"),
12
13
  ]
13
14
 
14
15
  operations = [
@@ -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
+ ]
@@ -1,6 +1,7 @@
1
1
  from .dbviews import (
2
2
  NOTE_STATUSES,
3
3
  GlucoseSummary,
4
+ ImpSubstitutions,
4
5
  MissingOgttNote,
5
6
  MissingScreeningOgtt,
6
7
  OnStudyMissingLabValues,
@@ -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
+ }
@@ -12,7 +12,6 @@ qa_cases = [
12
12
  label="No VL value or VL date",
13
13
  dbtable="meta_subject_patienthistory",
14
14
  label_lower="meta_subject.patienthistory",
15
- fld_name="viral_load_date",
16
15
  where="crf.viral_load is null or crf.viral_load_date is null",
17
16
  ),
18
17
  CrfCase(
@@ -1,5 +1,5 @@
1
1
  from django.test import TestCase
2
- from edc_sites import InvalidSiteError
2
+ from edc_sites.exceptions import InvalidSiteError
3
3
  from edc_sites.site import sites
4
4
 
5
5