Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 66 additions & 3 deletions src/clinical_dbs_annotator/controllers/wizard_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
views and models, handling user interactions and data flow.
"""

import csv

from PySide6.QtWidgets import QMessageBox

from ..config import (
Expand Down Expand Up @@ -427,6 +429,70 @@ def insert_session_row(self, view) -> None:
animate_button(view.insert_button)
view.session_notes_edit.clear()

# Enable undo button after successful insert
if hasattr(view, "undo_button"):
view.undo_button.setEnabled(True)

def undo_last_session_entry(self, view) -> None:
"""
Delete the last block_ID entry from the TSV file.

Args:
view: The Step3View instance
"""
if not self.session_data.is_file_open():
QMessageBox.warning(view, "Error", "No file is currently open.")
return

# Get the last written block_id (current block_id is the next one to write)
last_written_block_id = self.session_data.block_id - 1

if last_written_block_id < 0:
QMessageBox.warning(view, "Error", "No entries to undo.")
return

# Read the TSV file and filter out rows with the last block_id
file_path = self.session_data.file_path
rows_to_keep = []
rows_to_delete = []

with open(file_path, newline="", encoding="utf-8") as f:
reader = csv.DictReader(f, delimiter="\t")
fieldnames = reader.fieldnames

for row in reader:
block_id = row.get("block_id", "")
try:
if int(block_id) != last_written_block_id:
rows_to_keep.append(row)
else:
rows_to_delete.append(row)
except ValueError, TypeError:
# If block_id is not a number, keep the row
rows_to_keep.append(row)

if not rows_to_delete:
QMessageBox.warning(
view,
"Error",
f"No entries found with block_id {last_written_block_id}.",
)
return

# Decrement block_id to point to the previous entry
self.session_data.block_id = last_written_block_id

# Rewrite the TSV file with the filtered rows
with open(file_path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames, delimiter="\t")
writer.writeheader()
writer.writerows(rows_to_keep)

# Disable undo button if no more entries to undo
if self.session_data.block_id == 0 or len(rows_to_keep) == 0:
if hasattr(view, "undo_button"):
view.undo_button.setEnabled(False)

def close_session(self, parent) -> None:
"""
Close the current session and file.
Expand All @@ -445,9 +511,6 @@ def close_session(self, parent) -> None:

if reply == QMessageBox.Ok:
self.session_data.close_file()
QMessageBox.information(
parent, "Session closed", "Session closed and file saved."
)
parent.close()

def export_session_word(self, parent, scale_prefs=None, sections=None) -> None:
Expand Down
26 changes: 25 additions & 1 deletion src/clinical_dbs_annotator/views/step3_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
session data including stimulation parameters and scale values.
"""

from PySide6.QtCore import Qt
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QDoubleValidator, QIcon, QIntValidator, QPixmap
from PySide6.QtWidgets import (
QComboBox,
Expand Down Expand Up @@ -61,6 +61,8 @@ class Step3View(BaseStepView):
- Data insertion and session closing
"""

undo_requested = Signal()

def __init__(self, parent_style):
"""
Initialize Step 3 view.
Expand Down Expand Up @@ -111,6 +113,20 @@ def _get_theme_icon_color(self) -> str:

return get_theme_manager().get_theme_color("Icon")

def _undo_last_entry(self) -> None:
"""Show confirmation dialog and delete the last block_ID entry from TSV."""
reply = QMessageBox.question(
self,
"Confirm Undo",
"Are you sure you want to delete the last session entry?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
)

if reply == QMessageBox.Yes:
# Emit signal to request undo
self.undo_requested.emit()

def _setup_ui(self) -> None:
"""Set up the UI layout."""

Expand Down Expand Up @@ -196,6 +212,13 @@ def _setup_ui(self) -> None:
self.main_layout.addWidget(splitter)
# self.main_layout.addStretch(1)

self.undo_button = QPushButton("Undo")
self.undo_button.setIcon(
self.parent_style.standardIcon(QStyle.SP_DialogCancelButton)
)
self.undo_button.setMinimumWidth(100)
self.undo_button.setEnabled(False)

self.insert_button = QPushButton("Insert")
self.insert_button.setIcon(
self.parent_style.standardIcon(QStyle.SP_DialogApplyButton)
Expand Down Expand Up @@ -711,6 +734,7 @@ def _create_session_scales_group(self) -> QGroupBox:
self.step3_session_scales_form.setLabelAlignment(Qt.AlignRight)
self.step3_session_scales_form.setFormAlignment(Qt.AlignTop)
layout.addLayout(self.step3_session_scales_form)
layout.addStretch()

return gb_scales

Expand Down
10 changes: 10 additions & 0 deletions src/clinical_dbs_annotator/views/wizard_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,13 @@ def _connect_step2_signals(self) -> None:
def _connect_step3_signals(self) -> None:
"""Connect Step 3 view signals to controller."""
self.controller.prepare_step3(self.step3_view)
if hasattr(self.step3_view, "undo_button"):
self.step3_view.undo_button.clicked.connect(
self.step3_view._undo_last_entry
)
self.step3_view.undo_requested.connect(
lambda: self.controller.undo_last_session_entry(self.step3_view)
)
self.step3_view.insert_button.clicked.connect(
lambda: self.controller.insert_session_row(self.step3_view)
)
Expand Down Expand Up @@ -793,6 +800,9 @@ def _refresh_nav_right(self) -> None:
if hasattr(current, "next_button"):
widgets.append(current.next_button)

if hasattr(current, "undo_button"):
widgets.append(current.undo_button)

if hasattr(current, "insert_button"):
widgets.append(current.insert_button)

Expand Down