VIDEO NOT IMAGES + history4all
This commit is contained in:
1 parent
c716d23151
commit
4201a3cbb1
6 files changed
+66
-13
No files matched your search
@@ -14,6 +14,8 @@ wheels/
|
||||
|
||||
# artifacts/logs i forgor what are the paths uhhhhhhh
|
||||
alerts.jsonl
|
||||
output.jsonl
|
||||
output.mp4
|
||||
static/alerts/
|
||||
screenshots/
|
||||
events/
|
||||
|
||||
+1
-1
Submodule SmokeDetectionApp updated: 339616b827...7ace087582.
+5
-2
@@ -122,10 +122,13 @@ class MySQLRepository:
|
||||
conn.close()
|
||||
return [DetectionRecord(**r) for r in rows]
|
||||
|
||||
def clear_detections(self, user_id: int) -> int:
|
||||
def clear_detections(self, user_id: Optional[int] = None) -> int:
|
||||
conn = self._connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("DELETE FROM detections WHERE user_id = %s", (user_id,))
|
||||
if user_id is None:
|
||||
cursor.execute("DELETE FROM detections")
|
||||
else:
|
||||
cursor.execute("DELETE FROM detections WHERE user_id = %s", (user_id,))
|
||||
conn.commit()
|
||||
count = cursor.rowcount
|
||||
conn.close()
|
||||
|
||||
+2
-2
@@ -59,8 +59,8 @@ class Repository(Protocol):
|
||||
"""Get detection history, optionally filtered by user. Newest first."""
|
||||
...
|
||||
|
||||
def clear_detections(self, user_id: int) -> int:
|
||||
"""Delete all detections for a user. Returns number of rows deleted."""
|
||||
def clear_detections(self, user_id: Optional[int] = None) -> int:
|
||||
"""Delete detections, optionally filtered by user. Returns rows deleted."""
|
||||
...
|
||||
|
||||
def close(self) -> None:
|
||||
|
||||
+7
-2
@@ -109,9 +109,14 @@ class SQLiteRepository:
|
||||
).fetchall()
|
||||
return [DetectionRecord(**dict(r)) for r in rows]
|
||||
|
||||
def clear_detections(self, user_id: int) -> int:
|
||||
def clear_detections(self, user_id: Optional[int] = None) -> int:
|
||||
conn = self._get_conn()
|
||||
cursor = conn.execute("DELETE FROM detections WHERE user_id = ?", (user_id,))
|
||||
if user_id is None:
|
||||
cursor = conn.execute("DELETE FROM detections")
|
||||
else:
|
||||
cursor = conn.execute(
|
||||
"DELETE FROM detections WHERE user_id = ?", (user_id,)
|
||||
)
|
||||
conn.commit()
|
||||
return cursor.rowcount
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ from pipeline.config import PipelineConfig
|
||||
from pipeline.core import SmokingDetectionPipeline
|
||||
from pipeline.models import YoloCigaretteDetector, YoloClassifier, YoloPersonDetector
|
||||
from pipeline.protocols import Sink
|
||||
from pipeline.sinks import DatabaseSink, FileAlertStore, MetricsSink
|
||||
from pipeline.sinks import FileAlertStore, MetricsSink, SmartEventSink
|
||||
from pipeline.sources.stream import ThreadedStreamSource
|
||||
from pipeline.staged_core import StageBasedPipeline
|
||||
from pipeline.types import Frame
|
||||
@@ -62,6 +62,48 @@ class LiveFrameSink(Sink):
|
||||
pass
|
||||
|
||||
|
||||
class DatabaseSmartEventSink(SmartEventSink):
|
||||
"""Records event clips and stores clip path in DB as alert evidence."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
db_repo: Repository,
|
||||
fps: float,
|
||||
user_id: int = 1,
|
||||
camera_source: str = "Pipeline",
|
||||
output_dir: str = "static/events",
|
||||
):
|
||||
super().__init__(output_dir=output_dir, fps=fps)
|
||||
self.db_repo = db_repo
|
||||
self.user_id = user_id
|
||||
self.camera_source = camera_source
|
||||
|
||||
def _trigger_event(self, trigger_frame: Frame):
|
||||
was_recording = self.recording
|
||||
super()._trigger_event(trigger_frame)
|
||||
|
||||
# Save one DB row per newly started event clip
|
||||
if was_recording or not self.recording or self.current_event_id is None:
|
||||
return
|
||||
|
||||
ticket = trigger_frame.ticket
|
||||
if not ticket:
|
||||
return
|
||||
|
||||
filename = f"event_{self.current_event_id}.mp4"
|
||||
web_path = f"/static/events/{filename}"
|
||||
try:
|
||||
self.db_repo.save_detection(
|
||||
user_id=self.user_id,
|
||||
image_path=web_path,
|
||||
confidence=ticket.confidence,
|
||||
camera_source=self.camera_source,
|
||||
)
|
||||
logger.info("Saved event alert to DB: %s", web_path)
|
||||
except Exception as e:
|
||||
logger.error("Failed to save event alert to DB: %s", e)
|
||||
|
||||
|
||||
def _get_lan_address() -> tuple[str, str]:
|
||||
"""Return (ip, hostname) for the primary LAN interface."""
|
||||
ip = "127.0.0.1"
|
||||
@@ -160,7 +202,7 @@ async def app_lifespan(_app: FastAPI):
|
||||
# 4. Sinks
|
||||
sinks: list[Sink] = [
|
||||
LiveFrameSink(),
|
||||
DatabaseSink(db, image_dir="static/alerts"),
|
||||
DatabaseSmartEventSink(db, fps=source.fps, output_dir="static/events"),
|
||||
MetricsSink(log_interval=200),
|
||||
FileAlertStore("alerts.jsonl"),
|
||||
]
|
||||
@@ -231,6 +273,7 @@ app = FastAPI(title="Smoking Detection", lifespan=app_lifespan) # type: ignore
|
||||
|
||||
# Serve static assets (alert images, CSS/JS, etc.)
|
||||
Path("static/alerts").mkdir(parents=True, exist_ok=True)
|
||||
Path("static/events").mkdir(parents=True, exist_ok=True)
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
|
||||
@@ -269,7 +312,7 @@ def login(username: str = Form(...), password: str = Form(...)):
|
||||
|
||||
|
||||
@app.get("/history")
|
||||
def get_history(user_id: int):
|
||||
def get_history(user_id: int | None = None):
|
||||
try:
|
||||
records = db.get_detections(user_id)
|
||||
return [
|
||||
@@ -288,10 +331,10 @@ def get_history(user_id: int):
|
||||
|
||||
|
||||
@app.post("/clear_history")
|
||||
def clear_history(user_id: int = Form(...)):
|
||||
def clear_history():
|
||||
try:
|
||||
deleted = db.clear_detections(user_id)
|
||||
logger.info("Cleared %d records for user %d", deleted, user_id)
|
||||
deleted = db.clear_detections()
|
||||
logger.info("Cleared %d records", deleted)
|
||||
return {"status": "success", "message": "History cleared"}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
Reference in new issue
Block a user