diff --git a/dokumente/templates/standards/incomplete_vorgaben.html b/dokumente/templates/standards/incomplete_vorgaben.html new file mode 100644 index 0000000..d0d8251 --- /dev/null +++ b/dokumente/templates/standards/incomplete_vorgaben.html @@ -0,0 +1,167 @@ +{% extends "base.html" %} +{% block content %} +

Unvollständige Vorgaben

+ +
+ +
+
+
+
+ + Vorgaben ohne Referenzen + {{ no_references|length }} +
+
+
+ {% if no_references %} + + {% else %} +

Alle Vorgaben haben Referenzen.

+ {% endif %} +
+
+
+ + +
+
+
+
+ + Vorgaben ohne Stichworte + {{ no_stichworte|length }} +
+
+
+ {% if no_stichworte %} + + {% else %} +

Alle Vorgaben haben Stichworte.

+ {% endif %} +
+
+
+ + +
+
+
+
+ + Vorgaben ohne Kurz- oder Langtext + {{ no_text|length }} +
+
+
+ {% if no_text %} + + {% else %} +

Alle Vorgaben haben Kurz- oder Langtext.

+ {% endif %} +
+
+
+ + +
+
+
+
+ + Vorgaben ohne Checklistenfragen + {{ no_checklistenfragen|length }} +
+
+
+ {% if no_checklistenfragen %} + + {% else %} +

Alle Vorgaben haben Checklistenfragen.

+ {% endif %} +
+
+
+
+ + +
+
+
+
+
Zusammenfassung
+
+
+
+
+
+

{{ no_references|length }}

+

Ohne Referenzen

+
+
+
+
+

{{ no_stichworte|length }}

+

Ohne Stichworte

+
+
+
+
+

{{ no_text|length }}

+

Ohne Text

+
+
+
+
+

{{ no_checklistenfragen|length }}

+

Ohne Checklistenfragen

+
+
+
+
+
+
+
+ +
+ + Zurück zur Übersicht + +
+{% endblock %} \ No newline at end of file diff --git a/dokumente/tests.py b/dokumente/tests.py index a9f7c67..11455da 100644 --- a/dokumente/tests.py +++ b/dokumente/tests.py @@ -817,3 +817,278 @@ class SanityCheckManagementCommandTest(TestCase): self.assertIn("Found 1 conflicts:", output) self.assertIn("R0066.O.1", output) self.assertIn("intersecting validity periods", output) + + +class IncompleteVorgabenTest(TestCase): + """Test cases for incomplete Vorgaben functionality""" + + def setUp(self): + self.client = Client() + + # Create test data + self.dokumententyp = Dokumententyp.objects.create( + name="Test Typ", + verantwortliche_ve="Test VE" + ) + + self.thema = Thema.objects.create( + name="Test Thema", + erklaerung="Test Erklärung" + ) + + self.dokument = Dokument.objects.create( + nummer="TEST-001", + dokumententyp=self.dokumententyp, + name="Test Dokument", + gueltigkeit_von=date.today(), + aktiv=True + ) + + # Create complete Vorgabe (should not appear in any list) + self.complete_vorgabe = Vorgabe.objects.create( + order=1, + nummer=1, + dokument=self.dokument, + thema=self.thema, + titel="Vollständige Vorgabe", + gueltigkeit_von=date.today() + ) + + # Add all required components to make it complete + self.stichwort = Stichwort.objects.create( + stichwort="Test Stichwort" + ) + self.complete_vorgabe.stichworte.add(self.stichwort) + + self.referenz = Referenz.objects.create( + name_nummer="Test Referenz", + url="/test/path" + ) + self.complete_vorgabe.referenzen.add(self.referenz) + + VorgabeKurztext.objects.create( + abschnitt=self.complete_vorgabe, + inhalt="Test Kurztext" + ) + + Checklistenfrage.objects.create( + vorgabe=self.complete_vorgabe, + frage="Test Frage" + ) + + # Create incomplete Vorgaben + # 1. Vorgabe without references + self.no_refs_vorgabe = Vorgabe.objects.create( + order=2, + nummer=2, + dokument=self.dokument, + thema=self.thema, + titel="Vorgabe ohne Referenzen", + gueltigkeit_von=date.today() + ) + self.no_refs_vorgabe.stichworte.add(self.stichwort) + VorgabeKurztext.objects.create( + abschnitt=self.no_refs_vorgabe, + inhalt="Test Kurztext" + ) + Checklistenfrage.objects.create( + vorgabe=self.no_refs_vorgabe, + frage="Test Frage" + ) + + # 2. Vorgabe without Stichworte + self.no_stichworte_vorgabe = Vorgabe.objects.create( + order=3, + nummer=3, + dokument=self.dokument, + thema=self.thema, + titel="Vorgabe ohne Stichworte", + gueltigkeit_von=date.today() + ) + self.no_stichworte_vorgabe.referenzen.add(self.referenz) + VorgabeKurztext.objects.create( + abschnitt=self.no_stichworte_vorgabe, + inhalt="Test Kurztext" + ) + Checklistenfrage.objects.create( + vorgabe=self.no_stichworte_vorgabe, + frage="Test Frage" + ) + + # 3. Vorgabe without text + self.no_text_vorgabe = Vorgabe.objects.create( + order=4, + nummer=4, + dokument=self.dokument, + thema=self.thema, + titel="Vorgabe ohne Text", + gueltigkeit_von=date.today() + ) + self.no_text_vorgabe.stichworte.add(self.stichwort) + self.no_text_vorgabe.referenzen.add(self.referenz) + Checklistenfrage.objects.create( + vorgabe=self.no_text_vorgabe, + frage="Test Frage" + ) + + # 4. Vorgabe without Checklistenfragen + self.no_checklisten_vorgabe = Vorgabe.objects.create( + order=5, + nummer=5, + dokument=self.dokument, + thema=self.thema, + titel="Vorgabe ohne Checklistenfragen", + gueltigkeit_von=date.today() + ) + self.no_checklisten_vorgabe.stichworte.add(self.stichwort) + self.no_checklisten_vorgabe.referenzen.add(self.referenz) + VorgabeKurztext.objects.create( + abschnitt=self.no_checklisten_vorgabe, + inhalt="Test Kurztext" + ) + + def test_incomplete_vorgaben_page_status(self): + """Test that the incomplete Vorgaben page loads successfully""" + response = self.client.get(reverse('incomplete_vorgaben')) + self.assertEqual(response.status_code, 200) + + def test_incomplete_vorgaben_page_content(self): + """Test that the page contains expected content""" + response = self.client.get(reverse('incomplete_vorgaben')) + self.assertContains(response, 'Unvollständige Vorgaben') + self.assertContains(response, 'Vorgaben ohne Referenzen') + self.assertContains(response, 'Vorgaben ohne Stichworte') + self.assertContains(response, 'Vorgaben ohne Kurz- oder Langtext') + self.assertContains(response, 'Vorgaben ohne Checklistenfragen') + + def test_no_references_list(self): + """Test that Vorgaben without references are listed""" + response = self.client.get(reverse('incomplete_vorgaben')) + self.assertContains(response, 'Vorgabe ohne Referenzen') + self.assertNotContains(response, 'Vollständige Vorgabe') # Should not appear + + def test_no_stichworte_list(self): + """Test that Vorgaben without Stichworte are listed""" + response = self.client.get(reverse('incomplete_vorgaben')) + self.assertContains(response, 'Vorgabe ohne Stichworte') + self.assertNotContains(response, 'Vollständige Vorgabe') # Should not appear + + def test_no_text_list(self): + """Test that Vorgaben without Kurz- or Langtext are listed""" + response = self.client.get(reverse('incomplete_vorgaben')) + self.assertContains(response, 'Vorgabe ohne Text') + self.assertNotContains(response, 'Vollständige Vorgabe') # Should not appear + + def test_no_checklistenfragen_list(self): + """Test that Vorgaben without Checklistenfragen are listed""" + response = self.client.get(reverse('incomplete_vorgaben')) + self.assertContains(response, 'Vorgabe ohne Checklistenfragen') + self.assertNotContains(response, 'Vollständige Vorgabe') # Should not appear + + def test_vorgabe_links(self): + """Test that Vorgaben link to their detail pages""" + response = self.client.get(reverse('incomplete_vorgaben')) + # Should contain links to Vorgabe detail pages + self.assertContains(response, f'href="/dokumente/{self.dokument.nummer}/#TEST-001.T.2"') + self.assertContains(response, f'href="/dokumente/{self.dokument.nummer}/#TEST-001.T.3"') + self.assertContains(response, f'href="/dokumente/{self.dokument.nummer}/#TEST-001.T.4"') + self.assertContains(response, f'href="/dokumente/{self.dokument.nummer}/#TEST-001.T.5"') + + def test_badge_counts(self): + """Test that badge counts are correct""" + response = self.client.get(reverse('incomplete_vorgaben')) + # Each category should have exactly 1 Vorgabe + self.assertContains(response, '1', count=4) + + def test_summary_section(self): + """Test that summary section shows correct counts""" + response = self.client.get(reverse('incomplete_vorgaben')) + self.assertContains(response, 'Zusammenfassung') + self.assertContains(response, '

1

', count=2) # No refs, no stichworte + self.assertContains(response, '

1

') # No text + self.assertContains(response, '

1

') # No checklistenfragen + + def test_empty_lists_message(self): + """Test that appropriate messages are shown when lists are empty""" + # Delete all incomplete Vorgaben + Vorgabe.objects.exclude(pk=self.complete_vorgabe.pk).delete() + + response = self.client.get(reverse('incomplete_vorgaben')) + self.assertContains(response, 'Alle Vorgaben haben Referenzen.') + self.assertContains(response, 'Alle Vorgaben haben Stichworte.') + self.assertContains(response, 'Alle Vorgaben haben Kurz- oder Langtext.') + self.assertContains(response, 'Alle Vorgaben haben Checklistenfragen.') + + def test_back_link(self): + """Test that back link to standard list exists""" + response = self.client.get(reverse('incomplete_vorgaben')) + self.assertContains(response, 'href="/dokumente/"') + self.assertContains(response, 'Zurück zur Übersicht') + + def test_navigation_link(self): + """Test that navigation includes link to incomplete Vorgaben""" + response = self.client.get('/dokumente/') + self.assertContains(response, 'href="/dokumente/unvollstaendig/"') + self.assertContains(response, 'Unvollständig') + + def test_vorgabe_with_langtext_only(self): + """Test that Vorgabe with only Langtext is still considered incomplete for text""" + vorgabe_langtext_only = Vorgabe.objects.create( + order=6, + nummer=6, + dokument=self.dokument, + thema=self.thema, + titel="Vorgabe nur mit Langtext", + gueltigkeit_von=date.today() + ) + vorgabe_langtext_only.stichworte.add(self.stichwort) + vorgabe_langtext_only.referenzen.add(self.referenz) + + # Add only Langtext, no Kurztext + VorgabeLangtext.objects.create( + abschnitt=vorgabe_langtext_only, + inhalt="Test Langtext" + ) + # Add Checklistenfragen to make it complete in that aspect + Checklistenfrage.objects.create( + vorgabe=vorgabe_langtext_only, + frage="Test Frage" + ) + + response = self.client.get(reverse('incomplete_vorgaben')) + # Debug: print response content to see where it appears + print("Response content:", response.content.decode()) + # Should NOT appear in "no text" list because it has Langtext + self.assertNotContains(response, 'Vorgabe nur mit Langtext') + + def test_vorgabe_with_both_text_types(self): + """Test that Vorgabe with both Kurztext and Langtext is complete""" + vorgabe_both_text = Vorgabe.objects.create( + order=7, + nummer=7, + dokument=self.dokument, + thema=self.thema, + titel="Vorgabe mit beiden Texten", + gueltigkeit_von=date.today() + ) + vorgabe_both_text.stichworte.add(self.stichwort) + vorgabe_both_text.referenzen.add(self.referenz) + + # Add both Kurztext and Langtext + VorgabeKurztext.objects.create( + abschnitt=vorgabe_both_text, + inhalt="Test Kurztext" + ) + VorgabeLangtext.objects.create( + abschnitt=vorgabe_both_text, + inhalt="Test Langtext" + ) + # Add Checklistenfragen to make it complete in that aspect + Checklistenfrage.objects.create( + vorgabe=vorgabe_both_text, + frage="Test Frage" + ) + + response = self.client.get(reverse('incomplete_vorgaben')) + # Should NOT appear in "no text" list because it has both text types + self.assertNotContains(response, 'Vorgabe mit beiden Texten') diff --git a/dokumente/urls.py b/dokumente/urls.py index 4345d3d..54d6370 100644 --- a/dokumente/urls.py +++ b/dokumente/urls.py @@ -3,6 +3,7 @@ from . import views urlpatterns = [ path('', views.standard_list, name='standard_list'), + path('unvollstaendig/', views.incomplete_vorgaben, name='incomplete_vorgaben'), path('/', views.standard_detail, name='standard_detail'), path('/history//', views.standard_detail), path('/history/', views.standard_detail, {"check_date":"today"}, name='standard_history'), diff --git a/dokumente/views.py b/dokumente/views.py index 7e844c7..b802543 100644 --- a/dokumente/views.py +++ b/dokumente/views.py @@ -1,5 +1,5 @@ from django.shortcuts import render, get_object_or_404 -from .models import Dokument +from .models import Dokument, Vorgabe, VorgabeKurztext, VorgabeLangtext, Checklistenfrage from abschnitte.utils import render_textabschnitte from datetime import date @@ -56,3 +56,38 @@ def standard_checkliste(request, nummer): }) +def incomplete_vorgaben(request): + """ + Show lists of incomplete Vorgaben: + 1. Ones with no references + 2. Ones with no Stichworte + 3. Ones without Kurz- or Langtext + 4. Ones without Checklistenfragen + """ + # Get all active Vorgaben + all_vorgaben = Vorgabe.objects.all().select_related('dokument', 'thema') + + # 1. Vorgaben with no references + no_references = [v for v in all_vorgaben if not v.referenzen.exists()] + + # 2. Vorgaben with no Stichworte + no_stichworte = [v for v in all_vorgaben if not v.stichworte.exists()] + + # 3. Vorgaben without Kurz- or Langtext + no_text = [] + for vorgabe in all_vorgaben: + has_kurztext = VorgabeKurztext.objects.filter(abschnitt=vorgabe).exists() + has_langtext = VorgabeLangtext.objects.filter(abschnitt=vorgabe).exists() + + if not has_kurztext and not has_langtext: + no_text.append(vorgabe) + + # 4. Vorgaben without Checklistenfragen + no_checklistenfragen = [v for v in all_vorgaben if not v.checklistenfragen.exists()] + + return render(request, 'standards/incomplete_vorgaben.html', { + 'no_references': no_references, + 'no_stichworte': no_stichworte, + 'no_text': no_text, + 'no_checklistenfragen': no_checklistenfragen, + }) diff --git a/pages/templates/base.html b/pages/templates/base.html index 2406379..8145f73 100644 --- a/pages/templates/base.html +++ b/pages/templates/base.html @@ -17,6 +17,7 @@