Compare commits
26 Commits
django-deb
...
db06ae0630
| Author | SHA1 | Date | |
|---|---|---|---|
| db06ae0630 | |||
| 6afc9f8f4e | |||
| 5e0616dc6c | |||
| a55736f736 | |||
| d97a66690a | |||
| 784fbea088 | |||
| 6e8a978ae5 | |||
| 2065d69a80 | |||
| dbd75f9e30 | |||
| 077b376953 | |||
| 7c1b89a13b | |||
| b0bfb4a38a | |||
| 244e9e155f | |||
| bba32d08e3 | |||
| 4b257bae44 | |||
| 89f427462d | |||
| 94f381c02f | |||
| a24c1059c8 | |||
| d7ddb0a88c | |||
| dd75bd20c4 | |||
| 6c1b4938cf | |||
| 23f6c9bb31 | |||
| 53c828c77f | |||
| 412a5f3824 | |||
| 931131b8e6 | |||
| 506b40db6c |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,5 +7,7 @@ bin/
|
|||||||
pyvenv.cfg
|
pyvenv.cfg
|
||||||
include/
|
include/
|
||||||
keys/
|
keys/
|
||||||
|
.venv/
|
||||||
|
.idea/
|
||||||
|
|
||||||
*.kate-swp
|
*.kate-swp
|
||||||
|
|||||||
@@ -52,11 +52,9 @@ INSTALLED_APPS = [
|
|||||||
'pages',
|
'pages',
|
||||||
'nested_admin',
|
'nested_admin',
|
||||||
'revproxy.apps.RevProxyConfig',
|
'revproxy.apps.RevProxyConfig',
|
||||||
'debug_toolbar',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ from django.contrib import admin
|
|||||||
from django.urls import include, path, re_path
|
from django.urls import include, path, re_path
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from debug_toolbar.toolbar import debug_toolbar_urls
|
|
||||||
from diagramm_proxy.views import DiagrammProxyView
|
from diagramm_proxy.views import DiagrammProxyView
|
||||||
import standards.views
|
import standards.views
|
||||||
import pages.views
|
import pages.views
|
||||||
@@ -35,5 +34,5 @@ urlpatterns = [
|
|||||||
path('referenzen/', referenzen.views.tree, name="referenz_tree"),
|
path('referenzen/', referenzen.views.tree, name="referenz_tree"),
|
||||||
path('referenzen/<str:refid>/', referenzen.views.detail, name="referenz_detail"),
|
path('referenzen/<str:refid>/', referenzen.views.detail, name="referenz_detail"),
|
||||||
re_path(r'^diagramm/(?P<path>.*)$', DiagrammProxyView.as_view()),
|
re_path(r'^diagramm/(?P<path>.*)$', DiagrammProxyView.as_view()),
|
||||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) +debug_toolbar_urls()
|
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
|
||||||
|
|||||||
12
argocd/001_pvc.yaml
Normal file
12
argocd/001_pvc.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: django-data-pvc
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 2Gi
|
||||||
|
|
||||||
@@ -16,9 +16,16 @@ spec:
|
|||||||
securityContext:
|
securityContext:
|
||||||
fsGroup: 999
|
fsGroup: 999
|
||||||
fsGroupChangePolicy: "OnRootMismatch"
|
fsGroupChangePolicy: "OnRootMismatch"
|
||||||
|
initContainers:
|
||||||
|
- name: loader
|
||||||
|
image: git.baumann.gr/adebaumann/vgui-data-loader:0.5
|
||||||
|
command: [ "sh","-c","cp -n preload/preload.sqlite3 /data/db.sqlite3; chown -R 999:999 /data; ls -la /data; sleep 10; exit 0" ]
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
containers:
|
containers:
|
||||||
- name: web
|
- name: web
|
||||||
image: git.baumann.gr/adebaumann/vui:0.922
|
image: git.baumann.gr/adebaumann/vui:0.926
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8000
|
- containerPort: 8000
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: kroki
|
- name: kroki
|
||||||
image: docker.io/yuzutech/kroki:latest
|
image: git.baumann.gr/adebaumann/kroki:0.026
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8000
|
- containerPort: 8000
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
@@ -35,15 +35,15 @@ spec:
|
|||||||
timeoutSeconds: 2
|
timeoutSeconds: 2
|
||||||
failureThreshold: 3
|
failureThreshold: 3
|
||||||
- name: mermaid
|
- name: mermaid
|
||||||
image: docker.io/yuzutech/kroki-mermaid:latest
|
image: git.baumann.gr/adebaumann/kroki-mermaid:0.026
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8002
|
- containerPort: 8002
|
||||||
- name: bpmn
|
- name: bpmn
|
||||||
image: docker.io/yuzutech/kroki-bpmn:latest
|
image: git.baumann.gr/adebaumann/kroki-bpmn:0.026
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8003
|
- containerPort: 8003
|
||||||
- name: excalidraw
|
- name: excalidraw
|
||||||
image: docker.io/yuzutech/kroki-excalidraw:latest
|
image: git.baumann.gr/adebaumann/kroki-excalidraw:0.026
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8004
|
- containerPort: 8004
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ metadata:
|
|||||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||||
spec:
|
spec:
|
||||||
rules:
|
rules:
|
||||||
- host: vorgabenui.adebaumann.com
|
- host: vorgabenportal.knowyoursecurity.com
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
|
|||||||
@@ -8,5 +8,4 @@ RUN chown appuser:appuser /preload/preload.sqlite3
|
|||||||
RUN mkdir /data
|
RUN mkdir /data
|
||||||
RUN chown appuser:appuser /data
|
RUN chown appuser:appuser /data
|
||||||
USER root
|
USER root
|
||||||
CMD ["sh"]
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
BIN
data/db.sqlite3
BIN
data/db.sqlite3
Binary file not shown.
@@ -7,8 +7,8 @@ spec:
|
|||||||
restartPolicy: Never
|
restartPolicy: Never
|
||||||
containers:
|
containers:
|
||||||
- name: loader
|
- name: loader
|
||||||
image: adebaumann/vgui-preloader:0.4
|
image: adebaumann/vgui-preloader:0.5
|
||||||
command: ["sh","-c","cp -v /preload/preload.sqlite3 /data/db.sqlite3; chown -R 999:999 /data; ls -la /data"]
|
command: ["sh","-c","cp -v --debug --update=none /preload/preload.sqlite3 /data/db.sqlite3; chown -R 999:999 /data; ls -la /data; exit 0"]
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: data
|
- name: data
|
||||||
mountPath: /data
|
mountPath: /data
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ spec:
|
|||||||
securityContext:
|
securityContext:
|
||||||
fsGroup: 999
|
fsGroup: 999
|
||||||
fsGroupChangePolicy: "OnRootMismatch"
|
fsGroupChangePolicy: "OnRootMismatch"
|
||||||
|
initContainers:
|
||||||
|
- name: loader
|
||||||
|
image: adebaumann/vgui-preloader:0.5
|
||||||
|
command: [ "sh","-c","cp -v --debug --update=none /preload/preload.sqlite3 /data/db.sqlite3; chown -R 999:999 /data; ls -la /data; exit 0" ]
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
containers:
|
containers:
|
||||||
- name: web
|
- name: web
|
||||||
image: docker.io/adebaumann/vui:0.917
|
image: docker.io/adebaumann/vui:0.917
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""Django's command-line utility for administrative tasks."""
|
"""Django's command-line utility for administrative tasks."""
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -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.8</div>
|
<div>VorgabenUI v0.926</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from abschnitte.utils import render_textabschnitte
|
from abschnitte.utils import render_textabschnitte
|
||||||
from standards.models import Standard, VorgabeLangtext, VorgabeKurztext, Geltungsbereich
|
from standards.models import Dokument, VorgabeLangtext, VorgabeKurztext, Geltungsbereich
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
def startseite(request):
|
def startseite(request):
|
||||||
standards=list(Standard.objects.all())
|
standards=list(Dokument.objects.all())
|
||||||
return render(request, 'startseite.html', {"standards":standards,})
|
return render(request, 'startseite.html', {"standards":standards,})
|
||||||
|
|
||||||
def search(request):
|
def search(request):
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ 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-debug-toolbar==6.0.0
|
|
||||||
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
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ class PersonAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Standard)
|
@admin.register(Dokument)
|
||||||
class StandardAdmin(NestedModelAdmin):
|
class DokumentAdmin(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']
|
||||||
@@ -118,7 +118,7 @@ class StandardAdmin(NestedModelAdmin):
|
|||||||
#admin.site.register(Stichwort)
|
#admin.site.register(Stichwort)
|
||||||
|
|
||||||
admin.site.register(Checklistenfrage)
|
admin.site.register(Checklistenfrage)
|
||||||
#admin.site.register(Dokumententyp)
|
admin.site.register(Dokumententyp)
|
||||||
#admin.site.register(Person)
|
#admin.site.register(Person)
|
||||||
admin.site.register(Thema)
|
admin.site.register(Thema)
|
||||||
#admin.site.register(Referenz, DraggableM§PTTAdmin)
|
#admin.site.register(Referenz, DraggableM§PTTAdmin)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Standards/management/commands/import_standard.py
|
# Document/management/commands/import_standard.py
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from standards.models import (
|
from standards.models import (
|
||||||
Standard,
|
Dokument,
|
||||||
Dokumententyp,
|
Dokumententyp,
|
||||||
Thema,
|
Thema,
|
||||||
Vorgabe,
|
Vorgabe,
|
||||||
@@ -21,15 +21,15 @@ from stichworte.models import Stichwort
|
|||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = (
|
help = (
|
||||||
"Import a security standard from a structured text file.\n"
|
"Import a policy document from a structured text file.\n"
|
||||||
"Supports Einleitung, Geltungsbereich, Vorgaben (Kurztext/Langtext with AbschnittTyp), "
|
"Supports Einleitung, Geltungsbereich, Vorgaben (Kurztext/Langtext with AbschnittTyp), "
|
||||||
"Stichworte (comma-separated), Checklistenfragen, dry-run, verbose, and purge."
|
"Stichworte (comma-separated), Checklistenfragen, dry-run, verbose, and purge."
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument("file_path", type=str, help="Path to the plaintext file")
|
parser.add_argument("file_path", type=str, help="Path to the plaintext file")
|
||||||
parser.add_argument("--nummer", required=True, help="Standard number (e.g., STD-001)")
|
parser.add_argument("--nummer", required=True, help="Document number (e.g., STD-001)")
|
||||||
parser.add_argument("--name", required=True, help='Standard name (e.g., "IT-Sicherheit Container")')
|
parser.add_argument("--name", required=True, help='Document name (e.g., "IT-Sicherheit Container")')
|
||||||
parser.add_argument("--dokumententyp", required=True, help='Dokumententyp name (e.g., "IT-Sicherheit")')
|
parser.add_argument("--dokumententyp", required=True, help='Dokumententyp name (e.g., "IT-Sicherheit")')
|
||||||
parser.add_argument("--gueltigkeit_von", default=None, help="Start date (YYYY-MM-DD)")
|
parser.add_argument("--gueltigkeit_von", default=None, help="Start date (YYYY-MM-DD)")
|
||||||
parser.add_argument("--gueltigkeit_bis", default=None, help="End date (YYYY-MM-DD)")
|
parser.add_argument("--gueltigkeit_bis", default=None, help="End date (YYYY-MM-DD)")
|
||||||
@@ -63,8 +63,8 @@ class Command(BaseCommand):
|
|||||||
if dry_run:
|
if dry_run:
|
||||||
self.stdout.write(self.style.WARNING("Dry run: no database changes will be made."))
|
self.stdout.write(self.style.WARNING("Dry run: no database changes will be made."))
|
||||||
|
|
||||||
# get or create Standard (we want a real instance even in purge to count existing rows)
|
# get or create Document (we want a real instance even in purge to count existing rows)
|
||||||
standard, created = Standard.objects.get_or_create(
|
standard, created = Dokument.objects.get_or_create(
|
||||||
nummer=nummer,
|
nummer=nummer,
|
||||||
defaults={
|
defaults={
|
||||||
"dokumententyp": dokumententyp,
|
"dokumententyp": dokumententyp,
|
||||||
@@ -74,9 +74,9 @@ class Command(BaseCommand):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
if created:
|
if created:
|
||||||
self.stdout.write(self.style.SUCCESS(f"Created Standard {nummer} – {name}"))
|
self.stdout.write(self.style.SUCCESS(f"Created Document {nummer} – {name}"))
|
||||||
else:
|
else:
|
||||||
self.stdout.write(self.style.WARNING(f"Standard {nummer} already exists; content may be updated."))
|
self.stdout.write(self.style.WARNING(f"Document {nummer} already exists; content may be updated."))
|
||||||
|
|
||||||
# purge (Einleitung + Geltungsbereich + Vorgaben cascade)
|
# purge (Einleitung + Geltungsbereich + Vorgaben cascade)
|
||||||
if purge:
|
if purge:
|
||||||
@@ -347,6 +347,6 @@ class Command(BaseCommand):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS(
|
self.stdout.write(self.style.SUCCESS(
|
||||||
"Dry run complete" if dry_run else f"Imported standard {nummer} – {name} with {len(vorgaben_data)} Vorgaben"
|
"Dry run complete" if dry_run else f"Imported document {nummer} – {name} with {len(vorgaben_data)} Vorgaben"
|
||||||
))
|
))
|
||||||
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
# Standards/management/commands/import_standard.py
|
|
||||||
import re
|
|
||||||
from pathlib import Path
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
from standards.models import (
|
|
||||||
Standard,
|
|
||||||
Vorgabe,
|
|
||||||
VorgabeKurztext,
|
|
||||||
VorgabeLangtext,
|
|
||||||
Geltungsbereich,
|
|
||||||
Dokumententyp,
|
|
||||||
Thema,
|
|
||||||
)
|
|
||||||
from abschnitte.models import AbschnittTyp
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = "Import a security standard from a structured text file"
|
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
|
||||||
parser.add_argument("file_path", type=str, help="Path to the plaintext file")
|
|
||||||
parser.add_argument("--nummer", required=True, help="Standard number (e.g., STD-001)")
|
|
||||||
parser.add_argument("--name", required=True, help="Standard name (e.g., IT-Sicherheit Container)")
|
|
||||||
parser.add_argument("--dokumententyp", required=True, help="Dokumententyp name")
|
|
||||||
parser.add_argument("--gueltigkeit_von", default=None, help="Start date (YYYY-MM-DD)")
|
|
||||||
parser.add_argument("--gueltigkeit_bis", default=None, help="End date (YYYY-MM-DD)")
|
|
||||||
parser.add_argument("--dry-run", action="store_true", help="Perform a dry run without saving to the database")
|
|
||||||
parser.add_argument("--verbose", action="store_true", help="Verbose output for dry run")
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
dry_run = options["dry_run"]
|
|
||||||
verbose = options["verbose"]
|
|
||||||
file_path = Path(options["file_path"])
|
|
||||||
if not file_path.exists():
|
|
||||||
raise CommandError(f"File {file_path} does not exist")
|
|
||||||
|
|
||||||
nummer = options["nummer"]
|
|
||||||
name = options["name"]
|
|
||||||
dokumententyp_name = options["dokumententyp"]
|
|
||||||
|
|
||||||
try:
|
|
||||||
dokumententyp = Dokumententyp.objects.get(name=dokumententyp_name)
|
|
||||||
except Dokumententyp.DoesNotExist:
|
|
||||||
raise CommandError(f"Dokumententyp '{dokumententyp_name}' does not exist")
|
|
||||||
|
|
||||||
if dry_run:
|
|
||||||
self.stdout.write(self.style.WARNING("Dry run: no database changes will be made"))
|
|
||||||
|
|
||||||
# Create or get the Standard
|
|
||||||
if dry_run:
|
|
||||||
standard = {"nummer": nummer, "name": name, "dokumententyp": dokumententyp}
|
|
||||||
else:
|
|
||||||
standard, created = Standard.objects.get_or_create(
|
|
||||||
nummer=nummer,
|
|
||||||
defaults={
|
|
||||||
"dokumententyp": dokumententyp,
|
|
||||||
"name": name,
|
|
||||||
"gueltigkeit_von": options["gueltigkeit_von"],
|
|
||||||
"gueltigkeit_bis": options["gueltigkeit_bis"],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if not created:
|
|
||||||
self.stdout.write(self.style.WARNING(f"Standard {nummer} already exists, updating content"))
|
|
||||||
|
|
||||||
# Read and parse the file
|
|
||||||
content = file_path.read_text(encoding="utf-8")
|
|
||||||
blocks = re.split(r"^>>>", content, flags=re.MULTILINE)
|
|
||||||
blocks = [b.strip() for b in blocks if b.strip()]
|
|
||||||
|
|
||||||
geltungsbereich_sections = []
|
|
||||||
current_vorgabe = None
|
|
||||||
vorgaben_data = []
|
|
||||||
current_context = "geltungsbereich"
|
|
||||||
abschnittstyp_headers = ["text", "liste geordnet", "liste ungeordnet"]
|
|
||||||
|
|
||||||
for block in blocks:
|
|
||||||
lines = block.splitlines()
|
|
||||||
header = lines[0].strip()
|
|
||||||
text = "\n".join(lines[1:]).strip()
|
|
||||||
header_lower = header.lower()
|
|
||||||
|
|
||||||
# Determine AbschnittTyp if applicable
|
|
||||||
abschnitt_typ = None
|
|
||||||
if header_lower in abschnittstyp_headers:
|
|
||||||
try:
|
|
||||||
abschnitt_typ = AbschnittTyp.objects.get(abschnitttyp=header_lower)
|
|
||||||
except AbschnittTyp.DoesNotExist:
|
|
||||||
self.stdout.write(self.style.WARNING(f"AbschnittTyp '{header_lower}' not found, defaulting to 'text'"))
|
|
||||||
abschnitt_typ = AbschnittTyp.objects.get(abschnitttyp="text")
|
|
||||||
|
|
||||||
if header_lower == "geltungsbereich":
|
|
||||||
current_context = "geltungsbereich"
|
|
||||||
|
|
||||||
elif header_lower.startswith("vorgabe"):
|
|
||||||
if current_vorgabe:
|
|
||||||
vorgaben_data.append(current_vorgabe)
|
|
||||||
thema_name = header.split(" ", 1)[1].strip()
|
|
||||||
current_vorgabe = {"thema": thema_name, "titel": "", "nummer": None, "kurztext": [], "langtext": []}
|
|
||||||
current_context = "vorgabe_none"
|
|
||||||
|
|
||||||
elif header_lower.startswith("titel") and current_vorgabe:
|
|
||||||
current_vorgabe["titel"] = text
|
|
||||||
|
|
||||||
elif header_lower.startswith("nummer") and current_vorgabe:
|
|
||||||
nummer_match = re.search(r"\d+", header)
|
|
||||||
if nummer_match:
|
|
||||||
current_vorgabe["nummer"] = int(nummer_match.group())
|
|
||||||
current_context = "vorgabe_none"
|
|
||||||
|
|
||||||
elif header_lower == "kurztext":
|
|
||||||
current_context = "vorgabe_kurztext"
|
|
||||||
|
|
||||||
elif header_lower == "langtext":
|
|
||||||
current_context = "vorgabe_langtext"
|
|
||||||
|
|
||||||
elif header_lower in abschnittstyp_headers:
|
|
||||||
abschnitt = {"inhalt": text, "typ": abschnitt_typ}
|
|
||||||
if current_context == "geltungsbereich":
|
|
||||||
geltungsbereich_sections.append(abschnitt)
|
|
||||||
if dry_run and verbose:
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"[DRY RUN] Geltungsbereich Abschnitt (Abschnittstyp: {abschnitt_typ}): {text[:50]}..."))
|
|
||||||
elif current_context == "vorgabe_kurztext" and current_vorgabe:
|
|
||||||
current_vorgabe["kurztext"].append(abschnitt)
|
|
||||||
if dry_run and verbose:
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"[DRY RUN] Vorgabe {current_vorgabe['nummer']} Kurztext Abschnitt (Abschnittstyp: {abschnitt_typ}): {text[:50]}..."))
|
|
||||||
elif current_context == "vorgabe_langtext" and current_vorgabe:
|
|
||||||
current_vorgabe["langtext"].append(abschnitt)
|
|
||||||
if dry_run and verbose:
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"[DRY RUN] Vorgabe {current_vorgabe['nummer']} Langtext Abschnitt (Abschnittstyp: {abschnitt_typ}): {text[:50]}..."))
|
|
||||||
|
|
||||||
if current_vorgabe:
|
|
||||||
vorgaben_data.append(current_vorgabe)
|
|
||||||
|
|
||||||
# Save Geltungsbereich
|
|
||||||
for sektion in geltungsbereich_sections:
|
|
||||||
if dry_run:
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"[DRY RUN] Would create Geltungsbereich Abschnitt (Abschnittstyp: {sektion['typ']}): {sektion['inhalt'][:50]}..."))
|
|
||||||
else:
|
|
||||||
Geltungsbereich.objects.create(
|
|
||||||
geltungsbereich=standard,
|
|
||||||
abschnitttyp=sektion["typ"],
|
|
||||||
inhalt=sektion["inhalt"],
|
|
||||||
)
|
|
||||||
|
|
||||||
# Save Vorgaben
|
|
||||||
for v in vorgaben_data:
|
|
||||||
try:
|
|
||||||
thema = Thema.objects.get(name=v["thema"])
|
|
||||||
except Thema.DoesNotExist:
|
|
||||||
self.stdout.write(self.style.WARNING(f"Thema '{v['thema']}' not found, skipping Vorgabe {v['nummer']}"))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if dry_run:
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"[DRY RUN] Would create Vorgabe {v['nummer']}: '{v['titel']}' (Thema: {v['thema']})"))
|
|
||||||
for sektion in v["kurztext"]:
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"[DRY RUN] Kurztext Abschnitt (Abschnittstyp: {sektion['typ']}): {sektion['inhalt'][:50]}..."))
|
|
||||||
for sektion in v["langtext"]:
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"[DRY RUN] Langtext Abschnitt (Abschnittstyp: {sektion['typ']}): {sektion['inhalt'][:50]}..."))
|
|
||||||
else:
|
|
||||||
vorgabe = Vorgabe.objects.create(
|
|
||||||
nummer=v["nummer"],
|
|
||||||
dokument=standard,
|
|
||||||
thema=thema,
|
|
||||||
titel=v["titel"],
|
|
||||||
gueltigkeit_von=timezone.now().date(),
|
|
||||||
)
|
|
||||||
for sektion in v["kurztext"]:
|
|
||||||
VorgabeKurztext.objects.create(abschnitt=vorgabe, abschnitttyp=sektion["typ"], inhalt=sektion["inhalt"])
|
|
||||||
for sektion in v["langtext"]:
|
|
||||||
VorgabeLangtext.objects.create(abschnitt=vorgabe, abschnitttyp=sektion["typ"], inhalt=sektion["inhalt"])
|
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS(
|
|
||||||
f"{'Dry run complete' if dry_run else f'Imported standard {standard} with {len(vorgaben_data)} Vorgaben'}"
|
|
||||||
))
|
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-10-02 12:13
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('standards', '0005_vorgabe_relevanz'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name='Standard',
|
||||||
|
new_name='Dokument',
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='dokument',
|
||||||
|
options={'verbose_name': 'Dokument', 'verbose_name_plural': 'Dokumente'},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-10-06 11:29
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('standards', '0006_rename_standard_dokument_alter_dokument_options'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='changelog',
|
||||||
|
options={'verbose_name': 'Changelog-Eintrag', 'verbose_name_plural': 'Changelog'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='checklistenfrage',
|
||||||
|
options={'verbose_name': 'Frage für Checkliste', 'verbose_name_plural': 'Fragen für Checkliste'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='dokumententyp',
|
||||||
|
options={'verbose_name': 'Dokumententyp', 'verbose_name_plural': 'Dokumententypen'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='vorgabelangtext',
|
||||||
|
options={'verbose_name': 'Langtext-Abschnitt', 'verbose_name_plural': 'Langtext'},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -13,6 +13,10 @@ class Dokumententyp(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name="Dokumententyp"
|
||||||
|
verbose_name_plural="Dokumententypen"
|
||||||
|
|
||||||
|
|
||||||
class Person(models.Model):
|
class Person(models.Model):
|
||||||
name = models.CharField(max_length=100, primary_key=True)
|
name = models.CharField(max_length=100, primary_key=True)
|
||||||
@@ -33,7 +37,7 @@ class Thema(models.Model):
|
|||||||
verbose_name_plural="Themen"
|
verbose_name_plural="Themen"
|
||||||
|
|
||||||
|
|
||||||
class Standard(models.Model):
|
class Dokument(models.Model):
|
||||||
nummer = models.CharField(max_length=50, primary_key=True)
|
nummer = models.CharField(max_length=50, primary_key=True)
|
||||||
dokumententyp = models.ForeignKey(Dokumententyp, on_delete=models.PROTECT)
|
dokumententyp = models.ForeignKey(Dokumententyp, on_delete=models.PROTECT)
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
@@ -48,12 +52,12 @@ class Standard(models.Model):
|
|||||||
return f"{self.nummer} – {self.name}"
|
return f"{self.nummer} – {self.name}"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Standards"
|
verbose_name_plural="Dokumente"
|
||||||
verbose_name="Standard"
|
verbose_name="Dokument"
|
||||||
|
|
||||||
class Vorgabe(models.Model):
|
class Vorgabe(models.Model):
|
||||||
nummer = models.IntegerField()
|
nummer = models.IntegerField()
|
||||||
dokument = models.ForeignKey(Standard, 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)
|
||||||
titel = models.CharField(max_length=255)
|
titel = models.CharField(max_length=255)
|
||||||
referenzen = models.ManyToManyField(Referenz, blank=True)
|
referenzen = models.ManyToManyField(Referenz, blank=True)
|
||||||
@@ -77,17 +81,17 @@ class Vorgabe(models.Model):
|
|||||||
|
|
||||||
return "expired" if not verbose else "Ist seit dem "+self.gueltigkeit_bis.strftime('%d.%m.%Y')+" nicht mehr in Kraft."
|
return "expired" if not verbose else "Ist seit dem "+self.gueltigkeit_bis.strftime('%d.%m.%Y')+" nicht mehr in Kraft."
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.Vorgabennummer()}: {self.titel}"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Vorgaben"
|
verbose_name_plural="Vorgaben"
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.Vorgabennummer()}: {self.titel}"
|
|
||||||
|
|
||||||
class VorgabeLangtext(Textabschnitt):
|
class VorgabeLangtext(Textabschnitt):
|
||||||
abschnitt=models.ForeignKey(Vorgabe,on_delete=models.CASCADE)
|
abschnitt=models.ForeignKey(Vorgabe,on_delete=models.CASCADE)
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Langtext-Abschnitte"
|
verbose_name_plural="Langtext"
|
||||||
verbose_name="Langtext-Abschnitt"
|
verbose_name="Langtext-Abschnitt"
|
||||||
|
|
||||||
class VorgabeKurztext(Textabschnitt):
|
class VorgabeKurztext(Textabschnitt):
|
||||||
@@ -97,13 +101,13 @@ class VorgabeKurztext(Textabschnitt):
|
|||||||
verbose_name="Kurztext-Abschnitt"
|
verbose_name="Kurztext-Abschnitt"
|
||||||
|
|
||||||
class Geltungsbereich(Textabschnitt):
|
class Geltungsbereich(Textabschnitt):
|
||||||
geltungsbereich=models.ForeignKey(Standard,on_delete=models.CASCADE)
|
geltungsbereich=models.ForeignKey(Dokument,on_delete=models.CASCADE)
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Geltungsbereich"
|
verbose_name_plural="Geltungsbereich"
|
||||||
verbose_name="Geltungsbereichs-Abschnitt"
|
verbose_name="Geltungsbereichs-Abschnitt"
|
||||||
|
|
||||||
class Einleitung(Textabschnitt):
|
class Einleitung(Textabschnitt):
|
||||||
einleitung=models.ForeignKey(Standard,on_delete=models.CASCADE)
|
einleitung=models.ForeignKey(Dokument,on_delete=models.CASCADE)
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Einleitung"
|
verbose_name_plural="Einleitung"
|
||||||
verbose_name="Einleitungs-Abschnitt"
|
verbose_name="Einleitungs-Abschnitt"
|
||||||
@@ -117,12 +121,17 @@ class Checklistenfrage(models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Fragen für Checkliste"
|
verbose_name_plural="Fragen für Checkliste"
|
||||||
|
verbose_name="Frage für Checkliste"
|
||||||
|
|
||||||
class Changelog(models.Model):
|
class Changelog(models.Model):
|
||||||
dokument = models.ForeignKey(Standard, on_delete=models.CASCADE, related_name='changelog')
|
dokument = models.ForeignKey(Dokument, on_delete=models.CASCADE, related_name='changelog')
|
||||||
autoren = models.ManyToManyField(Person)
|
autoren = models.ManyToManyField(Person)
|
||||||
datum = models.DateField()
|
datum = models.DateField()
|
||||||
aenderung = models.TextField()
|
aenderung = models.TextField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.datum} – {self.dokument.nummer}"
|
return f"{self.datum} – {self.dokument.nummer}"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name_plural="Changelog"
|
||||||
|
verbose_name="Changelog-Eintrag"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from .models import Standard
|
from .models import Dokument
|
||||||
from abschnitte.utils import render_textabschnitte
|
from abschnitte.utils import render_textabschnitte
|
||||||
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
@@ -9,14 +9,14 @@ calendar=parsedatetime.Calendar()
|
|||||||
|
|
||||||
|
|
||||||
def standard_list(request):
|
def standard_list(request):
|
||||||
standards = Standard.objects.all()
|
standards = Dokument.objects.all()
|
||||||
return render(request, 'standards/standard_list.html',
|
return render(request, 'standards/standard_list.html',
|
||||||
{'standards': standards}
|
{'standards': standards}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def standard_detail(request, nummer,check_date=""):
|
def standard_detail(request, nummer,check_date=""):
|
||||||
standard = get_object_or_404(Standard, nummer=nummer)
|
standard = get_object_or_404(Dokument, nummer=nummer)
|
||||||
|
|
||||||
if check_date:
|
if check_date:
|
||||||
check_date = calendar.parseDT(check_date)[0].date()
|
check_date = calendar.parseDT(check_date)[0].date()
|
||||||
@@ -48,7 +48,7 @@ def standard_detail(request, nummer,check_date=""):
|
|||||||
|
|
||||||
|
|
||||||
def standard_checkliste(request, nummer):
|
def standard_checkliste(request, nummer):
|
||||||
standard = get_object_or_404(Standard, nummer=nummer)
|
standard = get_object_or_404(Dokument, nummer=nummer)
|
||||||
vorgaben = list(standard.vorgaben.all())
|
vorgaben = list(standard.vorgaben.all())
|
||||||
return render(request, 'standards/standard_checkliste.html', {
|
return render(request, 'standards/standard_checkliste.html', {
|
||||||
'standard': standard,
|
'standard': standard,
|
||||||
|
|||||||
Reference in New Issue
Block a user