Skip to content

Commit

Permalink
Merge branch 'release/0.3.16' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
erikvw committed Jul 10, 2023
2 parents 8779de2 + 04597b7 commit 0d6f99b
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 53 deletions.
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
0.3.16
------
- refactor ReferenceGetter to require `site`
- link ModelAdmin class to subject dashboard
- allow search of field values in ModelAdmin

16 changes: 13 additions & 3 deletions edc_reference/admin.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
from django.contrib import admin
from edc_model_admin.mixins import TemplatesModelAdminMixin
from edc_model_admin.dashboard import ModelAdminSubjectDashboardMixin
from edc_sites.admin import SiteModelAdminMixin

from .admin_site import edc_reference_admin
from .models import Reference


@admin.register(Reference, site=edc_reference_admin)
class ReferenceAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
class ReferenceAdmin(SiteModelAdminMixin, ModelAdminSubjectDashboardMixin, admin.ModelAdmin):
date_hierarchy = "report_datetime"

list_display = (
"identifier",
"dashboard",
"model",
"report_datetime",
"visit",
"timepoint",
"field_name",
"value",
)
list_filter = ("model", "timepoint", "field_name")
search_fields = ("identifier",)
search_fields = (
"identifier",
"value_str",
"value_int",
"value_date",
"value_datetime",
"value_uuid",
)

def visit(self, obj=None):
return f"{obj.visit_code}.{obj.visit_code_sequence}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 4.2.1 on 2023-07-08 16:25

from django.db import migrations, models
import django.db.models.deletion
import django.db.models.manager
import edc_sites.model_mixins


class Migration(migrations.Migration):
dependencies = [
("sites", "0002_alter_domain_unique"),
("edc_reference", "0011_auto_20200929_0238"),
]

operations = [
migrations.AlterModelManagers(
name="reference",
managers=[
("objects", django.db.models.manager.Manager()),
("on_site", edc_sites.model_mixins.CurrentSiteManager()),
],
),
migrations.AddField(
model_name="reference",
name="site",
field=models.ForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="+",
to="sites.site",
),
),
]
7 changes: 6 additions & 1 deletion edc_reference/models/reference.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.core.exceptions import FieldError
from django.db import models
from edc_model.models import BaseUuidModel
from edc_sites.model_mixins import SiteModelMixin

from .managers import ReferenceManager

Expand All @@ -9,7 +10,7 @@ class ReferenceFieldDatatypeNotFound(Exception):
pass


class Reference(BaseUuidModel):
class Reference(SiteModelMixin, BaseUuidModel):
identifier = models.CharField(max_length=50)

visit_schedule_name = models.CharField(max_length=150, null=True)
Expand Down Expand Up @@ -76,6 +77,10 @@ def natural_key(self):
self.field_name,
)

@property
def subject_identifier(self):
return self.identifier

def update_value(self, value=None, internal_type=None, field=None, related_name=None):
"""Updates the correct `value` field based on the
field class datatype.
Expand Down
105 changes: 60 additions & 45 deletions edc_reference/reference/reference_getter.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
from __future__ import annotations

from datetime import datetime
from decimal import Decimal

from django.apps import apps as django_apps
from django.core.exceptions import ObjectDoesNotExist

Expand All @@ -21,17 +26,18 @@ class ReferenceGetter:

def __init__(
self,
name=None,
field_name=None,
name: str | None = None,
field_name: str | None = None,
model_obj=None,
visit_obj=None,
subject_identifier=None,
report_datetime=None,
visit_schedule_name=None,
schedule_name=None,
visit_code=None,
visit_code_sequence=None,
timepoint=None,
related_visit=None,
subject_identifier: str | None = None,
report_datetime: datetime | None = None,
visit_schedule_name: str | None = None,
schedule_name: str | None = None,
visit_code: str | None = None,
visit_code_sequence: str | None = None,
timepoint: Decimal | None = None,
site=None,
create=None,
):
self._object = None
Expand All @@ -44,44 +50,49 @@ def __init__(
if model_obj:
try:
# given a crf model as model_obj
self.name = model_obj.reference_name
self.report_datetime = model_obj.related_visit.report_datetime
self.subject_identifier = model_obj.related_visit.subject_identifier
self.visit_schedule_name = model_obj.related_visit.visit_schedule_name
self.schedule_name = model_obj.related_visit.schedule_name
self.site = model_obj.related_visit.site
self.subject_identifier = model_obj.related_visit.subject_identifier
self.timepoint = model_obj.related_visit.timepoint
self.visit_code = model_obj.related_visit.visit_code
self.visit_code_sequence = model_obj.related_visit.visit_code_sequence
self.timepoint = model_obj.related_visit.timepoint
self.name = model_obj.reference_name
except AttributeError:
self.visit_schedule_name = model_obj.related_visit.visit_schedule_name
except AttributeError as e:
if "related_visit" not in str(e):
raise
# given a visit model as model_obj
self.subject_identifier = model_obj.subject_identifier
self.name = model_obj.reference_name
self.report_datetime = model_obj.report_datetime
self.visit_schedule_name = model_obj.visit_schedule_name
self.schedule_name = model_obj.schedule_name
self.site = model_obj.site
self.subject_identifier = model_obj.subject_identifier
self.timepoint = model_obj.timepoint
self.visit_code = model_obj.visit_code
self.visit_code_sequence = model_obj.visit_code_sequence
self.timepoint = model_obj.timepoint
self.name = model_obj.reference_name
elif visit_obj:
self.visit_schedule_name = model_obj.visit_schedule_name
elif related_visit:
self.name = name
self.subject_identifier = visit_obj.subject_identifier
self.report_datetime = visit_obj.report_datetime
self.visit_schedule_name = visit_obj.visit_schedule_name
self.schedule_name = visit_obj.schedule_name
self.visit_code = visit_obj.visit_code
self.visit_code_sequence = visit_obj.visit_code_sequence
self.timepoint = visit_obj.timepoint
self.report_datetime = related_visit.report_datetime
self.schedule_name = related_visit.schedule_name
self.site = related_visit.site
self.subject_identifier = related_visit.subject_identifier
self.timepoint = related_visit.timepoint
self.visit_code = related_visit.visit_code
self.visit_code_sequence = related_visit.visit_code_sequence
self.visit_schedule_name = related_visit.visit_schedule_name
else:
# given only the attrs
self.name = name
self.subject_identifier = subject_identifier
self.report_datetime = report_datetime
self.visit_schedule_name = visit_schedule_name
self.schedule_name = schedule_name
self.site = site
self.subject_identifier = subject_identifier
self.timepoint = timepoint
self.visit_code = visit_code
self.visit_code_sequence = visit_code_sequence
self.timepoint = timepoint

self.visit_schedule_name = visit_schedule_name
reference_model = site_reference_configs.get_reference_model(name=self.name)
self.reference_model_cls = django_apps.get_model(reference_model)

Expand All @@ -102,10 +113,13 @@ def object(self):
"""Returns a reference model instance."""
if not self._object:
self.created = False
opts = dict(
**self.required_options,
**{k: v for k, v in self.visit_options.items() if v is not None},
)
opts = self.required_options
opts.update(**{k: v for k, v in self.visit_options.items() if v is not None})
if {k: v for k, v in opts.items() if v is None}:
raise ReferenceGetterError(
"Unable to get a reference instance. Null values for attrs "
f"not allowed. {self}. Got {opts}."
)
try:
self._object = self.reference_model_cls.objects.get(**opts)
except ObjectDoesNotExist as e:
Expand All @@ -121,23 +135,25 @@ def required_options(self):
"""Returns a dictionary of query options required for both
get and create.
"""
return dict(
opts = dict(
identifier=self.subject_identifier,
model=self.name,
report_datetime=self.report_datetime,
field_name=self.field_name,
site=self.site,
)
return opts

@property
def visit_options(self):
"""Returns a dictionary of query options of the visit attrs."""
opts = {}
opts.update(
opts = dict(
visit_schedule_name=self.visit_schedule_name,
schedule_name=self.schedule_name,
visit_code=self.visit_code,
visit_code_sequence=self.visit_code_sequence,
timepoint=self.timepoint,
site=self.site,
)
return opts

Expand All @@ -146,12 +162,11 @@ def create_reference_obj(self):
Note: updater needs to "update_value".
"""
if {k: v for k, v in self.visit_options.items() if v is None}:
opts = self.required_options
opts.update(**{k: v for k, v in self.visit_options.items() if v is not None})
if {k: v for k, v in opts.items() if v is None}:
raise ReferenceGetterError(
f"Unable to create a reference instance. "
f"Null values for visit attrs not allowed. "
f"Got {self.visit_options}."
"Unable to create a reference instance. Null values for attrs "
f"not allowed. {self}. Got {opts}."
)
return self.reference_model_cls.objects.create(
**self.required_options, **self.visit_options
)
return self.reference_model_cls.objects.create(**opts)
8 changes: 6 additions & 2 deletions edc_reference/tests/tests/test_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def setUp(self):
self.panel_vl = Panel.objects.create(name="vl")
self.panel_wb = Panel.objects.create(name="wb")
self.subject_identifier = "123456789"
self.site = Site.objects.get_current()

dte = get_utcnow()

Expand Down Expand Up @@ -422,7 +423,7 @@ def test_reference_getter_without_crf_type_model(self):
reference = ReferenceGetter(
name="reference_app.crfone",
field_name="field_int",
visit_obj=crf_one.related_visit,
related_visit=crf_one.related_visit,
)
self.assertEqual(reference.field_int, integer)

Expand All @@ -443,6 +444,7 @@ def test_reference_getter_without_using_model_obj(self):
visit_code=crf_one.related_visit.visit_code,
visit_code_sequence=crf_one.related_visit.visit_code_sequence,
timepoint=crf_one.related_visit.timepoint,
site=crf_one.site,
)
reference = ReferenceGetter(**opts)
self.assertEqual(reference.field_int, integer)
Expand All @@ -463,6 +465,7 @@ def test_reference_getter_without_using_model_obj_and_missing_visit_attr(self):
schedule_name=crf_one.related_visit.schedule_name,
visit_code=crf_one.related_visit.visit_code,
timepoint=crf_one.related_visit.timepoint,
site=crf_one.site,
)
reference = ReferenceGetter(**opts)
self.assertEqual(reference.field_int, integer)
Expand All @@ -476,6 +479,7 @@ def test_reference_getter_raises(self):
field_name="field_int",
subject_identifier=self.subject_identifier,
report_datetime=self.subject_visit.report_datetime,
site=self.site,
)
self.assertRaises(ReferenceObjectDoesNotExist, ReferenceGetter, **opts)

Expand Down Expand Up @@ -517,7 +521,7 @@ def test_reference_getter_object_doesnotexist(self):
ReferenceGetter,
field_name="blah",
model_obj=crf_one_obj,
visit_obj=self.subject_visit,
related_visit=self.subject_visit,
create=False,
)

Expand Down
4 changes: 2 additions & 2 deletions reference_app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ class SubjectConsent(
SiteModelMixin,
BaseUuidModel,
):
on_site = CurrentSiteManager()

objects = SubjectIdentifierManager()

on_site = CurrentSiteManager()

def natural_key(self):
return (self.subject_identifier,)

Expand Down

0 comments on commit 0d6f99b

Please sign in to comment.