From dc64a66f0f397d2f9ba58377d88f4cbafebd408e Mon Sep 17 00:00:00 2001 From: Zoe Forbes Date: Sat, 14 Feb 2026 15:28:40 -0600 Subject: [PATCH] feat: show DAG status and job events in Activity panel Connects dag_updated, dag_validated, and job lifecycle signals from SiloEventListener to the Database Activity dock widget. - dag.updated: inserts DAG sync status (node/edge count) - dag.validated: inserts pass/fail badge with failed count - job.created: inserts queued job entry - job.completed: refreshes the full activity list - job.failed: inserts error entry Live entries are inserted at the top of the activity list, styled in Catppuccin Blue, capped at 50 entries. Closes kindred/create#219 --- freecad/silo_commands.py | 69 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/freecad/silo_commands.py b/freecad/silo_commands.py index 04b8985..91960ba 100644 --- a/freecad/silo_commands.py +++ b/freecad/silo_commands.py @@ -2732,6 +2732,11 @@ class SiloAuthDockWidget: self._event_listener.revision_created.connect(self._on_remote_revision) self._event_listener.connection_status.connect(self._on_sse_status) self._event_listener.server_mode_changed.connect(self._on_server_mode) + self._event_listener.dag_updated.connect(self._on_dag_updated) + self._event_listener.dag_validated.connect(self._on_dag_validated) + self._event_listener.job_created.connect(self._on_job_created) + self._event_listener.job_completed.connect(self._on_job_completed) + self._event_listener.job_failed.connect(self._on_job_failed) self._event_listener.start() else: if self._event_listener is not None and self._event_listener.isRunning(): @@ -2811,6 +2816,70 @@ class SiloAuthDockWidget: ) self._refresh_activity_panel() + def _on_dag_updated(self, part_number, node_count, edge_count): + FreeCAD.Console.PrintMessage( + f"Silo: DAG updated for {part_number}" + f" ({node_count} nodes, {edge_count} edges)\n" + ) + self._add_activity_entry( + f"\u25b6 {part_number} \u2013 DAG synced" + f" ({node_count} nodes, {edge_count} edges)", + part_number, + ) + + def _on_dag_validated(self, part_number, valid, failed_count): + if valid: + status = "\u2713 PASS" + FreeCAD.Console.PrintMessage(f"Silo: Validation passed for {part_number}\n") + else: + status = f"\u2717 FAIL ({failed_count} failed)" + FreeCAD.Console.PrintWarning( + f"Silo: Validation failed for {part_number}" + f" ({failed_count} features failed)\n" + ) + self._add_activity_entry(f"{status} \u2013 {part_number}", part_number) + + def _on_job_created(self, job_id, definition_name, part_number): + FreeCAD.Console.PrintMessage( + f"Silo: Job {definition_name} created for {part_number}\n" + ) + self._add_activity_entry( + f"\u23f3 {part_number} \u2013 {definition_name} queued", + part_number, + ) + + def _on_job_completed(self, job_id): + FreeCAD.Console.PrintMessage(f"Silo: Job {job_id} completed\n") + self._refresh_activity_panel() + + def _on_job_failed(self, job_id, error): + FreeCAD.Console.PrintError(f"Silo: Job {job_id} failed: {error}\n") + self._add_activity_entry(f"\u2717 Job {job_id[:8]} failed: {error}", None) + + def _add_activity_entry(self, text, part_number): + """Insert a live event entry at the top of the Activity panel.""" + from PySide import QtCore, QtGui, QtWidgets + + mw = FreeCADGui.getMainWindow() + if mw is None: + return + panel = mw.findChild(QtWidgets.QDockWidget, "SiloDatabaseActivity") + if panel is None: + return + activity_list = panel.findChild(QtWidgets.QListWidget) + if activity_list is None: + return + + item = QtWidgets.QListWidgetItem(text) + if part_number: + item.setData(QtCore.Qt.UserRole, part_number) + item.setForeground(QtGui.QColor("#89b4fa")) + activity_list.insertItem(0, item) + + # Cap the list at 50 entries + while activity_list.count() > 50: + activity_list.takeItem(activity_list.count() - 1) + def _refresh_activity_panel(self): """Refresh the Database Activity panel if it exists.""" from PySide import QtCore, QtGui, QtWidgets