Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91d5f3d422 | ||
|
|
8ba41eb71e | ||
|
|
5c7401f3bc | ||
|
|
9abf97a922 | ||
|
|
b5233bb6eb | ||
|
|
9c34e504ca | ||
|
|
ab58e13b88 | ||
|
|
9c3aef4a96 | ||
|
|
a238ba1a55 | ||
|
|
e8305771f8 | ||
|
|
6988aee875 | ||
|
|
2e33f2da38 | ||
|
|
494bfa8884 | ||
|
|
37e7e4e367 | ||
|
|
0314bced40 | ||
|
|
3c56459cb8 | ||
|
|
643d1a49d1 |
@@ -51,7 +51,6 @@ RUN mkdir -p \
|
|||||||
/var/www/html/intranet_repo/projects/contentpanel/insertion/news \
|
/var/www/html/intranet_repo/projects/contentpanel/insertion/news \
|
||||||
/var/www/html/intranet_repo/projects/developerpanel/work \
|
/var/www/html/intranet_repo/projects/developerpanel/work \
|
||||||
/var/www/html/intranet_repo/projects/seelsorge/data \
|
/var/www/html/intranet_repo/projects/seelsorge/data \
|
||||||
/var/www/html/intranet_repo/projects/dashboard/cache \
|
|
||||||
/var/www/html/intranet_repo/projects/dashboard/month \
|
/var/www/html/intranet_repo/projects/dashboard/month \
|
||||||
&& chown -R www-data:www-data \
|
&& chown -R www-data:www-data \
|
||||||
/data \
|
/data \
|
||||||
@@ -59,7 +58,6 @@ RUN mkdir -p \
|
|||||||
/var/www/html/intranet_repo/projects/contentpanel/insertion/news \
|
/var/www/html/intranet_repo/projects/contentpanel/insertion/news \
|
||||||
/var/www/html/intranet_repo/projects/developerpanel/work \
|
/var/www/html/intranet_repo/projects/developerpanel/work \
|
||||||
/var/www/html/intranet_repo/projects/seelsorge/data \
|
/var/www/html/intranet_repo/projects/seelsorge/data \
|
||||||
/var/www/html/intranet_repo/projects/dashboard/cache \
|
|
||||||
/var/www/html/intranet_repo/projects/dashboard/month \
|
/var/www/html/intranet_repo/projects/dashboard/month \
|
||||||
&& chmod +x /opt/intranet/docker/scheduler.py /opt/intranet/worker/pdf_python/pdf_to_png.sh /opt/intranet/worker/contentpanel_aushang/process.py /opt/intranet/worker/contentpanel_aushang/betteroffice_render.py
|
&& chmod +x /opt/intranet/docker/scheduler.py /opt/intranet/worker/pdf_python/pdf_to_png.sh /opt/intranet/worker/contentpanel_aushang/process.py /opt/intranet/worker/contentpanel_aushang/betteroffice_render.py
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -203,10 +203,9 @@
|
|||||||
clearTimeout(this.timer);
|
clearTimeout(this.timer);
|
||||||
if (!this.rotate) return;
|
if (!this.rotate) return;
|
||||||
const duration = this.items.length ? config.POSTER_MIN_DWELL_TIME_S * 1000 : config.EMPTY_REFRESH_MS;
|
const duration = this.items.length ? config.POSTER_MIN_DWELL_TIME_S * 1000 : config.EMPTY_REFRESH_MS;
|
||||||
const changing = this.items.length && this.layout().groups.length > 1;
|
|
||||||
this.progressAnimation?.cancel();
|
this.progressAnimation?.cancel();
|
||||||
this.progress.toggleAttribute('hidden', !changing);
|
this.progress.toggleAttribute('hidden', !this.items.length);
|
||||||
if (changing) {
|
if (this.items.length) {
|
||||||
this.progressAnimation = this.progress.querySelector('.poster-progress-value').animate(
|
this.progressAnimation = this.progress.querySelector('.poster-progress-value').animate(
|
||||||
[{ strokeDashoffset: 100 }, { strokeDashoffset: 0 }],
|
[{ strokeDashoffset: 100 }, { strokeDashoffset: 0 }],
|
||||||
{ duration, easing: 'linear', fill: 'forwards' });
|
{ duration, easing: 'linear', fill: 'forwards' });
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ body.dashboard {
|
|||||||
padding: var(--space-sm);
|
padding: var(--space-sm);
|
||||||
}
|
}
|
||||||
#month-panel { grid-area: month; --panel-accent: var(--nak-purple); }
|
#month-panel { grid-area: month; --panel-accent: var(--nak-purple); }
|
||||||
#poster-panel { grid-area: posters; --panel-accent: var(--nak-orange); --poster-frame-width: 1px; }
|
#poster-panel { grid-area: posters; --panel-accent: var(--nak-orange); --poster-frame-width: 2px; }
|
||||||
#announcements-panel { --panel-accent: var(--nak-teal); }
|
#announcements-panel { --panel-accent: var(--nak-teal); }
|
||||||
.dashboard-column { display: grid; gap: var(--space-sm); min-height: 0; min-width: 0; }
|
.dashboard-column { display: grid; gap: var(--space-sm); min-height: 0; min-width: 0; }
|
||||||
.announcements-column { grid-area: announcements; grid-template-rows: auto minmax(0, 1fr); }
|
.announcements-column { grid-area: announcements; grid-template-rows: auto minmax(0, 1fr); }
|
||||||
@@ -74,7 +74,6 @@ body.dashboard {
|
|||||||
.text-page { color: var(--dashboard-text); }
|
.text-page { color: var(--dashboard-text); }
|
||||||
.text-page p { margin: 0 0 var(--space-xs); white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--announcement-font); line-height: var(--announcement-leading); }
|
.text-page p { margin: 0 0 var(--space-xs); white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--announcement-font); line-height: var(--announcement-leading); }
|
||||||
.text-page p:last-child { margin-bottom: 0; }
|
.text-page p:last-child { margin-bottom: 0; }
|
||||||
.text-page .announcement-title { font-size: var(--announcement-detail-font); }
|
|
||||||
.text-page .announcement-title, .text-page .bible-reference { font-weight: 500; }
|
.text-page .announcement-title, .text-page .bible-reference { font-weight: 500; }
|
||||||
.text-page .announcement-subtitle { font-size: var(--announcement-detail-font); color: var(--dashboard-meta); }
|
.text-page .announcement-subtitle { font-size: var(--announcement-detail-font); color: var(--dashboard-meta); }
|
||||||
.text-page .announcement-meta { font-size: var(--announcement-meta-font); color: var(--dashboard-meta); }
|
.text-page .announcement-meta { font-size: var(--announcement-meta-font); color: var(--dashboard-meta); }
|
||||||
|
|||||||
@@ -53,37 +53,10 @@ def _set_text(paragraph, text):
|
|||||||
node.appendChild(document.createTextNode(part))
|
node.appendChild(document.createTextNode(part))
|
||||||
anchor.parentNode.insertBefore(node, anchor)
|
anchor.parentNode.insertBefore(node, anchor)
|
||||||
anchor.parentNode.removeChild(anchor)
|
anchor.parentNode.removeChild(anchor)
|
||||||
|
# Preserve verse/signature lines without stretching them to the full width.
|
||||||
if '\n' in text:
|
if '\n' in text:
|
||||||
paragraph.getElementsByTagNameNS(W, 'jc')[0].setAttributeNS(W, 'w:val', 'left')
|
for alignment in paragraph.getElementsByTagNameNS(W, 'jc'):
|
||||||
|
alignment.setAttributeNS(W, 'w:val', 'left')
|
||||||
|
|
||||||
def _items(text):
|
|
||||||
lines = [line.strip() for line in text.replace('\r\n', '\n').replace('\r', '\n').split('\n') if line.strip()]
|
|
||||||
bullets = [line[1:].lstrip() if line.startswith('•') and line[1:].strip() else None for line in lines]
|
|
||||||
if not any(bullets):
|
|
||||||
return [(text, None)]
|
|
||||||
return [(bullet, i + 1 < len(lines) and bullets[i + 1] is not None) if bullet else (line, None)
|
|
||||||
for i, (line, bullet) in enumerate(zip(lines, bullets))]
|
|
||||||
|
|
||||||
|
|
||||||
def _format_bullet(paragraph, compact):
|
|
||||||
properties = paragraph.getElementsByTagNameNS(W, 'pPr')[0]
|
|
||||||
anchor = properties.getElementsByTagNameNS(W, 'jc')[0]
|
|
||||||
document = paragraph.ownerDocument
|
|
||||||
tabs = document.createElementNS(W, 'w:tabs')
|
|
||||||
tab = document.createElementNS(W, 'w:tab')
|
|
||||||
tab.setAttributeNS(W, 'w:val', 'left')
|
|
||||||
tab.setAttributeNS(W, 'w:pos', '480')
|
|
||||||
tabs.appendChild(tab)
|
|
||||||
properties.insertBefore(tabs, anchor)
|
|
||||||
if compact:
|
|
||||||
spacing = document.createElementNS(W, 'w:spacing')
|
|
||||||
spacing.setAttributeNS(W, 'w:after', '0')
|
|
||||||
properties.insertBefore(spacing, anchor)
|
|
||||||
indent = document.createElementNS(W, 'w:ind')
|
|
||||||
indent.setAttributeNS(W, 'w:left', '480')
|
|
||||||
indent.setAttributeNS(W, 'w:hanging', '360')
|
|
||||||
properties.insertBefore(indent, anchor)
|
|
||||||
|
|
||||||
|
|
||||||
def fill_template(template: Path, output: Path, title: str, date_label: str, paragraphs: list[str]) -> None:
|
def fill_template(template: Path, output: Path, title: str, date_label: str, paragraphs: list[str]) -> None:
|
||||||
@@ -99,15 +72,12 @@ def fill_template(template: Path, output: Path, title: str, date_label: str, par
|
|||||||
used = {p.getAttributeNS(W14, 'paraId') for p in document.getElementsByTagNameNS(W, 'p')}
|
used = {p.getAttributeNS(W14, 'paraId') for p in document.getElementsByTagNameNS(W, 'p')}
|
||||||
index = 1
|
index = 1
|
||||||
for text in paragraphs:
|
for text in paragraphs:
|
||||||
for value, compact in _items(text):
|
|
||||||
paragraph = body.cloneNode(deep=True)
|
paragraph = body.cloneNode(deep=True)
|
||||||
while f'{index:08X}' in used:
|
while f'{index:08X}' in used:
|
||||||
index += 1
|
index += 1
|
||||||
paragraph.setAttributeNS(W14, 'w14:paraId', f'{index:08X}')
|
paragraph.setAttributeNS(W14, 'w14:paraId', f'{index:08X}')
|
||||||
used.add(f'{index:08X}')
|
used.add(f'{index:08X}')
|
||||||
_set_text(paragraph, f'•\t{value}' if compact is not None else value)
|
_set_text(paragraph, text)
|
||||||
if compact is not None:
|
|
||||||
_format_bullet(paragraph, compact)
|
|
||||||
body.parentNode.insertBefore(paragraph, body)
|
body.parentNode.insertBefore(paragraph, body)
|
||||||
body.parentNode.removeChild(body)
|
body.parentNode.removeChild(body)
|
||||||
output.parent.mkdir(parents=True, exist_ok=True)
|
output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|||||||
@@ -151,30 +151,6 @@ class PipelineTests(unittest.TestCase):
|
|||||||
self.assertEqual([t.text for t in paragraph.findall('.//w:t', ns)], ['Von Herzen', 'Eure Gastgeber'])
|
self.assertEqual([t.text for t in paragraph.findall('.//w:t', ns)], ['Von Herzen', 'Eure Gastgeber'])
|
||||||
self.assertEqual(paragraph.find('w:pPr/w:jc', ns).get('{' + ns['w'] + '}val'), 'left')
|
self.assertEqual(paragraph.find('w:pPr/w:jc', ns).get('{' + ns['w'] + '}val'), 'left')
|
||||||
|
|
||||||
def test_bullet_lines_become_hanging_list_paragraphs(self):
|
|
||||||
with tempfile.TemporaryDirectory() as temp:
|
|
||||||
target = Path(temp) / 'bullets.docx'
|
|
||||||
fill_template(TEMPLATE, target, 'Einladung', _date_label('2026-09-04'), [
|
|
||||||
'Einleitung.',
|
|
||||||
'SEPTEMBER\n•05.09.2026: Erster Stichpunkt.\n• Zweiter Stichpunkt.\nOKTOBER\n• Dritter Stichpunkt.',
|
|
||||||
'Abschluss.',
|
|
||||||
])
|
|
||||||
with zipfile.ZipFile(target) as archive:
|
|
||||||
document = ET.fromstring(archive.read('word/document.xml'))
|
|
||||||
ns = {'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}
|
|
||||||
q = lambda name: '{' + ns['w'] + '}' + name
|
|
||||||
bullets = [paragraph for paragraph in document.findall('.//w:p', ns)
|
|
||||||
if ''.join(paragraph.itertext()).startswith('•')]
|
|
||||||
self.assertEqual(len(bullets), 3)
|
|
||||||
self.assertTrue({'SEPTEMBER', 'OKTOBER'} <= {''.join(p.itertext()) for p in document.findall('.//w:p', ns)})
|
|
||||||
for paragraph in bullets:
|
|
||||||
self.assertIsNotNone(paragraph.find('w:r/w:tab', ns))
|
|
||||||
self.assertEqual(paragraph.find('w:pPr/w:tabs/w:tab', ns).get(q('pos')), '480')
|
|
||||||
indent = paragraph.find('w:pPr/w:ind', ns)
|
|
||||||
self.assertEqual((indent.get(q('left')), indent.get(q('hanging'))), ('480', '360'))
|
|
||||||
self.assertEqual(bullets[0].find('w:pPr/w:spacing', ns).get(q('after')), '0')
|
|
||||||
self.assertIsNone(bullets[1].find('w:pPr/w:spacing', ns))
|
|
||||||
|
|
||||||
def test_template_fill_creates_multiple_paragraphs(self):
|
def test_template_fill_creates_multiple_paragraphs(self):
|
||||||
with tempfile.TemporaryDirectory() as temp:
|
with tempfile.TemporaryDirectory() as temp:
|
||||||
target = Path(temp) / "filled.docx"
|
target = Path(temp) / "filled.docx"
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ class RenderingTests(unittest.TestCase):
|
|||||||
paragraphs = [
|
paragraphs = [
|
||||||
("Dies ist ein langer Testabsatz zur Prüfung von Schriftgröße, Zeilenumbruch und Seitenaufteilung. " * 12).strip()
|
("Dies ist ein langer Testabsatz zur Prüfung von Schriftgröße, Zeilenumbruch und Seitenaufteilung. " * 12).strip()
|
||||||
for _ in range(3)
|
for _ in range(3)
|
||||||
] + ["• Ein längerer Stichpunkt, dessen zweite Zeile unter dem Text und nicht unter dem Aufzählungszeichen beginnt.\n"
|
] + ["Erste Gedichtzeile\nZweite Gedichtzeile\n\nVon Herzen\nEure Gastgeber",
|
||||||
"• Ein zweiter Stichpunkt bestätigt den gleichmäßigen Abstand der Liste.",
|
|
||||||
"Erste Gedichtzeile\nZweite Gedichtzeile\n\nVon Herzen\nEure Gastgeber",
|
|
||||||
"Abschlussmarker: Dieser letzte Absatz muss vollständig auf der letzten Seite erscheinen."]
|
"Abschlussmarker: Dieser letzte Absatz muss vollständig auf der letzten Seite erscheinen."]
|
||||||
fill_template(TEMPLATE, cls.document,
|
fill_template(TEMPLATE, cls.document,
|
||||||
"Verabschiedung von Diakon Georg aus der Gemeinde Reinickendorf",
|
"Verabschiedung von Diakon Georg aus der Gemeinde Reinickendorf",
|
||||||
|
|||||||
Reference in New Issue
Block a user