From 660b9abef465bd8793c441ff84080bb0c70a1981 Mon Sep 17 00:00:00 2001 From: dsutanto Date: Fri, 10 Jul 2026 00:45:53 +0700 Subject: [PATCH] Tidur --- counter_dashboard.py | 12 +- env.example | 1 + templates/dashboard.html | 208 +++-------------- templates/snapshots.html | 489 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 528 insertions(+), 182 deletions(-) create mode 100644 templates/snapshots.html diff --git a/counter_dashboard.py b/counter_dashboard.py index 3e6cb0c..0ceef5b 100644 --- a/counter_dashboard.py +++ b/counter_dashboard.py @@ -204,6 +204,11 @@ def index(): return render_template("dashboard.html", site_name=SITE_NAME, show_detect=SAVE_DETECT_SNAPSHOT) +@app.route("/snapshots") +def snapshots_page(): + return render_template("snapshots.html", site_name=SITE_NAME, show_detect=SAVE_DETECT_SNAPSHOT) + + @app.route("/api/current-counter") def api_current_counter(): try: @@ -455,15 +460,16 @@ def export_daily_xlsx(): wb = Workbook() ws = wb.active ws.title = "Daily Counters" - _style_header(ws, [("A", "Date"), ("B", "Total"), ("C", "In"), ("D", "Out"), ("E", "First Count"), ("F", "Last Count")]) + _style_header(ws, [("A", "Date"), ("B", "Total"), ("C", "In"), ("D", "Out"), ("E", "Diff (In-Out)"), ("F", "First Count"), ("G", "Last Count")]) for r_idx, row in enumerate(rows, 2): ws.cell(row=r_idx, column=1, value=row["counting_date"]) ws.cell(row=r_idx, column=2, value=row["total_count"]) ws.cell(row=r_idx, column=3, value=row["total_in"]) ws.cell(row=r_idx, column=4, value=row["total_out"]) - ws.cell(row=r_idx, column=5, value=row["start_time"]) - ws.cell(row=r_idx, column=6, value=row["end_time"]) + ws.cell(row=r_idx, column=5, value=(row["total_in"] or 0) - (row["total_out"] or 0)) + ws.cell(row=r_idx, column=6, value=row["start_time"]) + ws.cell(row=r_idx, column=7, value=row["end_time"]) _auto_width(ws) filename = f"{SITE_NAME}_daily_records_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" diff --git a/env.example b/env.example index 319fba7..1c05e69 100644 --- a/env.example +++ b/env.example @@ -145,3 +145,4 @@ CURRENT_COUNTER_PATH=/tmp/bytetrack_current_counter.json SAVE_CROSS_SNAPSHOT=true CROSS_SNAPSHOT_DIR=/opt/zenai-kpc-snaps/snapshots CROSS_SNAPSHOT_CLEANUP_SEC=3600 +CROSS_SNAPSHOT_MAX_AGE_DAYS=3 diff --git a/templates/dashboard.html b/templates/dashboard.html index e3611cf..40648a4 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -149,6 +149,17 @@ } .theme-toggle:hover { border-color: var(--accent); background: var(--cell-bg); } + .nav-link { + display: inline-flex; align-items: center; gap: 6px; + background: none; border: 1px solid var(--cell-border); + border-radius: 8px; padding: 7px 12px; cursor: pointer; + font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: 1px; + color: var(--text-secondary); text-decoration: none; + transition: border-color 0.3s, color 0.3s, background 0.3s; + } + .nav-link:hover { border-color: var(--accent); color: var(--text-primary); background: var(--cell-bg); } + .nav-link.active { border-color: var(--accent); color: var(--accent); } + /* Section title */ .section-title { font-family: 'Orbitron', sans-serif; font-size: 13px; font-weight: 600; letter-spacing: 2px; @@ -406,55 +417,6 @@ } .table-toolbar .toolbar-group { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } - /* Snapshot gallery */ - .snapshot-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); - gap: 14px; - } - .snap-card { - background: var(--cell-bg); - border: 1px solid var(--cell-border); border-radius: 12px; - overflow: hidden; cursor: pointer; - transition: border-color 0.2s, transform 0.15s; - } - .snap-card:hover { border-color: var(--accent); transform: translateY(-2px); } - .snap-thumb { - width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; - background: #000; - } - .snap-info { padding: 8px 10px; } - .snap-info .si-top { - display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; - } - .snap-kind { - font-size: 8px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; - padding: 2px 8px; border-radius: 8px; - } - .snap-kind.in { background: rgba(0,255,136,0.12); color: var(--accent4); } - .snap-kind.out { background: rgba(255,45,120,0.12); color: var(--accent3); } - .snap-kind.detect { background: rgba(0,240,255,0.10); color: var(--accent); } - .snap-tid { - font-family: 'Orbitron', sans-serif; font-size: 11px; font-weight: 700; - color: var(--text-primary); - } - .snap-time { font-size: 9px; color: var(--text-secondary); } - - .snap-lightbox { - position: relative; - background: var(--bg-surface); - border: 1px solid var(--border-glow); border-radius: var(--radius); - padding: 14px; max-width: 92vw; max-height: 90vh; - display: flex; flex-direction: column; align-items: center; - } - .snap-lightbox img { - max-width: 88vw; max-height: 78vh; border-radius: 10px; display: block; - } - .snap-modal-meta { - margin-top: 10px; font-size: 11px; color: var(--text-secondary); letter-spacing: 1px; - text-align: center; - } - .table-toolbar input[type="date"] { font-family: 'JetBrains Mono', monospace; font-size: 11px; background: var(--input-bg); color: var(--text-primary); @@ -645,6 +607,7 @@ {{ site_name }} + SNAPSHOTS @@ -655,7 +618,7 @@ LIVE
--
-
Total Count Today
+
Diff Today (In − Out)
In
@@ -671,7 +634,7 @@ Yesterday
--
-
Total Count Yesterday
+
Diff Yesterday (In − Out)
In
@@ -749,8 +712,8 @@
-
Grand Total
-
All time
+
Grand Diff
+
All time (In − Out)
-- @@ -805,7 +768,7 @@ Date - Total Count + Diff (In − Out) In Out Status @@ -818,47 +781,9 @@
- -
-
-
- ◉ Crossing Snapshots -
Detected & counted frames — correlate by track ID
-
-
-
- - - - {% if show_detect %}{% endif %} -
- - - -
-
-
- -
- -
-
- - - + + +