From 658b9850bb7e056af8f1ae379bd9ec8d60c23ee5 Mon Sep 17 00:00:00 2001 From: naps62 Date: Fri, 14 Aug 2026 18:42:15 +0000 Subject: [PATCH] style: rule off the report, count only sessions in range The session count came from the mtime prefilter, so an old date range reported every file on disk as if it had contributed. Co-Authored-By: Claude Opus 5 --- skills/hourlog/scripts/scan-activity.py | 45 ++++++++++++++++--------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/skills/hourlog/scripts/scan-activity.py b/skills/hourlog/scripts/scan-activity.py index 9f26136..7c3c47d 100755 --- a/skills/hourlog/scripts/scan-activity.py +++ b/skills/hourlog/scripts/scan-activity.py @@ -186,7 +186,7 @@ def main(): proj = classify(cwd, cfg["_rules"]) if proj is None: continue - nfiles += 1 + hit = False for ts in stamps: try: t = dt.datetime.fromisoformat( @@ -195,8 +195,10 @@ def main(): continue if not (start <= t <= end): continue + hit = True grid[t.date().isoformat()][proj][t.hour].add( int(t.timestamp()) // slot) + nfiles += hit del stamps mins = cfg["slot_minutes"] @@ -242,40 +244,53 @@ def main(): print() return 0 - print(f"{since} .. {until} ({nfiles} sessions, {mins}-min slots, " - f"{cfg['timezone']}, {cfg['nominal_day_hours']}h day)") - print("hours are a suggested split of a full day, not measured time\n") + head = [ + f"{since} .. {until} ({nfiles} sessions, {mins}-min slots, " + f"{cfg['timezone']}, {cfg['nominal_day_hours']}h day)", + "hours are a suggested split of a full day, not measured time", + ] label_w = max([12] + [len(p["project"]) for e in report for p in e["projects"]]) + body = [] for e in report: if not e["projects"]: continue + if body: + body.append("") note = " — under 30min, treat as empty" if e["thin"] else "" - print(f"{e['date']} {e['weekday']} " - f"{e['total_active_minutes']}min active{note}") + body.append(f"{e['date']} {e['weekday']} " + f"{e['total_active_minutes']}min active{note}") for p in e["projects"]: out = "" if p["outside_workday"]: out = " outside " + ",".join( f"{h:02d}h" for h in p["outside_workday"]) - print(f" {p['project']:<{label_w}} " - f"{fmt_hours(p['suggested_hours']):>6} " - f"{p['share']*100:3.0f}% {p['active_minutes']:>4}min " - f"{p['first_hour']:02d}-{p['last_hour']:02d}h{out}") - print() + body.append(f" {p['project']:<{label_w}} " + f"{fmt_hours(p['suggested_hours']):>6} " + f"{p['share']*100:3.0f}% {p['active_minutes']:>4}min " + f"{p['first_hour']:02d}-{p['last_hour']:02d}h{out}") week = collections.Counter() for e in report: for p in e["projects"]: if p["suggested_hours"]: week[p["project"]] += p["suggested_hours"] + total = "" if week: - print("total " + " ".join( - f"{p} {fmt_hours(h)}" for p, h in week.most_common()) + - f" ({fmt_hours(sum(week.values()))})\n") + total = "total " + " ".join( + f"{p} {fmt_hours(h)}" for p, h in week.most_common()) + \ + f" ({fmt_hours(sum(week.values()))})" + + rule = "-" * max(len(x) for x in head + body + [total]) + print("\n".join(head)) + print(rule) + print("\n".join(body)) + if total: + print(rule) + print(total) if unmapped: - print("unmapped paths — add them to the config or the exclude list:") + print("\nunmapped paths — add them to the config or the exclude list:") for u in unmapped: print(" ", u) return 0