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.
87 lines
2.8 KiB
Python
87 lines
2.8 KiB
Python
from flask import Blueprint, jsonify, request
|
|
from server.permissions import admin_or_higher
|
|
from server.database import Session
|
|
from models.models import AcademicPeriod
|
|
from datetime import datetime
|
|
|
|
academic_periods_bp = Blueprint(
|
|
'academic_periods', __name__, url_prefix='/api/academic_periods')
|
|
|
|
|
|
@academic_periods_bp.route('', methods=['GET'])
|
|
def list_academic_periods():
|
|
session = Session()
|
|
try:
|
|
periods = session.query(AcademicPeriod).order_by(
|
|
AcademicPeriod.start_date.asc()).all()
|
|
return jsonify({
|
|
'periods': [p.to_dict() for p in periods]
|
|
})
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
@academic_periods_bp.route('/active', methods=['GET'])
|
|
def get_active_academic_period():
|
|
session = Session()
|
|
try:
|
|
period = session.query(AcademicPeriod).filter(
|
|
AcademicPeriod.is_active == True).first()
|
|
if not period:
|
|
return jsonify({'period': None}), 200
|
|
return jsonify({'period': period.to_dict()}), 200
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
@academic_periods_bp.route('/for_date', methods=['GET'])
|
|
def get_period_for_date():
|
|
"""
|
|
Returns the academic period that covers the provided date (YYYY-MM-DD).
|
|
If multiple match, prefer the one with the latest start_date.
|
|
"""
|
|
date_str = request.args.get('date')
|
|
if not date_str:
|
|
return jsonify({'error': 'Missing required query param: date (YYYY-MM-DD)'}), 400
|
|
try:
|
|
target = datetime.strptime(date_str, '%Y-%m-%d').date()
|
|
except ValueError:
|
|
return jsonify({'error': 'Invalid date format. Expected YYYY-MM-DD'}), 400
|
|
|
|
session = Session()
|
|
try:
|
|
period = (
|
|
session.query(AcademicPeriod)
|
|
.filter(AcademicPeriod.start_date <= target, AcademicPeriod.end_date >= target)
|
|
.order_by(AcademicPeriod.start_date.desc())
|
|
.first()
|
|
)
|
|
return jsonify({'period': period.to_dict() if period else None}), 200
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
@academic_periods_bp.route('/active', methods=['POST'])
|
|
@admin_or_higher
|
|
def set_active_academic_period():
|
|
data = request.get_json(silent=True) or {}
|
|
period_id = data.get('id')
|
|
if period_id is None:
|
|
return jsonify({'error': 'Missing required field: id'}), 400
|
|
session = Session()
|
|
try:
|
|
target = session.query(AcademicPeriod).get(period_id)
|
|
if not target:
|
|
return jsonify({'error': 'AcademicPeriod not found'}), 404
|
|
|
|
# Deactivate all, then activate target
|
|
session.query(AcademicPeriod).filter(AcademicPeriod.is_active == True).update(
|
|
{AcademicPeriod.is_active: False}
|
|
)
|
|
target.is_active = True
|
|
session.commit()
|
|
session.refresh(target)
|
|
return jsonify({'period': target.to_dict()}), 200
|
|
finally:
|
|
session.close()
|