Files
serienbrief_django/serienbrief/app/mailmerge/views.py
T

95 lines
3.5 KiB
Python
Raw Normal View History

2026-05-21 10:36:16 +02:00
from pathlib import Path
from django.contrib.auth.decorators import login_required
from django.http import FileResponse, Http404, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.views.decorators.http import require_http_methods
from .forms import LetterTemplateForm, MailMergeJobForm
from .models import LetterTemplate, MailMergeJob
from .services.docx_renderer import extract_placeholders
from .tasks import run_mailmerge
@login_required
def dashboard(request):
templates = LetterTemplate.objects.all()[:20]
jobs = MailMergeJob.objects.select_related("template")[:20]
return render(request, "mailmerge/dashboard.html",
{"templates": templates, "jobs": jobs})
@login_required
@require_http_methods(["GET", "POST"])
def template_upload(request):
form = LetterTemplateForm(request.POST or None, request.FILES or None)
if request.method == "POST" and form.is_valid():
tpl = form.save(commit=False)
tpl.created_by = request.user
tpl.save()
# Platzhalter extrahieren Datei liegt jetzt auf Disk
tpl.placeholders = extract_placeholders(Path(tpl.file.path))
tpl.save(update_fields=["placeholders"])
return redirect(reverse("template-detail", args=[tpl.id]))
return render(request, "mailmerge/template_form.html", {"form": form})
@login_required
def template_detail(request, pk):
tpl = get_object_or_404(LetterTemplate, pk=pk)
return render(request, "mailmerge/template_detail.html", {"template": tpl})
@login_required
@require_http_methods(["GET", "POST"])
def job_create(request):
form = MailMergeJobForm(request.POST or None, request.FILES or None)
if request.method == "POST" and form.is_valid():
job = form.save(commit=False)
job.created_by = request.user
job.save()
run_mailmerge.delay(str(job.id))
return redirect(reverse("job-detail", args=[job.id]))
return render(request, "mailmerge/job_form.html", {"form": form})
@login_required
def job_detail(request, pk):
job = get_object_or_404(
MailMergeJob.objects.select_related("template"), pk=pk
)
logs = job.logs.all()
# HTMX partials: nur das Status-Fragment ausliefern
if request.headers.get("HX-Request"):
return render(request, "mailmerge/_job_status.html",
{"job": job, "logs": logs})
return render(request, "mailmerge/job_detail.html",
{"job": job, "logs": logs})
@login_required
def job_download(request, pk):
"""PDF-Download via X-Accel-Redirect in Production, direkter Stream im Dev."""
job = get_object_or_404(MailMergeJob, pk=pk)
if not job.result_pdf:
raise Http404("Kein Ergebnis-PDF vorhanden.")
# In Dev (settings.DEBUG) direkt streamen
from django.conf import settings
if settings.DEBUG:
return FileResponse(job.result_pdf.open("rb"),
as_attachment=True,
filename=Path(job.result_pdf.name).name)
# In Production: Nginx serviert die Datei via internem Mount.
response = HttpResponse()
response["Content-Type"] = "application/pdf"
response["Content-Disposition"] = (
f'attachment; filename="{Path(job.result_pdf.name).name}"'
)
# Pfad relativ zu MEDIA_ROOT, gemappt auf /protected-media/
relative = job.result_pdf.name
response["X-Accel-Redirect"] = f"/protected-media/{relative}"
return response