Backend: generate EventException on create/update when skip_holidays or recurrence changes; emit RecurrenceException (EXDATE) with exact occurrence start time (UTC) API: return master events with RecurrenceRule + RecurrenceException Frontend: map RecurrenceException → recurrenceException; ensure SkipHolidays instances never render on holidays; place TentTree icon (black) next to main event icon via template Docs: update README and Copilot instructions for recurrence/holiday behavior Cleanup: remove dataSource and debug console logs
114 lines
4.7 KiB
Python
114 lines
4.7 KiB
Python
from flask import Blueprint, request, jsonify
|
|
from server.database import Session
|
|
from models.models import EventException, Event
|
|
from datetime import datetime, date
|
|
|
|
event_exceptions_bp = Blueprint("event_exceptions", __name__, url_prefix="/api/event_exceptions")
|
|
|
|
|
|
@event_exceptions_bp.route("", methods=["POST"])
|
|
def create_exception():
|
|
data = request.json
|
|
session = Session()
|
|
# required: event_id, exception_date
|
|
required = ["event_id", "exception_date"]
|
|
for f in required:
|
|
if f not in data:
|
|
return jsonify({"error": f"Missing field: {f}"}), 400
|
|
|
|
# Validate event exists
|
|
event = session.query(Event).filter_by(id=data["event_id"]).first()
|
|
if not event:
|
|
session.close()
|
|
return jsonify({"error": "Event not found"}), 404
|
|
|
|
exc_date = datetime.fromisoformat(data["exception_date"]).date() if isinstance(data["exception_date"], str) else data["exception_date"]
|
|
# Check if an exception for this event and date already exists
|
|
existing_exc = session.query(EventException).filter_by(event_id=event.id, exception_date=exc_date).first()
|
|
if existing_exc:
|
|
# Optionally, update the existing exception fields if needed
|
|
existing_exc.is_skipped = bool(data.get("is_skipped", existing_exc.is_skipped))
|
|
existing_exc.override_title = data.get("override_title", existing_exc.override_title)
|
|
existing_exc.override_description = data.get("override_description", existing_exc.override_description)
|
|
existing_exc.override_start = datetime.fromisoformat(data["override_start"]) if data.get("override_start") else existing_exc.override_start
|
|
existing_exc.override_end = datetime.fromisoformat(data["override_end"]) if data.get("override_end") else existing_exc.override_end
|
|
session.commit()
|
|
return jsonify({"success": True, "id": existing_exc.id, "updated": True})
|
|
# Otherwise, create a new exception
|
|
exc = EventException(
|
|
event_id=event.id,
|
|
exception_date=exc_date,
|
|
is_skipped=bool(data.get("is_skipped", False)),
|
|
override_title=data.get("override_title"),
|
|
override_description=data.get("override_description"),
|
|
override_start=(datetime.fromisoformat(data["override_start"]) if data.get("override_start") else None),
|
|
override_end=(datetime.fromisoformat(data["override_end"]) if data.get("override_end") else None),
|
|
)
|
|
session.add(exc)
|
|
session.commit()
|
|
return jsonify({"success": True, "id": exc.id})
|
|
|
|
|
|
@event_exceptions_bp.route("/<exc_id>", methods=["PUT"])
|
|
def update_exception(exc_id):
|
|
data = request.json
|
|
session = Session()
|
|
exc = session.query(EventException).filter_by(id=exc_id).first()
|
|
if not exc:
|
|
session.close()
|
|
return jsonify({"error": "Exception not found"}), 404
|
|
|
|
if "exception_date" in data:
|
|
exc.exception_date = datetime.fromisoformat(data["exception_date"]).date()
|
|
if "is_skipped" in data:
|
|
exc.is_skipped = bool(data["is_skipped"])
|
|
if "override_title" in data:
|
|
exc.override_title = data.get("override_title")
|
|
if "override_description" in data:
|
|
exc.override_description = data.get("override_description")
|
|
if "override_start" in data:
|
|
exc.override_start = datetime.fromisoformat(data["override_start"]) if data.get("override_start") else None
|
|
if "override_end" in data:
|
|
exc.override_end = datetime.fromisoformat(data["override_end"]) if data.get("override_end") else None
|
|
|
|
session.commit()
|
|
session.close()
|
|
return jsonify({"success": True})
|
|
|
|
|
|
@event_exceptions_bp.route("/<exc_id>", methods=["DELETE"])
|
|
def delete_exception(exc_id):
|
|
session = Session()
|
|
exc = session.query(EventException).filter_by(id=exc_id).first()
|
|
if not exc:
|
|
session.close()
|
|
return jsonify({"error": "Exception not found"}), 404
|
|
session.delete(exc)
|
|
session.commit()
|
|
session.close()
|
|
return jsonify({"success": True})
|
|
|
|
|
|
@event_exceptions_bp.route("", methods=["GET"])
|
|
def list_exceptions():
|
|
session = Session()
|
|
event_id = request.args.get("event_id")
|
|
q = session.query(EventException)
|
|
if event_id:
|
|
q = q.filter(EventException.event_id == int(event_id))
|
|
rows = q.all()
|
|
out = []
|
|
for r in rows:
|
|
out.append({
|
|
"id": r.id,
|
|
"event_id": r.event_id,
|
|
"exception_date": r.exception_date.isoformat(),
|
|
"is_skipped": r.is_skipped,
|
|
"override_title": r.override_title,
|
|
"override_description": r.override_description,
|
|
"override_start": r.override_start.isoformat() if r.override_start else None,
|
|
"override_end": r.override_end.isoformat() if r.override_end else None,
|
|
})
|
|
session.close()
|
|
return jsonify(out)
|