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