diff --git a/geest/core/json_tree_item.py b/geest/core/json_tree_item.py index fbdf9ce..1d9a024 100644 --- a/geest/core/json_tree_item.py +++ b/geest/core/json_tree_item.py @@ -49,8 +49,6 @@ def __init__(self, data, role, guid=None, parent=None): self._visible = True - self.updateStatus() - def set_visibility(self, visible: bool): """Sets the visibility of this item.""" self._visible = visible @@ -109,6 +107,14 @@ def isDimension(self): def isAnalysis(self): return self.role == "analysis" + def clear(self): + """ + Mark the item as not run, keeping any configurations made + """ + data = self.itemData[3] + data["result"] = "Not run" + data["result_file"] = "" + def getIcon(self): """Retrieve the appropriate icon for the item based on its role.""" if self.isDimension(): @@ -145,37 +151,23 @@ def getItemTooltip(self): def getStatusIcon(self): """Retrieve the appropriate icon for the item based on its role.""" status = self.getStatus() - if status == "✔️": + if status == "Completed successfully": return QIcon(resources_path("resources", "icons", "completed-success.svg")) - elif status == "-": + elif status == "Required and not configured": return QIcon( resources_path("resources", "icons", "required-not-configured.svg") ) - elif status == "!": + elif status == "Not configured (optional)": return QIcon(resources_path("resources", "icons", "not-configured.svg")) - elif status == "x": + elif status == "Configured, not run": + return QIcon(resources_path("resources", "icons", "not-run.svg")) + elif status == "Workflow failed": return QIcon(resources_path("resources", "icons", "failed.svg")) - elif status == "e": + elif status == "WRITE TOOL TIP": return QIcon(resources_path("resources", "icons", ".svg")) else: return QIcon(resources_path("resources", "icons", ".svg")) - def getStatusTooltip(self): - """Retrieve the appropriate tooltip for the item based on its role.""" - status = self.getStatus() - if status == "✔️": - return "Completed successfully" - elif status == "-": - return "Required and not configured" - elif status == "!": - return "Not configured (optional)" - elif status == "x": - return "Workflow failed" - elif status == "e": - return "WRITE TOOL TIP" - else: - return "" - def getStatus(self): """Return the status of the item as single character.""" try: @@ -188,23 +180,26 @@ def getStatus(self): # QgsMessageLog.logMessage(f"Data: {data}", tag="Geest", level=Qgis.Info) status = "" if "Error" in data.get("result", ""): - return "x" + return "Workflow failed" if "Failed" in data.get("result", ""): - return "x" + return "Workflow failed" # Item required and not configured if "Do Not Use" in data.get("analysis_mode", "") and data.get( "indicator_required", False ): - return "-" + return "Required and not configured" # Item not required but not configured if "Do Not Use" in data.get("analysis_mode", "") and not data.get( "indicator_required", False ): - return "!" + return "Not configured (optional)" + if "Not run" in data.get("result", "") and not data.get("result_file", ""): + return "Configured, not run" if "Workflow Completed" not in data.get("result", ""): - return "x" + return "Workflow failed" if "Workflow Completed" in data.get("result", ""): - return "✔️" + return "Completed successfully" + return "WRITE TOOL TIP" except Exception as e: verbose_mode = setting("verbose_mode", False) @@ -215,30 +210,7 @@ def getStatus(self): QgsMessageLog.logMessage( traceback.format_exc(), tag="Geest", level=Qgis.Warning ) - return "e" # e for error - - def updateStatus(self, status=None): - """Update the status of the item. - - If no status is provided we will compute the best status based on the item's attributes. - - :param status: The status to set the item to. - - :return: None - - Note: The status is stored in the second column of the itemData - """ - try: - if status is None: - status = self.getStatus() - # self.itemData[1] = status - taken care of by decoration role rather - except Exception as e: - QgsMessageLog.logMessage( - f"Error updating status: {e}", tag="Geest", level=Qgis.Warning - ) - QgsMessageLog.logMessage( - traceback.format_exc(), tag="Geest", level=Qgis.Warning - ) + return "WRITE TOOL TIP" def getFont(self): """Retrieve the appropriate font for the item based on its role.""" diff --git a/geest/gui/panels/tree_panel.py b/geest/gui/panels/tree_panel.py index b0377c9..2800c58 100644 --- a/geest/gui/panels/tree_panel.py +++ b/geest/gui/panels/tree_panel.py @@ -190,6 +190,10 @@ def __init__(self, parent=None, json_file=None): ) button_bar.addWidget(self.toggle_visibility_button) + self.clear_button = QPushButton("Clear", self) + self.clear_button.clicked.connect(self.clear_button_clicked) + button_bar.addWidget(self.clear_button) + self.project_button = QPushButton("Project") self.project_button.clicked.connect(self.switch_to_previous_tab) button_bar.addWidget(self.project_button) @@ -263,6 +267,15 @@ def on_previous_button_clicked(self): def on_next_button_clicked(self): self.switch_to_next_tab.emit() + def clear_button_clicked(self): + """ + Slot for handling the clear button click. + + This method is needed to avoid passing bool via the slot. + """ + self._clear_workflows() + self.save_json_to_working_directory() + @pyqtSlot(str) def working_directory_changed(self, new_directory): """Change the working directory and load the model.json if available.""" @@ -812,6 +825,49 @@ def _start_workflows(self, parent_item, role=None): # Recursively process children (dimensions, factors) self._start_workflows(child_item, role) + def _clear_workflows(self, parent_item=None): + """ + Recursively mark workflows as not done, delete their working directories and their file_paths. + + It will reflect the self.run_only_incomplete flag to only clear incomplete workflows if requested. + + :param parent_item: The parent item to process. If none, start from the root. + """ + # if the parent item is a boolean, it means we are running this from the clear button + if parent_item is None: + parent_item = self.model.rootItem + for i in range(parent_item.childCount()): + child_item = parent_item.child(i) + self._clear_workflows(child_item) + if child_item.data(3).get("result_file", None) and self.run_only_incomplete: + # if the item role is indicator, remove its entire folder + if child_item.role == "indicator": + result_file = child_item.data(3).get("result_file") + if result_file: + folder = os.path.dirname(result_file) + # check the folder exists + if os.path.exists(folder): + QgsMessageLog.logMessage( + f"Removing {folder}", tag="Geest", level=Qgis.Info + ) + shutil.rmtree(folder) + # if the item rols if factor or dimension, remove the files in it + # but not subdirs in case the user elected to keep them + else: + result_file = child_item.data(3).get("result_file") + if result_file: + folder = os.path.dirname(result_file) + # check if the folder exists + if os.path.exists(folder): + for file in os.listdir(folder): + file_path = os.path.join(folder, file) + if os.path.isfile(file_path): + os.remove(file_path) + child_item.clear() # sets status to not run and blanks file path + + elif not self.run_only_incomplete: + child_item.clear() + def _count_workflows_to_run(self, parent_item=None): """ Recursively count workflows that need to be run visiting each node in the tree. @@ -942,8 +998,6 @@ def on_workflow_completed(self, item, success): """ self.overall_progress_bar.setValue(self.overall_progress_bar.value() + 1) self.workflow_progress_bar.setValue(100) - - item.updateStatus() self.save_json_to_working_directory() self.add_to_map(item) @@ -978,6 +1032,7 @@ def run_all(self): self.run_only_incomplete = False self.items_to_run = 0 self._count_workflows_to_run() + self._clear_workflows() self._queue_workflows() def run_incomplete(self): diff --git a/geest/gui/views/treeview.py b/geest/gui/views/treeview.py index 9efb032..44cb0cd 100644 --- a/geest/gui/views/treeview.py +++ b/geest/gui/views/treeview.py @@ -233,11 +233,11 @@ def toggle_indicator_visibility(self, visible: bool, parent_item=None): visible (bool): Whether to show or hide the indicator nodes. parent_item (JsonTreeItem): Optional parent item to start from. If None, start from root. """ - QgsMessageLog.logMessage( - f"Toggling item visibility for item (was {visible})", - tag="Geest", - level=Qgis.Info, - ) + # QgsMessageLog.logMessage( + # f"Toggling item visibility for item (was {visible})", + # tag="Geest", + # level=Qgis.Info, + # ) parent_item = parent_item if parent_item else self.rootItem for i in range(parent_item.childCount()): @@ -289,7 +289,7 @@ def data(self, index, role): ): # Icon for the status column return item.getStatusIcon() elif role == Qt.ToolTipRole and index.column() == 1: - return item.getStatusTooltip() + return item.getStatus() elif role == Qt.ToolTipRole and index.column() == 0: return item.getItemTooltip() elif role == Qt.FontRole: @@ -708,11 +708,11 @@ def toggle_indicator_nodes(self): """Toggles visibility of all indicator nodes.""" indicators_visible = self._indicators_visible() self.model().toggle_indicator_visibility(not indicators_visible) - QgsMessageLog.logMessage( - f"Toggled indicator nodes (was {indicators_visible})", - tag="Geest", - level=Qgis.Info, - ) + # QgsMessageLog.logMessage( + # f"Toggled indicator nodes (was {indicators_visible})", + # tag="Geest", + # level=Qgis.Info, + # ) def _indicators_visible(self): """Checks if indicators are currently visible.""" diff --git a/geest/resources/icons/not-run.svg b/geest/resources/icons/not-run.svg new file mode 100644 index 0000000..59de05f --- /dev/null +++ b/geest/resources/icons/not-run.svg @@ -0,0 +1,31 @@ + + + + + + + + + +