diff --git a/castellum/recruitment/templates/recruitment/contact.html b/castellum/recruitment/templates/recruitment/contact.html
index 398e66f1221586a9b88465dc2f88b16aa0c95c40..fc06518f69e22859aa89ccbf5236503db6d08abf 100644
--- a/castellum/recruitment/templates/recruitment/contact.html
+++ b/castellum/recruitment/templates/recruitment/contact.html
@@ -72,16 +72,19 @@
{% if can_change_contact %}
{% trans 'Edit contact data'%}
{% endif %}
- {% has_perm 'subjects.change_subject' user study as can_change_subject %}
- {% if can_change_subject %}
-
- {% trans 'Edit subject' %}
-
- {% endif %}
{% has_perm 'recruitment.change_attributeset' user study as can_change_attributeset %}
{% if can_change_attributeset %}
{% trans 'Edit attributes'%}
{% endif %}
+ {% has_perm 'subjects.change_subject' user study as can_change_subject %}
+ {% if can_change_subject %}
+
+ {% trans 'Edit data protection' %}
+
+
+ {% trans 'Edit additional info' %}
+
+ {% endif %}
diff --git a/castellum/recruitment/urls.py b/castellum/recruitment/urls.py
index a32baf4b92b38c3129379237bbd2393c5f860405..1e4b3991e595946591fa146d6218d60b9702e003 100644
--- a/castellum/recruitment/urls.py
+++ b/castellum/recruitment/urls.py
@@ -23,16 +23,17 @@ from django.urls import path
from .feeds import FollowUpFeedForStudy
from .feeds import FollowUpFeedForUser
+from .views import AdditionalInfoUpdateView
from .views import AttributeSetUpdateView
from .views import ContactUpdateView
from .views import ContactView
+from .views import DataProtectionUpdateView
from .views import MailRecruitmentView
from .views import RecruitmentViewInvited
from .views import RecruitmentViewOpen
from .views import RecruitmentViewUnsuitable
from .views import SearchView
from .views import StudyListView
-from .views import SubjectUpdateView
app_name = 'recruitment'
urlpatterns = [
@@ -60,8 +61,13 @@ urlpatterns = [
name='attributeset-update',
),
path(
- '
//update-subject/',
- SubjectUpdateView.as_view(),
- name='subject-update',
+ '//update-data-protection/',
+ DataProtectionUpdateView.as_view(),
+ name='data-protection-update',
+ ),
+ path(
+ '//update-additional-info/',
+ AdditionalInfoUpdateView.as_view(),
+ name='additional-info-update',
),
]
diff --git a/castellum/recruitment/views.py b/castellum/recruitment/views.py
index b1700e79587ce074192657ce563a0bd175d16b14..62628a1610592a2ffff74bccd2e724bdecb0ea38 100644
--- a/castellum/recruitment/views.py
+++ b/castellum/recruitment/views.py
@@ -51,7 +51,8 @@ from castellum.recruitment.mixins import BaseAttributeSetUpdateView
from castellum.recruitment.models.attributesets import get_description_by_statistics_rank
from castellum.studies.mixins import StudyMixin
from castellum.studies.models import Study
-from castellum.subjects.mixins import BaseSubjectUpdateView
+from castellum.subjects.mixins import BaseAdditionalInfoUpdateView
+from castellum.subjects.mixins import BaseDataProtectionUpdateView
from castellum.subjects.mixins import SubjectMixin
from castellum.utils.views import GetFormView
@@ -468,16 +469,14 @@ class ContactView(StudyMixin, SubjectMixin, PermissionRequiredMixin, UpdateView)
return reverse('recruitment:recruitment-open', args=[self.object.study.pk])
-class ContactUpdateView(StudyMixin, BaseContactUpdateView):
+class RecruitmentUpdateMixin(StudyMixin):
study_status = [Study.EXECUTION]
def get_object(self):
- participation_request = get_object_or_404(
+ return get_object_or_404(
ParticipationRequest, study=self.study, pk=self.kwargs['pk']
)
- return participation_request.subject.contact
-
def get_success_url(self):
return reverse('recruitment:contact', args=[self.kwargs['study_pk'], self.kwargs['pk']])
@@ -490,41 +489,28 @@ class ContactUpdateView(StudyMixin, BaseContactUpdateView):
return context
-class AttributeSetUpdateView(StudyMixin, BaseAttributeSetUpdateView):
- study_status = [Study.EXECUTION]
-
+class ContactUpdateView(RecruitmentUpdateMixin, BaseContactUpdateView):
def get_object(self):
- participation_request = get_object_or_404(
- ParticipationRequest, study=self.study, pk=self.kwargs['pk']
- )
- return participation_request.subject.attributeset
+ participation_request = super().get_object()
+ return participation_request.subject.contact
- def get_success_url(self):
- return reverse('recruitment:contact', args=[self.kwargs['study_pk'], self.kwargs['pk']])
- def get_back_url(self):
- return self.get_success_url()
+class AttributeSetUpdateView(RecruitmentUpdateMixin, BaseAttributeSetUpdateView):
+ def get_object(self):
+ participation_request = super().get_object()
+ return participation_request.subject.attributeset
-class SubjectUpdateView(StudyMixin, BaseSubjectUpdateView):
- study_status = [Study.EXECUTION]
-
+class DataProtectionUpdateView(RecruitmentUpdateMixin, BaseDataProtectionUpdateView):
def get_object(self):
- participation_request = get_object_or_404(
- ParticipationRequest, study=self.study, pk=self.kwargs['pk']
- )
+ participation_request = super().get_object()
return participation_request.subject
- def get_success_url(self):
- return reverse('recruitment:contact', args=[self.kwargs['study_pk'], self.kwargs['pk']])
-
- def get_back_url(self):
- return self.get_success_url()
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['base_template'] = "recruitment/base.html"
- return context
+class AdditionalInfoUpdateView(RecruitmentUpdateMixin, BaseAdditionalInfoUpdateView):
+ def get_object(self):
+ participation_request = super().get_object()
+ return participation_request.subject
class SearchView(LoginRequiredMixin, GetFormView):
diff --git a/castellum/subjects/forms.py b/castellum/subjects/forms.py
index 9347c3d29634963fc1f431ae88f3019e94ef1b8b..a0116f515469636fb26ba217ba1a0ab648c93c83 100644
--- a/castellum/subjects/forms.py
+++ b/castellum/subjects/forms.py
@@ -32,11 +32,11 @@ from .models import ConsentDocument
from .models import Subject
-class SubjectForm(forms.ModelForm):
+class DataProtectionForm(forms.ModelForm):
class Meta:
model = Subject
- exclude = ('to_be_deleted', 'to_be_deleted_notified', 'export_requested')
+ fields = ('privacy_level', 'no_recruitment')
widgets = {
'privacy_level': forms.RadioSelect,
'source': DatalistWidget(datalist=(
diff --git a/castellum/subjects/mixins.py b/castellum/subjects/mixins.py
index 275a07e19926be9e17d2e8aec472d75fae99768d..c2548417392e9c40686a5740084d648bdb1fa9f3 100644
--- a/castellum/subjects/mixins.py
+++ b/castellum/subjects/mixins.py
@@ -29,7 +29,7 @@ from django.views.generic import UpdateView
from castellum.castellum_auth.mixins import PermissionRequiredMixin
-from .forms import SubjectForm
+from .forms import DataProtectionForm
from .models import Subject
from .models import SubjectNote
@@ -61,16 +61,70 @@ class SubjectMixin:
return context
-class BaseSubjectUpdateView(SubjectMixin, PermissionRequiredMixin, UpdateView):
+class BaseDataProtectionUpdateView(SubjectMixin, PermissionRequiredMixin, UpdateView):
model = Subject
- form_class = SubjectForm
+ form_class = DataProtectionForm
permission_required = 'subjects.change_subject'
+ template_name = 'subjects/subject_data_protection_form.html'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
+ def form_valid(self, form):
+ if settings.CASTELLUM_DELETE_NOTIFICATION_TO:
+ old = Subject.objects.get(pk=self.object.pk)
+ if self.object.export_requested and not old.export_requested:
+ rel = reverse('subjects:detail', args=[self.object.pk])
+ url = self.request.build_absolute_uri(rel)
+
+ self.object.to_be_deleted_notified = send_mail(
+ settings.CASTELLUM_EXPORT_SUBJECT_NOTIFICATION_SUBJECT,
+ settings.CASTELLUM_EXPORT_SUBJECT_NOTIFICATION_BODY.format(url=url),
+ settings.DEFAULT_FROM_EMAIL,
+ [settings.CASTELLUM_DELETE_NOTIFICATION_TO],
+ )
+
+ if self.object.to_be_deleted and not self.object.to_be_deleted_notified:
+ rel = reverse('subjects:detail', args=[self.object.pk])
+ url = self.request.build_absolute_uri(rel)
+
+ self.object.to_be_deleted_notified = send_mail(
+ settings.CASTELLUM_DELETE_SUBJECT_NOTIFICATION_SUBJECT,
+ settings.CASTELLUM_DELETE_SUBJECT_NOTIFICATION_BODY.format(url=url),
+ settings.DEFAULT_FROM_EMAIL,
+ [settings.CASTELLUM_DELETE_NOTIFICATION_TO],
+ )
+
+ # If a user explicitly removes the ``to_be_deleted`` they
+ # expect that this task is done. If there are still reasons
+ # for deletion there should be a new notificaition.
+ if not self.object.to_be_deleted and self.object.to_be_deleted_notified:
+ old = Subject.objects.get(pk=self.object.pk)
+ if old.to_be_deleted:
+ self.object.to_be_deleted_notified = False
+
+ return super().form_valid(form)
+
+
+class BaseAdditionalInfoUpdateView(SubjectMixin, PermissionRequiredMixin, UpdateView):
+ model = Subject
+ fields = (
+ 'note_hard_of_hearing',
+ 'note_difficult_to_understand',
+ 'note_abusive_language',
+ 'availability_monday',
+ 'availability_tuesday',
+ 'availability_wednesday',
+ 'availability_thursday',
+ 'availability_friday',
+ 'not_available_until',
+ 'source',
+ )
+ permission_required = 'subjects.change_subject'
+ template_name = 'subjects/subject_additional_info_form.html'
+
def get_notes_form(self):
form_class = forms.modelform_factory(SubjectNote, fields=('content',))
return form_class(prefix='{prefix}')
@@ -80,7 +134,6 @@ class BaseSubjectUpdateView(SubjectMixin, PermissionRequiredMixin, UpdateView):
SubjectNote, fields=('content',), extra=0, can_delete=True
)
kwargs = self.get_form_kwargs()
- kwargs.pop('user')
kwargs.pop('instance')
kwargs.pop('initial')
return formset_class(queryset=SubjectNote.objects.filter(subject=self.object), **kwargs)
@@ -108,38 +161,6 @@ class BaseSubjectUpdateView(SubjectMixin, PermissionRequiredMixin, UpdateView):
)
def form_valid(self, form, notes_formset):
- if settings.CASTELLUM_DELETE_NOTIFICATION_TO:
- old = Subject.objects.get(pk=self.object.pk)
- if self.object.export_requested and not old.export_requested:
- rel = reverse('subjects:detail', args=[self.object.pk])
- url = self.request.build_absolute_uri(rel)
-
- self.object.to_be_deleted_notified = send_mail(
- settings.CASTELLUM_EXPORT_SUBJECT_NOTIFICATION_SUBJECT,
- settings.CASTELLUM_EXPORT_SUBJECT_NOTIFICATION_BODY.format(url=url),
- settings.DEFAULT_FROM_EMAIL,
- [settings.CASTELLUM_DELETE_NOTIFICATION_TO],
- )
-
- if self.object.to_be_deleted and not self.object.to_be_deleted_notified:
- rel = reverse('subjects:detail', args=[self.object.pk])
- url = self.request.build_absolute_uri(rel)
-
- self.object.to_be_deleted_notified = send_mail(
- settings.CASTELLUM_DELETE_SUBJECT_NOTIFICATION_SUBJECT,
- settings.CASTELLUM_DELETE_SUBJECT_NOTIFICATION_BODY.format(url=url),
- settings.DEFAULT_FROM_EMAIL,
- [settings.CASTELLUM_DELETE_NOTIFICATION_TO],
- )
-
- # If a user explicitly removes the ``to_be_deleted`` they
- # expect that this task is done. If there are still reasons
- # for deletion there should be a new notificaition.
- if not self.object.to_be_deleted and self.object.to_be_deleted_notified:
- old = Subject.objects.get(pk=self.object.pk)
- if old.to_be_deleted:
- self.object.to_be_deleted_notified = False
-
response = super().form_valid(form)
for subform in notes_formset:
diff --git a/castellum/subjects/templates/subjects/subject_additional_info_form.html b/castellum/subjects/templates/subjects/subject_additional_info_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..ac1153a3bde1c75a7e9b203e5247228c0133fb86
--- /dev/null
+++ b/castellum/subjects/templates/subjects/subject_additional_info_form.html
@@ -0,0 +1,96 @@
+{% extends base_template|default:"subjects/base.html" %}
+{% load static i18n bootstrap4 subjects utils %}
+
+{% block title %}
+ {% trans "Edit subject" %} · {{ block.super }}
+{% endblock %}
+
+{% block content %}
+
+
+
+
+
+{% endblock %}
+
+{% block extra_scripts %}
+
+
+{% endblock %}
diff --git a/castellum/subjects/templates/subjects/subject_base.html b/castellum/subjects/templates/subjects/subject_base.html
index 4ea9bc96b5d5fc2878d956f383549a6afbad4b8b..fe88c72a2e7320ca46759923ed35bf9ff51bb728 100644
--- a/castellum/subjects/templates/subjects/subject_base.html
+++ b/castellum/subjects/templates/subjects/subject_base.html
@@ -24,22 +24,27 @@
{% endif %}
+ {% has_perm 'recruitment.change_attributeset' user as can_change_attributeset %}
+ {% if can_change_attributeset %}
+
+ {% trans "Attributes" %}
+
+ {% endif %}
+
{% has_perm 'subjects.change_subject' user as can_change_subject %}
{% if can_change_subject %}
-
+
{% if not subject.is_complete %}
{% endif %}
- {% trans 'Subject' %}
+ {% trans 'Data Protection' %}
- {% endif %}
-
- {% has_perm 'recruitment.change_attributeset' user as can_change_attributeset %}
- {% if can_change_attributeset %}
- {% trans "Attributes" %}
+
+ {% trans 'Additional Info' %}
+
{% endif %}
diff --git a/castellum/subjects/templates/subjects/subject_data_protection_form.html b/castellum/subjects/templates/subjects/subject_data_protection_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..bb7e36003305bc3a94ac9f2a1c3e71c90600df7a
--- /dev/null
+++ b/castellum/subjects/templates/subjects/subject_data_protection_form.html
@@ -0,0 +1,68 @@
+{% extends base_template|default:"subjects/base.html" %}
+{% load static i18n bootstrap4 subjects utils %}
+
+{% block title %}
+ {% trans "Edit subject" %} · {{ block.super }}
+{% endblock %}
+
+{% block content %}
+
+{% endblock %}
+
+{% block extra_scripts %}
+
+{% endblock %}
diff --git a/castellum/subjects/templates/subjects/subject_form.html b/castellum/subjects/templates/subjects/subject_form.html
deleted file mode 100644
index 26cc68781fbb1e7f357c4f314dbe1cf263d61439..0000000000000000000000000000000000000000
--- a/castellum/subjects/templates/subjects/subject_form.html
+++ /dev/null
@@ -1,139 +0,0 @@
-{% extends base_template|default:"subjects/base.html" %}
-{% load static i18n bootstrap4 subjects utils %}
-
-{% block title %}
- {% trans "Edit subject" %} · {{ block.super }}
-{% endblock %}
-
-{% block content %}
-
-
-
-
-
-{% endblock %}
-
-{% block extra_scripts %}
-
-
-{% endblock %}
diff --git a/castellum/subjects/urls.py b/castellum/subjects/urls.py
index a395f3c039648b59b20e2a6ba3b11a72ba9384b4..711274b40a6a95e03fd567e99babfbeeaf1a6a84 100644
--- a/castellum/subjects/urls.py
+++ b/castellum/subjects/urls.py
@@ -21,10 +21,12 @@
from django.urls import path
+from .views import AdditionalInfoUpdateView
from .views import AddToStudyView
from .views import AttributeSetUpdateView
from .views import ContactUpdateView
from .views import CoverletterView
+from .views import DataProtectionUpdateView
from .views import GuardianSearchView
from .views import MaintenanceAttributesView
from .views import MaintenanceConsentView
@@ -35,7 +37,6 @@ from .views import SubjectDeleteView
from .views import SubjectDetailView
from .views import SubjectExportView
from .views import SubjectSearchView
-from .views import SubjectUpdateView
app_name = 'subjects'
urlpatterns = [
@@ -43,9 +44,10 @@ urlpatterns = [
path('/', SubjectDetailView.as_view(), name='detail'),
path('/delete/', SubjectDeleteView.as_view(), name='delete'),
path('/export/', SubjectExportView.as_view(), name='export'),
- path('/subject/', SubjectUpdateView.as_view(), name='subject'),
path('/contact/', ContactUpdateView.as_view(), name='contact'),
path('/attributes/', AttributeSetUpdateView.as_view(), name='attributeset'),
+ path('/data-protection/', DataProtectionUpdateView.as_view(), name='data-protection'),
+ path('/additional-info/', AdditionalInfoUpdateView.as_view(), name='additional-info'),
path('/coverletter.docx', CoverletterView.as_view(), name='coverletter'),
path(
'/participations/',
diff --git a/castellum/subjects/views.py b/castellum/subjects/views.py
index 85d5ea82185f417c0cce7c46f277ae9dae55d101..b6a0002e3e205051abbca6933595e6ce19c98ec8 100644
--- a/castellum/subjects/views.py
+++ b/castellum/subjects/views.py
@@ -57,7 +57,8 @@ from castellum.studies.models import Study
from castellum.utils.views import GetFormView
from .forms import SubjectPrivacyLevelForm
-from .mixins import BaseSubjectUpdateView
+from .mixins import BaseAdditionalInfoUpdateView
+from .mixins import BaseDataProtectionUpdateView
from .mixins import SubjectMixin
from .models import Consent
from .models import ExportAnswer
@@ -240,12 +241,7 @@ class SubjectExportView(SubjectMixin, PermissionRequiredMixin, DetailView):
return redirect('subjects:export', self.object.pk)
-class SubjectUpdateView(BaseSubjectUpdateView):
- tab = 'subject'
-
- def get_success_url(self):
- return reverse('subjects:subject', args=[self.object.pk])
-
+class SubjectUpdateMixin:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['base_template'] = 'subjects/subject_base.html'
@@ -256,7 +252,21 @@ class SubjectUpdateView(BaseSubjectUpdateView):
return super().form_valid(form, *args)
-class ContactUpdateView(BaseContactUpdateView):
+class DataProtectionUpdateView(SubjectUpdateMixin, BaseDataProtectionUpdateView):
+ tab = 'data-protection'
+
+ def get_success_url(self):
+ return reverse('subjects:data-protection', args=[self.object.pk])
+
+
+class AdditionalInfoUpdateView(SubjectUpdateMixin, BaseAdditionalInfoUpdateView):
+ tab = 'additional-info'
+
+ def get_success_url(self):
+ return reverse('subjects:additional-info', args=[self.object.pk])
+
+
+class ContactUpdateView(SubjectUpdateMixin, BaseContactUpdateView):
tab = 'contact'
def get_object(self):
@@ -266,17 +276,8 @@ class ContactUpdateView(BaseContactUpdateView):
def get_success_url(self):
return reverse('subjects:contact', args=[self.object.subject.pk])
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['base_template'] = 'subjects/subject_base.html'
- return context
-
- def form_valid(self, form):
- messages.success(self.request, _('Data has been saved.'))
- return super().form_valid(form)
-
-class AttributeSetUpdateView(BaseAttributeSetUpdateView):
+class AttributeSetUpdateView(SubjectUpdateMixin, BaseAttributeSetUpdateView):
tab = 'attributeset'
def get_object(self):
@@ -289,15 +290,6 @@ class AttributeSetUpdateView(BaseAttributeSetUpdateView):
def get_success_url(self):
return reverse('subjects:attributeset', args=[self.object.subject.pk])
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['base_template'] = "subjects/subject_base.html"
- return context
-
- def form_valid(self, form):
- messages.success(self.request, _('Data has been saved.'))
- return super().form_valid(form)
-
class ParticipationListView(SubjectMixin, PermissionRequiredMixin, ListView):
tab = 'participations'
diff --git a/tests/recruitment/views/test_subject_update_view.py b/tests/recruitment/views/test_subject_update_view.py
index 4b7068e88b7e66daef1641459ab2f023310b3099..75569d8f7d1e0142f72d600c680cdd23486e894a 100644
--- a/tests/recruitment/views/test_subject_update_view.py
+++ b/tests/recruitment/views/test_subject_update_view.py
@@ -4,12 +4,24 @@ from castellum.recruitment.models import ParticipationRequest
@pytest.mark.smoketest
-def test_200(client, member, participation_request):
+def test_data_protection_200(client, member, participation_request):
participation_request.status = ParticipationRequest.NOT_CONTACTED
participation_request.save()
client.force_login(member)
- response = client.get('/recruitment/{}/{}/update-subject/'.format(
+ response = client.get('/recruitment/{}/{}/update-data-protection/'.format(
+ participation_request.study.pk, participation_request.pk
+ ))
+ assert response.status_code == 200
+
+
+@pytest.mark.smoketest
+def test_additional_info_200(client, member, participation_request):
+ participation_request.status = ParticipationRequest.NOT_CONTACTED
+ participation_request.save()
+
+ client.force_login(member)
+ response = client.get('/recruitment/{}/{}/update-additional-info/'.format(
participation_request.study.pk, participation_request.pk
))
assert response.status_code == 200
diff --git a/tests/subjects/views/test_views.py b/tests/subjects/views/test_views.py
index d564646b9eb688c14c8f7959f8fd792fcbc77c8c..77236448285817139a1e04df34dc5f70926afb81 100644
--- a/tests/subjects/views/test_views.py
+++ b/tests/subjects/views/test_views.py
@@ -51,19 +51,19 @@ def test_detail_with_attributeset_200(client, user, contact, attributeset):
'subject_manager',
pytest.param('data_protection_coordinator', marks=pytest.mark.xfail(strict=True)),
])
-def test_subject_200(request, client, user_fixture, contact):
+def test_data_protection_200(request, client, user_fixture, contact):
user = request.getfixturevalue(user_fixture)
client.force_login(user)
- url = '/subjects/{}/subject/'.format(contact.subject.pk)
+ url = '/subjects/{}/data-protection/'.format(contact.subject.pk)
response = client.get(url)
assert response.status_code == 200
-def test_subject_post_valid(client, user, contact):
+def test_data_protection_post_valid(client, user, contact):
client.force_login(user)
assert contact.subject.privacy_level == 0
- url = '/subjects/{}/subject/'.format(contact.subject.pk)
+ url = '/subjects/{}/data-protection/'.format(contact.subject.pk)
response = client.post(url, {
'privacy_level': 1,
'form-TOTAL_FORMS': 0,
@@ -75,10 +75,10 @@ def test_subject_post_valid(client, user, contact):
assert contact.subject.privacy_level == 1
-def test_subject_post_invalid_privacy_level(client, user, contact):
+def test_data_protection_post_invalid_privacy_level(client, user, contact):
client.force_login(user)
- url = '/subjects/{}/subject/'.format(contact.subject.pk)
+ url = '/subjects/{}/data-protection/'.format(contact.subject.pk)
response = client.post(url, {
'privacy_level': 2,
'form-TOTAL_FORMS': 0,
@@ -88,6 +88,20 @@ def test_subject_post_invalid_privacy_level(client, user, contact):
assert b'Please choose lower privacy level for the subject.' in response.content
+@pytest.mark.parametrize('user_fixture', [
+ pytest.param('study_coordinator', marks=pytest.mark.xfail(strict=True)),
+ pytest.param('recruiter', marks=pytest.mark.xfail(strict=True)),
+ 'subject_manager',
+ pytest.param('data_protection_coordinator', marks=pytest.mark.xfail(strict=True)),
+])
+def test_additional_info_200(request, client, user_fixture, contact):
+ user = request.getfixturevalue(user_fixture)
+ client.force_login(user)
+ url = '/subjects/{}/additional-info/'.format(contact.subject.pk)
+ response = client.get(url)
+ assert response.status_code == 200
+
+
@pytest.mark.smoketest
@pytest.mark.parametrize('user_fixture', [
pytest.param('study_coordinator', marks=pytest.mark.xfail(strict=True)),