From c506e9ec24abb9b4cf2f1c82781d2d0ed517f930 Mon Sep 17 00:00:00 2001 From: berkio_admin_gitea Date: Tue, 14 Jul 2026 11:59:56 +0000 Subject: [PATCH] =?UTF-8?q?=D0=90=D0=B4=D0=BC=D0=B8=D0=BD=D0=BA=D0=B0:=20?= =?UTF-8?q?=D0=B3=D1=80=D0=B0=D1=84=D0=B8=D0=BA=20=C2=AB=D0=97=D0=B0=D1=85?= =?UTF-8?q?=D0=BE=D0=B4=D1=8B=20=D0=BF=D0=BE=20=D0=B8=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D1=87=D0=BD=D0=B8=D0=BA=D0=B0=D0=BC=C2=BB=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=B4=D0=BD=D1=8F=D0=BC=20(=D1=81=D1=82=D0=B5=D0=BA,=20=D1=86?= =?UTF-8?q?=D0=B2=D0=B5=D1=82=20=D0=B7=D0=B0=D0=BA=D1=80=D0=B5=D0=BF=D0=BB?= =?UTF-8?q?=D1=91=D0=BD=20=D0=B7=D0=B0=20=D0=B8=D1=81=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D0=BE=D0=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Бэкенд: разбивка WEB_SOURCES по дням (out.daily[].sources + source_names). Фронт: 4-й малый график в «Активность по дням» с легендой; цвета — из валидированной палитры, закреплены за источником (Yandex/Google/Прямые/…). Co-Authored-By: Claude Opus 4.8 --- app/main.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/app/main.py b/app/main.py index d9e378d..68913f9 100644 --- a/app/main.py +++ b/app/main.py @@ -3158,6 +3158,10 @@ _ADMIN_HTML = """
Обработано страниц
+
+
Заходы по источникам
+
+
@@ -3317,6 +3321,38 @@ _ADMIN_HTML = """ }).join('') : emptyBars; document.getElementById('pages-cap').textContent = '· ' + totPages.toLocaleString('ru-RU') + ' за 14 дн.'; + // 4) Заходы по источникам — стек по источникам (цвет закреплён за источником) + var SRC_COLORS = { 'Yandex':'#e34948', 'Google':'#2a78d6', 'Прямые заходы':'#898781', + 'Telegram':'#1baf7a', 'VK':'#4a3aa7', 'Mail.ru':'#eb6834' }; + var SRC_FALLBACK = ['#eda100','#008300','#e87ba4','#eb6834']; + var _fbCache = {}, _fbN = 0; + function srcColor(n){ if(SRC_COLORS[n]) return SRC_COLORS[n]; + if(!_fbCache[n]){ _fbCache[n] = SRC_FALLBACK[_fbN % SRC_FALLBACK.length]; _fbN++; } return _fbCache[n]; } + var srcNames = d.source_names || []; + var mS = 1; + dl.forEach(function(x){ var t=0, s=x.sources||{}; srcNames.forEach(function(n){ t+=s[n]||0; }); if(t>mS) mS=t; }); + var CHS = 78, srcTot = {}; + document.getElementById('bars-sources').innerHTML = (dl.length && srcNames.length) ? dl.map(function(x){ + var s = x.sources||{}, tot=0; srcNames.forEach(function(n){ tot += s[n]||0; }); + var segs='', acc=0; + srcNames.forEach(function(n){ + var v = s[n]||0; if(v<=0) return; + srcTot[n] = (srcTot[n]||0) + v; + var px = Math.max(2, Math.round(v/mS*CHS)); + var top = (acc+px >= Math.round(tot/mS*CHS) - 1); + segs += '
'; + acc += px; + }); + var lbl = tot ? ''+tot+'' : ''; + return '
'+lbl+segs+''+dd(x)+'
'; + }).join('') : emptyBars; + document.getElementById('src-legend').innerHTML = srcNames.map(function(n){ + return ''+esc(n)+''; + }).join(''); + var srcSum = srcNames.reduce(function(a,n){ return a + (srcTot[n]||0); }, 0); + document.getElementById('src-cap').textContent = '· ' + srcSum + ' за 14 дн. (учёт с 07.07)'; + // Источники трафика var sources = d.sources || []; var stot = sources.reduce(function(a,x){ return a + (x.c||0); }, 0) || 1; @@ -3752,6 +3788,15 @@ def _admin_collect_stats() -> dict: if sec is not None: e["secs"] += sec e["pages_timed"] += (r["pg"] or 0) + # Источники по дням (для графика «заходы по источникам») + _src_cut = (_today - datetime.timedelta(days=13)).strftime("%Y-%m-%d") + src_by_day = {} + src_totals = {} + for r in con.execute( + "SELECT day, source, count FROM WEB_SOURCES WHERE day >= ?", (_src_cut,)): + src_by_day.setdefault(r["day"], {})[r["source"]] = r["count"] + src_totals[r["source"]] = src_totals.get(r["source"], 0) + (r["count"] or 0) + out["source_names"] = [s for s, _v in sorted(src_totals.items(), key=lambda kv: -kv[1])] daily = [] for i in range(13, -1, -1): dkey = (_today - datetime.timedelta(days=i)).strftime("%Y-%m-%d") @@ -3763,7 +3808,8 @@ def _admin_collect_stats() -> dict: "files_site": fb.get("files_site", 0), "files_bot": fb.get("files_bot", 0), "pages": fb.get("pages", 0), "src_mb": fb.get("src_mb", 0), "res_mb": fb.get("res_mb", 0), - "avg_page": (fb.get("secs", 0) / ptimed) if ptimed else 0}) + "avg_page": (fb.get("secs", 0) / ptimed) if ptimed else 0, + "sources": src_by_day.get(dkey, {})}) out["daily"] = daily return out