"""LOG494 の集計:ev-stats を「その日以降」で日ごとに取った値の差から、1日ずつの数を出す。 使い方: python3 summarize_LOG494.py 入力: ev_by_since_day.json(fetch_ev_by_day.py が /api/ev-stats?since=日付&who=human|bot|unknown を取ったもの) kpi_since1026.json(/api/kpi-stats?since=2026-10-26) linkedin_posted_by_day.txt(r2r2r-content の git log から数えた LinkedIn の自動投稿の数) 出力: 標準出力(summarize_LOG494_output.txt に保存する) 注意: - 差で出しているので、0時をまたいで送り直された表示は、送り直した日の側に寄る(ev-stats は期間の中で同じ表示を1件にまとめるため) - スマホ幅(sp)は ev-stats が割合(四捨五入)で返すので、表示数×割合から戻した概数。1日ずつの差は当てにならないので、この表では使わない """ import json import os import sys d = sys.argv[1] r = json.load(open(os.path.join(d, 'ev_by_since_day.json'))) days = sorted(r) zero = {'views': 0, 'sp': 0, 'ref': {}, 'trunc': False} rows = [] for i, day in enumerate(days): nx = days[i + 1] if i + 1 < len(days) else None def diff(who, key): return r[day][who][key] - (r[nx][who] if nx else zero)[key] h, hn = r[day]['human'], (r[nx]['human'] if nx else zero) ref = {k: v - hn['ref'].get(k, 0) for k, v in h['ref'].items()} g = ref.get('www.google.com', 0) di = ref.get('(直接・不明)', 0) inner = ref.get('(サイト内)', 0) human = diff('human', 'views') rows.append({ 'day': day, 'human': human, 'bot': diff('bot', 'views'), 'unknown': diff('unknown', 'views'), 'google': g, 'direct': di, 'inner': inner, 'other': human - g - di - inner, 'truncated': any(r[day][w]['trunc'] for w in r[day]), }) print('== 1日ずつの表示(ev。人=人の操作あり・機械・不明=操作なし)と、人の表示の来た所') print('日 人 機械 不明 Google 直接・不明 サイト内 その他 打ち切り') for x in rows: print(f"{x['day']} {x['human']:>3} {x['bot']:>5} {x['unknown']:>4} {x['google']:>6} {x['direct']:>9} {x['inner']:>8} {x['other']:>6} {x['truncated']}") def total(ds, key): return sum(x[key] for x in rows if x['day'] in ds) pairs = [ ('月〜水', ['2026-11-02', '2026-11-03', '2026-11-04'], ['2026-11-09', '2026-11-10', '2026-11-11']), ('月〜火', ['2026-11-02', '2026-11-03'], ['2026-11-09', '2026-11-10']), ('水だけ', ['2026-11-04'], ['2026-11-11']), ] print() print('== 同じ曜日どうしの比べ(11/2の週 → 11/9の週)') for name, a, b in pairs: print(f"{name}: 人 {total(a,'human')} → {total(b,'human')} Google {total(a,'google')} → {total(b,'google')} 直接・不明 {total(a,'direct')} → {total(b,'direct')} 機械 {total(a,'bot')} → {total(b,'bot')} 不明 {total(a,'unknown')} → {total(b,'unknown')}") k = json.load(open(os.path.join(d, 'kpi_since1026.json'))) print() print('== AI の依頼読み(kpi。取得の回数・robots.txt を除く)') for x in k['byDay']: print(f"{x['day']} {x['aiReads']:>3} {json.dumps(x['aiReadsByBot'], ensure_ascii=False)}{' (途中)' if x['partial'] else ''}") same = lambda ds: sum(x['aiReads'] for x in k['byDay'] if x['day'] in ds) print(f"11/2〜11/4: {same(['2026-11-02','2026-11-03','2026-11-04'])} 11/9〜11/11: {same(['2026-11-09','2026-11-10','2026-11-11'])}") print(f"11/2〜11/4(11/3 の試し12件を除く): {same(['2026-11-02','2026-11-03','2026-11-04']) - 12}") lp = os.path.join(d, 'linkedin_posted_by_day.txt') if os.path.exists(lp): print() print('== LinkedIn の自動投稿の数(日本時間・r2r2r-content の「の結果(posted)」の commit)') print(open(lp, encoding='utf-8').read().rstrip())