feat(Метрики МП): top KPI cards, grid/list toggle, clickable cards with chart modal
- 4 top KPI cards (registrations, downloads by OS+total, MAU, DAU) from
drb_iliyas_telecomkz_daily_info + telecomkz_mau_stats; JSON gets top{} block
- grid/list view toggle (list = full-width cards), persisted in localStorage
- click any card -> modal with Chart.js line chart + per-month table; tooltip
shows monthly delta for both years; ESC/overlay/✕ to close
This commit is contained in:
parent
9d331db8a3
commit
b3ec72ca34
File diff suppressed because it is too large
Load Diff
@ -145,6 +145,121 @@ order by t.report_period_id
|
||||
""".strip()
|
||||
|
||||
|
||||
# ─── Верхние KPI: регистрации / скачивания / MAU / DAU ───
|
||||
TOP_SQL = """
|
||||
with t as (
|
||||
select report_period_id, entry_date, sum(registered) over (order by entry_date) as registered_total, sum(ios_installs) over (order by entry_date) as ios_installs, sum(android_installs) over (order by entry_date) as android_installs,
|
||||
sum(ios_installs+android_installs) over (order by entry_date) as sum_installs
|
||||
from drb.drb_iliyas_telecomkz_daily_info
|
||||
)
|
||||
, t1 as (
|
||||
select period as report_period_id,
|
||||
cast(date_add(DATE '1970-01-01', entry_date) as date) AS entry_date,
|
||||
dau_wo_none_plat as dau, mau_wo_none_plat as mau
|
||||
from drb.drb_iliyas_telecomkz_mau_stats
|
||||
where cast(date_add(DATE '1970-01-01', entry_date) as date) < cast(now() as date)
|
||||
)
|
||||
select t.report_period_id, t.entry_date, t.registered_total, t.ios_installs, t.android_installs, t.sum_installs, t1.mau, t1.dau
|
||||
from t
|
||||
left join t1 on t.entry_date = t1.entry_date
|
||||
""".strip()
|
||||
|
||||
TOP_TREND_MONTHS = 13 # сколько последних месяцев показывать в тренде верхних карточек
|
||||
|
||||
|
||||
def fetch_top(conn):
|
||||
cur = conn.cursor()
|
||||
log.info("Выполнение запроса верхних KPI...")
|
||||
cur.execute(TOP_SQL)
|
||||
rows = cur.fetchall()
|
||||
names = [d[0].lower() for d in cur.description]
|
||||
cur.close()
|
||||
idx = {n: i for i, n in enumerate(names)}
|
||||
|
||||
def g(r, name):
|
||||
v = r[idx[name]] if name in idx else None
|
||||
return v
|
||||
|
||||
recs = []
|
||||
for r in rows:
|
||||
recs.append({
|
||||
"rpid": int(g(r, "report_period_id")) if g(r, "report_period_id") is not None else None,
|
||||
"date": str(g(r, "entry_date")),
|
||||
"registered_total": g(r, "registered_total"),
|
||||
"ios_installs": g(r, "ios_installs"),
|
||||
"android_installs": g(r, "android_installs"),
|
||||
"sum_installs": g(r, "sum_installs"),
|
||||
"mau": g(r, "mau"),
|
||||
"dau": g(r, "dau"),
|
||||
})
|
||||
recs.sort(key=lambda x: x["date"])
|
||||
log.info("Верхние KPI: дней %d, по %s", len(recs), recs[-1]["date"] if recs else "—")
|
||||
return recs
|
||||
|
||||
|
||||
def build_top(recs):
|
||||
if not recs:
|
||||
return None
|
||||
last = recs[-1]
|
||||
|
||||
def last_nonnull(field):
|
||||
for r in reversed(recs):
|
||||
if r[field] is not None:
|
||||
return int(r[field]), r["date"]
|
||||
return 0, last["date"]
|
||||
|
||||
mau_v, _ = last_nonnull("mau")
|
||||
dau_v, _ = last_nonnull("dau")
|
||||
|
||||
# помесячная агрегация: month-end для кумулятивных, среднее для активности
|
||||
months = {} # rpid -> aggregate
|
||||
for r in recs:
|
||||
m = months.setdefault(r["rpid"], {
|
||||
"last_date": "", "registered_total": 0, "sum_installs": 0,
|
||||
"mau_sum": 0, "mau_cnt": 0, "dau_sum": 0, "dau_cnt": 0,
|
||||
})
|
||||
if r["date"] >= m["last_date"]:
|
||||
m["last_date"] = r["date"]
|
||||
if r["registered_total"] is not None:
|
||||
m["registered_total"] = int(r["registered_total"])
|
||||
if r["sum_installs"] is not None:
|
||||
m["sum_installs"] = int(r["sum_installs"])
|
||||
if r["mau"] is not None:
|
||||
m["mau_sum"] += int(r["mau"]); m["mau_cnt"] += 1
|
||||
if r["dau"] is not None:
|
||||
m["dau_sum"] += int(r["dau"]); m["dau_cnt"] += 1
|
||||
|
||||
ordered = sorted(m for m in months if m is not None)
|
||||
ordered = ordered[-TOP_TREND_MONTHS:]
|
||||
labels, reg_s, inst_s, mau_s, dau_s = [], [], [], [], []
|
||||
for rpid in ordered:
|
||||
a = months[rpid]
|
||||
y, mo = rpid // 100, rpid % 100
|
||||
labels.append(f"{_MONTHS_RU_SHORT[mo]} {y % 100:02d}")
|
||||
reg_s.append(a["registered_total"])
|
||||
inst_s.append(a["sum_installs"])
|
||||
mau_s.append(round(a["mau_sum"] / a["mau_cnt"]) if a["mau_cnt"] else 0)
|
||||
dau_s.append(round(a["dau_sum"] / a["dau_cnt"]) if a["dau_cnt"] else 0)
|
||||
|
||||
def _i(v):
|
||||
return int(v) if v is not None else 0
|
||||
|
||||
return {
|
||||
"as_of": last["date"],
|
||||
"registered_total": _i(last["registered_total"]),
|
||||
"installs_total": _i(last["sum_installs"]),
|
||||
"installs_ios": _i(last["ios_installs"]),
|
||||
"installs_android": _i(last["android_installs"]),
|
||||
"mau": mau_v,
|
||||
"dau": dau_v,
|
||||
"trend_labels": labels,
|
||||
"registered_series": reg_s,
|
||||
"installs_series": inst_s,
|
||||
"mau_series": mau_s,
|
||||
"dau_series": dau_s,
|
||||
}
|
||||
|
||||
|
||||
def fetch_monthly(conn, sql):
|
||||
cur = conn.cursor()
|
||||
log.info("Выполнение запроса метрик МП (помесячно)...")
|
||||
@ -256,6 +371,7 @@ def main() -> int:
|
||||
conn = base.connect_impala(cfg)
|
||||
try:
|
||||
rows = fetch_monthly(conn, sql)
|
||||
top = build_top(fetch_top(conn))
|
||||
finally:
|
||||
try:
|
||||
conn.close()
|
||||
@ -268,6 +384,7 @@ def main() -> int:
|
||||
return 1
|
||||
|
||||
payload = build_payload(rows, b)
|
||||
payload["top"] = top
|
||||
if write_if_changed(payload):
|
||||
base.git_commit_push(cfg, [OUT_REL],
|
||||
f"data: update app metrics {dt.date.today():%Y-%m-%d}")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user