Files
infoscreen/server/routes/conversions.py
RobbStarkAustria a7df3c2708 feat(dashboard): header user dropdown (Syncfusion) + proper logout; docs: clarify architecture; build: add splitbuttons; bump alpha.10
Dashboard

Add top-right user dropdown using Syncfusion DropDownButton: shows username + role; menu entries “Profil” and “Abmelden”.
Replace custom dropdown logic with Syncfusion component; position at header’s right edge.
Update /logout page to call backend logout and redirect to /login (reliable user switching).
Build/Config

Add @syncfusion/ej2-react-splitbuttons and @syncfusion/ej2-splitbuttons dependencies.
Update Vite optimizeDeps.include to pre-bundle splitbuttons and avoid import-analysis errors.
Docs

README: Rework Architecture Overview with clearer data flow:
Listener consumes MQTT (discovery/heartbeats) and updates API.
Scheduler reads from API and publishes events via MQTT to clients.
Clients send via MQTT and receive via MQTT.
Worker receives commands directly from API and reports results back (no MQTT).
Explicit note: MariaDB is accessed exclusively by the API Server; Dashboard never talks to DB directly.
README: Add SplitButtons to “Syncfusion Components Used”; add troubleshooting steps for @syncfusion/ej2-react-splitbuttons import issues (optimizeDeps + volume reset).
Copilot instructions: Document header user menu and splitbuttons technical notes (deps, optimizeDeps, dev-container node_modules volume).
Program info

Bump to 2025.1.0-alpha.10 with changelog:
UI: Header user menu (DropDownButton with username/role; Profil/Abmelden).
Frontend: Syncfusion SplitButtons integration + Vite pre-bundling config.
Fix: Added README guidance for splitbuttons import errors.
No breaking changes.
2025-10-15 16:33:35 +00:00

97 lines
3.4 KiB
Python

from flask import Blueprint, jsonify, request
from server.permissions import editor_or_higher
from server.database import Session
from models.models import Conversion, ConversionStatus, EventMedia, MediaType
from server.task_queue import get_queue
from server.worker import convert_event_media_to_pdf
from datetime import datetime, timezone
import hashlib
conversions_bp = Blueprint("conversions", __name__,
url_prefix="/api/conversions")
def sha256_file(abs_path: str) -> str:
h = hashlib.sha256()
with open(abs_path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
@conversions_bp.route("/<int:media_id>/pdf", methods=["POST"])
@editor_or_higher
def ensure_conversion(media_id: int):
session = Session()
try:
media = session.query(EventMedia).get(media_id)
if not media or not media.file_path:
return jsonify({"error": "Media not found or no file"}), 404
# Only enqueue for office presentation formats
if media.media_type not in {MediaType.ppt, MediaType.pptx, MediaType.odp}:
return jsonify({"message": "No conversion required for this media_type"}), 200
# Compute file hash
import os
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
media_root = os.path.join(base_dir, "media")
abs_source = os.path.join(media_root, media.file_path)
file_hash = sha256_file(abs_source)
# Find or create conversion row
conv = (
session.query(Conversion)
.filter_by(
source_event_media_id=media.id,
target_format="pdf",
file_hash=file_hash,
)
.one_or_none()
)
if not conv:
conv = Conversion(
source_event_media_id=media.id,
target_format="pdf",
status=ConversionStatus.pending,
file_hash=file_hash,
)
session.add(conv)
session.commit()
# Enqueue if not already processing/ready
if conv.status in {ConversionStatus.pending, ConversionStatus.failed}:
q = get_queue()
job = q.enqueue(convert_event_media_to_pdf, conv.id)
return jsonify({"id": conv.id, "status": conv.status.value, "job_id": job.get_id()}), 202
else:
return jsonify({"id": conv.id, "status": conv.status.value, "target_path": conv.target_path}), 200
finally:
session.close()
@conversions_bp.route("/<int:media_id>/status", methods=["GET"])
def conversion_status(media_id: int):
session = Session()
try:
conv = (
session.query(Conversion)
.filter_by(source_event_media_id=media_id, target_format="pdf")
.order_by(Conversion.id.desc())
.first()
)
if not conv:
return jsonify({"status": "missing"}), 404
return jsonify(
{
"id": conv.id,
"status": conv.status.value,
"target_path": conv.target_path,
"started_at": conv.started_at.isoformat() if conv.started_at else None,
"completed_at": conv.completed_at.isoformat() if conv.completed_at else None,
"error_message": conv.error_message,
}
)
finally:
session.close()