Fix cover page: underline only 'Disclaimer', ensure page break, add SERVER_TIMEZONE env var
This commit is contained in:
@@ -36,6 +36,9 @@ ENV CELERY_BROKER_URL=redis://localhost:6379/0
|
||||
ENV CELERY_RESULT_BACKEND=redis://localhost:6379/0
|
||||
ENV SCRAIBE_UPLOAD_DIR=/tmp/scraibe_uploads
|
||||
|
||||
# Server timezone for document dates (e.g., America/Toronto, Europe/Berlin)
|
||||
ENV SERVER_TIMEZONE=UTC
|
||||
|
||||
# Email and template configuration
|
||||
ENV EMAIL_CONTACT_ADDRESS=support@example.com
|
||||
ENV EMAIL_CSS_PATH=
|
||||
|
||||
+30
-41
@@ -3,27 +3,24 @@ Cover-page generator for transcript and summary DOCX files.
|
||||
|
||||
Layout (all centered):
|
||||
- 5 blank lines
|
||||
- Logo from COVER_PAGE_LOGO_URL (if set)
|
||||
- 3 blank lines
|
||||
- Title in uppercase: "TRANSCRIPT" or "SUMMARY"
|
||||
- Next line: date of transcription (e.g. "June 19, 2026")
|
||||
- Next line: "Created by <COVER_PAGE_ORGANIZATION>"
|
||||
- 3 blank lines
|
||||
- Disclaimer text
|
||||
|
||||
A page break is inserted after the cover page so the main content starts on page 2.
|
||||
- Disclaimer (underlined)
|
||||
- Page break (ensures content starts on page 2)
|
||||
|
||||
Environment variables:
|
||||
- COVER_PAGE_ENABLED: "true"/"false" (default: false)
|
||||
- COVER_PAGE_ORGANIZATION: e.g., "A.P.Strom"
|
||||
- COVER_PAGE_LOGO_URL: URL of logo image to include on the cover page
|
||||
- SERVER_TIMEZONE: Timezone for document dates (e.g., "America/Toronto"). Defaults to "UTC".
|
||||
"""
|
||||
|
||||
import os
|
||||
import io
|
||||
import httpx
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
from docx import Document
|
||||
from docx.shared import Pt
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
@@ -31,7 +28,15 @@ from docx.oxml import OxmlElement
|
||||
from docx.oxml.ns import qn
|
||||
|
||||
|
||||
def _add_centered_paragraph(doc: Document, text: str = "", font_size: Pt = Pt(12), bold: bool = False):
|
||||
def _get_server_timezone():
|
||||
tz_name = (os.getenv("SERVER_TIMEZONE") or "UTC").strip()
|
||||
try:
|
||||
return ZoneInfo(tz_name)
|
||||
except (ZoneInfoNotFoundError, KeyError, ValueError):
|
||||
return ZoneInfo("UTC")
|
||||
|
||||
|
||||
def _add_centered_paragraph(doc: Document, text: str = "", font_size: Pt = Pt(12), bold: bool = False, underline: bool = False):
|
||||
"""Add a centered paragraph with given text and style."""
|
||||
p = doc.add_paragraph()
|
||||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
@@ -40,6 +45,7 @@ def _add_centered_paragraph(doc: Document, text: str = "", font_size: Pt = Pt(12
|
||||
run.font.name = "Courier"
|
||||
run.font.size = font_size
|
||||
run.bold = bold
|
||||
run.underline = underline
|
||||
return p
|
||||
|
||||
|
||||
@@ -50,9 +56,10 @@ def _add_blank_lines(doc: Document, count: int):
|
||||
|
||||
|
||||
def _add_page_break(doc: Document):
|
||||
"""Insert a page break paragraph."""
|
||||
"""Insert a page break paragraph to force next content to page 2."""
|
||||
p = doc.add_paragraph()
|
||||
pPr = p._p.get_or_add_pPr()
|
||||
# Clean existing formatting
|
||||
for child in list(pPr):
|
||||
tag = child.tag.split("}")[-1] if "}" in child.tag else child.tag
|
||||
if tag in ("tabs", "spacing", "ind"):
|
||||
@@ -62,17 +69,6 @@ def _add_page_break(doc: Document):
|
||||
pPr.append(page_break)
|
||||
|
||||
|
||||
def _download_image_bytes(url: str) -> Optional[bytes]:
|
||||
"""Download image from URL and return bytes, or None on failure."""
|
||||
try:
|
||||
with httpx.stream("GET", url, timeout=10) as resp:
|
||||
if resp.status_code == 200:
|
||||
return resp.content
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def add_cover_page(
|
||||
doc: Document,
|
||||
title: str,
|
||||
@@ -83,35 +79,23 @@ def add_cover_page(
|
||||
|
||||
- title: e.g., "TRANSCRIPT" or "SUMMARY"
|
||||
- date_str: optional date string (e.g., "June 19, 2026");
|
||||
if not provided, uses today's date.
|
||||
if not provided, uses current server time in SERVER_TIMEZONE.
|
||||
"""
|
||||
|
||||
# 5 blank lines
|
||||
_add_blank_lines(doc, 5)
|
||||
|
||||
# Logo (if COVER_PAGE_LOGO_URL is set)
|
||||
logo_url = (os.getenv("COVER_PAGE_LOGO_URL") or "").strip()
|
||||
if logo_url:
|
||||
img_bytes = _download_image_bytes(logo_url)
|
||||
if img_bytes:
|
||||
p = doc.add_paragraph()
|
||||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
try:
|
||||
# Add image centered
|
||||
p.add_run().add_picture(io.BytesIO(img_bytes), width=None)
|
||||
except Exception:
|
||||
# Fallback: do nothing if image fails
|
||||
pass
|
||||
|
||||
# 3 blank lines
|
||||
_add_blank_lines(doc, 3)
|
||||
|
||||
# Title in uppercase
|
||||
_add_centered_paragraph(doc, text=title.upper().strip(), font_size=Pt(18), bold=True)
|
||||
|
||||
# Date of transcription
|
||||
# Date of transcription (use provided or current server time)
|
||||
if not date_str:
|
||||
date_str = datetime.utcnow().strftime("%B %d, %Y")
|
||||
tz = _get_server_timezone()
|
||||
now = datetime.now(tz)
|
||||
date_str = now.strftime("%B %d, %Y")
|
||||
_add_centered_paragraph(doc, text=date_str, font_size=Pt(14))
|
||||
|
||||
# Created by + organization
|
||||
@@ -122,12 +106,17 @@ def add_cover_page(
|
||||
# 3 blank lines
|
||||
_add_blank_lines(doc, 3)
|
||||
|
||||
# Disclaimer
|
||||
disclaimer = (
|
||||
"Disclaimer\n"
|
||||
# Disclaimer: only the word "Disclaimer" is underlined
|
||||
disclaimer_title = "Disclaimer"
|
||||
disclaimer_body = (
|
||||
"This transcription and any summary derived therefrom may contain errors or inaccuracies."
|
||||
)
|
||||
_add_centered_paragraph(doc, text=disclaimer, font_size=Pt(10))
|
||||
|
||||
# Underlined "Disclaimer" line
|
||||
p = _add_centered_paragraph(doc, text=disclaimer_title, font_size=Pt(10), underline=True)
|
||||
|
||||
# Body text (not underlined)
|
||||
_add_centered_paragraph(doc, text=disclaimer_body, font_size=Pt(10), underline=False)
|
||||
|
||||
# Page break after cover page so main content starts on page 2
|
||||
_add_page_break(doc)
|
||||
|
||||
Reference in New Issue
Block a user