feat(ui): modern theme, dark/light toggle, polished templates
This commit is contained in:
129
app/routes.py
Normal file
129
app/routes.py
Normal file
@@ -0,0 +1,129 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
from typing import Dict, Any
|
||||
|
||||
from flask import Blueprint, current_app, render_template, request, redirect, url_for, jsonify, flash
|
||||
|
||||
from .clients.zammad import ZammadClient, ZammadError
|
||||
from .services import aggregations as agg
|
||||
|
||||
|
||||
web_bp = Blueprint("web", __name__)
|
||||
|
||||
|
||||
def _client() -> ZammadClient:
|
||||
return ZammadClient(current_app.config["ZAMMAD_URL"], current_app.config["ZAMMAD_TOKEN"])
|
||||
|
||||
|
||||
@web_bp.route("/")
|
||||
def index():
|
||||
return render_template(
|
||||
"index.html",
|
||||
default_date_from=current_app.config["DEFAULT_DATE_FROM"],
|
||||
default_date_to=current_app.config["DEFAULT_DATE_TO"],
|
||||
)
|
||||
|
||||
|
||||
AVAILABLE_GROUPS: Dict[str, Dict[str, Any]] = {
|
||||
"overview": {"title": "Общее количество тикетов"},
|
||||
"by_agent": {"title": "Тикеты по агентам"},
|
||||
"by_group": {"title": "Тикеты по группам"},
|
||||
"by_state": {"title": "Тикеты по статусам"},
|
||||
"open_closed": {"title": "Открытые vs Закрытые"},
|
||||
"by_priority": {"title": "Тикеты по приоритетам"},
|
||||
"by_day": {"title": "Динамика: тикеты по дням"},
|
||||
}
|
||||
|
||||
|
||||
@web_bp.get("/report")
|
||||
def report():
|
||||
date_from = request.args.get("date_from")
|
||||
date_to = request.args.get("date_to")
|
||||
group_by = request.args.get("group_by", "overview")
|
||||
|
||||
if not date_from or not date_to:
|
||||
flash("Укажите период дат.", "warning")
|
||||
return redirect(url_for("web:index"))
|
||||
|
||||
if group_by not in AVAILABLE_GROUPS:
|
||||
group_by = "overview"
|
||||
|
||||
z = _client()
|
||||
try:
|
||||
tickets = z.search_tickets(date_from, date_to)
|
||||
except ZammadError as e:
|
||||
flash(f"Ошибка Zammad API: {e}", "danger")
|
||||
return redirect(url_for("web:index"))
|
||||
|
||||
overview = agg.summarize_overview(tickets, z)
|
||||
|
||||
# Determine main chart
|
||||
if group_by == "by_agent":
|
||||
data = agg.by_agent(tickets, z)
|
||||
elif group_by == "by_group":
|
||||
data = agg.by_group(tickets, z)
|
||||
elif group_by == "by_state":
|
||||
data = agg.by_state(tickets, z)
|
||||
elif group_by == "open_closed":
|
||||
data = agg.by_open_closed(tickets, z)
|
||||
elif group_by == "by_priority":
|
||||
data = agg.by_priority(tickets, z)
|
||||
elif group_by == "by_day":
|
||||
data = agg.by_day_created(tickets)
|
||||
else:
|
||||
data = {"Всего": overview["total"]}
|
||||
|
||||
labels, values = agg.dict_to_series(data)
|
||||
title = AVAILABLE_GROUPS[group_by]["title"]
|
||||
|
||||
# Supplemental tables
|
||||
by_agent = sorted(agg.by_agent(tickets, z).items(), key=lambda x: x[1], reverse=True)
|
||||
by_group = sorted(agg.by_group(tickets, z).items(), key=lambda x: x[1], reverse=True)
|
||||
|
||||
return render_template(
|
||||
"report.html",
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
group_by=group_by,
|
||||
title=title,
|
||||
chart_labels=labels,
|
||||
chart_values=values,
|
||||
overview=overview,
|
||||
by_agent=by_agent,
|
||||
by_group=by_group,
|
||||
)
|
||||
|
||||
|
||||
@web_bp.get("/api/report.json")
|
||||
def report_json():
|
||||
date_from = request.args.get("date_from")
|
||||
date_to = request.args.get("date_to")
|
||||
group_by = request.args.get("group_by", "overview")
|
||||
if not date_from or not date_to:
|
||||
return jsonify({"error": "date_from and date_to are required"}), 400
|
||||
|
||||
z = _client()
|
||||
tickets = z.search_tickets(date_from, date_to)
|
||||
|
||||
overview = agg.summarize_overview(tickets, z)
|
||||
payload: Dict[str, Any] = {"overview": overview}
|
||||
|
||||
payload.update(
|
||||
{
|
||||
"by_agent": agg.by_agent(tickets, z),
|
||||
"by_group": agg.by_group(tickets, z),
|
||||
"by_state": agg.by_state(tickets, z),
|
||||
"open_closed": agg.by_open_closed(tickets, z),
|
||||
"by_priority": agg.by_priority(tickets, z),
|
||||
"by_day": agg.by_day_created(tickets),
|
||||
}
|
||||
)
|
||||
|
||||
# Optional: main_group result
|
||||
if group_by in payload:
|
||||
labels, values = agg.dict_to_series(payload[group_by])
|
||||
payload["chart"] = {"labels": labels, "values": values}
|
||||
|
||||
return jsonify(payload)
|
||||
|
||||
Reference in New Issue
Block a user