diff --git a/dokumente/management/commands/export_xml.py b/dokumente/management/commands/export_xml.py
index ab98e40..fe0bb28 100644
--- a/dokumente/management/commands/export_xml.py
+++ b/dokumente/management/commands/export_xml.py
@@ -4,6 +4,39 @@ from datetime import datetime
from dokumente.models import Dokument, Vorgabe, VorgabeKurztext, VorgabeLangtext, Checklistenfrage
+def _parse_markdown_table(markdown_content):
+ """
+ Parse markdown table content and return XML element with
structure
+ """
+ lines = [line.strip() for line in markdown_content.strip().split('\n') if line.strip()]
+ if not lines:
+ return None
+
+ # Create table element
+ table = ET.Element('table')
+
+ # Parse first row as header
+ header_row = [cell.strip() for cell in lines[0].split('|') if cell.strip()]
+ header = ET.SubElement(table, 'header')
+ for cell in header_row:
+ column = ET.SubElement(header, 'column')
+ column.text = cell
+
+ # Parse remaining rows (skip separator row if it exists)
+ for line in lines[2:] if len(lines) > 1 and all(c in '-| ' for c in lines[1]) else lines[1:]:
+ # Check if this is a separator row
+ if all(c in '-| ' for c in line):
+ continue
+
+ row = ET.SubElement(table, 'row')
+ row_cells = [cell.strip() for cell in line.split('|') if cell.strip()]
+ for cell in row_cells:
+ column = ET.SubElement(row, 'column')
+ column.text = cell
+
+ return table
+
+
class Command(BaseCommand):
help = 'Export all dokumente as XML'
@@ -49,20 +82,34 @@ class Command(BaseCommand):
geltungsbereich_sections = dokument.geltungsbereich_set.all().order_by('order')
if geltungsbereich_sections:
geltungsbereich_element = ET.SubElement(doc_element, 'Geltungsbereich')
- abschnitt_element = ET.SubElement(geltungsbereich_element, 'Abschnitt')
for gb in geltungsbereich_sections:
- section = ET.SubElement(abschnitt_element, 'Teil')
- section.set('typ', gb.abschnitttyp.abschnitttyp if gb.abschnitttyp else "text")
- section.text = gb.inhalt
+ section_type = gb.abschnitttyp.abschnitttyp if gb.abschnitttyp else "text"
+ if section_type in ('tabelle', 'table'):
+ table = _parse_markdown_table(gb.inhalt)
+ if table is not None:
+ abschnitt_element = ET.SubElement(geltungsbereich_element, 'Abschnitt')
+ abschnitt_element.set('typ', section_type)
+ abschnitt_element.append(table)
+ else:
+ abschnitt_element = ET.SubElement(geltungsbereich_element, 'Abschnitt')
+ abschnitt_element.set('typ', section_type)
+ abschnitt_element.text = gb.inhalt
einleitung_sections = dokument.einleitung_set.all().order_by('order')
if einleitung_sections:
einleitung_element = ET.SubElement(doc_element, 'Einleitung')
- abschnitt_element = ET.SubElement(einleitung_element, 'Abschnitt')
for ei in einleitung_sections:
- section = ET.SubElement(abschnitt_element, 'Teil')
- section.set('typ', ei.abschnitttyp.abschnitttyp if ei.abschnitttyp else "text")
- section.text = ei.inhalt
+ section_type = ei.abschnitttyp.abschnitttyp if ei.abschnitttyp else "text"
+ if section_type in ('tabelle', 'table'):
+ table = _parse_markdown_table(ei.inhalt)
+ if table is not None:
+ abschnitt_element = ET.SubElement(einleitung_element, 'Abschnitt')
+ abschnitt_element.set('typ', section_type)
+ abschnitt_element.append(table)
+ else:
+ abschnitt_element = ET.SubElement(einleitung_element, 'Abschnitt')
+ abschnitt_element.set('typ', section_type)
+ abschnitt_element.text = ei.inhalt
ET.SubElement(doc_element, 'Ziel').text = ""
ET.SubElement(doc_element, 'Grundlagen').text = ""
@@ -96,20 +143,34 @@ class Command(BaseCommand):
kurztext_sections = vorgabe.vorgabekurztext_set.all().order_by('order')
if kurztext_sections:
kurztext_element = ET.SubElement(vorgabe_el, 'Kurztext')
- abschnitt = ET.SubElement(kurztext_element, 'Abschnitt')
for kt in kurztext_sections:
- teil = ET.SubElement(abschnitt, 'Teil')
- teil.set('typ', kt.abschnitttyp.abschnitttyp if kt.abschnitttyp else "text")
- teil.text = kt.inhalt
-
+ section_type = kt.abschnitttyp.abschnitttyp if kt.abschnitttyp else "text"
+ if section_type in ('tabelle', 'table'):
+ table = _parse_markdown_table(kt.inhalt)
+ if table is not None:
+ abschnitt = ET.SubElement(kurztext_element, 'Abschnitt')
+ abschnitt.set('typ', section_type)
+ abschnitt.append(table)
+ else:
+ abschnitt = ET.SubElement(kurztext_element, 'Abschnitt')
+ abschnitt.set('typ', section_type)
+ abschnitt.text = kt.inhalt
+
langtext_sections = vorgabe.vorgabelangtext_set.all().order_by('order')
if langtext_sections:
langtext_element = ET.SubElement(vorgabe_el, 'Langtext')
- abschnitt = ET.SubElement(langtext_element, 'Abschnitt')
for lt in langtext_sections:
- teil = ET.SubElement(abschnitt, 'Teil')
- teil.set('typ', lt.abschnitttyp.abschnitttyp if lt.abschnitttyp else "text")
- teil.text = lt.inhalt
+ section_type = lt.abschnitttyp.abschnitttyp if lt.abschnitttyp else "text"
+ if section_type in ('tabelle', 'table'):
+ table = _parse_markdown_table(lt.inhalt)
+ if table is not None:
+ abschnitt = ET.SubElement(langtext_element, 'Abschnitt')
+ abschnitt.set('typ', section_type)
+ abschnitt.append(table)
+ else:
+ abschnitt = ET.SubElement(langtext_element, 'Abschnitt')
+ abschnitt.set('typ', section_type)
+ abschnitt.text = lt.inhalt
referenz_element = ET.SubElement(vorgabe_el, 'Referenzen')
for ref in vorgabe.referenzen.all():
diff --git a/dokumente/tests.py b/dokumente/tests.py
index 9bf141a..d315f89 100644
--- a/dokumente/tests.py
+++ b/dokumente/tests.py
@@ -1675,6 +1675,54 @@ class ExportXMLCommandTest(TestCase):
# Should still contain active document
self.assertIn('TEST-001', output)
self.assertIn('Test Standard', output)
+
+ def test_export_xml_command_table_structure(self):
+ """Test export_xml command converts markdown tables to proper XML structure"""
+ # Create document with table
+ table_doc = Dokument.objects.create(
+ nummer="TABLE-001",
+ dokumententyp=self.dokumententyp,
+ name="Table Test Document",
+ aktiv=True
+ )
+ table_doc.autoren.add(self.autor1)
+
+ table_vorgabe = Vorgabe.objects.create(
+ order=1,
+ nummer=1,
+ dokument=table_doc,
+ thema=self.thema,
+ titel="Table Test Vorgabe",
+ gueltigkeit_von=date(2023, 1, 1),
+ gueltigkeit_bis=date(2025, 12, 31)
+ )
+
+ table_content = "| Spalte1 | Spalte2 |\n|---------|---------|\n| Wert1 | Wert2 |\n| Wert3 | Wert4 |"
+
+ self.langtext_table = VorgabeLangtext.objects.create(
+ abschnitt=table_vorgabe,
+ abschnitttyp=self.abschnitttyp_table,
+ inhalt=table_content,
+ order=1
+ )
+
+ out = StringIO()
+ call_command('export_xml', stdout=out)
+
+ output = out.getvalue()
+
+ # Check that table structure is properly exported
+ self.assertIn('', output)
+ self.assertIn('', output)
+ self.assertIn('Spalte1', output)
+ self.assertIn('Spalte2', output)
+ self.assertIn('', output)
+ self.assertIn('Wert1', output)
+ self.assertIn('Wert2', output)
+ self.assertIn('Wert3', output)
+ self.assertIn('Wert4', output)
+ # Should not contain the markdown table content as plain text
+ self.assertNotIn('| Spalte1 | Spalte2 |', output)
class StandardJSONViewTest(TestCase):
@@ -1915,6 +1963,7 @@ class StandardXMLViewTest(TestCase):
# Create text sections
self.abschnitttyp_text = AbschnittTyp.objects.create(abschnitttyp="text")
+ self.abschnitttyp_table = AbschnittTyp.objects.create(abschnitttyp="table")
self.geltungsbereich = Geltungsbereich.objects.create(
geltungsbereich=self.dokument,
@@ -2049,6 +2098,71 @@ class StandardXMLViewTest(TestCase):
self.assertIn(' structure
+ """
+ lines = [line.strip() for line in markdown_content.strip().split('\n') if line.strip()]
+ if not lines:
+ return None
+
+ # Create table element
+ table = ET.Element('table')
+
+ # Parse first row as header
+ header_row = [cell.strip() for cell in lines[0].split('|') if cell.strip()]
+ header = ET.SubElement(table, 'header')
+ for cell in header_row:
+ column = ET.SubElement(header, 'column')
+ column.text = cell
+
+ # Parse remaining rows (skip separator row if it exists)
+ for line in lines[2:] if len(lines) > 1 and all(c in '-| ' for c in lines[1]) else lines[1:]:
+ # Check if this is a separator row
+ if all(c in '-| ' for c in line):
+ continue
+
+ row = ET.SubElement(table, 'row')
+ row_cells = [cell.strip() for cell in line.split('|') if cell.strip()]
+ for cell in row_cells:
+ column = ET.SubElement(row, 'column')
+ column.text = cell
+
+ return table
+
+
def standard_list(request):
dokumente = Dokument.objects.all()
return render(request, 'standards/standard_list.html',
@@ -294,20 +327,34 @@ def standard_xml(request, nummer):
geltungsbereich_sections = dokument.geltungsbereich_set.all().order_by('order')
if geltungsbereich_sections:
geltungsbereich_element = ET.SubElement(root, 'Geltungsbereich')
- abschnitt_element = ET.SubElement(geltungsbereich_element, 'Abschnitt')
for gb in geltungsbereich_sections:
- section = ET.SubElement(abschnitt_element, 'Teil')
- section.set('typ', gb.abschnitttyp.abschnitttyp if gb.abschnitttyp else "text")
- section.text = gb.inhalt
+ section_type = gb.abschnitttyp.abschnitttyp if gb.abschnitttyp else "text"
+ if section_type in ('tabelle', 'table'):
+ table = _parse_markdown_table(gb.inhalt)
+ if table is not None:
+ abschnitt_element = ET.SubElement(geltungsbereich_element, 'Abschnitt')
+ abschnitt_element.set('typ', section_type)
+ abschnitt_element.append(table)
+ else:
+ abschnitt_element = ET.SubElement(geltungsbereich_element, 'Abschnitt')
+ abschnitt_element.set('typ', section_type)
+ abschnitt_element.text = gb.inhalt
einleitung_sections = dokument.einleitung_set.all().order_by('order')
if einleitung_sections:
einleitung_element = ET.SubElement(root, 'Einleitung')
- abschnitt_element = ET.SubElement(einleitung_element, 'Abschnitt')
for ei in einleitung_sections:
- section = ET.SubElement(abschnitt_element, 'Teil')
- section.set('typ', ei.abschnitttyp.abschnitttyp if ei.abschnitttyp else "text")
- section.text = ei.inhalt
+ section_type = ei.abschnitttyp.abschnitttyp if ei.abschnitttyp else "text"
+ if section_type in ('tabelle', 'table'):
+ table = _parse_markdown_table(ei.inhalt)
+ if table is not None:
+ abschnitt_element = ET.SubElement(einleitung_element, 'Abschnitt')
+ abschnitt_element.set('typ', section_type)
+ abschnitt_element.append(table)
+ else:
+ abschnitt_element = ET.SubElement(einleitung_element, 'Abschnitt')
+ abschnitt_element.set('typ', section_type)
+ abschnitt_element.text = ei.inhalt
ET.SubElement(root, 'Ziel').text = ""
ET.SubElement(root, 'Grundlagen').text = ""
@@ -341,20 +388,34 @@ def standard_xml(request, nummer):
kurztext_sections = vorgabe.vorgabekurztext_set.all().order_by('order')
if kurztext_sections:
kurztext_element = ET.SubElement(vorgabe_el, 'Kurztext')
- abschnitt = ET.SubElement(kurztext_element, 'Abschnitt')
for kt in kurztext_sections:
- teil = ET.SubElement(abschnitt, 'Teil')
- teil.set('typ', kt.abschnitttyp.abschnitttyp if kt.abschnitttyp else "text")
- teil.text = kt.inhalt
+ section_type = kt.abschnitttyp.abschnitttyp if kt.abschnitttyp else "text"
+ if section_type in ('tabelle', 'table'):
+ table = _parse_markdown_table(kt.inhalt)
+ if table is not None:
+ abschnitt = ET.SubElement(kurztext_element, 'Abschnitt')
+ abschnitt.set('typ', section_type)
+ abschnitt.append(table)
+ else:
+ abschnitt = ET.SubElement(kurztext_element, 'Abschnitt')
+ abschnitt.set('typ', section_type)
+ abschnitt.text = kt.inhalt
langtext_sections = vorgabe.vorgabelangtext_set.all().order_by('order')
if langtext_sections:
langtext_element = ET.SubElement(vorgabe_el, 'Langtext')
- abschnitt = ET.SubElement(langtext_element, 'Abschnitt')
for lt in langtext_sections:
- teil = ET.SubElement(abschnitt, 'Teil')
- teil.set('typ', lt.abschnitttyp.abschnitttyp if lt.abschnitttyp else "text")
- teil.text = lt.inhalt
+ section_type = lt.abschnitttyp.abschnitttyp if lt.abschnitttyp else "text"
+ if section_type in ('tabelle', 'table'):
+ table = _parse_markdown_table(lt.inhalt)
+ if table is not None:
+ abschnitt = ET.SubElement(langtext_element, 'Abschnitt')
+ abschnitt.set('typ', section_type)
+ abschnitt.append(table)
+ else:
+ abschnitt = ET.SubElement(langtext_element, 'Abschnitt')
+ abschnitt.set('typ', section_type)
+ abschnitt.text = lt.inhalt
referenz_element = ET.SubElement(vorgabe_el, 'Referenzen')
for ref in vorgabe.referenzen.all():