Skip to content

Commit 0061dfc

Browse files
committed
modify context data before upcall to the parent get_context_data. Only create action item if user has change perms
1 parent 27bc211 commit 0061dfc

File tree

3 files changed

+71
-34
lines changed

3 files changed

+71
-34
lines changed

edc_locator/tests/test_view_mixins.py

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
from unittest.case import skip
22

3+
from django.conf import settings
4+
from django.contrib.auth import get_user_model
5+
from django.contrib.auth.models import Permission
36
from django.contrib.messages.storage.fallback import FallbackStorage
47
from django.http.request import HttpRequest
58
from django.test import TestCase
69
from django.views.generic.base import ContextMixin
710
from edc_action_item import site_action_items
811
from edc_action_item.models import ActionItem
912
from edc_registration.models import RegisteredSubject
13+
from edc_sites.utils import get_site_model_cls
14+
from edc_sites.view_mixins import SiteViewMixin
1015

1116
from ..action_items import SUBJECT_LOCATOR_ACTION
1217
from ..view_mixins import SubjectLocatorViewMixin, SubjectLocatorViewMixinError
@@ -21,62 +26,76 @@ class TestViewMixins(TestCase):
2126
def setUp(self):
2227
self.subject_identifier = "12345"
2328
RegisteredSubject.objects.create(subject_identifier=self.subject_identifier)
29+
self.user = get_user_model().objects.create_superuser(
30+
"user_login", "[email protected]", "pass"
31+
)
32+
self.user.is_active = True
33+
self.user.is_staff = True
34+
self.user.save()
35+
self.user.refresh_from_db()
36+
self.user.userprofile.sites.add(get_site_model_cls().objects.get(id=settings.SITE_ID))
37+
self.user.user_permissions.add(
38+
Permission.objects.get(
39+
codename="view_appointment", content_type__app_label="edc_appointment"
40+
)
41+
)
42+
43+
def get_request_object(self) -> HttpRequest:
44+
request = HttpRequest()
45+
setattr(request, "session", "session")
46+
messages = FallbackStorage(request)
47+
setattr(request, "_messages", messages)
48+
setattr(request, "user", self.user)
49+
setattr(request, "site", get_site_model_cls().objects.get(id=settings.SITE_ID))
50+
return request
2451

2552
def test_subject_locator_raises_on_bad_model(self):
26-
class MySubjectLocatorViewMixin(SubjectLocatorViewMixin, ContextMixin):
53+
class MySubjectLocatorViewMixin(SiteViewMixin, SubjectLocatorViewMixin, ContextMixin):
2754
subject_locator_model_wrapper_cls = DummyModelWrapper
2855
subject_locator_model = "blah.blahblah"
2956

3057
mixin = MySubjectLocatorViewMixin()
58+
mixin.kwargs = {"subject_identifier": self.subject_identifier}
59+
mixin.request = self.get_request_object()
3160
self.assertRaises(SubjectLocatorViewMixinError, mixin.get_context_data)
3261

3362
def test_subject_locator_raisesmissing_wrapper_cls(self):
34-
class MySubjectLocatorViewMixin(SubjectLocatorViewMixin, ContextMixin):
63+
class MySubjectLocatorViewMixin(SiteViewMixin, SubjectLocatorViewMixin, ContextMixin):
3564
subject_locator_model = "edc_locator.subjectlocator"
3665

3766
self.assertRaises(SubjectLocatorViewMixinError, MySubjectLocatorViewMixin)
3867

3968
@skip("problems emulating message framework")
4069
def test_mixin_messages(self):
41-
class MySubjectLocatorViewMixin(SubjectLocatorViewMixin, ContextMixin):
70+
class MySubjectLocatorViewMixin(SiteViewMixin, SubjectLocatorViewMixin, ContextMixin):
4271
subject_locator_model_wrapper_cls = DummyModelWrapper
4372
subject_locator_model = "edc_locator.subjectlocator"
4473

4574
mixin = MySubjectLocatorViewMixin()
4675
mixin.kwargs = {"subject_identifier": self.subject_identifier}
47-
mixin.request = HttpRequest()
48-
setattr(mixin.request, "session", "session")
49-
messages = FallbackStorage(mixin.request)
50-
setattr(mixin.request, "_messages", messages)
76+
mixin.request = self.get_request_object()
5177
self.assertGreater(len(mixin.request._messages._queued_messages), 0)
5278

5379
def test_subject_locator_view_ok(self):
54-
class MySubjectLocatorViewMixin(SubjectLocatorViewMixin, ContextMixin):
80+
class MySubjectLocatorViewMixin(SiteViewMixin, SubjectLocatorViewMixin, ContextMixin):
5581
subject_locator_model_wrapper_cls = DummyModelWrapper
5682
subject_locator_model = "edc_locator.subjectlocator"
5783

5884
mixin = MySubjectLocatorViewMixin()
59-
mixin.request = HttpRequest()
60-
setattr(mixin.request, "session", "session")
61-
messages = FallbackStorage(mixin.request)
62-
setattr(mixin.request, "_messages", messages)
63-
# add this manually
85+
mixin.request = self.get_request_object()
6486
mixin.kwargs = {"subject_identifier": self.subject_identifier}
6587
try:
6688
mixin.get_context_data()
6789
except SubjectLocatorViewMixinError as e:
6890
self.fail(e)
6991

7092
def test_subject_locator_self_corrects_if_multiple_actionitems(self):
71-
class MySubjectLocatorViewMixin(SubjectLocatorViewMixin, ContextMixin):
93+
class MySubjectLocatorViewMixin(SiteViewMixin, SubjectLocatorViewMixin, ContextMixin):
7294
subject_locator_model_wrapper_cls = DummyModelWrapper
7395
subject_locator_model = "edc_locator.subjectlocator"
7496

7597
mixin = MySubjectLocatorViewMixin()
76-
mixin.request = HttpRequest()
77-
setattr(mixin.request, "session", "session")
78-
messages = FallbackStorage(mixin.request)
79-
setattr(mixin.request, "_messages", messages)
98+
mixin.request = self.get_request_object()
8099
mixin.kwargs = {"subject_identifier": self.subject_identifier}
81100
try:
82101
mixin.get_context_data()

edc_locator/view_mixins.py

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, Type
4+
15
from django.apps import apps as django_apps
26
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
37
from edc_action_item.site_action_items import site_action_items
8+
from edc_sites.utils import get_user_codenames_or_raise
49

510
from .action_items import SUBJECT_LOCATOR_ACTION
611

12+
if TYPE_CHECKING:
13+
from edc_model_wrapper import ModelWrapper
14+
15+
from .models import SubjectLocator
16+
717

818
class SubjectLocatorViewMixinError(Exception):
919
pass
@@ -16,8 +26,8 @@ class SubjectLocatorViewMixin:
1626
Declare with SubjectIdentifierViewMixin.
1727
"""
1828

19-
subject_locator_model_wrapper_cls = None
20-
subject_locator_model = None
29+
subject_locator_model_wrapper_cls: Type[ModelWrapper] = None
30+
subject_locator_model: int = None
2131

2232
def __init__(self, **kwargs):
2333
super().__init__(**kwargs)
@@ -31,19 +41,23 @@ def __init__(self, **kwargs):
3141
)
3242

3343
def get_context_data(self, **kwargs):
34-
context = super().get_context_data(**kwargs)
44+
self.create_subject_locator_action_if_required()
3545
wrapper = self.subject_locator_model_wrapper_cls(model_obj=self.subject_locator)
36-
context.update(subject_locator=wrapper)
37-
self.get_subject_locator_or_message()
38-
return context
46+
kwargs.update(subject_locator=wrapper)
47+
return super().get_context_data(**kwargs)
48+
49+
def create_subject_locator_action_if_required(self) -> None:
50+
"""Create a subject locator action if the subject locator
51+
does not exist.
3952
40-
def get_subject_locator_or_message(self):
41-
obj = None
53+
Only check if user has change permissions.
54+
"""
55+
56+
# kwargs `subject_identifier` updated by RegisteredSubject
57+
# view mixin get()
4258
subject_identifier = self.kwargs.get("subject_identifier")
4359
try:
44-
obj = self.subject_locator_model_cls.objects.get(
45-
subject_identifier=subject_identifier
46-
)
60+
self.subject_locator_model_cls.objects.get(subject_identifier=subject_identifier)
4761
except ObjectDoesNotExist:
4862
action_cls = site_action_items.get(SUBJECT_LOCATOR_ACTION)
4963
action_item_model_cls = action_cls.action_item_model_cls()
@@ -53,18 +67,20 @@ def get_subject_locator_or_message(self):
5367
action_type__name=SUBJECT_LOCATOR_ACTION,
5468
)
5569
except ObjectDoesNotExist:
56-
action_cls(subject_identifier=subject_identifier)
70+
# only create missing action item if user has change perms
71+
_, model_name = self.subject_locator_model.split(".")
72+
if f"change_{model_name}" in get_user_codenames_or_raise(self.request.user):
73+
action_cls(subject_identifier=subject_identifier)
5774
except MultipleObjectsReturned:
5875
# if condition exists, correct here
5976
action_item_model_cls.objects.filter(
6077
subject_identifier=subject_identifier,
6178
action_type__name=SUBJECT_LOCATOR_ACTION,
6279
).delete()
6380
action_cls(subject_identifier=subject_identifier)
64-
return obj
6581

6682
@property
67-
def subject_locator_model_cls(self):
83+
def subject_locator_model_cls(self) -> Type[SubjectLocator]:
6884
try:
6985
model_cls = django_apps.get_model(self.subject_locator_model)
7086
except LookupError as e:
@@ -75,10 +91,10 @@ def subject_locator_model_cls(self):
7591
return model_cls
7692

7793
@property
78-
def subject_locator(self):
94+
def subject_locator(self) -> SubjectLocator:
7995
"""Returns a model instance either saved or unsaved.
8096
81-
If a save instance does not exits, returns a new unsaved instance.
97+
If a saved instance does not exist, returns a new unsaved instance.
8298
"""
8399
model_cls = self.subject_locator_model_cls
84100
try:

runtests.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
APP_NAME=app_name,
1515
ETC_DIR=str(base_dir / app_name / "tests" / "etc"),
1616
SUBJECT_VISIT_MODEL="edc_visit_tracking.subjectvisit",
17+
EDC_SITES_REGISTER_DEFAULT=True,
1718
INSTALLED_APPS=[
1819
"django.contrib.admin",
1920
"django.contrib.auth",
@@ -26,6 +27,7 @@
2627
"django_crypto_fields.apps.AppConfig",
2728
"edc_appointment.apps.AppConfig",
2829
"edc_action_item.apps.AppConfig",
30+
"edc_auth.apps.AppConfig",
2931
"edc_crf.apps.AppConfig",
3032
"edc_consent.apps.AppConfig",
3133
"edc_randomization.apps.AppConfig",

0 commit comments

Comments
 (0)