107 lines
3.0 KiB
Python
107 lines
3.0 KiB
Python
"""Server-side data for the Lukenya Marathon dashboard page."""
|
|
|
|
import frappe
|
|
from frappe.utils import add_days, flt, getdate, nowdate
|
|
|
|
CURRENCY = "KES"
|
|
PAID_STATUSES = ("Paid", "Reconciled")
|
|
|
|
|
|
def _count(doctype, filters=None):
|
|
if not frappe.db.exists("DocType", doctype):
|
|
return 0
|
|
return frappe.db.count(doctype, filters or {})
|
|
|
|
|
|
def _created_between(doctype, start, end):
|
|
return _count(doctype, {"creation": ["between", [start, end]]})
|
|
|
|
|
|
def _revenue(start=None, end=None):
|
|
"""Sum of paid/reconciled payments, optionally limited to a date window."""
|
|
if not frappe.db.exists("DocType", "Payment Transaction"):
|
|
return 0.0
|
|
|
|
values = {"paid": PAID_STATUSES}
|
|
window = ""
|
|
if start and end:
|
|
window = "and date(coalesce(payment_date, creation)) between %(start)s and %(end)s"
|
|
values.update(start=start, end=end)
|
|
|
|
result = frappe.db.sql(
|
|
f"""
|
|
select coalesce(sum(amount), 0)
|
|
from `tabPayment Transaction`
|
|
where status in %(paid)s {window}
|
|
""",
|
|
values,
|
|
)
|
|
return flt(result[0][0])
|
|
|
|
|
|
def _trend(current, previous):
|
|
"""Percent change against the previous period, or None if there is nothing to compare."""
|
|
if not previous:
|
|
return None
|
|
return round((flt(current) - flt(previous)) / flt(previous) * 100)
|
|
|
|
|
|
def _current_edition():
|
|
fields = ["name", "edition_name", "event_date", "status"]
|
|
edition = frappe.db.get_value(
|
|
"Event Edition", {"status": "Open"}, fields, as_dict=True, order_by="event_date desc"
|
|
)
|
|
return edition or frappe.db.get_value(
|
|
"Event Edition", {}, fields, as_dict=True, order_by="event_date desc"
|
|
)
|
|
|
|
|
|
@frappe.whitelist()
|
|
def get_dashboard_stats():
|
|
frappe.only_for("System Manager")
|
|
|
|
today = getdate(nowdate())
|
|
recent_start, recent_end = add_days(today, -29), today
|
|
prior_start, prior_end = add_days(today, -59), add_days(today, -30)
|
|
|
|
participants_recent = _created_between("Participant", recent_start, recent_end)
|
|
participants_prior = _created_between("Participant", prior_start, prior_end)
|
|
|
|
editions_recent = _created_between("Event Edition", recent_start, recent_end)
|
|
editions_prior = _created_between("Event Edition", prior_start, prior_end)
|
|
|
|
revenue_recent = _revenue(recent_start, recent_end)
|
|
revenue_prior = _revenue(prior_start, prior_end)
|
|
|
|
return {
|
|
"currency": CURRENCY,
|
|
"edition": _current_edition(),
|
|
"kpis": {
|
|
"participants": {
|
|
"value": _count("Participant"),
|
|
"trend": _trend(participants_recent, participants_prior),
|
|
},
|
|
"editions": {
|
|
"value": _count("Event Edition"),
|
|
"trend": _trend(editions_recent, editions_prior),
|
|
},
|
|
"revenue": {
|
|
"value": _revenue(),
|
|
"trend": _trend(revenue_recent, revenue_prior),
|
|
},
|
|
"categories": {
|
|
"value": _count("Race Category", {"active": 1}),
|
|
"trend": None,
|
|
},
|
|
},
|
|
"tiles": {
|
|
"event_setup": _count("Event Edition"),
|
|
"race_categories": _count("Race Category"),
|
|
"registrations": _count("Registration"),
|
|
"payments": _count("Payment Transaction"),
|
|
"race_day": _count("Bib Allocation"),
|
|
"prizes": _count("Prize Award"),
|
|
"volunteers": _count("Volunteer"),
|
|
},
|
|
}
|