diff --git a/castellum/execution/templates/execution/participation_detail.html b/castellum/execution/templates/execution/participation_detail.html
index 93b3c7f1c1f2ddaced4b24570530b9168d4dd0ba..a48b3ae97becc86a0af571aebc7a7afb101dfe93 100644
--- a/castellum/execution/templates/execution/participation_detail.html
+++ b/castellum/execution/templates/execution/participation_detail.html
@@ -14,7 +14,7 @@
{% endfor %}
- {% else %}
+ {% elif domains|length > 0 %}
- {% translate 'Pseudonym' %}
{% for domain in domains %}
diff --git a/castellum/execution/templates/execution/study_export.html b/castellum/execution/templates/execution/study_export.html
index b78758e78bdbb256d557b4a90e2b8df27bfdb2d3..d1ff65109e4aa48032b8814381190935ab3b6ceb 100644
--- a/castellum/execution/templates/execution/study_export.html
+++ b/castellum/execution/templates/execution/study_export.html
@@ -5,6 +5,7 @@
{% block content %}
diff --git a/castellum/execution/templates/execution/study_resolve.html b/castellum/execution/templates/execution/study_resolve.html
index d9ba259f6f093635a4f8f0471bcbef32af45205a..71b27716d3b12e5ac67c41f02329e67df115ed85 100644
--- a/castellum/execution/templates/execution/study_resolve.html
+++ b/castellum/execution/templates/execution/study_resolve.html
@@ -6,7 +6,7 @@
{% include 'utils/form_errors.html' with form=form %}
{% csrf_token %}
- {% if form.domain.field.choices|length > 1 %}
+ {% if form.domain.field.choices|length != 1 %}
{% bootstrap_field form.domain %}
{% else %}
{{ form.domain.as_hidden }}
diff --git a/castellum/pseudonyms/forms.py b/castellum/pseudonyms/forms.py
index 4f4ceaf741529b953cebe6edceb3705462d2abf1..800e2406782c72e3c104b745a608df54348c1303 100644
--- a/castellum/pseudonyms/forms.py
+++ b/castellum/pseudonyms/forms.py
@@ -40,7 +40,12 @@ class DomainForm(forms.Form):
super().__init__(*args, **kwargs)
self.fields['domain'].choices = [(d.key, str(d)) for d in context.domains.all()]
- self.fields['domain'].initial = self.fields['domain'].choices[0][0]
+ if self.fields['domain'].choices:
+ self.fields['domain'].initial = self.fields['domain'].choices[0][0]
+ else:
+ self.fields['domain'].disabled = True
+ self.cleaned_data = {}
+ self.add_error(None, _('All pseudonym domains have been deleted.'))
class PseudonymForm(DomainForm):
diff --git a/castellum/studies/apps.py b/castellum/studies/apps.py
index a55922c3a5985c3c6f28811cbac53d5321ceb4d2..4d9c19b5218297414d4b2a5977699b7a4f73f930 100644
--- a/castellum/studies/apps.py
+++ b/castellum/studies/apps.py
@@ -29,7 +29,6 @@ class StudiesConfig(AppConfig):
name = 'castellum.studies'
def ready(self):
- post_save.connect(signals.create_study_domain)
pre_delete.connect(signals.delete_study_domain)
post_save.connect(signals.create_session_domain)
diff --git a/castellum/studies/signals.py b/castellum/studies/signals.py
index 97e1d7486d6df94ec7ee7859cf6835209da1d6cb..108685d2999710ebc7e8dfd2e0fcaa640a79918b 100644
--- a/castellum/studies/signals.py
+++ b/castellum/studies/signals.py
@@ -21,17 +21,6 @@
from django.conf import settings
-def create_study_domain(sender, instance, using, **kwargs):
- from castellum.pseudonyms.models import Domain
- from castellum.studies.models import Study
-
- if sender is Study and not instance.domains.exists():
- Domain.objects.create(
- bits=settings.CASTELLUM_STUDY_DOMAIN_BITS,
- context=instance,
- )
-
-
def delete_study_domain(sender, instance, using, **kwargs):
from castellum.studies.models import Study
diff --git a/castellum/studies/templates/studies/domain_confirm_delete.html b/castellum/studies/templates/studies/domain_confirm_delete.html
new file mode 100644
index 0000000000000000000000000000000000000000..a800809b8e6e22ea1807b03ce3028b525d3528e1
--- /dev/null
+++ b/castellum/studies/templates/studies/domain_confirm_delete.html
@@ -0,0 +1,22 @@
+{% extends "studies/study_domains_base.html" %}
+{% load i18n auth django_bootstrap5 utils %}
+
+{% block title %}{% translate "Delete" %} · {% translate "Study domains" %} · {{ block.super }}{% endblock %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/castellum/studies/templates/studies/study_domains_study.html b/castellum/studies/templates/studies/study_domains_study.html
index ad0e582117e05ca9783dbe7620b321fa3c4c61b8..6b1afc7acb0d921da40fd345d1b5e367054c183f 100644
--- a/castellum/studies/templates/studies/study_domains_study.html
+++ b/castellum/studies/templates/studies/study_domains_study.html
@@ -32,6 +32,9 @@
+
{% endfor %}
diff --git a/castellum/studies/urls/__init__.py b/castellum/studies/urls/__init__.py
index 243691e7132796ab35af62e190595e2faf41a1c5..d0f0669190c92c4e01df876b051e9aa4001519cc 100644
--- a/castellum/studies/urls/__init__.py
+++ b/castellum/studies/urls/__init__.py
@@ -30,6 +30,7 @@ from ..views.studies import StudyCreateView
from ..views.studies import StudyDeleteView
from ..views.studies import StudyDetailView
from ..views.studies import StudyDiffView
+from ..views.studies import StudyDomainDeleteView
from ..views.studies import StudyDomainsGeneralView
from ..views.studies import StudyDomainsView
from ..views.studies import StudyExportView
@@ -52,6 +53,11 @@ urlpatterns = [
path('/delete/', StudyDeleteView.as_view(), name='delete'),
path('/domains/', StudyDomainsView.as_view(), name='domains'),
path('/domains/general', StudyDomainsGeneralView.as_view(), name='domains-general'),
+ path(
+ '/domains//',
+ StudyDomainDeleteView.as_view(),
+ name='domain-delete',
+ ),
path('/start/', StudyStartRecruitmentView.as_view(), name='start'),
path('/finish/', StudyFinishRecruitmentView.as_view(), name='finish'),
path('/mail/', OneTimeInvitationMailRecruitmentView.as_view(), name='mail'),
diff --git a/castellum/studies/views/studies.py b/castellum/studies/views/studies.py
index 329533c913dea63d85e5b9c7a054234715db0b5b..771aefd8f3998f74f9a6ee30e1e45c6adc57704e 100644
--- a/castellum/studies/views/studies.py
+++ b/castellum/studies/views/studies.py
@@ -260,6 +260,11 @@ class StudyCreateView(PermissionRequiredMixin, CreateView):
for tag in self.duplicate.executiontag_set.all():
self.object.executiontag_set.create(name=tag.name)
else:
+ Domain.objects.create(
+ bits=settings.CASTELLUM_STUDY_DOMAIN_BITS,
+ context=self.object,
+ )
+
for name, color in settings.CASTELLUM_DEFAULT_EXECUTION_TAGS:
self.object.executiontag_set.create(name=name, color=color)
@@ -457,6 +462,18 @@ class StudyDomainsGeneralView(StudyDomainsMixin, UpdateView):
return super().form_valid(form)
+class StudyDomainDeleteView(StudyDomainsMixin, DeleteView):
+ model = Domain
+ template_name = 'studies/domain_confirm_delete.html'
+ subtab = 'domains-study'
+
+ def get_object(self):
+ return get_object_or_404(self.study.domains.all(), key=self.kwargs['key'])
+
+ def get_success_url(self):
+ return reverse('studies:domains', args=[self.study.pk])
+
+
class StudyImportView(PermissionRequiredMixin, FormView):
permission_required = 'studies.change_study'
template_name = 'studies/study_import.html'
diff --git a/tests/execution/views/test_api_domains_view.py b/tests/execution/views/test_api_domains_view.py
index 7b082fef758f037f3c5179e5af10870e08d723fd..bfe499ed1c3f087fcf6a6fa7417e3bda73b7ff9b 100644
--- a/tests/execution/views/test_api_domains_view.py
+++ b/tests/execution/views/test_api_domains_view.py
@@ -12,7 +12,7 @@ def test_apidomainsview(client, conductor, study_in_execution_status):
assert response.status_code == 200
data = response.json()
- assert len(data['domains']) == 3
+ assert len(data['domains']) == 2
def test_no_general_domains(client, conductor, study_in_execution_status):
@@ -25,7 +25,7 @@ def test_no_general_domains(client, conductor, study_in_execution_status):
assert response.status_code == 200
data = response.json()
- assert len(data['domains']) == 1
+ assert len(data['domains']) == 0
def test_permission(client, recruiter, study_in_execution_status):
diff --git a/tests/execution/views/test_export_view.py b/tests/execution/views/test_export_view.py
index cace1e11c2fdc56a3b4cc8a84667770768046bf1..32a26d55c18fd13b2027d86889d5b16bb57bbb3e 100644
--- a/tests/execution/views/test_export_view.py
+++ b/tests/execution/views/test_export_view.py
@@ -1,4 +1,3 @@
-import pytest
from model_bakery import baker
from castellum.pseudonyms.models import Domain
@@ -7,8 +6,9 @@ from castellum.recruitment.models import Participation
from castellum.studies.models import Study
-@pytest.mark.smoketest
-def test_200(client, conductor, study_in_execution_status):
+def test_single_domain(client, conductor, study_in_execution_status):
+ baker.make(Domain, context=study_in_execution_status)
+
client.force_login(conductor)
url = '/execution/{}/export/'.format(study_in_execution_status.pk)
response = client.get(url)
@@ -17,17 +17,19 @@ def test_200(client, conductor, study_in_execution_status):
assert response['Content-Type'] == 'application/zip'
-def test_single_domain(client, conductor, study_in_execution_status):
+def test_no_domain(client, conductor, study_in_execution_status):
client.force_login(conductor)
url = '/execution/{}/export/'.format(study_in_execution_status.pk)
response = client.get(url)
assert response.status_code == 200
- assert response['Content-Type'] == 'application/zip'
+ assert response['Content-Type'] == 'text/html; charset=utf-8'
+ assert b'All pseudonym domains have been deleted.' in response.content
def test_multi_domain(client, conductor, study_in_execution_status):
baker.make(Domain, context=study_in_execution_status)
+ baker.make(Domain, context=study_in_execution_status)
client.force_login(conductor)
url = '/execution/{}/export/'.format(study_in_execution_status.pk)
@@ -39,6 +41,7 @@ def test_multi_domain(client, conductor, study_in_execution_status):
def test_multi_domain_given(client, conductor, study_in_execution_status):
baker.make(Participation, study=study_in_execution_status, status=Participation.INVITED)
+ baker.make(Domain, context=study_in_execution_status)
domain2 = baker.make(Domain, context=study_in_execution_status)
client.force_login(conductor)
@@ -55,6 +58,7 @@ def test_multi_domain_given(client, conductor, study_in_execution_status):
def test_multi_domain_given_invalid(client, conductor, study_in_execution_status):
baker.make(Domain, context=study_in_execution_status)
+ baker.make(Domain, context=study_in_execution_status)
client.force_login(conductor)
url = '/execution/{}/export/?domain={}'.format(study_in_execution_status.pk, 'invalid')
@@ -65,6 +69,7 @@ def test_multi_domain_given_invalid(client, conductor, study_in_execution_status
def test_multi_domain_given_wrong_study(client, conductor, study_in_execution_status):
study2 = baker.make(Study)
+ baker.make(Domain, context=study_in_execution_status)
domain2 = baker.make(Domain, context=study_in_execution_status)
client.force_login(conductor)
diff --git a/tests/execution/views/test_pseudonyms_view.py b/tests/execution/views/test_pseudonyms_view.py
index 01da3e21ef49f6b06ccbd9440d87934cc1562e63..0981f0a4136019352a7f7a20a657963c85c6ab57 100644
--- a/tests/execution/views/test_pseudonyms_view.py
+++ b/tests/execution/views/test_pseudonyms_view.py
@@ -28,7 +28,7 @@ def test_lists_all_pseudonyms(client, conductor, study):
study.pk, participation.pk
))
assert response.status_code == 200
- assert response.content.count(b'|