>Renamed app "standards" to "dokumente" - finally working as expected.
This commit is contained in:
0
dokumente/__init__.py
Normal file
0
dokumente/__init__.py
Normal file
127
dokumente/admin.py
Normal file
127
dokumente/admin.py
Normal file
@@ -0,0 +1,127 @@
|
||||
from django.contrib import admin
|
||||
#from nested_inline.admin import NestedStackedInline, NestedModelAdmin
|
||||
from nested_admin import NestedStackedInline, NestedModelAdmin, NestedTabularInline
|
||||
from django import forms
|
||||
from mptt.forms import TreeNodeMultipleChoiceField
|
||||
from mptt.admin import DraggableMPTTAdmin
|
||||
|
||||
# Register your models here.
|
||||
from .models import *
|
||||
from stichworte.models import Stichwort, Stichworterklaerung
|
||||
from referenzen.models import Referenz
|
||||
|
||||
|
||||
|
||||
#class ChecklistenForm(forms.ModelForm):
|
||||
# class Meta:
|
||||
# model=Checklistenfrage
|
||||
# fields="__all__"
|
||||
# widgets = {
|
||||
# 'frage': forms.Textarea(attrs={'rows': 1, 'cols': 100}),
|
||||
# }
|
||||
|
||||
class ChecklistenfragenInline(NestedTabularInline):
|
||||
model=Checklistenfrage
|
||||
extra=0
|
||||
fk_name="vorgabe"
|
||||
# form=ChecklistenForm
|
||||
classes = ['collapse']
|
||||
|
||||
|
||||
class VorgabeKurztextInline(NestedTabularInline):
|
||||
model=VorgabeKurztext
|
||||
extra=0
|
||||
sortable_field_name = "order"
|
||||
show_change_link=True
|
||||
classes = ['collapse']
|
||||
#inline=inhalt
|
||||
|
||||
class VorgabeLangtextInline(NestedStackedInline):
|
||||
model=VorgabeLangtext
|
||||
extra=0
|
||||
sortable_field_name = "order"
|
||||
show_change_link=True
|
||||
classes = ['collapse']
|
||||
#inline=inhalt
|
||||
|
||||
class GeltungsbereichInline(NestedTabularInline):
|
||||
model=Geltungsbereich
|
||||
extra=0
|
||||
sortable_field_name = "order"
|
||||
show_change_link=True
|
||||
classes = ['collapse']
|
||||
classes = ['collapse']
|
||||
#inline=inhalt
|
||||
|
||||
class EinleitungInline(NestedTabularInline):
|
||||
model = Einleitung
|
||||
extra = 0
|
||||
sortable_field_name = "order"
|
||||
show_change_link = True
|
||||
classes = ['collapse']
|
||||
|
||||
class VorgabeForm(forms.ModelForm):
|
||||
# referenzen = TreeNodeMultipleChoiceField(queryset=Referenz.objects.all(), required=False)
|
||||
class Meta:
|
||||
model = Vorgabe
|
||||
fields = '__all__'
|
||||
|
||||
class VorgabeInline(NestedTabularInline): # or StackedInline for more vertical layout
|
||||
model = Vorgabe
|
||||
form = VorgabeForm
|
||||
extra = 0
|
||||
#show_change_link = True
|
||||
inlines = [VorgabeKurztextInline,VorgabeLangtextInline,ChecklistenfragenInline]
|
||||
autocomplete_fields = ['stichworte','referenzen','relevanz']
|
||||
#search_fields=['nummer','name']ModelAdmin.
|
||||
list_filter=['stichworte']
|
||||
#classes=["collapse"]
|
||||
|
||||
class StichworterklaerungInline(NestedStackedInline):
|
||||
model=Stichworterklaerung
|
||||
extra=0
|
||||
sortable_field_name = "order"
|
||||
ordering=("order",)
|
||||
show_change_link = True
|
||||
|
||||
@admin.register(Stichwort)
|
||||
class StichwortAdmin(NestedModelAdmin):
|
||||
search_fields = ('stichwort',)
|
||||
ordering=('stichwort',)
|
||||
inlines=[StichworterklaerungInline]
|
||||
|
||||
@admin.register(Person)
|
||||
class PersonAdmin(admin.ModelAdmin):
|
||||
class Media:
|
||||
js = ['admin/js/jquery.init.js', 'custom/js/inline_toggle.js']
|
||||
css = {'all': ['custom/css/admin_extras.css']}
|
||||
list_display=['name']
|
||||
|
||||
|
||||
|
||||
@admin.register(Dokument)
|
||||
class DokumentAdmin(NestedModelAdmin):
|
||||
actions_on_top=True
|
||||
inlines = [EinleitungInline,GeltungsbereichInline,VorgabeInline]
|
||||
#filter_horizontal=['autoren','pruefende']
|
||||
list_display=['nummer','name','dokumententyp']
|
||||
search_fields=['nummer','name']
|
||||
class Media:
|
||||
# js = ('admin/js/vorgabe_collapse.js',)
|
||||
css = {
|
||||
'all': ('admin/css/vorgabe_border.css',
|
||||
# 'admin/css/vorgabe_collapse.css',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#admin.site.register(Stichwort)
|
||||
|
||||
admin.site.register(Checklistenfrage)
|
||||
admin.site.register(Dokumententyp)
|
||||
#admin.site.register(Person)
|
||||
admin.site.register(Thema)
|
||||
#admin.site.register(Referenz, DraggableM§PTTAdmin)
|
||||
admin.site.register(Vorgabe)
|
||||
|
||||
#admin.site.register(Changelog)
|
||||
6
dokumente/apps.py
Normal file
6
dokumente/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class standardsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'dokumente'
|
||||
352
dokumente/management/commands/import-document.py
Executable file
352
dokumente/management/commands/import-document.py
Executable file
@@ -0,0 +1,352 @@
|
||||
# Document/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 dokumente.models import (
|
||||
Dokument,
|
||||
Dokumententyp,
|
||||
Thema,
|
||||
Vorgabe,
|
||||
VorgabeKurztext,
|
||||
VorgabeLangtext,
|
||||
Geltungsbereich,
|
||||
Einleitung, # <-- make sure this model exists as a Textabschnitt subclass
|
||||
Checklistenfrage,
|
||||
)
|
||||
from abschnitte.models import AbschnittTyp
|
||||
from stichworte.models import Stichwort
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = (
|
||||
"Import a policy document from a structured text file.\n"
|
||||
"Supports Einleitung, Geltungsbereich, Vorgaben (Kurztext/Langtext with AbschnittTyp), "
|
||||
"Stichworte (comma-separated), Checklistenfragen, dry-run, verbose, and purge."
|
||||
)
|
||||
|
||||
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="Document number (e.g., STD-001)")
|
||||
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("--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 DB")
|
||||
parser.add_argument("--verbose", action="store_true", help="Verbose output for dry run")
|
||||
parser.add_argument("--purge", action="store_true", help="Delete existing Einleitung/Geltungsbereich/Vorgaben first")
|
||||
|
||||
# normalize header: "liste-ungeordnet" -> "liste ungeordnet"
|
||||
@staticmethod
|
||||
def _norm_header(h: str) -> str:
|
||||
return h.lower().replace("-", " ").strip()
|
||||
|
||||
def handle(self, *args, **options):
|
||||
dry_run = options["dry_run"]
|
||||
verbose = options["verbose"]
|
||||
purge = options["purge"]
|
||||
|
||||
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."))
|
||||
|
||||
# get or create Document (we want a real instance even in purge to count existing rows)
|
||||
standard, created = Dokument.objects.get_or_create(
|
||||
nummer=nummer,
|
||||
defaults={
|
||||
"dokumententyp": dokumententyp,
|
||||
"name": name,
|
||||
"gueltigkeit_von": options["gueltigkeit_von"],
|
||||
"gueltigkeit_bis": options["gueltigkeit_bis"],
|
||||
},
|
||||
)
|
||||
if created:
|
||||
self.stdout.write(self.style.SUCCESS(f"Created Document {nummer} – {name}"))
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING(f"Document {nummer} already exists; content may be updated."))
|
||||
|
||||
# purge (Einleitung + Geltungsbereich + Vorgaben cascade)
|
||||
if purge:
|
||||
qs_vorgaben = standard.vorgaben.all()
|
||||
qs_check = Checklistenfrage.objects.filter(vorgabe__in=qs_vorgaben)
|
||||
qs_kurz = VorgabeKurztext.objects.filter(abschnitt__in=qs_vorgaben)
|
||||
qs_lang = VorgabeLangtext.objects.filter(abschnitt__in=qs_vorgaben)
|
||||
qs_gb = Geltungsbereich.objects.filter(geltungsbereich=standard)
|
||||
qs_einl = Einleitung.objects.filter(einleitung=standard)
|
||||
|
||||
c_vorgaben = qs_vorgaben.count()
|
||||
c_check = qs_check.count()
|
||||
c_kurz = qs_kurz.count()
|
||||
c_lang = qs_lang.count()
|
||||
c_gb = qs_gb.count()
|
||||
c_einl = qs_einl.count()
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.WARNING(
|
||||
f"[DRY RUN] Would purge: {c_einl} Einleitung-Abschnitte, "
|
||||
f"{c_gb} Geltungsbereich-Abschnitte, {c_vorgaben} Vorgaben "
|
||||
f"({c_kurz} Kurztext, {c_lang} Langtext, {c_check} Checklistenfragen)."
|
||||
))
|
||||
else:
|
||||
deleted_einl = qs_einl.delete()[0]
|
||||
deleted_gb = qs_gb.delete()[0]
|
||||
deleted_vorgaben = qs_vorgaben.delete()[0]
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"Purged {deleted_einl} Einleitung, {deleted_gb} Geltungsbereich, "
|
||||
f"{deleted_vorgaben} Vorgaben (incl. Kurz/Lang/Checklistenfragen)."
|
||||
))
|
||||
|
||||
# read and split 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()]
|
||||
|
||||
# state
|
||||
abschnittstyp_names = {"text", "liste geordnet", "liste ungeordnet"}
|
||||
current_context = "geltungsbereich" # default before first Vorgabe
|
||||
|
||||
einleitung_sections = [] # list of {inhalt, typ: AbschnittTyp}
|
||||
geltungsbereich_sections = [] # list of {inhalt, typ: AbschnittTyp}
|
||||
|
||||
current_vorgabe = None
|
||||
vorgaben_data = [] # each: {thema, titel, nummer, kurztext:[{inhalt,typ}], langtext:[{inhalt,typ}], stichworte:set(), checkliste:[str]}
|
||||
|
||||
for block in blocks:
|
||||
lines = block.splitlines()
|
||||
header = lines[0].strip()
|
||||
text = "\n".join(lines[1:]).strip()
|
||||
header_norm = self._norm_header(header)
|
||||
|
||||
# resolve AbschnittTyp if this is a section block
|
||||
abschnitt_typ = None
|
||||
if header_norm in abschnittstyp_names:
|
||||
try:
|
||||
abschnitt_typ = AbschnittTyp.objects.get(abschnitttyp=header_norm)
|
||||
except AbschnittTyp.DoesNotExist:
|
||||
self.stdout.write(self.style.WARNING(
|
||||
f"AbschnittTyp '{header_norm}' not found; defaulting to 'text'."
|
||||
))
|
||||
abschnitt_typ = AbschnittTyp.objects.get(abschnitttyp="text")
|
||||
|
||||
# contexts
|
||||
if header_norm == "einleitung":
|
||||
current_context = "einleitung"
|
||||
continue
|
||||
|
||||
if header_norm == "geltungsbereich":
|
||||
current_context = "geltungsbereich"
|
||||
continue
|
||||
|
||||
if header_norm.startswith("vorgabe"):
|
||||
# save previous
|
||||
if current_vorgabe:
|
||||
vorgaben_data.append(current_vorgabe)
|
||||
|
||||
parts = header.split(" ", 1)
|
||||
thema_name = parts[1].strip() if len(parts) > 1 else ""
|
||||
current_vorgabe = {
|
||||
"thema": thema_name,
|
||||
"titel": "",
|
||||
"nummer": None,
|
||||
"kurztext": [],
|
||||
"langtext": [],
|
||||
"stichworte": set(),
|
||||
"checkliste": [],
|
||||
}
|
||||
current_context = "vorgabe_none"
|
||||
continue
|
||||
|
||||
if header_norm.startswith("titel") and current_vorgabe:
|
||||
# inline title or next text block
|
||||
inline = header[len("Titel"):].strip() if header.startswith("Titel") else ""
|
||||
title_value = inline or text
|
||||
current_vorgabe["titel"] = title_value
|
||||
continue
|
||||
|
||||
if header_norm.startswith("nummer") and current_vorgabe:
|
||||
m = re.search(r"\d+", header)
|
||||
if m:
|
||||
current_vorgabe["nummer"] = int(m.group())
|
||||
current_context = "vorgabe_none"
|
||||
continue
|
||||
|
||||
if header_norm == "kurztext":
|
||||
current_context = "vorgabe_kurztext"
|
||||
continue
|
||||
|
||||
if header_norm == "langtext":
|
||||
current_context = "vorgabe_langtext"
|
||||
continue
|
||||
|
||||
if header_norm.startswith("stichworte") and current_vorgabe:
|
||||
inline = header[len("Stichworte"):].strip() if header.startswith("Stichworte") else ""
|
||||
kw_str = inline or text
|
||||
if kw_str:
|
||||
for k in kw_str.split(","):
|
||||
k = k.strip()
|
||||
if k:
|
||||
current_vorgabe["stichworte"].add(k)
|
||||
else:
|
||||
current_context = "vorgabe_stichworte"
|
||||
continue
|
||||
|
||||
if header_norm == "checkliste" and current_vorgabe:
|
||||
if text:
|
||||
current_vorgabe["checkliste"].extend([q.strip() for q in text.splitlines() if q.strip()])
|
||||
else:
|
||||
current_context = "vorgabe_checkliste"
|
||||
continue
|
||||
|
||||
# Abschnitt content blocks
|
||||
if header_norm in abschnittstyp_names:
|
||||
section = {"inhalt": text, "typ": abschnitt_typ}
|
||||
|
||||
if current_context == "einleitung":
|
||||
einleitung_sections.append(section)
|
||||
if dry_run and verbose:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"[DRY RUN] Einleitung Abschnitt ({section['typ']}): {text[:50]}..."
|
||||
))
|
||||
|
||||
elif current_context == "geltungsbereich":
|
||||
geltungsbereich_sections.append(section)
|
||||
if dry_run and verbose:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"[DRY RUN] Geltungsbereich Abschnitt ({section['typ']}): {text[:50]}..."
|
||||
))
|
||||
|
||||
elif current_context == "vorgabe_kurztext" and current_vorgabe:
|
||||
current_vorgabe["kurztext"].append(section)
|
||||
if dry_run and verbose:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"[DRY RUN] Vorgabe {current_vorgabe.get('nummer')} Kurztext ({section['typ']}): {text[:50]}..."
|
||||
))
|
||||
|
||||
elif current_context == "vorgabe_langtext" and current_vorgabe:
|
||||
current_vorgabe["langtext"].append(section)
|
||||
if dry_run and verbose:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"[DRY RUN] Vorgabe {current_vorgabe.get('nummer')} Langtext ({section['typ']}): {text[:50]}..."
|
||||
))
|
||||
|
||||
elif current_context == "vorgabe_stichworte" and current_vorgabe:
|
||||
for k in text.split(","):
|
||||
k = k.strip()
|
||||
if k:
|
||||
current_vorgabe["stichworte"].add(k)
|
||||
current_context = "vorgabe_none"
|
||||
|
||||
elif current_context == "vorgabe_checkliste" and current_vorgabe:
|
||||
current_vorgabe["checkliste"].extend([q.strip() for q in text.splitlines() if q.strip()])
|
||||
current_context = "vorgabe_none"
|
||||
|
||||
# append last vorgabe
|
||||
if current_vorgabe:
|
||||
vorgaben_data.append(current_vorgabe)
|
||||
|
||||
# === SAVE: Einleitung ===
|
||||
for sektion in einleitung_sections:
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"[DRY RUN] Would create Einleitung Abschnitt ({sektion['typ']}): {sektion['inhalt'][:50]}..."
|
||||
))
|
||||
else:
|
||||
Einleitung.objects.create(
|
||||
einleitung=standard,
|
||||
abschnitttyp=sektion["typ"],
|
||||
inhalt=sektion["inhalt"],
|
||||
)
|
||||
|
||||
# === SAVE: Geltungsbereich ===
|
||||
for sektion in geltungsbereich_sections:
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"[DRY RUN] Would create Geltungsbereich Abschnitt ({sektion['typ']}): {sektion['inhalt'][:50]}..."
|
||||
))
|
||||
else:
|
||||
Geltungsbereich.objects.create(
|
||||
geltungsbereich=standard,
|
||||
abschnitttyp=sektion["typ"],
|
||||
inhalt=sektion["inhalt"],
|
||||
)
|
||||
|
||||
# === SAVE: Vorgaben and children ===
|
||||
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']})"
|
||||
))
|
||||
if v["stichworte"]:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"[DRY RUN] Stichworte: {', '.join(sorted(v['stichworte']))}"
|
||||
))
|
||||
if v["checkliste"]:
|
||||
for frage in v["checkliste"]:
|
||||
self.stdout.write(self.style.SUCCESS(f"[DRY RUN] Checkliste: {frage}"))
|
||||
for s in v["kurztext"]:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"[DRY RUN] Kurztext ({s['typ']}): {s['inhalt'][:50]}..."
|
||||
))
|
||||
for s in v["langtext"]:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"[DRY RUN] Langtext ({s['typ']}): {s['inhalt'][:50]}..."
|
||||
))
|
||||
else:
|
||||
vorgabe = Vorgabe.objects.create(
|
||||
nummer=v["nummer"],
|
||||
dokument=standard,
|
||||
thema=thema,
|
||||
titel=v["titel"],
|
||||
gueltigkeit_von=timezone.now().date(),
|
||||
)
|
||||
|
||||
# Stichworte
|
||||
for kw in sorted(v["stichworte"]):
|
||||
stw, _ = Stichwort.objects.get_or_create(stichwort=kw)
|
||||
vorgabe.stichworte.add(stw)
|
||||
|
||||
# Checklistenfragen
|
||||
for frage in v["checkliste"]:
|
||||
Checklistenfrage.objects.create(vorgabe=vorgabe, frage=frage)
|
||||
|
||||
# Kurztext sections
|
||||
for s in v["kurztext"]:
|
||||
VorgabeKurztext.objects.create(
|
||||
abschnitt=vorgabe,
|
||||
abschnitttyp=s["typ"],
|
||||
inhalt=s["inhalt"],
|
||||
)
|
||||
|
||||
# Langtext sections
|
||||
for s in v["langtext"]:
|
||||
VorgabeLangtext.objects.create(
|
||||
abschnitt=vorgabe,
|
||||
abschnitttyp=s["typ"],
|
||||
inhalt=s["inhalt"],
|
||||
)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
"Dry run complete" if dry_run else f"Imported document {nummer} – {name} with {len(vorgaben_data)} Vorgaben"
|
||||
))
|
||||
|
||||
169
dokumente/migrations/0001_initial.py
Normal file
169
dokumente/migrations/0001_initial.py
Normal file
@@ -0,0 +1,169 @@
|
||||
# Generated by Django 5.2.5 on 2025-08-26 09:34
|
||||
|
||||
import django.db.models.deletion
|
||||
import mptt.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('abschnitte', '0001_initial'),
|
||||
('stichworte', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Dokumententyp',
|
||||
fields=[
|
||||
('name', models.CharField(max_length=100, primary_key=True, serialize=False)),
|
||||
('verantwortliche_ve', models.CharField(max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Person',
|
||||
fields=[
|
||||
('name', models.CharField(max_length=100, primary_key=True, serialize=False)),
|
||||
('funktion', models.CharField(max_length=255)),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Personen',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Thema',
|
||||
fields=[
|
||||
('name', models.CharField(max_length=100, primary_key=True, serialize=False)),
|
||||
('erklaerung', models.TextField(blank=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Themen',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Referenz',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name_nummer', models.CharField(max_length=100)),
|
||||
('name_text', models.CharField(blank=True, max_length=255)),
|
||||
('url', models.URLField(blank=True)),
|
||||
('lft', models.PositiveIntegerField(editable=False)),
|
||||
('rght', models.PositiveIntegerField(editable=False)),
|
||||
('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
|
||||
('level', models.PositiveIntegerField(editable=False)),
|
||||
('oberreferenz', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='unterreferenzen', to='dokumente.referenz')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Referenzen',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Referenzerklaerung',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('inhalt', models.TextField(blank=True, null=True)),
|
||||
('abschnitttyp', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='abschnitte.abschnitttyp')),
|
||||
('erklaerung', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dokumente.referenz')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Erklärung',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Standard',
|
||||
fields=[
|
||||
('nummer', models.CharField(max_length=50, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('gueltigkeit_von', models.DateField(blank=True, null=True)),
|
||||
('gueltigkeit_bis', models.DateField(blank=True, null=True)),
|
||||
('signatur_cso', models.CharField(blank=True, max_length=255)),
|
||||
('anhaenge', models.TextField(blank=True)),
|
||||
('autoren', models.ManyToManyField(related_name='verfasste_dokumente', to='dokumente.person')),
|
||||
('dokumententyp', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dokumente.dokumententyp')),
|
||||
('pruefende', models.ManyToManyField(related_name='gepruefte_dokumente', to='dokumente.person')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Standard',
|
||||
'verbose_name_plural': 'Standards',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Geltungsbereich',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('inhalt', models.TextField(blank=True, null=True)),
|
||||
('abschnitttyp', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='abschnitte.abschnitttyp')),
|
||||
('geltungsbereich', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dokumente.standard')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Geltungsbereichs-Abschnitt',
|
||||
'verbose_name_plural': 'Geltungsbereich',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Changelog',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('datum', models.DateField()),
|
||||
('aenderung', models.TextField()),
|
||||
('autoren', models.ManyToManyField(to='dokumente.person')),
|
||||
('dokument', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='changelog', to='dokumente.standard')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Vorgabe',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('nummer', models.IntegerField()),
|
||||
('titel', models.CharField(max_length=255)),
|
||||
('gueltigkeit_von', models.DateField()),
|
||||
('gueltigkeit_bis', models.DateField(blank=True, null=True)),
|
||||
('dokument', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vorgaben', to='dokumente.standard')),
|
||||
('referenzen', models.ManyToManyField(blank=True, to='dokumente.referenz')),
|
||||
('stichworte', models.ManyToManyField(blank=True, to='stichworte.stichwort')),
|
||||
('thema', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dokumente.thema')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Vorgaben',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Checklistenfrage',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('frage', models.CharField(max_length=255)),
|
||||
('vorgabe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='checklistenfragen', to='dokumente.vorgabe')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Fragen für Checkliste',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VorgabeKurztext',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('inhalt', models.TextField(blank=True, null=True)),
|
||||
('abschnitt', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dokumente.vorgabe')),
|
||||
('abschnitttyp', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='abschnitte.abschnitttyp')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Kurztext-Abschnitt',
|
||||
'verbose_name_plural': 'Kurztext',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VorgabeLangtext',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('inhalt', models.TextField(blank=True, null=True)),
|
||||
('abschnitt', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dokumente.vorgabe')),
|
||||
('abschnitttyp', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='abschnitte.abschnitttyp')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Langtext-Abschnitt',
|
||||
'verbose_name_plural': 'Langtext-Abschnitte',
|
||||
},
|
||||
),
|
||||
]
|
||||
28
dokumente/migrations/0002_einleitung.py
Normal file
28
dokumente/migrations/0002_einleitung.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.2.5 on 2025-08-26 09:35
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('abschnitte', '0001_initial'),
|
||||
('dokumente', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Einleitung',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('inhalt', models.TextField(blank=True, null=True)),
|
||||
('abschnitttyp', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='abschnitte.abschnitttyp')),
|
||||
('einleitung', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dokumente.standard')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Einleitungs-Abschnitt',
|
||||
'verbose_name_plural': 'Einleitung',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
# Generated by Django 5.2.5 on 2025-08-27 09:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dokumente', '0002_einleitung'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='einleitung',
|
||||
name='order',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='geltungsbereich',
|
||||
name='order',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='referenzerklaerung',
|
||||
name='order',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='vorgabekurztext',
|
||||
name='order',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='vorgabelangtext',
|
||||
name='order',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 5.2.5 on 2025-09-04 13:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('referenzen', '0001_initial'),
|
||||
('dokumente', '0003_einleitung_order_geltungsbereich_order_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='referenzerklaerung',
|
||||
name='erklaerung',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='referenzerklaerung',
|
||||
name='abschnitttyp',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vorgabe',
|
||||
name='referenzen',
|
||||
field=models.ManyToManyField(blank=True, to='referenzen.referenz'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Referenz',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Referenzerklaerung',
|
||||
),
|
||||
]
|
||||
19
dokumente/migrations/0005_vorgabe_relevanz.py
Normal file
19
dokumente/migrations/0005_vorgabe_relevanz.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.2.5 on 2025-09-05 08:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('rollen', '0001_initial'),
|
||||
('dokumente', '0004_remove_referenzerklaerung_erklaerung_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='vorgabe',
|
||||
name='relevanz',
|
||||
field=models.ManyToManyField(blank=True, to='rollen.rolle'),
|
||||
),
|
||||
]
|
||||
@@ -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 = [
|
||||
('dokumente', '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 = [
|
||||
('dokumente', '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'},
|
||||
),
|
||||
]
|
||||
0
dokumente/migrations/__init__.py
Normal file
0
dokumente/migrations/__init__.py
Normal file
137
dokumente/models.py
Normal file
137
dokumente/models.py
Normal file
@@ -0,0 +1,137 @@
|
||||
from django.db import models
|
||||
from mptt.models import MPTTModel, TreeForeignKey
|
||||
from abschnitte.models import Textabschnitt
|
||||
from stichworte.models import Stichwort
|
||||
from referenzen.models import Referenz
|
||||
from rollen.models import Rolle
|
||||
import datetime
|
||||
|
||||
class Dokumententyp(models.Model):
|
||||
name = models.CharField(max_length=100, primary_key=True)
|
||||
verantwortliche_ve = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name="Dokumententyp"
|
||||
verbose_name_plural="Dokumententypen"
|
||||
|
||||
|
||||
class Person(models.Model):
|
||||
name = models.CharField(max_length=100, primary_key=True)
|
||||
funktion = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
class Meta:
|
||||
verbose_name_plural="Personen"
|
||||
|
||||
class Thema(models.Model):
|
||||
name = models.CharField(max_length=100, primary_key=True)
|
||||
erklaerung = models.TextField(blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
class Meta:
|
||||
verbose_name_plural="Themen"
|
||||
|
||||
|
||||
class Dokument(models.Model):
|
||||
nummer = models.CharField(max_length=50, primary_key=True)
|
||||
dokumententyp = models.ForeignKey(Dokumententyp, on_delete=models.PROTECT)
|
||||
name = models.CharField(max_length=255)
|
||||
autoren = models.ManyToManyField(Person, related_name='verfasste_dokumente')
|
||||
pruefende = models.ManyToManyField(Person, related_name='gepruefte_dokumente')
|
||||
gueltigkeit_von = models.DateField(null=True, blank=True)
|
||||
gueltigkeit_bis = models.DateField(null=True, blank=True)
|
||||
signatur_cso = models.CharField(max_length=255, blank=True)
|
||||
anhaenge = models.TextField(blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.nummer} – {self.name}"
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural="Dokumente"
|
||||
verbose_name="Dokument"
|
||||
|
||||
class Vorgabe(models.Model):
|
||||
nummer = models.IntegerField()
|
||||
dokument = models.ForeignKey(Dokument, on_delete=models.CASCADE, related_name='vorgaben')
|
||||
thema = models.ForeignKey(Thema, on_delete=models.PROTECT)
|
||||
titel = models.CharField(max_length=255)
|
||||
referenzen = models.ManyToManyField(Referenz, blank=True)
|
||||
gueltigkeit_von = models.DateField()
|
||||
gueltigkeit_bis = models.DateField(blank=True,null=True)
|
||||
stichworte = models.ManyToManyField(Stichwort, blank=True)
|
||||
relevanz = models.ManyToManyField(Rolle,blank=True)
|
||||
|
||||
def Vorgabennummer(self):
|
||||
return str(self.dokument.nummer)+"."+self.thema.name[0]+"."+str(self.nummer)
|
||||
|
||||
def get_status(self, check_date: datetime.date = datetime.date.today(), verbose: bool = False) -> str:
|
||||
if self.gueltigkeit_von > check_date:
|
||||
return "future" if not verbose else "Ist erst ab dem "+self.gueltigkeit_von.strftime('%d.%m.%Y')+" in Kraft."
|
||||
|
||||
if not self.gueltigkeit_bis:
|
||||
return "active"
|
||||
|
||||
if self.gueltigkeit_bis > check_date:
|
||||
return "active"
|
||||
|
||||
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:
|
||||
verbose_name_plural="Vorgaben"
|
||||
|
||||
|
||||
class VorgabeLangtext(Textabschnitt):
|
||||
abschnitt=models.ForeignKey(Vorgabe,on_delete=models.CASCADE)
|
||||
class Meta:
|
||||
verbose_name_plural="Langtext"
|
||||
verbose_name="Langtext-Abschnitt"
|
||||
|
||||
class VorgabeKurztext(Textabschnitt):
|
||||
abschnitt=models.ForeignKey(Vorgabe,on_delete=models.CASCADE)
|
||||
class Meta:
|
||||
verbose_name_plural="Kurztext"
|
||||
verbose_name="Kurztext-Abschnitt"
|
||||
|
||||
class Geltungsbereich(Textabschnitt):
|
||||
geltungsbereich=models.ForeignKey(Dokument,on_delete=models.CASCADE)
|
||||
class Meta:
|
||||
verbose_name_plural="Geltungsbereich"
|
||||
verbose_name="Geltungsbereichs-Abschnitt"
|
||||
|
||||
class Einleitung(Textabschnitt):
|
||||
einleitung=models.ForeignKey(Dokument,on_delete=models.CASCADE)
|
||||
class Meta:
|
||||
verbose_name_plural="Einleitung"
|
||||
verbose_name="Einleitungs-Abschnitt"
|
||||
|
||||
class Checklistenfrage(models.Model):
|
||||
vorgabe=models.ForeignKey(Vorgabe, on_delete=models.CASCADE, related_name="checklistenfragen")
|
||||
frage = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.frage
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural="Fragen für Checkliste"
|
||||
verbose_name="Frage für Checkliste"
|
||||
|
||||
class Changelog(models.Model):
|
||||
dokument = models.ForeignKey(Dokument, on_delete=models.CASCADE, related_name='changelog')
|
||||
autoren = models.ManyToManyField(Person)
|
||||
datum = models.DateField()
|
||||
aenderung = models.TextField()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.datum} – {self.dokument.nummer}"
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural="Changelog"
|
||||
verbose_name="Changelog-Eintrag"
|
||||
28
dokumente/templates/standards/standard_checkliste.html
Normal file
28
dokumente/templates/standards/standard_checkliste.html
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ standard }}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
{% load static %}
|
||||
</head>
|
||||
<body class="container py-4">
|
||||
|
||||
|
||||
|
||||
<h1>{{ standard.nummer }} – {{ standard.name }}</h1>
|
||||
|
||||
|
||||
<h2>Checkliste</h2>
|
||||
<ul class="list-group">
|
||||
{% for vorgabe in vorgaben %}
|
||||
{% if vorgabe.checklistenfragen.all %}
|
||||
{% for frage in vorgabe.checklistenfragen.all %}
|
||||
<li class="list-group-item">{{ vorgabe.Vorgabennummer }}: {{ frage.frage }}</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
109
dokumente/templates/standards/standard_detail.html
Normal file
109
dokumente/templates/standards/standard_detail.html
Normal file
@@ -0,0 +1,109 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ standard }}{% endblock %}
|
||||
{% block content %}
|
||||
<h1>{{ standard.nummer }} – {{ standard.name }}</h1>
|
||||
{% if standard.history == True %}
|
||||
<h2>Version vom {{ standard.check_date }}</h2>
|
||||
{% endif %}
|
||||
<!-- Autoren, Prüfende etc. -->
|
||||
<p><strong>Autoren:</strong> {{ standard.autoren.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>
|
||||
|
||||
<!-- Start Einleitung -->
|
||||
{% if standard.einleitung_html %}
|
||||
<h2>Einleitung</h2>
|
||||
{% for typ, html in standard.einleitung_html %}
|
||||
<div>{{ html|safe }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<!-- End Einleitung -->
|
||||
|
||||
<!-- Start Geltungsbereich -->
|
||||
{% if standard.geltungsbereich_html %}
|
||||
<h2>Geltungsbereich</h2>
|
||||
{% for typ, html in standard.geltungsbereich_html %}
|
||||
<div>{{ html|safe }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<!-- End Geltungsbereich -->
|
||||
|
||||
<h2>Vorgaben</h2>
|
||||
{% for vorgabe in vorgaben %}
|
||||
<!-- Start Vorgabe -->
|
||||
{% if standard.history == True or vorgabe.long_status == "active" %}
|
||||
<a id="{{ vorgabe.Vorgabennummer }}"></a><div class="card mb-4">
|
||||
{% if vorgabe.long_status == "active"%}
|
||||
<div class="card-header d-flex justify-content-between align-items-center bg-secondary text-light">
|
||||
{% elif standard.history == True %}
|
||||
<div class="card-header d-flex justify-content-between align-items-center bg-danger-subtle">
|
||||
{% endif %}
|
||||
<h3 class="h5 m-0">{{ vorgabe.Vorgabennummer }} – {{ vorgabe.titel }}
|
||||
{% if vorgabe.long_status != "active" and standard.history == True %}<span class="text-danger"> ({{ vorgabe.long_status}})</span>{% endif %}
|
||||
</h3>
|
||||
{% if vorgabe.relevanzset %}
|
||||
<span class="badge bg-light text-black"> Relevanz:
|
||||
{{ vorgabe.relevanzset|join:", " }}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
<span class="badge bg-light text-black">{{ vorgabe.thema }}</span>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-0">
|
||||
<!-- Start Kurztext -->
|
||||
{% comment %} KURZTEXT BLOCK {% endcomment %}
|
||||
{% if vorgabe.kurztext_html.0.1 %}
|
||||
<div class="p-3 mb-3 bg-light border-3" style="width: 100%;">
|
||||
{% for typ, html in vorgabe.kurztext_html %}
|
||||
{% if html %}
|
||||
<div class="mb-2">{{ html|safe }}</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Langtext -->
|
||||
<div class="p-3 mb-3">
|
||||
{% comment %} LANGTEXT BLOCK {% endcomment %}
|
||||
{# <h5>Langtext</h5> #}
|
||||
{% for typ, html in vorgabe.langtext_html %}
|
||||
{% if html %}<div class="mb-3">{{ html|safe }}</div>{% endif %}
|
||||
{% endfor %}
|
||||
<!-- Checklistenfragen -->
|
||||
{% comment %} CHECKLISTENFRAGEN BLOCK {% endcomment %}
|
||||
<h5>Checklistenfragen</h5>
|
||||
{% if vorgabe.checklistenfragen.all %}
|
||||
<ul class="list-group">
|
||||
{% for frage in vorgabe.checklistenfragen.all %}
|
||||
<li class="list-group-item">{{ frage.frage }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p><em>Keine Checklistenfragen</em></p>
|
||||
{% endif %}
|
||||
{% comment %} STICHWORTE + REFERENZEN AT BOTTOM {% endcomment %}
|
||||
<div class="mt-4 small text-muted">
|
||||
<strong>Stichworte:</strong>
|
||||
{% if vorgabe.stichworte.all %}
|
||||
{% for s in vorgabe.stichworte.all %}
|
||||
<a href="{% url 'stichwort_detail' stichwort=s %}">{{ s }}</a>{% if not forloop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<em>Keine</em>
|
||||
{% endif %}
|
||||
<br>
|
||||
<strong>Referenzen:</strong>
|
||||
{% if vorgabe.referenzpfade %}
|
||||
{% for ref in vorgabe.referenzpfade %}
|
||||
{{ ref|safe }}{% if not forloop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<em>Keine</em>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
35
dokumente/templates/standards/standard_detail_barebones.html
Normal file
35
dokumente/templates/standards/standard_detail_barebones.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<h1>{{ standard.nummer }} – {{ standard.name }}</h1>
|
||||
|
||||
<p><strong>Autoren:</strong> {{ standard.autoren.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>
|
||||
|
||||
{% if standard.geltungsbereich_html %}
|
||||
<h2>Geltungsbereich</h2>
|
||||
{% for typ, html in standard.geltungsbereich_html %}
|
||||
<div>{{ html|safe }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
<h2>Vorgaben</h2>
|
||||
{% for vorgabe in vorgaben %}
|
||||
<h3>{{ vorgabe.Vorgabennummer }} – {{ vorgabe.titel }}</h3>
|
||||
{% if vorgabe.referenzpfade %}
|
||||
<h4>Referenzen:</h4>
|
||||
{% endif %}
|
||||
{% for ref in vorgabe.referenzpfade %}
|
||||
{{ref | safe}}<br>
|
||||
{% empty %}
|
||||
Keine Referenzen angegeben
|
||||
{% endfor %}
|
||||
{# <h4>Kurztext</h4> #}
|
||||
{% for typ, html in vorgabe.kurztext_html %}
|
||||
{% if html %}<div>{{ html|safe }}</div>{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<h4>Langtext</h4>
|
||||
{% for typ, html in vorgabe.langtext_html %}
|
||||
{% if html %}<div>{{ html|safe }}</div>{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
13
dokumente/templates/standards/standard_list.html
Normal file
13
dokumente/templates/standards/standard_list.html
Normal file
@@ -0,0 +1,13 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>Standards Informatiksicherheit</h1>
|
||||
<ul>
|
||||
{% for standard in standards %}
|
||||
<li>
|
||||
<a href="{% url 'standard_detail' nummer=standard.nummer %}">
|
||||
{{ standard.nummer }} – {{ standard.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
3
dokumente/tests.py
Normal file
3
dokumente/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
11
dokumente/urls.py
Normal file
11
dokumente/urls.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.standard_list, name='standard_list'),
|
||||
path('<str:nummer>/', views.standard_detail, name='standard_detail'),
|
||||
path('<str:nummer>/history/<str:check_date>/', views.standard_detail),
|
||||
path('<str:nummer>/history/', views.standard_detail, {"check_date":"today"}, name='standard_history'),
|
||||
path('<str:nummer>/checkliste/', views.standard_checkliste, name='standard_checkliste')
|
||||
]
|
||||
|
||||
58
dokumente/views.py
Normal file
58
dokumente/views.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from .models import Dokument
|
||||
from abschnitte.utils import render_textabschnitte
|
||||
|
||||
from datetime import date
|
||||
import parsedatetime
|
||||
|
||||
calendar=parsedatetime.Calendar()
|
||||
|
||||
|
||||
def standard_list(request):
|
||||
standards = Dokument.objects.all()
|
||||
return render(request, 'standards/standard_list.html',
|
||||
{'dokumente': standards}
|
||||
)
|
||||
|
||||
|
||||
def standard_detail(request, nummer,check_date=""):
|
||||
standard = get_object_or_404(Dokument, nummer=nummer)
|
||||
|
||||
if check_date:
|
||||
check_date = calendar.parseDT(check_date)[0].date()
|
||||
standard.history = True
|
||||
else:
|
||||
check_date = date.today()
|
||||
standard.history = False
|
||||
standard.check_date=check_date
|
||||
vorgaben = list(standard.vorgaben.order_by("thema","nummer").select_related("thema","dokument")) # convert queryset to list so we can attach attributes
|
||||
|
||||
standard.geltungsbereich_html = render_textabschnitte(standard.geltungsbereich_set.order_by("order").select_related("abschnitttyp"))
|
||||
standard.einleitung_html=render_textabschnitte(standard.einleitung_set.order_by("order"))
|
||||
for vorgabe in vorgaben:
|
||||
# Prepare Kurztext HTML
|
||||
vorgabe.kurztext_html = render_textabschnitte(vorgabe.vorgabekurztext_set.order_by("order").select_related("abschnitttyp","abschnitt"))
|
||||
vorgabe.langtext_html = render_textabschnitte(vorgabe.vorgabelangtext_set.order_by("order").select_related("abschnitttyp","abschnitt"))
|
||||
vorgabe.long_status=vorgabe.get_status(check_date,verbose=True)
|
||||
vorgabe.relevanzset=list(vorgabe.relevanz.all())
|
||||
|
||||
referenz_items = []
|
||||
for r in vorgabe.referenzen.all():
|
||||
referenz_items.append(r.Path())
|
||||
vorgabe.referenzpfade = referenz_items
|
||||
|
||||
return render(request, 'standards/standard_detail.html', {
|
||||
'standard': standard,
|
||||
'vorgaben': vorgaben,
|
||||
})
|
||||
|
||||
|
||||
def standard_checkliste(request, nummer):
|
||||
standard = get_object_or_404(Dokument, nummer=nummer)
|
||||
vorgaben = list(standard.vorgaben.all())
|
||||
return render(request, 'standards/standard_checkliste.html', {
|
||||
'standard': standard,
|
||||
'vorgaben': vorgaben,
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user