Files
SemapForm/app/main.py

386 lines
15 KiB
Python
Executable File

import os
import re
import smtplib
from email.mime.text import MIMEText
from xml.etree.ElementTree import Element, SubElement, tostring
from bibapi import catalogue
from fastapi import FastAPI, Form, HTTPException, Request, status
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="app/templates")
# Serve static files (CSS, images)
app.mount("/static", StaticFiles(directory="app/static"), name="static")
# Initialize catalogue for signature validation
cat = catalogue.Catalogue()
# add somewhere near the top-level constants
EMAIL_REGEX = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
# SMTP / email configuration via environment
MAIL_ENABLED = os.getenv("MAIL_ENABLED", "false").lower() == "true"
SMTP_HOST = os.getenv("SMTP_HOST", "smtp")
SMTP_PORT = int(os.getenv("SMTP_PORT", "25"))
MAIL_FROM = os.getenv("MAIL_FROM", "noreply@example.com")
MAIL_TO = os.getenv("MAIL_TO", "destination@example.com")
@app.get("/", response_class=HTMLResponse)
async def landing_page(request: Request):
"""Landing page with service selection"""
return templates.TemplateResponse("index.html", {"request": request})
@app.get("/semesterapparat", response_class=HTMLResponse)
async def semesterapparat_form(request: Request):
"""Semesterapparat form page"""
return templates.TemplateResponse("semesterapparat_form.html", {"request": request})
@app.get("/elsa", response_class=HTMLResponse)
async def elsa_form(request: Request):
"""ELSA form page"""
return templates.TemplateResponse("elsa_mono_form.html", {"request": request})
@app.get("/api/validate-signature")
async def validate_signature(signature: str):
"""Validate a book signature and return total pages"""
try:
book_result = cat.get_book_with_data(signature)
if book_result and hasattr(book_result, "pages") and book_result.pages:
# Try to extract numeric page count
pages_str = str(book_result.pages)
# Extract first number from pages string (e.g., "245 S." -> 245)
match = re.search(r"(\d+)", pages_str)
if match:
total_pages = int(match.group(1))
return JSONResponse(
{"valid": True, "total_pages": total_pages, "signature": signature}
)
return JSONResponse(
{
"valid": False,
"error": "Signatur nicht gefunden oder keine Seitenzahl verfügbar",
"signature": signature,
}
)
except Exception as e:
return JSONResponse(
{
"valid": False,
"error": f"Fehler bei der Validierung: {str(e)}",
"signature": signature,
}
)
@app.post("/submit")
async def handle_form(
request: Request,
name: str = Form(...),
lastname: str = Form(...),
title: str = Form(...),
telno: str = Form(...),
mail: str = Form(...),
apparatsname: str = Form(...),
subject: str = Form(...),
semester_type: str = Form(...), # "summer" or "winter"
semester_year: str = Form(...),
authorname: list[str] = Form(...),
year: list[str] = Form(...),
booktitle: list[str] = Form(...),
signature: list[str] = Form(...),
message: str = Form(default=""),
):
# Basic email validation (server-side)
if not EMAIL_REGEX.match(mail):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid email address format.",
)
# Build XML
root = Element("form_submission")
static_data = SubElement(root, "static")
SubElement(static_data, "name").text = name
SubElement(static_data, "lastname").text = lastname
SubElement(static_data, "title").text = title
SubElement(static_data, "telno").text = telno
SubElement(static_data, "mail").text = mail
SubElement(static_data, "apparatsname").text = apparatsname
SubElement(static_data, "subject").text = subject
SubElement(static_data, "semester").text = f"{semester_type} {semester_year}"
if message:
SubElement(static_data, "message").text = message
books = SubElement(root, "books")
for i in range(len(authorname)):
book = SubElement(books, "book")
SubElement(book, "authorname").text = authorname[i]
SubElement(book, "year").text = year[i]
SubElement(book, "title").text = booktitle[i]
SubElement(book, "signature").text = signature[i]
xml_data = tostring(root, encoding="unicode")
# Send mail
msg = MIMEText(xml_data, "xml")
msg["Subject"] = "New Form Submission"
msg["From"] = MAIL_FROM
msg["To"] = MAIL_TO
if MAIL_ENABLED:
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.send_message(msg)
else:
print("=" * 80)
print("MAIL SENDING DISABLED - Would have sent:")
print(f"From: {MAIL_FROM}")
print(f"To: {MAIL_TO}")
print(f"Subject: {msg['Subject']}")
print("-" * 80)
print(xml_data)
print("=" * 80)
return RedirectResponse("/?success=true", status_code=303)
@app.post("/elsa/submit")
async def handle_elsa_form(request: Request):
"""Handle ELSA form submission with multiple media types"""
form_data = await request.form()
# Extract general information
name = str(form_data.get("name", ""))
lastname = str(form_data.get("lastname", ""))
title_field = str(form_data.get("title", ""))
mail = str(form_data.get("mail", ""))
subject = str(form_data.get("subject", ""))
classname = str(form_data.get("classname", ""))
usage_date_from = str(form_data.get("usage_date_from", ""))
usage_date_to = str(form_data.get("usage_date_to", ""))
availability_date = str(form_data.get("availability_date", ""))
message = str(form_data.get("message", ""))
# Basic email validation
if not EMAIL_REGEX.match(mail):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid email address format.",
)
# Build XML structure
root = Element("elsa_submission")
# General information
general_info = SubElement(root, "general_info")
SubElement(general_info, "name").text = name
SubElement(general_info, "lastname").text = lastname
SubElement(general_info, "title").text = title_field
SubElement(general_info, "mail").text = mail
SubElement(general_info, "subject").text = subject
SubElement(general_info, "classname").text = classname
SubElement(general_info, "usage_date_from").text = usage_date_from
SubElement(general_info, "usage_date_to").text = usage_date_to
SubElement(general_info, "availability_date").text = availability_date
if message:
SubElement(general_info, "message").text = message
# Process media sections
media_root = SubElement(root, "media")
# Process Monografie entries
if "monografie_author[]" in form_data:
monografie_authors = [str(v) for v in form_data.getlist("monografie_author[]")]
monografie_years = [str(v) for v in form_data.getlist("monografie_year[]")]
monografie_editions = [
str(v) for v in form_data.getlist("monografie_edition[]")
]
monografie_titles = [str(v) for v in form_data.getlist("monografie_title[]")]
monografie_signatures = [
str(v) for v in form_data.getlist("monografie_signature[]")
]
monografie_pages_from = [
str(v) for v in form_data.getlist("monografie_pages_from[]")
]
monografie_pages_to = [
str(v) for v in form_data.getlist("monografie_pages_to[]")
]
# Get section IDs from the form (assuming they're in data-section attributes)
# Since we can't directly access data-section from form_data, we'll process sequentially
monografie_section = SubElement(media_root, "monografien")
for i in range(len(monografie_authors)):
entry = SubElement(monografie_section, "entry")
SubElement(entry, "author").text = (
monografie_authors[i] if i < len(monografie_authors) else ""
)
SubElement(entry, "year").text = (
monografie_years[i] if i < len(monografie_years) else ""
)
SubElement(entry, "edition").text = (
monografie_editions[i] if i < len(monografie_editions) else ""
)
SubElement(entry, "title").text = (
monografie_titles[i] if i < len(monografie_titles) else ""
)
SubElement(entry, "signature").text = (
monografie_signatures[i] if i < len(monografie_signatures) else ""
)
SubElement(entry, "pages_from").text = (
monografie_pages_from[i] if i < len(monografie_pages_from) else ""
)
SubElement(entry, "pages_to").text = (
monografie_pages_to[i] if i < len(monografie_pages_to) else ""
)
# Process Zeitschriftenartikel entries
if "zeitschrift_author[]" in form_data:
zeitschrift_authors = [
str(v) for v in form_data.getlist("zeitschrift_author[]")
]
zeitschrift_years = [str(v) for v in form_data.getlist("zeitschrift_year[]")]
zeitschrift_volumes = [
str(v) for v in form_data.getlist("zeitschrift_volume[]")
]
zeitschrift_article_titles = [
str(v) for v in form_data.getlist("zeitschrift_article_title[]")
]
zeitschrift_journal_titles = [
str(v) for v in form_data.getlist("zeitschrift_journal_title[]")
]
zeitschrift_signatures = [
str(v) for v in form_data.getlist("zeitschrift_signature[]")
]
zeitschrift_pages_from = [
str(v) for v in form_data.getlist("zeitschrift_pages_from[]")
]
zeitschrift_pages_to = [
str(v) for v in form_data.getlist("zeitschrift_pages_to[]")
]
zeitschrift_section = SubElement(media_root, "zeitschriftenartikel")
for i in range(len(zeitschrift_authors)):
entry = SubElement(zeitschrift_section, "entry")
SubElement(entry, "author").text = (
zeitschrift_authors[i] if i < len(zeitschrift_authors) else ""
)
SubElement(entry, "year").text = (
zeitschrift_years[i] if i < len(zeitschrift_years) else ""
)
SubElement(entry, "volume").text = (
zeitschrift_volumes[i] if i < len(zeitschrift_volumes) else ""
)
SubElement(entry, "article_title").text = (
zeitschrift_article_titles[i]
if i < len(zeitschrift_article_titles)
else ""
)
SubElement(entry, "journal_title").text = (
zeitschrift_journal_titles[i]
if i < len(zeitschrift_journal_titles)
else ""
)
SubElement(entry, "signature").text = (
zeitschrift_signatures[i] if i < len(zeitschrift_signatures) else ""
)
SubElement(entry, "pages_from").text = (
zeitschrift_pages_from[i] if i < len(zeitschrift_pages_from) else ""
)
SubElement(entry, "pages_to").text = (
zeitschrift_pages_to[i] if i < len(zeitschrift_pages_to) else ""
)
# Process Herausgeberwerk entries
if "herausgeber_publisher[]" in form_data:
herausgeber_publishers = [
str(v) for v in form_data.getlist("herausgeber_publisher[]")
]
herausgeber_work_titles = [
str(v) for v in form_data.getlist("herausgeber_work_title[]")
]
herausgeber_years = [str(v) for v in form_data.getlist("herausgeber_year[]")]
herausgeber_editions = [
str(v) for v in form_data.getlist("herausgeber_edition[]")
]
herausgeber_article_authors = [
str(v) for v in form_data.getlist("herausgeber_article_author[]")
]
herausgeber_article_titles = [
str(v) for v in form_data.getlist("herausgeber_article_title[]")
]
herausgeber_signatures = [
str(v) for v in form_data.getlist("herausgeber_signature[]")
]
herausgeber_pages_from = [
str(v) for v in form_data.getlist("herausgeber_pages_from[]")
]
herausgeber_pages_to = [
str(v) for v in form_data.getlist("herausgeber_pages_to[]")
]
herausgeber_section = SubElement(media_root, "herausgeberwerke")
for i in range(len(herausgeber_publishers)):
entry = SubElement(herausgeber_section, "entry")
SubElement(entry, "publisher").text = (
herausgeber_publishers[i] if i < len(herausgeber_publishers) else ""
)
SubElement(entry, "work_title").text = (
herausgeber_work_titles[i] if i < len(herausgeber_work_titles) else ""
)
SubElement(entry, "year").text = (
herausgeber_years[i] if i < len(herausgeber_years) else ""
)
SubElement(entry, "edition").text = (
herausgeber_editions[i] if i < len(herausgeber_editions) else ""
)
SubElement(entry, "article_author").text = (
herausgeber_article_authors[i]
if i < len(herausgeber_article_authors)
else ""
)
SubElement(entry, "article_title").text = (
herausgeber_article_titles[i]
if i < len(herausgeber_article_titles)
else ""
)
SubElement(entry, "signature").text = (
herausgeber_signatures[i] if i < len(herausgeber_signatures) else ""
)
SubElement(entry, "pages_from").text = (
herausgeber_pages_from[i] if i < len(herausgeber_pages_from) else ""
)
SubElement(entry, "pages_to").text = (
herausgeber_pages_to[i] if i < len(herausgeber_pages_to) else ""
)
xml_data = tostring(root, encoding="unicode")
# Send or print email
msg = MIMEText(xml_data, "xml")
msg["Subject"] = "New ELSA Form Submission"
msg["From"] = MAIL_FROM
msg["To"] = MAIL_TO
if MAIL_ENABLED:
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.send_message(msg)
else:
print("=" * 80)
print("MAIL SENDING DISABLED - Would have sent:")
print(f"From: {MAIL_FROM}")
print(f"To: {MAIL_TO}")
print(f"Subject: {msg['Subject']}")
print("-" * 80)
print(xml_data)
print("=" * 80)
return RedirectResponse("/?success=true", status_code=303)