Compare commits

..

18 Commits

Author SHA1 Message Date
Adrian A. Baumann
4d2ffeea27 .gitignore extended by npm stuff 2025-10-29 14:11:53 +01:00
Adrian A. Baumann
6df72c95cb Tests for documents fixed (Vorgabe-Order added) 2025-10-29 13:47:16 +01:00
2afada0bce Date 'bis None' changed to 'bis auf weiteres' 2025-10-29 13:30:46 +01:00
Adrian A. Baumann
a42a65b40f Make Vorgaben draggable; Deploy 938 2025-10-28 16:19:37 +01:00
5609a735f4 Deploy 937 2025-10-28 13:41:13 +01:00
6654779e67 Corrections on Dokumente-Admin; Homepage now only shows active documents 2025-10-28 13:36:26 +01:00
7befde104d Added 'aktiv' to document tests 2025-10-27 21:24:23 +01:00
96819a7427 Merge branch 'feature/dokumente-unit-tests' into development 2025-10-27 21:18:53 +01:00
a437af554b Deploy 936 2025-10-27 20:53:13 +01:00
650fe0a87b added 'aktiv' to dokument (so people can play around with standards) 2025-10-27 20:49:22 +01:00
Adrian A. Baumann
ddf035c50f Deploy 935 2025-10-27 16:57:35 +01:00
Adrian A. Baumann
886baa163e Increase whitespace between Vorgabe boxes
Increased margin-bottom from 30px to 50px for better visual separation between Vorgaben.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 16:47:19 +01:00
Adrian A. Baumann
1146506ca2 Fix selector for tabular Vorgabe identifiers with tbody target
Changed selector to target tbody.djn-dynamic-form-dokumente-vorgabe and added !important to override existing styles.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 16:46:05 +01:00
Adrian A. Baumann
9610024739 Make Vorgabe identifier text in tabular view prominent
Styled the td.original cell containing Vorgabe identifiers (e.g., "R0066.O.3: Dateninhaber"):
- Font size: 16px
- Font weight: 700 (bold)
- Blue color matching border (#2c5aa0)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 16:44:30 +01:00
Adrian A. Baumann
c8755e4339 Make Vorgabe titles bigger and more prominent
- Increased font size to 18px with bold weight (700)
- Blue color (#2c5aa0) matching the border
- Light blue gradient background
- Bottom border separator
- Extends full width with negative margins

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 16:42:34 +01:00
Adrian A. Baumann
0bc1fe7413 Add prominent border boxes around each Vorgabe
- 3px solid blue border (#2c5aa0)
- Increased margin between Vorgaben (30px)
- Added subtle box shadow
- Support both Standards and dokumente class names

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 16:41:14 +01:00
Adrian A. Baumann
8ce761c248 Deploy 934 2025-10-27 14:00:21 +01:00
Adrian A. Baumann
39a2021cc3 Attempt at improving reference choice in documents 2025-10-27 13:50:14 +01:00
15 changed files with 117 additions and 24 deletions

4
.gitignore vendored
View File

@@ -10,6 +10,8 @@ keys/
.idea/ .idea/
*.kate-swp *.kate-swp
node_modules/
package-lock.json
package.json
# Diagram cache directory # Diagram cache directory
media/diagram_cache/ media/diagram_cache/

View File

@@ -25,7 +25,7 @@ spec:
mountPath: /data mountPath: /data
containers: containers:
- name: web - name: web
image: git.baumann.gr/adebaumann/vui:0.933 image: git.baumann.gr/adebaumann/vui:0.938
imagePullPolicy: Always imagePullPolicy: Always
ports: ports:
- containerPort: 8000 - containerPort: 8000

Binary file not shown.

Binary file not shown.

View File

@@ -4,6 +4,7 @@ from nested_admin import NestedStackedInline, NestedModelAdmin, NestedTabularInl
from django import forms from django import forms
from mptt.forms import TreeNodeMultipleChoiceField from mptt.forms import TreeNodeMultipleChoiceField
from mptt.admin import DraggableMPTTAdmin from mptt.admin import DraggableMPTTAdmin
from adminsortable2.admin import SortableInlineAdminMixin, SortableAdminBase
# Register your models here. # Register your models here.
from .models import * from .models import *
@@ -36,7 +37,7 @@ class VorgabeKurztextInline(NestedTabularInline):
classes = ['collapse'] classes = ['collapse']
#inline=inhalt #inline=inhalt
class VorgabeLangtextInline(NestedStackedInline): class VorgabeLangtextInline(NestedTabularInline):
model=VorgabeLangtext model=VorgabeLangtext
extra=0 extra=0
sortable_field_name = "order" sortable_field_name = "order"
@@ -61,15 +62,16 @@ class EinleitungInline(NestedTabularInline):
classes = ['collapse'] classes = ['collapse']
class VorgabeForm(forms.ModelForm): class VorgabeForm(forms.ModelForm):
# referenzen = TreeNodeMultipleChoiceField(queryset=Referenz.objects.all(), required=False) referenzen = TreeNodeMultipleChoiceField(queryset=Referenz.objects.all(), required=False)
class Meta: class Meta:
model = Vorgabe model = Vorgabe
fields = '__all__' fields = '__all__'
class VorgabeInline(NestedTabularInline): # or StackedInline for more vertical layout class VorgabeInline(SortableInlineAdminMixin, NestedTabularInline): # or StackedInline for more vertical layout
model = Vorgabe model = Vorgabe
form = VorgabeForm form = VorgabeForm
extra = 0 extra = 0
sortable_field_name = "order" # Add this - make sure your Vorgabe model has an 'order' field
#show_change_link = True #show_change_link = True
inlines = [VorgabeKurztextInline,VorgabeLangtextInline,ChecklistenfragenInline] inlines = [VorgabeKurztextInline,VorgabeLangtextInline,ChecklistenfragenInline]
autocomplete_fields = ['stichworte','referenzen','relevanz'] autocomplete_fields = ['stichworte','referenzen','relevanz']
@@ -77,7 +79,7 @@ class VorgabeInline(NestedTabularInline): # or StackedInline for more vertical
list_filter=['stichworte'] list_filter=['stichworte']
#classes=["collapse"] #classes=["collapse"]
class StichworterklaerungInline(NestedStackedInline): class StichworterklaerungInline(NestedTabularInline):
model=Stichworterklaerung model=Stichworterklaerung
extra=0 extra=0
sortable_field_name = "order" sortable_field_name = "order"
@@ -100,7 +102,7 @@ class PersonAdmin(admin.ModelAdmin):
@admin.register(Dokument) @admin.register(Dokument)
class DokumentAdmin(NestedModelAdmin): class DokumentAdmin(SortableAdminBase, NestedModelAdmin):
actions_on_top=True actions_on_top=True
inlines = [EinleitungInline,GeltungsbereichInline,VorgabeInline] inlines = [EinleitungInline,GeltungsbereichInline,VorgabeInline]
#filter_horizontal=['autoren','pruefende'] #filter_horizontal=['autoren','pruefende']

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.5 on 2025-10-27 19:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dokumente', '0007_alter_changelog_options_and_more'),
]
operations = [
migrations.AddField(
model_name='dokument',
name='aktiv',
field=models.BooleanField(blank=True, default=False),
preserve_default=False,
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.2.5 on 2025-10-28 14:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dokumente', '0008_dokument_aktiv'),
]
operations = [
migrations.AlterModelOptions(
name='vorgabe',
options={'ordering': ['order'], 'verbose_name_plural': 'Vorgaben'},
),
migrations.AddField(
model_name='vorgabe',
name='order',
field=models.IntegerField(default=0),
preserve_default=False,
),
]

View File

@@ -47,6 +47,7 @@ class Dokument(models.Model):
gueltigkeit_bis = models.DateField(null=True, blank=True) gueltigkeit_bis = models.DateField(null=True, blank=True)
signatur_cso = models.CharField(max_length=255, blank=True) signatur_cso = models.CharField(max_length=255, blank=True)
anhaenge = models.TextField(blank=True) anhaenge = models.TextField(blank=True)
aktiv = models.BooleanField(blank=True)
def __str__(self): def __str__(self):
return f"{self.nummer} {self.name}" return f"{self.nummer} {self.name}"
@@ -56,6 +57,7 @@ class Dokument(models.Model):
verbose_name="Dokument" verbose_name="Dokument"
class Vorgabe(models.Model): class Vorgabe(models.Model):
order = models.IntegerField()
nummer = models.IntegerField() nummer = models.IntegerField()
dokument = models.ForeignKey(Dokument, on_delete=models.CASCADE, related_name='vorgaben') dokument = models.ForeignKey(Dokument, on_delete=models.CASCADE, related_name='vorgaben')
thema = models.ForeignKey(Thema, on_delete=models.PROTECT) thema = models.ForeignKey(Thema, on_delete=models.PROTECT)
@@ -86,7 +88,7 @@ class Vorgabe(models.Model):
class Meta: class Meta:
verbose_name_plural="Vorgaben" verbose_name_plural="Vorgaben"
ordering = ['order']
class VorgabeLangtext(Textabschnitt): class VorgabeLangtext(Textabschnitt):
abschnitt=models.ForeignKey(Vorgabe,on_delete=models.CASCADE) abschnitt=models.ForeignKey(Vorgabe,on_delete=models.CASCADE)

View File

@@ -8,7 +8,7 @@
<!-- Autoren, Prüfende etc. --> <!-- Autoren, Prüfende etc. -->
<p><strong>Autoren:</strong> {{ standard.autoren.all|join:", " }}</p> <p><strong>Autoren:</strong> {{ standard.autoren.all|join:", " }}</p>
<p><strong>Prüfende:</strong> {{ standard.pruefende.all|join:", " }}</p> <p><strong>Prüfende:</strong> {{ standard.pruefende.all|join:", " }}</p>
<p><strong>Gültigkeit:</strong> {{ standard.gueltigkeit_von }} bis {{ standard.gueltigkeit_bis }}</p> <p><strong>Gültigkeit:</strong> {{ standard.gueltigkeit_von }} bis {{ standard.gueltigkeit_bis|default_if_none:"auf weiteres" }}</p>
<!-- Start Einleitung --> <!-- Start Einleitung -->
{% if standard.einleitung_html %} {% if standard.einleitung_html %}

View File

@@ -114,7 +114,8 @@ class DokumentModelTest(TestCase):
name="Security Policy", name="Security Policy",
gueltigkeit_von=date.today(), gueltigkeit_von=date.today(),
signatur_cso="CSO-123", signatur_cso="CSO-123",
anhaenge="Appendix A, B" anhaenge="Appendix A, B",
aktiv=True
) )
self.dokument.autoren.add(self.autor) self.dokument.autoren.add(self.autor)
self.dokument.pruefende.add(self.pruefer) self.dokument.pruefende.add(self.pruefer)
@@ -124,6 +125,7 @@ class DokumentModelTest(TestCase):
self.assertEqual(self.dokument.nummer, "DOC-001") self.assertEqual(self.dokument.nummer, "DOC-001")
self.assertEqual(self.dokument.name, "Security Policy") self.assertEqual(self.dokument.name, "Security Policy")
self.assertEqual(self.dokument.dokumententyp, self.dokumententyp) self.assertEqual(self.dokument.dokumententyp, self.dokumententyp)
self.assertEqual(self.dokument.aktiv, True)
def test_dokument_str(self): def test_dokument_str(self):
"""Test string representation of Dokument""" """Test string representation of Dokument"""
@@ -139,7 +141,8 @@ class DokumentModelTest(TestCase):
dokument = Dokument.objects.create( dokument = Dokument.objects.create(
nummer="DOC-002", nummer="DOC-002",
dokumententyp=self.dokumententyp, dokumententyp=self.dokumententyp,
name="Test Document" name="Test Document",
aktiv=True
) )
self.assertIsNone(dokument.gueltigkeit_von) self.assertIsNone(dokument.gueltigkeit_von)
self.assertIsNone(dokument.gueltigkeit_bis) self.assertIsNone(dokument.gueltigkeit_bis)
@@ -158,10 +161,12 @@ class VorgabeModelTest(TestCase):
self.dokument = Dokument.objects.create( self.dokument = Dokument.objects.create(
nummer="R01234", nummer="R01234",
dokumententyp=self.dokumententyp, dokumententyp=self.dokumententyp,
name="IT Standard" name="IT Standard",
aktiv=True
) )
self.thema = Thema.objects.create(name="Security") self.thema = Thema.objects.create(name="Security")
self.vorgabe = Vorgabe.objects.create( self.vorgabe = Vorgabe.objects.create(
order=1,
nummer=1, nummer=1,
dokument=self.dokument, dokument=self.dokument,
thema=self.thema, thema=self.thema,
@@ -171,6 +176,7 @@ class VorgabeModelTest(TestCase):
def test_vorgabe_creation(self): def test_vorgabe_creation(self):
"""Test that Vorgabe is created correctly""" """Test that Vorgabe is created correctly"""
self.assertEqual(self.vorgabe.order, 1)
self.assertEqual(self.vorgabe.nummer, 1) self.assertEqual(self.vorgabe.nummer, 1)
self.assertEqual(self.vorgabe.dokument, self.dokument) self.assertEqual(self.vorgabe.dokument, self.dokument)
self.assertEqual(self.vorgabe.thema, self.thema) self.assertEqual(self.vorgabe.thema, self.thema)
@@ -193,6 +199,7 @@ class VorgabeModelTest(TestCase):
def test_get_status_future(self): def test_get_status_future(self):
"""Test get_status returns 'future' for future vorgabe""" """Test get_status returns 'future' for future vorgabe"""
future_vorgabe = Vorgabe.objects.create( future_vorgabe = Vorgabe.objects.create(
order=2,
nummer=2, nummer=2,
dokument=self.dokument, dokument=self.dokument,
thema=self.thema, thema=self.thema,
@@ -205,6 +212,7 @@ class VorgabeModelTest(TestCase):
def test_get_status_expired(self): def test_get_status_expired(self):
"""Test get_status returns 'expired' for expired vorgabe""" """Test get_status returns 'expired' for expired vorgabe"""
expired_vorgabe = Vorgabe.objects.create( expired_vorgabe = Vorgabe.objects.create(
order=3,
nummer=3, nummer=3,
dokument=self.dokument, dokument=self.dokument,
thema=self.thema, thema=self.thema,
@@ -218,6 +226,7 @@ class VorgabeModelTest(TestCase):
def test_get_status_verbose(self): def test_get_status_verbose(self):
"""Test get_status with verbose=True""" """Test get_status with verbose=True"""
future_vorgabe = Vorgabe.objects.create( future_vorgabe = Vorgabe.objects.create(
order=4,
nummer=4, nummer=4,
dokument=self.dokument, dokument=self.dokument,
thema=self.thema, thema=self.thema,
@@ -231,6 +240,7 @@ class VorgabeModelTest(TestCase):
def test_get_status_with_custom_check_date(self): def test_get_status_with_custom_check_date(self):
"""Test get_status with custom check_date""" """Test get_status with custom check_date"""
vorgabe = Vorgabe.objects.create( vorgabe = Vorgabe.objects.create(
order=5,
nummer=5, nummer=5,
dokument=self.dokument, dokument=self.dokument,
thema=self.thema, thema=self.thema,
@@ -254,10 +264,12 @@ class VorgabeTextAbschnitteTest(TestCase):
self.dokument = Dokument.objects.create( self.dokument = Dokument.objects.create(
nummer="R01234", nummer="R01234",
dokumententyp=self.dokumententyp, dokumententyp=self.dokumententyp,
name="Test Standard" name="Test Standard",
aktiv=True
) )
self.thema = Thema.objects.create(name="Testing") self.thema = Thema.objects.create(name="Testing")
self.vorgabe = Vorgabe.objects.create( self.vorgabe = Vorgabe.objects.create(
order=1,
nummer=1, nummer=1,
dokument=self.dokument, dokument=self.dokument,
thema=self.thema, thema=self.thema,
@@ -302,7 +314,8 @@ class DokumentTextAbschnitteTest(TestCase):
self.dokument = Dokument.objects.create( self.dokument = Dokument.objects.create(
nummer="POL-001", nummer="POL-001",
dokumententyp=self.dokumententyp, dokumententyp=self.dokumententyp,
name="Test Policy" name="Test Policy",
aktiv=True
) )
self.abschnitttyp = AbschnittTyp.objects.create( self.abschnitttyp = AbschnittTyp.objects.create(
abschnitttyp="Paragraph" abschnitttyp="Paragraph"
@@ -342,10 +355,12 @@ class ChecklistenfrageModelTest(TestCase):
self.dokument = Dokument.objects.create( self.dokument = Dokument.objects.create(
nummer="QA-001", nummer="QA-001",
dokumententyp=self.dokumententyp, dokumententyp=self.dokumententyp,
name="QA Standard" name="QA Standard",
aktiv=True
) )
self.thema = Thema.objects.create(name="Quality") self.thema = Thema.objects.create(name="Quality")
self.vorgabe = Vorgabe.objects.create( self.vorgabe = Vorgabe.objects.create(
order=1,
nummer=1, nummer=1,
dokument=self.dokument, dokument=self.dokument,
thema=self.thema, thema=self.thema,
@@ -382,7 +397,8 @@ class ChangelogModelTest(TestCase):
self.dokument = Dokument.objects.create( self.dokument = Dokument.objects.create(
nummer="R01234", nummer="R01234",
dokumententyp=self.dokumententyp, dokumententyp=self.dokumententyp,
name="IT Standard" name="IT Standard",
aktiv=True
) )
self.autor = Person.objects.create( self.autor = Person.objects.create(
name="John Doe", name="John Doe",
@@ -419,10 +435,12 @@ class ViewsTestCase(TestCase):
self.dokument = Dokument.objects.create( self.dokument = Dokument.objects.create(
nummer="R01234", nummer="R01234",
dokumententyp=self.dokumententyp, dokumententyp=self.dokumententyp,
name="Test Standard" name="Test Standard",
aktiv=True
) )
self.thema = Thema.objects.create(name="Testing") self.thema = Thema.objects.create(name="Testing")
self.vorgabe = Vorgabe.objects.create( self.vorgabe = Vorgabe.objects.create(
order=1,
nummer=1, nummer=1,
dokument=self.dokument, dokument=self.dokument,
thema=self.thema, thema=self.thema,

View File

@@ -28,6 +28,6 @@
<div class="flex-fill">{% block content %}Main Content{% endblock %}</div> <div class="flex-fill">{% block content %}Main Content{% endblock %}</div>
<div class="col-md-2">{% block sidebar_right %}{% endblock %}</div> <div class="col-md-2">{% block sidebar_right %}{% endblock %}</div>
</div> </div>
<div>VorgabenUI v0.931</div> <div>VorgabenUI v0.936</div>
</body> </body>
</html> </html>

View File

@@ -6,7 +6,7 @@ import datetime
import pprint import pprint
def startseite(request): def startseite(request):
standards=list(Dokument.objects.all()) standards=list(Dokument.objects.filter(aktiv=True))
return render(request, 'startseite.html', {"dokumente":standards,}) return render(request, 'startseite.html', {"dokumente":standards,})
def search(request): def search(request):

View File

@@ -13,4 +13,4 @@ class ReferenzerklaerungInline(NestedStackedInline):
class ReferenzAdmin(NestedModelAdmin): class ReferenzAdmin(NestedModelAdmin):
inlines=[ReferenzerklaerungInline] inlines=[ReferenzerklaerungInline]
list_display =['Path'] list_display =['Path']
search_fields=("referenz",) search_fields=("referenz","path")

View File

@@ -6,6 +6,7 @@ charset-normalizer==3.4.3
curtsies==0.4.3 curtsies==0.4.3
cwcwidth==0.1.10 cwcwidth==0.1.10
Django==5.2.5 Django==5.2.5
django-admin-sortable2==2.2.8
django-js-asset==3.1.2 django-js-asset==3.1.2
django-mptt==0.17.0 django-mptt==0.17.0
django-mptt-admin==2.8.0 django-mptt-admin==2.8.0

View File

@@ -1,14 +1,40 @@
/* Style each Vorgabe inline block */ /* Style each Vorgabe inline block */
.djn-dynamic-form-Standards-vorgabe { .djn-dynamic-form-Standards-vorgabe,
border: 2px solid #ccc; .djn-dynamic-form-dokumente-vorgabe {
border: 3px solid #2c5aa0;
border-radius: 8px; border-radius: 8px;
padding: 15px; padding: 15px;
margin-bottom: 20px; margin-bottom: 50px;
background-color: #f9f9f9; background-color: #f9f9f9;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
/* Make Vorgabe title prominent */
.djn-dynamic-form-Standards-vorgabe > h3,
.djn-dynamic-form-dokumente-vorgabe > h3 {
font-size: 18px;
font-weight: 700;
color: #2c5aa0;
margin: -15px -15px 15px -15px;
padding: 12px 15px;
background: linear-gradient(to bottom, #e8f0f8, #d4e4f3);
border-bottom: 2px solid #2c5aa0;
border-radius: 5px 5px 0 0;
}
/* Make Vorgabe identifier in tabular view prominent */
tbody.djn-dynamic-form-Standards-vorgabe td.original,
tbody.djn-dynamic-form-dokumente-vorgabe td.original,
tbody.djn-dynamic-form-Standards-vorgabe td.original p,
tbody.djn-dynamic-form-dokumente-vorgabe td.original p {
font-size: 16px !important;
font-weight: 700 !important;
color: #2c5aa0 !important;
} }
/* Optional: Slight padding for inner fieldsets (e.g., Langtext/Kurztext inlines) */ /* Optional: Slight padding for inner fieldsets (e.g., Langtext/Kurztext inlines) */
.djn-dynamic-form-Standards-vorgabe .inline-related { .djn-dynamic-form-Standards-vorgabe .inline-related,
.djn-dynamic-form-dokumente-vorgabe .inline-related {
margin-top: 10px; margin-top: 10px;
padding-left: 10px; padding-left: 10px;
border-left: 2px dashed #ccc; border-left: 2px dashed #ccc;