API, holiday indicators; UI polish; bump version Dashboard: Add Syncfusion academic period dropdown next to group selector Navigate scheduler to today's month/day within selected period year on change Show adjacent holiday plan badge; keep "holidays in view" counter on the right Compact dropdown widths for a tighter toolbar Default blocking of scheduling on holidays; block entries styled like all-day; black text styling API: Add academic periods routes: list, get active, set active (POST), for_date Register blueprint in wsgi Holidays: Support TXT/CSV upload; headerless TXT uses columns 2-4; region remains null Docs: Update shared Copilot instructions with academic periods endpoints and dashboard integration details
85 lines
2.7 KiB
Python
85 lines
2.7 KiB
Python
from flask import Blueprint, jsonify, request
|
|
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'])
|
|
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()
|