122 lines
5.1 KiB
Python
122 lines
5.1 KiB
Python
"""Дашборд участника — персональный экран после входа (§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,
|
||
)
|