"""Дашборд участника — персональный экран после входа (§3.6 ТЗ).""" from datetime import datetime, timezone from flask import render_template, session from sqlmodel import Session, select from ..db import engine from ..models import ( Event, Project, ProjectMember, ProjectTask, Workshop, WorkshopMaterial, WorkshopRegistration, Notification, ) from ..helpers import login_required from .. import gitea_api # Приоритеты в порядке важности (0 = самый важный) _PRIORITY_ORDER = {"critical": 0, "high": 1, "medium": 2, "low": 3} def register(app): @app.route("/dashboard") @login_required def dashboard(): user_id = session.get("user_id") now = datetime.now(timezone.utc) with Session(engine) as db_session: # ── 1. Ближайшие события (5) ───────────────────────────── upcoming_events = db_session.exec( select(Event) .where(Event.is_public == True) .where(Event.start_time >= now) .order_by(Event.start_time) .limit(5) ).all() # ── 2. Мои проекты ──────────────────────────────────────── my_projects = db_session.exec( select(Project, ProjectMember) .join(ProjectMember, ProjectMember.project_id == Project.id) .where(ProjectMember.user_id == user_id) .order_by(Project.created_at.desc()) ).all() # ── 3. Мои задачи (не done, топ-10) ─────────────────────── my_tasks_raw = list(db_session.exec( select(ProjectTask, Project) .join(Project, ProjectTask.project_id == Project.id) .where(ProjectTask.assignee_id == user_id) .where(ProjectTask.status != "done") ).all()) # Сортируем в Python: по приоритету (critical → low), потом по due_date my_tasks_raw.sort(key=lambda row: ( _PRIORITY_ORDER.get(row[0].priority, 4), row[0].due_date or "9999-12-31", )) my_tasks = my_tasks_raw[:10] # ── 4. Непрочитанные уведомления (10) ──────────────────── unread = db_session.exec( select(Notification) .where(Notification.user_id == user_id) .where(Notification.is_read == False) .order_by(Notification.created_at.desc()) .limit(10) ).all() unread_count = len(unread) # ── 5. Новые материалы МК (где записан, 5) ─────────────── my_workshop_ids = [ r.workshop_id for r in db_session.exec( select(WorkshopRegistration) .where(WorkshopRegistration.user_id == user_id) .where(WorkshopRegistration.status.in_(("registered", "attended"))) ).all() ] new_materials = [] if my_workshop_ids: new_materials = db_session.exec( select(WorkshopMaterial, Workshop) .join(Workshop, WorkshopMaterial.workshop_id == Workshop.id) .where(WorkshopMaterial.workshop_id.in_(my_workshop_ids)) .order_by(WorkshopMaterial.created_at.desc()) .limit(5) ).all() # ── 6. Gitea-коммиты (топ-3 проекта с repo, по 3 коммита) ─ gitea_widget = [] for proj, _member in my_projects[:10]: repo = proj.gitea_repo or ( gitea_api.extract_repo_from_url(proj.gitea_url) if proj.gitea_url else None ) if not repo or "/" not in repo: continue try: owner, name = repo.split("/", 1) commits = gitea_api.get_latest_commits(owner, name, limit=3) if commits: gitea_widget.append({ "project_id": proj.id, "project_title": proj.title, "commits": commits, }) except Exception as e: print(f"[dashboard] gitea {repo}: {e}", flush=True) if len(gitea_widget) >= 3: break return render_template( "dashboard.html", upcoming_events=upcoming_events, my_projects=my_projects, my_tasks=my_tasks, unread=unread, unread_count=unread_count, new_materials=new_materials, gitea_widget=gitea_widget, )