130 lines
4.0 KiB
Python
130 lines
4.0 KiB
Python
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)
|
||
|