From 51e3e2a39cbe6731d847cf4cc4f0ec987373a28c Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 19 Nov 2025 14:10:22 +0100 Subject: [PATCH] feat: add elsa form with a modular addition of content type --- app/main.py | 241 ++++++++++++++++++++++- app/static/styles.css | 127 ++++++++++++ app/templates/elsa_mono_form.html | 310 ++++++++++++++++++++++++++++++ app/templates/index.html | 8 +- 4 files changed, 681 insertions(+), 5 deletions(-) create mode 100644 app/templates/elsa_mono_form.html diff --git a/app/main.py b/app/main.py index 5d7a1e9..2ea2be1 100755 --- a/app/main.py +++ b/app/main.py @@ -38,6 +38,12 @@ async def semesterapparat_form(request: Request): 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.post("/submit") async def handle_form( request: Request, @@ -62,7 +68,7 @@ async def handle_form( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid email address format.", ) - + # Build XML root = Element("form_submission") @@ -107,3 +113,236 @@ async def handle_form( 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) diff --git a/app/static/styles.css b/app/static/styles.css index 60e5080..33b94b8 100644 --- a/app/static/styles.css +++ b/app/static/styles.css @@ -598,3 +598,130 @@ input[type="radio"] { accent-color: var(--control-accent); } transform: translateY(0); } } + +/* ELSA specific styles */ +.legal-notice { + background: #eff6ff; + border: 1px solid #93c5fd; + border-radius: 12px; + padding: 16px 20px; + margin-bottom: 24px; +} + +[data-theme="dark"] .legal-notice { + background: #0f2942; + border-color: #1e3a52; +} + +.legal-notice h3 { + display: flex; + align-items: center; + gap: 8px; + margin: 0 0 8px; + font-size: 1rem; + color: #1e40af; +} + +[data-theme="dark"] .legal-notice h3 { + color: #93c5fd; +} + +.legal-notice p { + margin: 0; + line-height: 1.6; + font-size: 0.95rem; + color: var(--text); +} + +.media-controls { + display: flex; + gap: 12px; + flex-wrap: wrap; + margin-bottom: 20px; +} + +.media-section { + background: var(--table-head-bg); + border: 1px solid var(--border); + border-radius: 12px; + padding: 20px; + margin-bottom: 20px; +} + +.media-section-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; +} + +.media-section-header h3 { + display: flex; + align-items: center; + gap: 8px; + margin: 0; + font-size: 1.15rem; +} + +.btn-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + padding: 0; + border-radius: 8px; + border: 1px solid var(--border); + background: var(--card-bg); + color: var(--text); + cursor: pointer; + transition: background-color 0.2s ease, color 0.2s ease; +} + +.btn-icon:hover { + background: var(--table-head-bg); + color: #ef4444; +} + +.btn-sm { + font-size: 0.9rem; + padding: 8px 14px; +} + +.media-table { + margin-bottom: 12px; +} + +.media-table th { + font-size: 0.85rem; + white-space: nowrap; +} + +.media-table td { + padding: 8px 10px; +} + +.media-table input[type="text"], +.media-table input[type="number"] { + font-size: 0.9rem; + padding: 8px 10px; +} + +.media-table input[type="number"] { + width: 80px; +} + +@media (max-width: 1024px) { + .media-controls { + flex-direction: column; + } + + .media-table { + font-size: 0.85rem; + } + + .media-table th, + .media-table td { + padding: 6px 8px; + } +} diff --git a/app/templates/elsa_mono_form.html b/app/templates/elsa_mono_form.html new file mode 100644 index 0000000..1f1a081 --- /dev/null +++ b/app/templates/elsa_mono_form.html @@ -0,0 +1,310 @@ + + + + ELSA - Elektronischer Semesterapparat + + + + + + + +
+
+

ELSA - Elektronischer Semesterapparat

+ + + +
+

Allgemeine Informationen

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +

Medien

+
+ + + +
+ +
+ +
+ + +
+ +
+ +
+
+
+
+ + + + diff --git a/app/templates/index.html b/app/templates/index.html index aed8ab4..b3f7e70 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -40,16 +40,16 @@ -
+

ELSA

Elektronischer Semesterapparat

-
- Im Aufbau +
+
-
+