Skip to content

Commit b6fe35b

Browse files
authored
Merge pull request #28 from Brain-Modulation-Lab/feat/undo-tsv
feat: undo from tsv table
2 parents 72df413 + d795d54 commit b6fe35b

3 files changed

Lines changed: 101 additions & 4 deletions

File tree

src/clinical_dbs_annotator/controllers/wizard_controller.py

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
views and models, handling user interactions and data flow.
66
"""
77

8+
import csv
9+
810
from PySide6.QtWidgets import QMessageBox
911

1012
from ..config import (
@@ -427,6 +429,70 @@ def insert_session_row(self, view) -> None:
427429
animate_button(view.insert_button)
428430
view.session_notes_edit.clear()
429431

432+
# Enable undo button after successful insert
433+
if hasattr(view, "undo_button"):
434+
view.undo_button.setEnabled(True)
435+
436+
def undo_last_session_entry(self, view) -> None:
437+
"""
438+
Delete the last block_ID entry from the TSV file.
439+
440+
Args:
441+
view: The Step3View instance
442+
"""
443+
if not self.session_data.is_file_open():
444+
QMessageBox.warning(view, "Error", "No file is currently open.")
445+
return
446+
447+
# Get the last written block_id (current block_id is the next one to write)
448+
last_written_block_id = self.session_data.block_id - 1
449+
450+
if last_written_block_id < 0:
451+
QMessageBox.warning(view, "Error", "No entries to undo.")
452+
return
453+
454+
# Read the TSV file and filter out rows with the last block_id
455+
file_path = self.session_data.file_path
456+
rows_to_keep = []
457+
rows_to_delete = []
458+
459+
with open(file_path, newline="", encoding="utf-8") as f:
460+
reader = csv.DictReader(f, delimiter="\t")
461+
fieldnames = reader.fieldnames
462+
463+
for row in reader:
464+
block_id = row.get("block_id", "")
465+
try:
466+
if int(block_id) != last_written_block_id:
467+
rows_to_keep.append(row)
468+
else:
469+
rows_to_delete.append(row)
470+
except ValueError, TypeError:
471+
# If block_id is not a number, keep the row
472+
rows_to_keep.append(row)
473+
474+
if not rows_to_delete:
475+
QMessageBox.warning(
476+
view,
477+
"Error",
478+
f"No entries found with block_id {last_written_block_id}.",
479+
)
480+
return
481+
482+
# Decrement block_id to point to the previous entry
483+
self.session_data.block_id = last_written_block_id
484+
485+
# Rewrite the TSV file with the filtered rows
486+
with open(file_path, "w", newline="", encoding="utf-8") as f:
487+
writer = csv.DictWriter(f, fieldnames=fieldnames, delimiter="\t")
488+
writer.writeheader()
489+
writer.writerows(rows_to_keep)
490+
491+
# Disable undo button if no more entries to undo
492+
if self.session_data.block_id == 0 or len(rows_to_keep) == 0:
493+
if hasattr(view, "undo_button"):
494+
view.undo_button.setEnabled(False)
495+
430496
def close_session(self, parent) -> None:
431497
"""
432498
Close the current session and file.
@@ -445,9 +511,6 @@ def close_session(self, parent) -> None:
445511

446512
if reply == QMessageBox.Ok:
447513
self.session_data.close_file()
448-
QMessageBox.information(
449-
parent, "Session closed", "Session closed and file saved."
450-
)
451514
parent.close()
452515

453516
def export_session_word(self, parent, scale_prefs=None, sections=None) -> None:

src/clinical_dbs_annotator/views/step3_view.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
session data including stimulation parameters and scale values.
66
"""
77

8-
from PySide6.QtCore import Qt
8+
from PySide6.QtCore import Qt, Signal
99
from PySide6.QtGui import QDoubleValidator, QIcon, QIntValidator, QPixmap
1010
from PySide6.QtWidgets import (
1111
QComboBox,
@@ -61,6 +61,8 @@ class Step3View(BaseStepView):
6161
- Data insertion and session closing
6262
"""
6363

64+
undo_requested = Signal()
65+
6466
def __init__(self, parent_style):
6567
"""
6668
Initialize Step 3 view.
@@ -111,6 +113,20 @@ def _get_theme_icon_color(self) -> str:
111113

112114
return get_theme_manager().get_theme_color("Icon")
113115

116+
def _undo_last_entry(self) -> None:
117+
"""Show confirmation dialog and delete the last block_ID entry from TSV."""
118+
reply = QMessageBox.question(
119+
self,
120+
"Confirm Undo",
121+
"Are you sure you want to delete the last session entry?",
122+
QMessageBox.Yes | QMessageBox.No,
123+
QMessageBox.No,
124+
)
125+
126+
if reply == QMessageBox.Yes:
127+
# Emit signal to request undo
128+
self.undo_requested.emit()
129+
114130
def _setup_ui(self) -> None:
115131
"""Set up the UI layout."""
116132

@@ -196,6 +212,13 @@ def _setup_ui(self) -> None:
196212
self.main_layout.addWidget(splitter)
197213
# self.main_layout.addStretch(1)
198214

215+
self.undo_button = QPushButton("Undo")
216+
self.undo_button.setIcon(
217+
self.parent_style.standardIcon(QStyle.SP_DialogCancelButton)
218+
)
219+
self.undo_button.setMinimumWidth(100)
220+
self.undo_button.setEnabled(False)
221+
199222
self.insert_button = QPushButton("Insert")
200223
self.insert_button.setIcon(
201224
self.parent_style.standardIcon(QStyle.SP_DialogApplyButton)
@@ -711,6 +734,7 @@ def _create_session_scales_group(self) -> QGroupBox:
711734
self.step3_session_scales_form.setLabelAlignment(Qt.AlignRight)
712735
self.step3_session_scales_form.setFormAlignment(Qt.AlignTop)
713736
layout.addLayout(self.step3_session_scales_form)
737+
layout.addStretch()
714738

715739
return gb_scales
716740

src/clinical_dbs_annotator/views/wizard_window.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,13 @@ def _connect_step2_signals(self) -> None:
686686
def _connect_step3_signals(self) -> None:
687687
"""Connect Step 3 view signals to controller."""
688688
self.controller.prepare_step3(self.step3_view)
689+
if hasattr(self.step3_view, "undo_button"):
690+
self.step3_view.undo_button.clicked.connect(
691+
self.step3_view._undo_last_entry
692+
)
693+
self.step3_view.undo_requested.connect(
694+
lambda: self.controller.undo_last_session_entry(self.step3_view)
695+
)
689696
self.step3_view.insert_button.clicked.connect(
690697
lambda: self.controller.insert_session_row(self.step3_view)
691698
)
@@ -793,6 +800,9 @@ def _refresh_nav_right(self) -> None:
793800
if hasattr(current, "next_button"):
794801
widgets.append(current.next_button)
795802

803+
if hasattr(current, "undo_button"):
804+
widgets.append(current.undo_button)
805+
796806
if hasattr(current, "insert_button"):
797807
widgets.append(current.insert_button)
798808

0 commit comments

Comments
 (0)