From 01bad7a85a1d3974baad18144d5406da4bb5e726 Mon Sep 17 00:00:00 2001 From: Andrei Pradan Date: Thu, 8 Feb 2024 19:00:28 +0200 Subject: [PATCH] huey tasks FE improvements --- backend/clients/prediction.py | 2 +- backend/core/tasks.py | 23 ++++++------- backend/finance/tasks.py | 2 +- frontend/src/app/App.scss | 3 ++ frontend/src/app/tasks/Tasks.js | 57 ++++++++++++++++++++------------- 5 files changed, 52 insertions(+), 35 deletions(-) diff --git a/backend/clients/prediction.py b/backend/clients/prediction.py index b2dde0de..e4477bab 100644 --- a/backend/clients/prediction.py +++ b/backend/clients/prediction.py @@ -63,7 +63,7 @@ def train(cls, df, logger): log_status("train", accuracy=f"{accuracy:.2f}") if accuracy < 0.95: error = f"Insufficient accuracy: {accuracy:.2f}" - log_status("train", status=SIGNAL_ERROR, error=error) + log_status("train", status=SIGNAL_ERROR, errors=[error]) raise ValueError(error) prefix = f"{timezone.now():%Y_%m_%d_%H_%M_%S}_{accuracy}" diff --git a/backend/core/tasks.py b/backend/core/tasks.py index 57560a1b..4235ce68 100644 --- a/backend/core/tasks.py +++ b/backend/core/tasks.py @@ -5,20 +5,20 @@ redis_client = HUEY.storage.redis_client() -def log_status(key, **kwargs): +def log_status(key, errors=None, **kwargs): new_event = {"timestamp": timezone.now().isoformat(), **kwargs} + errors = errors or [] details = json.loads(redis_client.get(key) or "{}") if not details: - details = {"history": [new_event], **new_event} + details = {"errors": errors, "history": [new_event], **new_event} else: - if "errors" in details: - details["errors"] = [ - *new_event.pop("errors", []), - *details["errors"][:30], - ] details.update(new_event) - details["history"] = [new_event, *details["history"][:1000]] + if "errors" not in details: + details["errors"] = errors + else: + details["errors"] = [*errors, *details["errors"][:29]] + details["history"] = [new_event, *details["history"][:999]] redis_client.set(key, json.dumps(details)) return details @@ -26,7 +26,8 @@ def log_status(key, **kwargs): @HUEY.signal() def signal_handler(signal, task, exc=None): now = timezone.now().isoformat() - kwargs = {"errors": [], "id": task.id, "status": signal, "timestamp": now} + errors = [] + kwargs = {"id": task.id, "status": signal, "timestamp": now} if exc: - kwargs["errors"].append({"msg": str(exc), "timestamp": now}) - log_status(task.name, **kwargs) + errors.append({"timestamp": now, "msg": str(exc)}) + log_status(task.name, errors, **kwargs) diff --git a/backend/finance/tasks.py b/backend/finance/tasks.py index 8b82c742..a7ff6275 100644 --- a/backend/finance/tasks.py +++ b/backend/finance/tasks.py @@ -65,7 +65,7 @@ def train(logger): ) count = qs.count() if not count: - log_status("train", status=SIGNAL_ERROR, error="No trained data") + log_status("train", status=SIGNAL_ERROR, errors=["No trained data"]) raise ValueError("No trained data") log_status("train", count=count) logger.info("Training on %d confirmed transactions...", count) diff --git a/frontend/src/app/App.scss b/frontend/src/app/App.scss index b45e1f5f..ea21a828 100644 --- a/frontend/src/app/App.scss +++ b/frontend/src/app/App.scss @@ -95,4 +95,7 @@ } .min-vw-75 { min-width: 75vw; +} +.min-vw-50 { + min-width: 50vw; } \ No newline at end of file diff --git a/frontend/src/app/tasks/Tasks.js b/frontend/src/app/tasks/Tasks.js index 1af0f328..a46d3919 100644 --- a/frontend/src/app/tasks/Tasks.js +++ b/frontend/src/app/tasks/Tasks.js @@ -10,13 +10,16 @@ import { selectItem } from "../../redux/tasksSlice"; import Errors from "../shared/Errors"; import TasksApi from "../../api/tasks"; -const parseStatus = status => status === "complete" +const parseStatus = status => { + const icon = status === "complete" ? "✅" : status === "executing" ? "⚙️" : ["canceled", "error", "interrupted"].includes(status) - ? `${status} ❌` - : status + ? "❌" + : "❓" + return `${capitalize(status)} ${icon}` +} const Tasks = () => { const dispatch = useDispatch(); @@ -25,7 +28,9 @@ const Tasks = () => { const [taskErrorsOpen, setTaskErrorsOpen] = useState(false) const [taskHistoryOpen, setTaskHistoryOpen] = useState(false) - useEffect(() => !results && dispatch(TasksApi.getList(token)), []) + useEffect(() => { + if (!results) dispatch(TasksApi.getList(token)) + }, []) return (
@@ -66,10 +71,11 @@ const Tasks = () => { # Task - Is periodic? + Is periodic? Status Last Run - {/*Actions*/} + Events + Errors @@ -84,22 +90,24 @@ const Tasks = () => { > {i + 1} {task.app}.{task.name} - {task.is_periodic ? : null} + {task.is_periodic ? : null} { task.history?.length ? <> {task.status ? capitalize(task.status) : "-"} {new Date(task.timestamp).toLocaleString()} + {task.history.length} + {task.errors?.length} - : Didn't run + : Didn't run } ) : - No crons available + No tasks available : - + { dispatch(selectItem(null)) setTaskErrorsOpen(false) setTaskHistoryOpen(false) - }}> + }} + dialogClassName="min-vw-50" + + >
@@ -124,20 +135,22 @@ const Tasks = () => { {selectedItem?.name}
-

App: {selectedItem?.app}

+

App: {selectedItem?.app}

{ selectedItem?.timestamp - ?

Last run: {selectedItem?.timestamp ? new Date(selectedItem.timestamp).toLocaleString() : null}

+ ?

Last + run: {selectedItem?.timestamp ? new Date(selectedItem.timestamp).toLocaleString() : null}

: null } - +

ID: {selectedItem?.id}

    { selectedItem - ? Object.keys(selectedItem).filter(k => !["timestamp", "name", "app"].includes(k)).map((k, i) => + ? Object.keys(selectedItem).filter(k => !["timestamp", "name", "app", "id"].includes(k)).map((k, i) => ["errors", "history"].includes(k) ?
  • { @@ -151,17 +164,15 @@ const Tasks = () => { { selectedItem[k].map((h, i) =>
  • -   + { - Object.keys(h).map(hkey => + Object.keys(h).filter(k => k !== "id").map(hkey => hkey === "timestamp" ? new Date(h[hkey]).toLocaleString() : hkey === "status" ? parseStatus(h[hkey]) - : hkey === "id" - ? `[${h[hkey].slice(0, 3)}..${h[hkey].slice(h[hkey].length - 3, h[hkey].length)}]` - : h[hkey] - ).join(" ") + : h[hkey] + ).join(" - ") }
  • ) @@ -178,7 +189,9 @@ const Tasks = () => { } - :
  • {capitalize(k)}: {selectedItem[k]}
  • + : k === "status" + ?
  • Status: {parseStatus(selectedItem[k])}
  • + :
  • {capitalize(k)}: {selectedItem[k]}
  • ) : null }