New tool for processing damage protocols: - Tab 1: Generate pre-filled PDF from Schadenlaufzettel.docx - Tab 2: Analyze filled PDF forms and extract dropdown selections - Tab 3: Generate Vorbericht.docx from filled PDF data Backend: Flask API with pypdf and python-docx Frontend: Vanilla JS SPA with drag-drop file upload 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
110 lines
3.6 KiB
Python
110 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Schadenprotokoll API - Flask Backend
|
|
Endpunkte: /generate, /analyze, /vorbericht
|
|
"""
|
|
from flask import Flask, request, jsonify, send_file
|
|
from flask_cors import CORS
|
|
import tempfile
|
|
import os
|
|
from processors import parse_laufzettel, fill_pdf, analyze_pdf, generate_vorbericht
|
|
|
|
app = Flask(__name__)
|
|
CORS(app)
|
|
|
|
TEMPLATE_PDF = "/opt/stacks/spa-hosting/html/schadenprotokoll/templates/protokoll.pdf"
|
|
TEMPLATE_DOCX = "/opt/stacks/spa-hosting/html/schadenprotokoll/templates/vorbericht.docx"
|
|
|
|
|
|
@app.route("/health", methods=["GET"])
|
|
def health():
|
|
return jsonify({"status": "ok"})
|
|
|
|
|
|
@app.route("/generate", methods=["POST"])
|
|
def generate():
|
|
"""Laufzettel.docx -> vorausgefuelltes Protokoll.pdf"""
|
|
if "file" not in request.files:
|
|
return jsonify({"error": "Keine Datei hochgeladen"}), 400
|
|
|
|
file = request.files["file"]
|
|
if not file.filename.endswith(".docx"):
|
|
return jsonify({"error": "Nur .docx Dateien erlaubt"}), 400
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".docx", delete=False) as tmp_in:
|
|
file.save(tmp_in.name)
|
|
|
|
try:
|
|
data = parse_laufzettel(tmp_in.name)
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as tmp_out:
|
|
fill_pdf(TEMPLATE_PDF, tmp_out.name, data)
|
|
|
|
os.unlink(tmp_in.name)
|
|
return send_file(
|
|
tmp_out.name,
|
|
mimetype="application/pdf",
|
|
as_attachment=True,
|
|
download_name="Schadenprotokoll_vorbefuellt.pdf"
|
|
)
|
|
except Exception as e:
|
|
os.unlink(tmp_in.name)
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
@app.route("/analyze", methods=["POST"])
|
|
def analyze():
|
|
"""Ausgefuelltes PDF -> JSON mit allen Feldern"""
|
|
if "file" not in request.files:
|
|
return jsonify({"error": "Keine Datei hochgeladen"}), 400
|
|
|
|
file = request.files["file"]
|
|
if not file.filename.endswith(".pdf"):
|
|
return jsonify({"error": "Nur .pdf Dateien erlaubt"}), 400
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as tmp:
|
|
file.save(tmp.name)
|
|
|
|
try:
|
|
result = analyze_pdf(tmp.name)
|
|
os.unlink(tmp.name)
|
|
return jsonify({"success": True, "data": result})
|
|
except Exception as e:
|
|
os.unlink(tmp.name)
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
@app.route("/vorbericht", methods=["POST"])
|
|
def vorbericht():
|
|
"""Ausgefuelltes PDF -> Vorbericht.docx"""
|
|
if "file" not in request.files:
|
|
return jsonify({"error": "Keine Datei hochgeladen"}), 400
|
|
|
|
file = request.files["file"]
|
|
if not file.filename.endswith(".pdf"):
|
|
return jsonify({"error": "Nur .pdf Dateien erlaubt"}), 400
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as tmp_in:
|
|
file.save(tmp_in.name)
|
|
|
|
try:
|
|
pdf_data = analyze_pdf(tmp_in.name)
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".docx", delete=False) as tmp_out:
|
|
generate_vorbericht(pdf_data, TEMPLATE_DOCX, tmp_out.name)
|
|
|
|
os.unlink(tmp_in.name)
|
|
return send_file(
|
|
tmp_out.name,
|
|
mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
as_attachment=True,
|
|
download_name="Vorbericht.docx"
|
|
)
|
|
except Exception as e:
|
|
os.unlink(tmp_in.name)
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host="0.0.0.0", port=5050, debug=False)
|