diff --git a/app/database.py b/app/database.py index bb2f10c..df2a12b 100644 --- a/app/database.py +++ b/app/database.py @@ -68,7 +68,34 @@ def insert_event(event_id, camera, start_time, snapshot_path, clip_path, plate, conn.commit() conn.close() -def get_events(limit=100, offset=0, plate_filter=None, camera_filter=None, date_filter=None): +def _apply_date_filter(query, params, date_filter): + """Append date range clause for YYYY, YYYY-MM, or YYYY-MM-DD.""" + from datetime import datetime + s = date_filter.strip() + try: + if len(s) == 4: + dt = datetime.strptime(s, "%Y") + ts_start = int(dt.timestamp()) + ts_end = int(datetime(dt.year + 1, 1, 1).timestamp()) + elif len(s) == 7: + dt = datetime.strptime(s, "%Y-%m") + ts_start = int(dt.timestamp()) + nm = dt.month % 12 + 1 + ny = dt.year + (1 if dt.month == 12 else 0) + ts_end = int(datetime(ny, nm, 1).timestamp()) + else: + dt = datetime.strptime(s, "%Y-%m-%d") + ts_start = int(dt.timestamp()) + ts_end = ts_start + 86400 + query += " AND start_time >= ? AND start_time < ?" + params.extend([ts_start, ts_end]) + except Exception: + pass + return query, params + + +def get_events(limit=100, offset=0, plate_filter=None, camera_filter=None, + date_filter=None, has_plate=None, color_filter=None): conn = get_db() query = "SELECT * FROM events WHERE 1=1" params = [] @@ -79,23 +106,23 @@ def get_events(limit=100, offset=0, plate_filter=None, camera_filter=None, date_ query += " AND camera = ?" params.append(camera_filter) if date_filter: - import time - from datetime import datetime - try: - dt = datetime.strptime(date_filter, "%Y-%m-%d") - ts_start = int(dt.timestamp()) - ts_end = ts_start + 86400 - query += " AND start_time >= ? AND start_time < ?" - params.extend([ts_start, ts_end]) - except Exception: - pass + query, params = _apply_date_filter(query, params, date_filter) + if has_plate == "yes": + query += " AND plate IS NOT NULL AND plate != ''" + elif has_plate == "no": + query += " AND (plate IS NULL OR plate = '')" + if color_filter: + query += " AND color_name = ?" + params.append(color_filter) query += " ORDER BY start_time DESC LIMIT ? OFFSET ?" params.extend([limit, offset]) rows = conn.execute(query, params).fetchall() conn.close() return [dict(r) for r in rows] -def count_events(plate_filter=None, camera_filter=None, date_filter=None): + +def count_events(plate_filter=None, camera_filter=None, date_filter=None, + has_plate=None, color_filter=None): conn = get_db() query = "SELECT COUNT(*) FROM events WHERE 1=1" params = [] @@ -106,16 +133,14 @@ def count_events(plate_filter=None, camera_filter=None, date_filter=None): query += " AND camera = ?" params.append(camera_filter) if date_filter: - import time - from datetime import datetime - try: - dt = datetime.strptime(date_filter, "%Y-%m-%d") - ts_start = int(dt.timestamp()) - ts_end = ts_start + 86400 - query += " AND start_time >= ? AND start_time < ?" - params.extend([ts_start, ts_end]) - except Exception: - pass + query, params = _apply_date_filter(query, params, date_filter) + if has_plate == "yes": + query += " AND plate IS NOT NULL AND plate != ''" + elif has_plate == "no": + query += " AND (plate IS NULL OR plate = '')" + if color_filter: + query += " AND color_name = ?" + params.append(color_filter) count = conn.execute(query, params).fetchone()[0] conn.close() return count @@ -148,6 +173,15 @@ def get_cameras(): return [r[0] for r in rows] +def get_distinct_colors() -> list[str]: + conn = get_db() + rows = conn.execute( + "SELECT DISTINCT color_name FROM events WHERE color_name IS NOT NULL AND color_name != '' ORDER BY color_name" + ).fetchall() + conn.close() + return [r[0] for r in rows] + + # ── Whitelist ───────────────────────────────────────────────────────────────── def get_whitelist() -> list[dict]: diff --git a/app/main.py b/app/main.py index 29f185d..402284c 100644 --- a/app/main.py +++ b/app/main.py @@ -66,17 +66,18 @@ async def index( plate: str = Query(""), camera: str = Query(""), date: str = Query(""), + has_plate: str = Query(""), + color: str = Query(""), ): limit = 20 offset = (page - 1) * limit - events = database.get_events(limit=limit, offset=offset, - plate_filter=plate or None, - camera_filter=camera or None, - date_filter=date or None) - total = database.count_events(plate_filter=plate or None, - camera_filter=camera or None, - date_filter=date or None) + kw = dict(plate_filter=plate or None, camera_filter=camera or None, + date_filter=date or None, has_plate=has_plate or None, + color_filter=color or None) + events = database.get_events(limit=limit, offset=offset, **kw) + total = database.count_events(**kw) cameras = database.get_cameras() + colors = database.get_distinct_colors() total_pages = max(1, (total + limit - 1) // limit) for ev in events: @@ -89,9 +90,12 @@ async def index( "page": page, "total_pages": total_pages, "cameras": cameras, + "colors": colors, "filter_plate": plate, "filter_camera": camera, "filter_date": date, + "filter_has_plate": has_plate, + "filter_color": color, }) diff --git a/app/templates/index.html b/app/templates/index.html index 121395f..538950c 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -10,8 +10,10 @@ body { background: #0f172a; color: #e2e8f0; } .card { background: #1e293b; border: 1px solid #334155; } .plate { font-family: monospace; letter-spacing: 0.12em; } - input { background: #0f172a; border: 1px solid #475569; color: #e2e8f0; border-radius: 6px; padding: 6px 10px; } - input:focus { outline: none; border-color: #60a5fa; } + input, select { background: #0f172a; border: 1px solid #475569; color: #e2e8f0; border-radius: 6px; padding: 6px 10px; } + input:focus, select:focus { outline: none; border-color: #60a5fa; } + select option { background: #0f172a; } + .filter-label { color: #64748b; font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; display: block; margin-bottom: 3px; } .btn { background: #2563eb; color: #fff; border-radius: 6px; padding: 7px 16px; font-size: 0.85rem; cursor: pointer; } .btn:hover { background: #1d4ed8; } .btn-ghost { background: #1e293b; border: 1px solid #475569; color: #94a3b8; } @@ -59,11 +61,36 @@ -
@@ -112,13 +139,14 @@ {% if total_pages > 1 %} + {% set fq = "&plate=" ~ filter_plate ~ "&date=" ~ filter_date ~ "&has_plate=" ~ filter_has_plate ~ "&color=" ~ filter_color %} {% endif %}