Админка: график «Заходы по источникам» по дням (стек, цвет закреплён за источником)
Бэкенд: разбивка WEB_SOURCES по дням (out.daily[].sources + source_names). Фронт: 4-й малый график в «Активность по дням» с легендой; цвета — из валидированной палитры, закреплены за источником (Yandex/Google/Прямые/…). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
48
app/main.py
48
app/main.py
@@ -3158,6 +3158,10 @@ _ADMIN_HTML = """<!doctype html>
|
||||
<div class="daychart-cap">Обработано страниц <span class="muted" id="pages-cap"></span></div>
|
||||
<div class="bars" id="bars-pages"></div>
|
||||
</div>
|
||||
<div class="daychart">
|
||||
<div class="daychart-cap">Заходы по источникам <span id="src-legend"></span> <span class="muted" id="src-cap"></span></div>
|
||||
<div class="bars" id="bars-sources"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@@ -3317,6 +3321,38 @@ _ADMIN_HTML = """<!doctype 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 += '<div class="seg" title="'+x.day+': '+esc(n)+' — '+v+'" style="bottom:'+acc+'px;height:'+px+'px;background:'+
|
||||
srcColor(n)+(top?';border-radius:4px 4px 0 0':'')+(acc>0?';border-bottom:1.5px solid var(--card)':'')+'"></div>';
|
||||
acc += px;
|
||||
});
|
||||
var lbl = tot ? '<b style="top:auto;bottom:'+(acc+2)+'px">'+tot+'</b>' : '';
|
||||
return '<div class="b stack" title="'+x.day+': '+tot+' заходов">'+lbl+segs+'<span>'+dd(x)+'</span></div>';
|
||||
}).join('') : emptyBars;
|
||||
document.getElementById('src-legend').innerHTML = srcNames.map(function(n){
|
||||
return '<span class="dc-leg"><i style="background:'+srcColor(n)+'"></i>'+esc(n)+'</span>';
|
||||
}).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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user