fix reset state
This commit is contained in:
@@ -1224,6 +1224,27 @@ def serve_dashboard(host: str, port: int, status_file: str, *, image_dir: str |
|
|||||||
except (ValueError, json.JSONDecodeError) as exc:
|
except (ValueError, json.JSONDecodeError) as exc:
|
||||||
self._send(400, "application/json", json.dumps({"error": str(exc)}).encode("utf-8"))
|
self._send(400, "application/json", json.dumps({"error": str(exc)}).encode("utf-8"))
|
||||||
return
|
return
|
||||||
|
if self.path == "/api/feedback/clear" and self._is_loopback_client():
|
||||||
|
try:
|
||||||
|
payload = json.loads(self.rfile.read(min(int(self.headers.get("Content-Length", "0")), 16_384)).decode("utf-8"))
|
||||||
|
status = str(payload.get("status", "") or "")
|
||||||
|
self._send(200, "application/json", json.dumps(FeedbackStore().clear(status=status or None)).encode("utf-8"))
|
||||||
|
except (ValueError, json.JSONDecodeError) as exc:
|
||||||
|
self._send(400, "application/json", json.dumps({"error": str(exc)}).encode("utf-8"))
|
||||||
|
return
|
||||||
|
if self.path == "/api/review-state/clear" and self._is_loopback_client():
|
||||||
|
try:
|
||||||
|
payload = json.loads(self.rfile.read(min(int(self.headers.get("Content-Length", "0")), 16_384)).decode("utf-8") or "{}")
|
||||||
|
clear_incidents = bool(payload.get("incidents", True))
|
||||||
|
clear_feedback = bool(payload.get("feedback", True))
|
||||||
|
result = {
|
||||||
|
"incidents": IncidentStore().clear() if clear_incidents else {"removed": 0, "remaining": len(IncidentStore().entries()), "status": "skipped"},
|
||||||
|
"feedback": FeedbackStore().clear() if clear_feedback else {"removed": 0, "remaining": len(FeedbackStore().entries()), "status": "skipped"},
|
||||||
|
}
|
||||||
|
self._send(200, "application/json", json.dumps(result).encode("utf-8"))
|
||||||
|
except (ValueError, json.JSONDecodeError) as exc:
|
||||||
|
self._send(400, "application/json", json.dumps({"error": str(exc)}).encode("utf-8"))
|
||||||
|
return
|
||||||
if self.path == "/api/incidents" and self._is_loopback_client():
|
if self.path == "/api/incidents" and self._is_loopback_client():
|
||||||
try:
|
try:
|
||||||
payload = json.loads(self.rfile.read(min(int(self.headers.get("Content-Length", "0")), 16_384)).decode("utf-8"))
|
payload = json.loads(self.rfile.read(min(int(self.headers.get("Content-Length", "0")), 16_384)).decode("utf-8"))
|
||||||
|
|||||||
@@ -25,3 +25,19 @@ class FeedbackStore:
|
|||||||
entries = [value for value in self.entries() if (value.get("stream_id"), value.get("entity"), value.get("field"), value.get("value", "")) != (entry["stream_id"], entry["entity"], entry["field"], entry["value"])]
|
entries = [value for value in self.entries() if (value.get("stream_id"), value.get("entity"), value.get("field"), value.get("value", "")) != (entry["stream_id"], entry["entity"], entry["field"], entry["value"])]
|
||||||
entries.append(entry); self.path.parent.mkdir(parents=True, exist_ok=True); self.path.write_text(json.dumps(entries, indent=2), encoding="utf-8")
|
entries.append(entry); self.path.parent.mkdir(parents=True, exist_ok=True); self.path.write_text(json.dumps(entries, indent=2), encoding="utf-8")
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
|
def clear(self, *, status: str | None = None) -> dict[str, object]:
|
||||||
|
entries = self.entries()
|
||||||
|
if status is None:
|
||||||
|
removed = len(entries)
|
||||||
|
entries = []
|
||||||
|
else:
|
||||||
|
status = status.lower()
|
||||||
|
if status not in {"false_positive", "expected", "confirmed"}:
|
||||||
|
raise ValueError("invalid feedback status")
|
||||||
|
before = len(entries)
|
||||||
|
entries = [item for item in entries if str(item.get("status", "")).lower() != status]
|
||||||
|
removed = before - len(entries)
|
||||||
|
self.path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
self.path.write_text(json.dumps(entries, indent=2), encoding="utf-8")
|
||||||
|
return {"removed": removed, "remaining": len(entries), "status": status or "all"}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import json
|
import json
|
||||||
|
import inspect
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import fgai.dashboard as dashboard
|
||||||
from fgai.dashboard import HTML, _ollama_models
|
from fgai.dashboard import HTML, _ollama_models
|
||||||
|
|
||||||
|
|
||||||
@@ -62,6 +64,11 @@ class DashboardTests(unittest.TestCase):
|
|||||||
self.assertIn('id="clearAllIncidents"', HTML)
|
self.assertIn('id="clearAllIncidents"', HTML)
|
||||||
self.assertIn("/api/incidents/clear", HTML)
|
self.assertIn("/api/incidents/clear", HTML)
|
||||||
|
|
||||||
|
def test_dashboard_exposes_review_state_reset_endpoints(self):
|
||||||
|
source = inspect.getsource(dashboard)
|
||||||
|
self.assertIn("/api/feedback/clear", source)
|
||||||
|
self.assertIn("/api/review-state/clear", source)
|
||||||
|
|
||||||
def test_dashboard_does_not_clear_streams_when_picker_is_unloaded(self):
|
def test_dashboard_does_not_clear_streams_when_picker_is_unloaded(self):
|
||||||
self.assertIn("const streamValues = Object.values(window.streamSelection || {})", HTML)
|
self.assertIn("const streamValues = Object.values(window.streamSelection || {})", HTML)
|
||||||
self.assertIn("if (streamValues.length)", HTML)
|
self.assertIn("if (streamValues.length)", HTML)
|
||||||
|
|||||||
25
tests/test_feedback.py
Normal file
25
tests/test_feedback.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from fgai.feedback import FeedbackStore
|
||||||
|
|
||||||
|
|
||||||
|
class FeedbackTests(unittest.TestCase):
|
||||||
|
def test_feedback_store_can_clear_by_status_or_all(self):
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
store = FeedbackStore(str(Path(directory) / "feedback.json"))
|
||||||
|
store.add({"status": "expected", "entity": "alice", "stream_id": "windows", "field": "username"})
|
||||||
|
store.add({"status": "confirmed", "entity": "bob", "stream_id": "vpn", "field": "username"})
|
||||||
|
|
||||||
|
expected = store.clear(status="expected")
|
||||||
|
self.assertEqual(expected, {"removed": 1, "remaining": 1, "status": "expected"})
|
||||||
|
self.assertEqual(store.entries()[0]["status"], "confirmed")
|
||||||
|
|
||||||
|
all_items = store.clear()
|
||||||
|
self.assertEqual(all_items, {"removed": 1, "remaining": 0, "status": "all"})
|
||||||
|
self.assertEqual(store.entries(), [])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user