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
8.1 KiB
8.1 KiB
Copilot instructions for infoscreen_2025
Use this as your shared context when proposing changes. Keep edits minimal and match existing patterns referenced below.
Big picture
- Multi-service app orchestrated by Docker Compose.
- API: Flask + SQLAlchemy (MariaDB), in
server/exposed on :8000 (health:/health). - Dashboard: React + Vite in
dashboard/, dev on :5173, served via Nginx in prod. - MQTT broker: Eclipse Mosquitto, config in
mosquitto/config/mosquitto.conf. - Listener: MQTT consumer handling discovery + heartbeats in
listener/listener.py. - Scheduler: Publishes active events (per group) to MQTT retained topics in
scheduler/scheduler.py. - Nginx: Reverse proxy routes
/api/*and/screenshots/*to API; everything else to dashboard (nginx.conf).
- API: Flask + SQLAlchemy (MariaDB), in
Service boundaries & data flow
- Database connection string is passed as
DB_CONN(mysql+pymysql) to Python services.- API builds its engine in
server/database.py(loads.envonly in development). - Scheduler loads
DB_CONNinscheduler/db_utils.py. - Listener also creates its own engine for writes to
clients.
- API builds its engine in
- MQTT topics (paho-mqtt v2, use Callback API v2):
- Discovery:
infoscreen/discovery(JSON includesuuid, hw/ip data). ACK toinfoscreen/{uuid}/discovery_ack. Seelistener/listener.py. - Heartbeat:
infoscreen/{uuid}/heartbeatupdatesClient.last_alive(UTC). - Event lists (retained):
infoscreen/events/{group_id}fromscheduler/scheduler.py. - Per-client group assignment (retained):
infoscreen/{uuid}/group_idviaserver/mqtt_helper.py.
- Discovery:
- Screenshots: server-side folders
server/received_screenshots/andserver/screenshots/; Nginx exposes/screenshots/{uuid}.jpgviaserver/wsgi.pyroute.
Data model highlights (see models/models.py)
- Enums:
EventType(presentation, website, video, message, webuntis),MediaType(file/website types), andAcademicPeriodType(schuljahr, semester, trimester). - Tables:
clients,client_groups,events,event_media,users,academic_periods,school_holidays. - Academic periods:
academic_periodstable supports educational institution cycles (school years, semesters). Events and media can be optionally linked viaacademic_period_id(nullable for backward compatibility). - Times are stored as timezone-aware; treat comparisons in UTC (see scheduler and routes/events).
API patterns
- Blueprints live in
server/routes/*and are registered inserver/wsgi.pywith/api/...prefixes. - Session usage: instantiate
Session()per request, commit when mutating, and alwayssession.close()before returning. - Examples:
- Clients:
server/routes/clients.pyincludes bulk group updates and MQTT sync (publish_multiple_client_groups). - Groups:
server/routes/groups.pycomputes “alive” using a grace period that varies byENV. - Events:
server/routes/events.pyserializes enum values to strings and normalizes times to UTC. - Media:
server/routes/eventmedia.pyimplements a simple file manager API rooted atserver/media/. - Academic periods:
server/routes/academic_periods.pyexposes:GET /api/academic_periods— list all periodsGET /api/academic_periods/active— currently active periodPOST /api/academic_periods/active— set active period (deactivates others)GET /api/academic_periods/for_date?date=YYYY-MM-DD— period covering given date
- Clients:
Frontend patterns (dashboard)
- Vite React app; proxies
/apiand/screenshotsto API in dev (vite.config.ts). - Uses Syncfusion components; Vite config pre-bundles specific packages to avoid alias issues.
- Environment:
VITE_API_URLprovided at build/run; in dev compose, proxy handles/apiso local fetches can use relative/api/...paths. - Scheduler (appointments page): top bar includes Group and Academic Period selectors (Syncfusion DropDownList). Selecting a period calls
POST /api/academic_periods/active, moves the calendar to today’s month/day within the period year, and refreshes a right-aligned indicator row showing:- Holidays present in the current view (count)
- Period label (display_name or name) with a badge indicating whether any holidays exist in that period (overlap check)
Local development
- Compose: development is
docker-compose.yml+docker-compose.override.yml.- API (dev):
server/Dockerfile.devwith debugpy on 5678, Flask appwsgi:appon :8000. - Dashboard (dev):
dashboard/Dockerfile.devexposes :5173 and waits for API viadashboard/wait-for-backend.sh. - Mosquitto: allows anonymous in dev; WebSocket on :9001.
- API (dev):
- Common env vars:
DB_CONN,DB_USER,DB_PASSWORD,DB_HOST=db,DB_NAME,ENV,MQTT_USER,MQTT_PASSWORD. - Alembic: prod compose runs
alembic ... upgrade headandserver/init_defaults.pybefore gunicorn.- Use
server/init_academic_periods.pyto populate default Austrian school years after migration.
- Use
Production
docker-compose.prod.ymluses prebuilt images (ghcr.io/robbstarkaustria/*).- Nginx serves dashboard and proxies API; TLS certs expected in
certs/and mounted to/etc/nginx/certs.
Environment variables (reference)
- DB_CONN — Preferred DB URL for services. Example:
mysql+pymysql://${DB_USER}:${DB_PASSWORD}@db/${DB_NAME} - DB_USER, DB_PASSWORD, DB_NAME, DB_HOST — Used to assemble DB_CONN in dev if missing; inside containers
DB_HOST=db. - ENV —
developmentorproduction; in development,server/database.pyloads.env. - MQTT_BROKER_HOST, MQTT_BROKER_PORT — Defaults
mqttand1883; MQTT_USER/MQTT_PASSWORD optional (dev often anonymous per Mosquitto config). - VITE_API_URL — Dashboard build-time base URL (prod); in dev the Vite proxy serves
/apitoserver:8000. - HEARTBEAT_GRACE_PERIOD_DEV / HEARTBEAT_GRACE_PERIOD_PROD — Groups “alive” window (defaults ~15s dev / 180s prod).
- REFRESH_SECONDS — Optional scheduler republish interval;
0disables periodic refresh.
Conventions & gotchas
- Always compare datetimes in UTC; some DB values may be naive—normalize before comparing (see
routes/events.py). - Use retained MQTT messages for state that clients must recover after reconnect (events per group, client group_id).
- In-container DB host is
db; do not uselocalhostinside services. - No separate dev vs prod secret conventions: use the same env var keys across environments (e.g.,
DB_CONN,MQTT_USER,MQTT_PASSWORD). - When adding a new route:
- Create a Blueprint in
server/routes/..., - Register it in
server/wsgi.py, - Manage
Session()lifecycle, and - Return JSON-safe values (serialize enums and datetimes).
- Create a Blueprint in
- When extending media types, update
MediaTypeand any logic ineventmediaand dashboard that depends on it. - Academic periods: Events/media can be optionally associated with periods for educational organization. Only one period should be active at a time (
is_active=True).
Quick examples
- Add client description persists to DB and publishes group via MQTT: see
PUT /api/clients/<uuid>/descriptioninroutes/clients.py. - Bulk group assignment emits retained messages for each client:
PUT /api/clients/group. - Listener heartbeat path:
infoscreen/<uuid>/heartbeat→ setsclients.last_alive.
Questions or unclear areas? Tell us if you need: exact devcontainer debugging steps, stricter Alembic workflow, or a seed dataset beyond init_defaults.py.
Academic Periods System
- Purpose: Organize events and media by educational cycles (school years, semesters, trimesters).
- Design: Fully backward compatible - existing events/media continue to work without period assignment.
- Usage: New events/media can optionally reference
academic_period_idfor better organization and filtering. - Constraints: Only one period can be active at a time; use
init_academic_periods.pyfor Austrian school year setup. - UI Integration: The dashboard highlights the currently selected period and whether a holiday plan exists within that date range. Holiday linkage currently uses date overlap with
school_holidays; an explicitacademic_period_idonschool_holidayscan be added later if tighter association is required.