diff --git a/castellum/contacts/models.py b/castellum/contacts/models.py
index afb831efda6216199f3b1625f07071f4c7405fb4..dede59e86771ef148084e7681d64931b2471306a 100644
--- a/castellum/contacts/models.py
+++ b/castellum/contacts/models.py
@@ -204,7 +204,7 @@ class Contact(TimeStampedModel):
return super().save(*args, **kwargs)
def get_absolute_url(self):
- return reverse('subjects:detail', args=[self.subject.pk])
+ return reverse('subjects:detail', args=[self.subject.pk, self.subject.slug])
class Address(models.Model):
diff --git a/castellum/data_protection/management/commands/notify_to_be_deleted.py b/castellum/data_protection/management/commands/notify_to_be_deleted.py
index ddd69ef211c8c134bcdd7cf7d812f6f1d376af42..01f05fd369a42f01489031cb2eaaabd25396320f 100644
--- a/castellum/data_protection/management/commands/notify_to_be_deleted.py
+++ b/castellum/data_protection/management/commands/notify_to_be_deleted.py
@@ -37,7 +37,7 @@ def subjects_notify(subjects, base_url):
subject.save()
count += 1
- url = base_url + reverse('subjects:detail', args=[subject.pk])
+ url = base_url + reverse('subjects:detail', args=[subject.pk, subject.slug])
send_mail(
settings.CASTELLUM_DELETE_SUBJECT_NOTIFICATION_SUBJECT,
settings.CASTELLUM_DELETE_SUBJECT_NOTIFICATION_BODY.format(url=url),
diff --git a/castellum/data_protection/templates/data_protection/__subject_list.html b/castellum/data_protection/templates/data_protection/__subject_list.html
index 772073bb34f8bd8382bed759f345f818bc2cb90f..c363f83a5d23a3a51523d9721daa12aa83241cd1 100644
--- a/castellum/data_protection/templates/data_protection/__subject_list.html
+++ b/castellum/data_protection/templates/data_protection/__subject_list.html
@@ -15,7 +15,7 @@
{% endif %}
{% empty %}
diff --git a/castellum/recruitment/models/attributesets.py b/castellum/recruitment/models/attributesets.py
index 0e6d2d0676a9b4da169afeda1ea8abc3e45a7c6d..e69017941f25294bf4a15cca4050f17249c5f7ef 100644
--- a/castellum/recruitment/models/attributesets.py
+++ b/castellum/recruitment/models/attributesets.py
@@ -95,7 +95,7 @@ class AttributeSet(TimeStampedModel):
verbose_name_plural = _('Attribute sets')
def get_absolute_url(self):
- return reverse('subjects:detail', args=[self.subject.pk])
+ return reverse('subjects:detail', args=[self.subject.pk, self.subject.slug])
def get_data(self):
qs = AttributeDescription.objects.all()
diff --git a/castellum/recruitment/templates/recruitment/participation_confirm_delete.html b/castellum/recruitment/templates/recruitment/participation_confirm_delete.html
index a0c086aecaad7ec4a3fc6c879b2fe3b509674d11..020b4a80e5ee506987ef60ae8fc35a8a9e470290 100644
--- a/castellum/recruitment/templates/recruitment/participation_confirm_delete.html
+++ b/castellum/recruitment/templates/recruitment/participation_confirm_delete.html
@@ -15,7 +15,7 @@
{% trans "I have made sure that all external data related to this study participation has been destroyed" %}
diff --git a/castellum/recruitment/views.py b/castellum/recruitment/views.py
index 09d1a8dbd1031b7ab41873751dd06aeaf24b0c2d..8c6cf3ca5dec9fdb0a8469e771d1b62fb85f0099 100644
--- a/castellum/recruitment/views.py
+++ b/castellum/recruitment/views.py
@@ -317,7 +317,8 @@ class ContactView(ParticipationMixin, PermissionRequiredMixin, UpdateView):
def get_success_url(self):
context = self.request.GET.get('context')
if context == 'subjects:participation-list':
- return reverse(context, args=[self.object.subject.pk])
+ subject = self.object.subject
+ return reverse(context, args=[subject.pk, subject.slug])
else:
return reverse('recruitment:recruitment-open', args=[self.object.study.pk])
diff --git a/castellum/subjects/migrations/0018_subject_slug.py b/castellum/subjects/migrations/0018_subject_slug.py
new file mode 100644
index 0000000000000000000000000000000000000000..0532698ccb1149359c93ae18df63de087b946769
--- /dev/null
+++ b/castellum/subjects/migrations/0018_subject_slug.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.0.8 on 2020-08-04 11:23
+
+import castellum.subjects.models
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('subjects', '0017_refactor_permissions'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='subject',
+ name='slug',
+ field=models.CharField(default=castellum.subjects.models.generate_slug, max_length=6, verbose_name='Slug'),
+ ),
+ ]
diff --git a/castellum/subjects/mixins.py b/castellum/subjects/mixins.py
index d0be11792c13f664f19dda6a3e5c0915aa3371c1..ffba589fb26459967c2492b73f5c766488fbca99 100644
--- a/castellum/subjects/mixins.py
+++ b/castellum/subjects/mixins.py
@@ -76,7 +76,7 @@ class BaseDataProtectionUpdateView(PermissionRequiredMixin, UpdateView):
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])
+ rel = reverse('subjects:detail', args=[self.object.pk, self.object.slug])
url = self.request.build_absolute_uri(rel)
self.object.to_be_deleted_notified = send_mail(
@@ -87,7 +87,7 @@ class BaseDataProtectionUpdateView(PermissionRequiredMixin, UpdateView):
)
if self.object.to_be_deleted and not self.object.to_be_deleted_notified:
- rel = reverse('subjects:detail', args=[self.object.pk])
+ rel = reverse('subjects:detail', args=[self.object.pk, self.object.slug])
url = self.request.build_absolute_uri(rel)
self.object.to_be_deleted_notified = send_mail(
diff --git a/castellum/subjects/models.py b/castellum/subjects/models.py
index 727bb310192dc2183b9d9ac50aa750958211f7f6..eefcf3df3fe9064018225732f9e24d9c612d05bd 100644
--- a/castellum/subjects/models.py
+++ b/castellum/subjects/models.py
@@ -24,6 +24,7 @@ import datetime
from django.conf import settings
from django.db import models
from django.utils import timezone
+from django.utils.crypto import get_random_string
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
@@ -34,6 +35,10 @@ from castellum.utils.fields import RestrictedFileField
from castellum.utils.models import TimeStampedModel
+def generate_slug():
+ return get_random_string(6)
+
+
class TimeSlot(models.Model):
hour = models.PositiveSmallIntegerField(_('hour'))
@@ -73,6 +78,8 @@ class SubjectManager(models.Manager):
class Subject(TimeStampedModel):
+ slug = models.CharField(_('Slug'), max_length=6, default=generate_slug)
+
privacy_level = models.PositiveIntegerField(
_('Privacy level'),
default=0,
diff --git a/castellum/subjects/templates/subjects/maintenance_attributes.html b/castellum/subjects/templates/subjects/maintenance_attributes.html
index 4c05e74df985aaf84fcaa5e8669ae7f090acba14..639b6694bdd0c06190c9564c6bae79abae61131b 100644
--- a/castellum/subjects/templates/subjects/maintenance_attributes.html
+++ b/castellum/subjects/templates/subjects/maintenance_attributes.html
@@ -22,7 +22,7 @@
{% if can_access %}
-
{% trans 'Details' %}
+
{% trans 'Details' %}
{% else %}
{% trans 'Details' %}
{% endif %}
diff --git a/castellum/subjects/templates/subjects/maintenance_consent.html b/castellum/subjects/templates/subjects/maintenance_consent.html
index 3ded9176299a106b76a6f959fd0b08e87bda8a5e..49888e8b32c64b6fcf1ee450252307a5ca65688f 100644
--- a/castellum/subjects/templates/subjects/maintenance_consent.html
+++ b/castellum/subjects/templates/subjects/maintenance_consent.html
@@ -17,7 +17,7 @@
{% if can_access %}
-
{% trans 'Details' %}
+
{% trans 'Details' %}
{% else %}
{% trans 'Details' %}
{% endif %}
diff --git a/castellum/subjects/templates/subjects/maintenance_contact.html b/castellum/subjects/templates/subjects/maintenance_contact.html
index 52e8863897f0f41c674b59026671215a47bb0039..c75a739146e77ccd6a60254f27239345cbe8e3a3 100644
--- a/castellum/subjects/templates/subjects/maintenance_contact.html
+++ b/castellum/subjects/templates/subjects/maintenance_contact.html
@@ -17,7 +17,7 @@
{% if can_access %}
-
{% trans 'Details' %}
+
{% trans 'Details' %}
{% else %}
{% trans 'Details' %}
{% endif %}
diff --git a/castellum/subjects/templates/subjects/maintenance_showup.html b/castellum/subjects/templates/subjects/maintenance_showup.html
index 98cc4f0fbc8c74d990976f1906663f84f4a433bd..db0fdea10b97b19905023f029552b3391af57b8a 100644
--- a/castellum/subjects/templates/subjects/maintenance_showup.html
+++ b/castellum/subjects/templates/subjects/maintenance_showup.html
@@ -17,7 +17,7 @@
{% if can_access %}
-
{% trans 'Details' %}
+
{% trans 'Details' %}
{% else %}
{% trans 'Details' %}
{% endif %}
diff --git a/castellum/subjects/templates/subjects/maintenance_waiting.html b/castellum/subjects/templates/subjects/maintenance_waiting.html
index f393eea06d155cab6cd590b85594efa428cc6677..bb757e5ff1e852deb3728ea9286492a8459f80e1 100644
--- a/castellum/subjects/templates/subjects/maintenance_waiting.html
+++ b/castellum/subjects/templates/subjects/maintenance_waiting.html
@@ -17,7 +17,7 @@
@@ -49,7 +49,7 @@
{% endif %}
diff --git a/castellum/subjects/templates/subjects/subject_detail.html b/castellum/subjects/templates/subjects/subject_detail.html
index 7455f4d83498d758cd9dd5d134c65dd335c73371..2b3ca7ca791f73941a54fd5576817f087e64322a 100644
--- a/castellum/subjects/templates/subjects/subject_detail.html
+++ b/castellum/subjects/templates/subjects/subject_detail.html
@@ -17,7 +17,7 @@
{% for c in subject.contact.guardians.all %}
{% has_privacy_level c.subject.privacy_level user as can_access %}
{% if can_access %}
- {{ c }} {% if not forloop.last %},{% endif %}
+ {{ c }} {% if not forloop.last %},{% endif %}
{% else %}
{% trans '(access denied)' %} {% if not forloop.last %},{% endif %}
{% endif %}
@@ -31,7 +31,7 @@
{% for c in subject.contact.guardian_of.all %}
{% has_privacy_level c.subject.privacy_level user as can_access %}
{% if can_access %}
- {{ c }} {% if not forloop.last %},{% endif %}
+ {{ c }} {% if not forloop.last %},{% endif %}
{% else %}
{% trans '(access denied)' %} {% if not forloop.last %},{% endif %}
{% endif %}
@@ -70,7 +70,7 @@
{% if subject.has_legal_basis %}
{{ subject|display:'has_consent_or_waiting' }}
{% else %}
- {% url 'subjects:data-protection' subject.pk as url %}
+ {% url 'subjects:data-protection' subject.pk subject.slug as url %}
{% blocktrans with url=url %}If applicable, set consent status .{% endblocktrans %}
{% endif %}
@@ -83,7 +83,7 @@
{% if subject.has_legal_basis %}
{{ subject|display:'has_study_consent' }}
{% else %}
- {% url 'subjects:add-to-study' subject.pk as url %}
+ {% url 'subjects:add-to-study' subject.pk subject.slug as url %}
{% blocktrans with url=url %}If applicable, add to a study .{% endblocktrans %}
{% endif %}
@@ -104,7 +104,7 @@
{% if coverletter_exists %}
{% endif %}
{% endblock %}
diff --git a/castellum/subjects/templates/subjects/subject_participationlist.html b/castellum/subjects/templates/subjects/subject_participationlist.html
index b0fc2ddbb24f1d0257e745e2bd489c5414aa7e5f..0e76fec61a459c66a7d404a22dbdd0dd5b54a71a 100644
--- a/castellum/subjects/templates/subjects/subject_participationlist.html
+++ b/castellum/subjects/templates/subjects/subject_participationlist.html
@@ -46,10 +46,10 @@
{% endblock %}
diff --git a/castellum/subjects/templates/subjects/subject_search.html b/castellum/subjects/templates/subjects/subject_search.html
index eac611f904959dd920b77b7aac4cae0176c3d156..f6a9610e55e784ab5f70c8462245e76102caebb5 100644
--- a/castellum/subjects/templates/subjects/subject_search.html
+++ b/castellum/subjects/templates/subjects/subject_search.html
@@ -79,7 +79,7 @@
{% if can_view_subject %}
{% if can_access %}
-
{% trans 'Details' %}
+
{% trans 'Details' %}
{% else %}
{% trans 'Details' %}
{% endif %}
diff --git a/castellum/subjects/urls.py b/castellum/subjects/urls.py
index 194e66bd88cdb44498a537d672d084e31a03c39f..c194eb8e9d99d0f21edba2b98d42a9132273b3c4 100644
--- a/castellum/subjects/urls.py
+++ b/castellum/subjects/urls.py
@@ -45,27 +45,35 @@ from .views import SubjectSearchView
app_name = 'subjects'
urlpatterns = [
path('', SubjectSearchView.as_view(), name='index'),
- path('
/', SubjectDetailView.as_view(), name='detail'),
- path('/delete/', SubjectDeleteView.as_view(), name='delete'),
- path('/export/', SubjectExportView.as_view(), name='export'),
- 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('//', SubjectDetailView.as_view(), name='detail'),
+ path('//delete/', SubjectDeleteView.as_view(), name='delete'),
+ path('//export/', SubjectExportView.as_view(), name='export'),
+ path('//contact/', ContactUpdateView.as_view(), name='contact'),
+ path('//attributes/', AttributeSetUpdateView.as_view(), name='attributeset'),
path(
- '/participations/',
+ '//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/',
ParticipationListView.as_view(),
name='participation-list',
),
- path('/participations/add/', AddToStudyView.as_view(), name='add-to-study'),
+ path('//participations/add/', AddToStudyView.as_view(), name='add-to-study'),
path(
- '/participations/add-finished/',
+ '//participations/add-finished/',
AddToFinishedStudyView.as_view(),
name='add-to-finished-study',
),
path(
- '/participations//delete/',
+ '//participations//delete/',
ParticipationDeleteView.as_view(),
name='participation-delete',
),
diff --git a/castellum/subjects/views.py b/castellum/subjects/views.py
index aa15b9f7a645a62e34b3dcfa3fc46c2e2c55a575..50f9e596f9042adbeef683380ac79b15ad4f87f5 100644
--- a/castellum/subjects/views.py
+++ b/castellum/subjects/views.py
@@ -158,7 +158,7 @@ class SubjectSearchView(LoginRequiredMixin, FormView):
monitoring_logger.info('SubjectData update: {} by {}'.format(
contact.subject_id, self.request.user.pk
))
- return redirect('subjects:detail', pk=contact.subject.pk)
+ return redirect('subjects:detail', pk=contact.subject.pk, slug=contact.subject.slug)
else:
messages.error(request, _(
'Both first and last name must be provided in order to create a subject!'
@@ -171,6 +171,7 @@ class SubjectSearchView(LoginRequiredMixin, FormView):
class SubjectDetailView(SubjectMixin, PermissionRequiredMixin, DetailView):
model = Subject
+ query_pk_and_slug = True
permission_required = 'subjects.view_subject'
template_name = 'subjects/subject_detail.html'
tab = 'detail'
@@ -196,6 +197,7 @@ class SubjectDetailView(SubjectMixin, PermissionRequiredMixin, DetailView):
class SubjectDeleteView(SubjectMixin, PermissionRequiredMixin, DeleteView):
model = Subject
+ query_pk_and_slug = True
permission_required = 'subjects.delete_subject'
tab = 'delete'
@@ -227,6 +229,7 @@ class SubjectDeleteView(SubjectMixin, PermissionRequiredMixin, DeleteView):
class SubjectExportView(SubjectMixin, PermissionRequiredMixin, DetailView):
model = Subject
+ query_pk_and_slug = True
template_name = 'subjects/subject_export.html'
permission_required = 'subjects.export_subject'
tab = 'export'
@@ -267,10 +270,12 @@ class SubjectExportView(SubjectMixin, PermissionRequiredMixin, DetailView):
else:
self.object.export_requested = datetime.date.today()
self.object.save()
- return redirect('subjects:export', self.object.pk)
+ return redirect('subjects:export', self.object.pk, self.object.slug)
class SubjectUpdateMixin(SubjectMixin):
+ query_pk_and_slug = True
+
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['base_template'] = 'subjects/subject_base.html'
@@ -285,39 +290,39 @@ class DataProtectionUpdateView(SubjectUpdateMixin, BaseDataProtectionUpdateView)
tab = 'data-protection'
def get_success_url(self):
- return reverse('subjects:data-protection', args=[self.object.pk])
+ return reverse('subjects:data-protection', args=[self.subject.pk, self.subject.slug])
class AdditionalInfoUpdateView(SubjectUpdateMixin, BaseAdditionalInfoUpdateView):
tab = 'additional-info'
def get_success_url(self):
- return reverse('subjects:additional-info', args=[self.object.pk])
+ return reverse('subjects:additional-info', args=[self.subject.pk, self.subject.slug])
class ContactUpdateView(SubjectUpdateMixin, BaseContactUpdateView):
tab = 'contact'
def get_object(self):
- subject = get_object_or_404(Subject, pk=self.kwargs['pk'])
+ subject = get_object_or_404(Subject, pk=self.kwargs['pk'], slug=self.kwargs['slug'])
return subject.contact
def get_success_url(self):
- return reverse('subjects:contact', args=[self.object.subject.pk])
+ return reverse('subjects:contact', args=[self.subject.pk, self.subject.slug])
class AttributeSetUpdateView(SubjectUpdateMixin, BaseAttributeSetUpdateView):
tab = 'attributeset'
def get_object(self):
- subject = get_object_or_404(Subject, pk=self.kwargs['pk'])
+ subject = get_object_or_404(Subject, pk=self.kwargs['pk'], slug=self.kwargs['slug'])
attributeset, __ = AttributeSet.objects.get_or_create(
subject=subject, defaults={'data': {}}
)
return attributeset
def get_success_url(self):
- return reverse('subjects:attributeset', args=[self.object.subject.pk])
+ return reverse('subjects:attributeset', args=[self.subject.pk, self.subject.slug])
class ParticipationListView(SubjectMixin, PermissionRequiredMixin, ListView):
@@ -328,7 +333,7 @@ class ParticipationListView(SubjectMixin, PermissionRequiredMixin, ListView):
@cached_property
def subject(self):
- return get_object_or_404(Subject, pk=self.kwargs['pk'])
+ return get_object_or_404(Subject, pk=self.kwargs['pk'], slug=self.kwargs['slug'])
def is_permitted(self, study):
return self.request.user.has_perm('studies.access_study', obj=study) and (
@@ -359,12 +364,13 @@ class ParticipationDeleteView(SubjectMixin, PermissionRequiredMixin, DeleteView)
permission_required = ['studies.access_study', 'subjects.delete_subject']
def get_object(self):
- return get_object_or_404(
- Participation, pk=self.kwargs['pk'], subject_id=self.kwargs['subject_pk']
+ subject = get_object_or_404(
+ Subject, pk=self.kwargs['subject_pk'], slug=self.kwargs['subject_slug']
)
+ return get_object_or_404(Participation, pk=self.kwargs['pk'], subject=subject)
def get_success_url(self):
- return reverse('subjects:delete', args=[self.subject.pk])
+ return reverse('subjects:delete', args=[self.subject.pk, self.subject.slug])
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
@@ -376,6 +382,7 @@ class ParticipationDeleteView(SubjectMixin, PermissionRequiredMixin, DeleteView)
class AddToStudyView(SubjectMixin, PermissionRequiredMixin, DetailView):
model = Subject
+ query_pk_and_slug = True
template_name = 'subjects/subject_add_to_study.html'
permission_required = 'subjects.view_subject'
tab = 'participations'
@@ -448,7 +455,7 @@ class AddToFinishedStudyView(SubjectMixin, PermissionRequiredMixin, DetailView):
subject=self.object, study=study, status=Participation.INVITED
)
- return redirect('subjects:participation-list', self.object.pk)
+ return redirect('subjects:participation-list', self.subject.pk, self.subject.slug)
class GuardianSearchView(PermissionRequiredMixin, SubjectSearchView):
@@ -630,6 +637,7 @@ class MaintenanceShowupView(BaseMaintenanceView):
class CoverletterView(SubjectMixin, PermissionRequiredMixin, SingleObjectMixin, View):
model = Subject
+ query_pk_and_slug = True
permission_required = 'subjects.view_subject'
def get_data(self):
diff --git a/tests/subjects/views/test_attributeset_view.py b/tests/subjects/views/test_attributeset_view.py
index 480e519a0e118fff8f511782475792e75ee15c4c..2eb354955e59d7ed00a8d5217c72946d6fa55cf4 100644
--- a/tests/subjects/views/test_attributeset_view.py
+++ b/tests/subjects/views/test_attributeset_view.py
@@ -10,7 +10,7 @@ import pytest
def test_update_200(request, client, user_fixture, attributeset, attribute_description):
user = request.getfixturevalue(user_fixture)
client.force_login(user)
- url = '/subjects/{}/attributes/'.format(attributeset.subject.pk)
+ url = '/subjects/{}/{}/attributes/'.format(attributeset.subject.pk, attributeset.subject.slug)
response = client.get(url)
assert response.status_code == 200
assert attribute_description.label.encode(response.charset) in response.content
@@ -18,7 +18,7 @@ def test_update_200(request, client, user_fixture, attributeset, attribute_descr
def test_update_post(client, user, attributeset, attribute_description):
client.force_login(user)
- url = '/subjects/{}/attributes/'.format(attributeset.subject.pk)
+ url = '/subjects/{}/{}/attributes/'.format(attributeset.subject.pk, attributeset.subject.slug)
response = client.post(url, {
'd1': 1,
'd2': 'de',
@@ -37,7 +37,7 @@ def test_update_blocked(client, user, attributeset, attribute_description):
attribute_description.save()
client.force_login(user)
- url = '/subjects/{}/attributes/'.format(attributeset.subject.pk)
+ url = '/subjects/{}/{}/attributes/'.format(attributeset.subject.pk, attributeset.subject.slug)
response = client.get(url)
assert response.status_code == 200
@@ -51,7 +51,7 @@ def test_update_blocked_post(client, user, attributeset, attribute_description):
attributeset.save()
client.force_login(user)
- url = '/subjects/{}/attributes/'.format(attributeset.subject.pk)
+ url = '/subjects/{}/{}/attributes/'.format(attributeset.subject.pk, attributeset.subject.slug)
response = client.post(url, {
'd1': 1,
'd2': 'de',
diff --git a/tests/subjects/views/test_subject_studies_view.py b/tests/subjects/views/test_subject_studies_view.py
index ed9595fe41ef5bbb12958dbe476bbb76bc18cc49..9537bd3aa6699ef8e1b21372eafe0c00e6a21f2e 100644
--- a/tests/subjects/views/test_subject_studies_view.py
+++ b/tests/subjects/views/test_subject_studies_view.py
@@ -3,14 +3,18 @@ import pytest
def test_200_without_attributeset(client, user, contact):
client.force_login(user)
- response = client.get('/subjects/{}/participations/add/'.format(contact.subject.pk))
+ response = client.get('/subjects/{}/{}/participations/add/'.format(
+ contact.subject.pk, contact.subject.slug
+ ))
assert response.status_code == 200
assert b'No attributes provided' in response.content
def test_200_with_attributeset(client, user, contact, attributeset):
client.force_login(user)
- response = client.get('/subjects/{}/participations/add/'.format(contact.subject.pk))
+ response = client.get('/subjects/{}/{}/participations/add/'.format(
+ contact.subject.pk, contact.subject.slug
+ ))
assert response.status_code == 200
assert b'No matching studies found' in response.content
@@ -19,7 +23,9 @@ def test_200_with_attributeset_and_study(
client, member, contact, attributeset, study_in_execution_status
):
client.force_login(member)
- response = client.get('/subjects/{}/participations/add/'.format(contact.subject.pk))
+ response = client.get('/subjects/{}/{}/participations/add/'.format(
+ contact.subject.pk, contact.subject.slug
+ ))
assert response.status_code == 200
assert b'No attributes provided' not in response.content
assert b'No matching studies found' not in response.content
@@ -28,5 +34,7 @@ def test_200_with_attributeset_and_study(
@pytest.mark.smoketest
def test_finished_200(client, member, contact):
client.force_login(member)
- response = client.get('/subjects/{}/participations/add-finished/'.format(contact.subject.pk))
+ response = client.get('/subjects/{}/{}/participations/add-finished/'.format(
+ contact.subject.pk, contact.subject.slug
+ ))
assert response.status_code == 200
diff --git a/tests/subjects/views/test_views.py b/tests/subjects/views/test_views.py
index ddcaaa6247e7ee1fcf4a0cc42b1e6fff93997cc7..f482157700628b73d8128803c69c1f86f088e307 100644
--- a/tests/subjects/views/test_views.py
+++ b/tests/subjects/views/test_views.py
@@ -15,7 +15,7 @@ from castellum.contacts.models import Contact
def test_detail_200(request, client, user_fixture, contact):
user = request.getfixturevalue(user_fixture)
client.force_login(user)
- url = '/subjects/{}/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/'.format(contact.subject.pk, contact.subject.slug)
response = client.get(url)
assert response.status_code == 200
@@ -23,7 +23,7 @@ def test_detail_200(request, client, user_fixture, contact):
@pytest.mark.smoketest
def test_detail_with_attributeset_200(client, user, contact, attributeset):
client.force_login(user)
- url = '/subjects/{}/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/'.format(contact.subject.pk, contact.subject.slug)
response = client.get(url)
assert response.status_code == 200
@@ -38,7 +38,7 @@ def test_detail_with_attributeset_200(client, user, contact, attributeset):
def test_data_protection_200(request, client, user_fixture, contact):
user = request.getfixturevalue(user_fixture)
client.force_login(user)
- url = '/subjects/{}/data-protection/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/data-protection/'.format(contact.subject.pk, contact.subject.slug)
response = client.get(url)
assert response.status_code == 200
@@ -47,7 +47,7 @@ def test_data_protection_post_valid(client, user, contact):
client.force_login(user)
assert contact.subject.privacy_level == 0
- url = '/subjects/{}/data-protection/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/data-protection/'.format(contact.subject.pk, contact.subject.slug)
response = client.post(url, {
'privacy_level': 1,
})
@@ -60,7 +60,7 @@ def test_data_protection_post_valid(client, user, contact):
def test_data_protection_post_invalid_privacy_level(client, user, contact):
client.force_login(user)
- url = '/subjects/{}/data-protection/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/data-protection/'.format(contact.subject.pk, contact.subject.slug)
response = client.post(url, {
'privacy_level': 2,
})
@@ -71,7 +71,7 @@ def test_data_protection_post_invalid_privacy_level(client, user, contact):
def test_data_protection_post_invalid_to_be_deleted_consent(client, user, contact):
client.force_login(user)
- url = '/subjects/{}/data-protection/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/data-protection/'.format(contact.subject.pk, contact.subject.slug)
response = client.post(url, {
'privacy_level': 1,
'to_be_deleted': 'on',
@@ -91,7 +91,7 @@ def test_data_protection_post_invalid_to_be_deleted_consent(client, user, contac
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)
+ url = '/subjects/{}/{}/additional-info/'.format(contact.subject.pk, contact.subject.slug)
response = client.get(url)
assert response.status_code == 200
@@ -106,7 +106,7 @@ def test_additional_info_200(request, client, user_fixture, contact):
def test_contact_200(request, client, user_fixture, contact):
user = request.getfixturevalue(user_fixture)
client.force_login(user)
- url = '/subjects/{}/contact/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/contact/'.format(contact.subject.pk, contact.subject.slug)
response = client.get(url)
assert response.status_code == 200
@@ -118,10 +118,12 @@ def test_contact_200(request, client, user_fixture, contact):
'subject_manager',
'data_protection_coordinator',
])
-def test_partcipation_list_200(request, client, user_fixture, participation):
+def test_participation_list_200(request, client, user_fixture, participation):
user = request.getfixturevalue(user_fixture)
client.force_login(user)
- url = '/subjects/{}/participations/'.format(participation.subject.pk)
+ url = '/subjects/{}/{}/participations/'.format(
+ participation.subject.pk, participation.subject.slug
+ )
response = client.get(url)
assert response.status_code == 200
@@ -136,7 +138,7 @@ def test_partcipation_list_200(request, client, user_fixture, participation):
def test_export_200(request, client, user_fixture, contact):
user = request.getfixturevalue(user_fixture)
client.force_login(user)
- url = '/subjects/{}/export/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/export/'.format(contact.subject.pk, contact.subject.slug)
response = client.get(url)
assert response.status_code == 200
@@ -146,7 +148,7 @@ def test_export_requested(client, user, contact):
contact.subject.save()
client.force_login(user)
- url = '/subjects/{}/export/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/export/'.format(contact.subject.pk, contact.subject.slug)
response = client.get(url)
assert response.status_code == 200
@@ -155,7 +157,7 @@ def test_export_requested(client, user, contact):
def test_export_post(client, user, contact):
client.force_login(user)
- url = '/subjects/{}/export/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/export/'.format(contact.subject.pk, contact.subject.slug)
client.post(url)
del contact.__dict__['subject'] # clear cached_property
@@ -176,14 +178,14 @@ def test_export_post(client, user, contact):
def test_delete_200(request, client, user_fixture, contact):
user = request.getfixturevalue(user_fixture)
client.force_login(user)
- url = '/subjects/{}/delete/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/delete/'.format(contact.subject.pk, contact.subject.slug)
response = client.get(url)
assert response.status_code == 200
def test_delete_post(client, user, contact):
client.force_login(user)
- url = '/subjects/{}/delete/'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/delete/'.format(contact.subject.pk, contact.subject.slug)
client.post(url)
assert not Contact.objects.filter(pk=contact.pk).exists()
@@ -198,6 +200,6 @@ def test_delete_post(client, user, contact):
def test_coverletter_200(request, client, user_fixture, contact):
user = request.getfixturevalue(user_fixture)
client.force_login(user)
- url = '/subjects/{}/coverletter.docx'.format(contact.subject.pk)
+ url = '/subjects/{}/{}/coverletter.docx'.format(contact.subject.pk, contact.subject.slug)
response = client.get(url)
assert response.status_code == 200