Skip to content

Suggest next sub ses remote #484

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
af61d0a
add: custom spinner widget
Mar 12, 2025
6d55461
add: search remote for suggestions checkbox
Mar 12, 2025
aabfe98
remove: minor bug
Mar 12, 2025
938a79e
add: tooltips for search remote checkbox
Mar 12, 2025
c289849
convert fill input to a worker; add a spinner to indicate loading
Mar 12, 2025
866f06d
fix merge conflicts, slight refactor
Mar 19, 2025
27c3261
update: display popup for suggesting next sub/ses remote
Apr 8, 2025
36177c0
update: handle popup closing on error
Apr 9, 2025
8863982
slight refactor
Apr 9, 2025
fe9d00c
rename remote to central and change tooltip
May 23, 2025
fdefeb8
refactor functions and some minor changes
May 24, 2025
5ffe35d
add: new tests for searching central for suggestions feature
May 28, 2025
951be88
fix: failing existing tests
May 28, 2025
450cf27
first attempt at fixing tests
May 30, 2025
5d2f82c
Apply suggestions from code review
cs7-shrey Jun 6, 2025
a45e5c0
refactor: apply code review changes
Jun 6, 2025
25617d4
fix minor bug
Jun 9, 2025
a0421ff
first attempt at testing error popup
Jun 9, 2025
f050150
minor changes
Jun 12, 2025
5f0d0f2
remove: existing textual version bug
Jun 16, 2025
0285538
fix: minor bug
Jun 16, 2025
0c9e3f7
update: enhanced decorator for input box
Jun 16, 2025
deef98c
refactor: changes to decorator specifying id of clicked element
Jun 18, 2025
990a43d
Revert `node` rename
JoeZiminski Jun 18, 2025
203a8a8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 18, 2025
049d4ab
Fix linting.
JoeZiminski Jun 18, 2025
824e2f8
Revert node->event.
JoeZiminski Jun 18, 2025
f7b5ef6
Use correct event types, and use these in the double click decorator.
JoeZiminski Jun 19, 2025
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
1 change: 1 addition & 0 deletions datashuttle/configs/canonical_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ def get_tui_config_defaults() -> Dict:
"bypass_validation": False,
"overwrite_existing_files": "never",
"dry_run": False,
"suggest_next_sub_ses_central": False,
}
}

Expand Down
6 changes: 5 additions & 1 deletion datashuttle/datashuttle_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,11 @@ def _update_settings_with_new_canonical_keys(self, settings: Dict):
if "tui" not in settings:
settings.update(canonical_tui_configs)

for key in ["overwrite_existing_files", "dry_run"]:
for key in [
"overwrite_existing_files",
"dry_run",
"suggest_next_sub_ses_central",
]:
if key not in settings["tui"]:
settings["tui"][key] = canonical_tui_configs["tui"][key]

Expand Down
7 changes: 7 additions & 0 deletions datashuttle/tui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ def load_project_page(self, interface: Interface) -> None:
def show_modal_error_dialog(self, message: str) -> None:
self.push_screen(modal_dialogs.MessageBox(message, border_color="red"))

def show_modal_error_dialog_from_main_thread(self, message: str) -> None:
"""
Used to call `show_modal_error_dialog from main thread when executing
in another thread. Throws error when called from main thread.
"""
self.call_from_thread(self.show_modal_error_dialog, message)

def handle_open_filesystem_browser(self, path_: Path) -> None:
"""
Open the system file browser to the path with the `showinfm`
Expand Down
46 changes: 44 additions & 2 deletions datashuttle/tui/css/tui_menu.tcss
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,16 @@ CreateFoldersSettingsScreen {
width: 80%;
background: $primary-background;
border: tall $panel-lighten-3;
}
}
#checkbox_container {
height: 65%;
overflow: hidden auto;
}
#toplevel_folder_select_container {
height: 15%;
}
#template_top_container {
height: 50%;
height: 70%;
background: $primary-background;
border: tall $panel-lighten-3;
overflow: hidden auto;
Expand Down Expand Up @@ -436,6 +443,41 @@ ConfirmAndAwaitTransferPopup {
content-align: center middle;
}

/* Suggest next subject / session loading pop up --------------------------------------------------- */

SearchingCentralForNextSubSesPopup {
align: center middle;
}

#searching_top_container {
align: center middle;
height: 15;
width: 65;
border: tall $panel-lighten-1;
background: $primary-background;
}

#searching_top_container:light {
background: $boost;
border: tall $panel-darken-3;
}

#searching_message_label {
align: center middle;
text-align: center;
overflow: hidden auto;
width: 100%;
margin: 1 0 0 0;
}

#searching_animated_indicator {
align: center middle;
padding: 0;
height: 3;
margin: 1 0 0 0;
content-align: center middle;
}

/* Light Mode Error Screen */

MessageBox:light > #confirm_top_container {
Expand Down
8 changes: 4 additions & 4 deletions datashuttle/tui/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,28 +432,28 @@ def get_textual_compatible_project_configs(self) -> Configs:
return cfg_to_load

def get_next_sub(
self, top_level_folder: TopLevelFolder
self, top_level_folder: TopLevelFolder, include_central: bool
) -> InterfaceOutput:
try:
next_sub = self.project.get_next_sub(
top_level_folder,
return_with_prefix=True,
include_central=False,
include_central=include_central,
)
return True, next_sub
except BaseException as e:
return False, str(e)

def get_next_ses(
self, top_level_folder: TopLevelFolder, sub: str
self, top_level_folder: TopLevelFolder, sub: str, include_central: bool
) -> InterfaceOutput:

try:
next_ses = self.project.get_next_ses(
top_level_folder,
sub,
return_with_prefix=True,
include_central=False,
include_central=include_central,
)
return True, next_ses
except BaseException as e:
Expand Down
73 changes: 45 additions & 28 deletions datashuttle/tui/screens/create_folder_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ def compose(self) -> ComposeResult:
"""

bypass_validation = self.interface.tui_settings["bypass_validation"]
suggest_next_sub_ses_central = self.interface.tui_settings[
"suggest_next_sub_ses_central"
]

yield Container(
Horizontal(
Expand All @@ -97,43 +100,52 @@ def compose(self) -> ComposeResult:
self.interface,
id="create_folders_settings_toplevel_select",
),
),
Checkbox(
"Bypass validation",
value=bypass_validation,
id="create_folders_settings_bypass_validation_checkbox",
id="toplevel_folder_select_container",
),
Container(
Horizontal(
Checkbox(
"Template Validation",
id="template_settings_validation_on_checkbox",
value=self.interface.get_name_templates()["on"],
),
id="template_inner_horizontal_container",
Checkbox(
"Search Central For Suggestions",
value=suggest_next_sub_ses_central,
id="suggest_next_sub_ses_central_checkbox",
),
Checkbox(
"Bypass validation",
value=bypass_validation,
id="create_folders_settings_bypass_validation_checkbox",
),
Container(
Label(explanation, id="template_message_label"),
Horizontal(
Checkbox(
"Template validation",
id="template_settings_validation_on_checkbox",
value=self.interface.get_name_templates()["on"],
),
id="template_inner_horizontal_container",
),
Container(
RadioSet(
RadioButton(
"Subject",
id="template_settings_subject_radiobutton",
value=sub_on,
),
RadioButton(
"Session",
id="template_settings_session_radiobutton",
value=ses_on,
Label(explanation, id="template_message_label"),
Container(
RadioSet(
RadioButton(
"Subject",
id="template_settings_subject_radiobutton",
value=sub_on,
),
RadioButton(
"Session",
id="template_settings_session_radiobutton",
value=ses_on,
),
id="template_settings_radioset",
),
id="template_settings_radioset",
Input(id="template_settings_input"),
id="template_other_widgets_container",
),
Input(id="template_settings_input"),
id="template_other_widgets_container",
id="template_inner_container",
),
id="template_inner_container",
id="template_top_container",
),
id="template_top_container",
id="checkbox_container",
),
Container(),
Button("Close", id="create_folders_settings_close_button"),
Expand All @@ -145,6 +157,7 @@ def on_mount(self) -> None:
"#create_folders_settings_toplevel_select",
"#create_folders_settings_bypass_validation_checkbox",
"#template_settings_validation_on_checkbox",
"#suggest_next_sub_ses_central_checkbox",
]:
self.query_one(id).tooltip = get_tooltip(id)

Expand Down Expand Up @@ -235,6 +248,10 @@ def on_checkbox_changed(self, event: Checkbox.Changed) -> None:
self.query_one("#template_inner_container").disabled = (
disable_container
)
elif event.checkbox.id == "suggest_next_sub_ses_central_checkbox":
self.interface.save_tui_settings(
is_on, "suggest_next_sub_ses_central"
)

def on_radio_set_changed(self, event: RadioSet.Changed) -> None:
"""
Expand Down
39 changes: 33 additions & 6 deletions datashuttle/tui/screens/modal_dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
from pathlib import Path

from textual.app import ComposeResult
from textual.widgets import DirectoryTree
from textual.worker import Worker

from datashuttle.tui.app import TuiApp
from datashuttle.utils.custom_types import InterfaceOutput
from datashuttle.utils.custom_types import InterfaceOutput, Prefix

from pathlib import Path

Expand All @@ -19,7 +20,10 @@
from textual.widgets import Button, Input, Label, LoadingIndicator, Static

from datashuttle.tui.custom_widgets import CustomDirectoryTree
from datashuttle.tui.utils.tui_decorators import require_double_click
from datashuttle.tui.utils.tui_decorators import (
ClickInfo,
require_double_click,
)


class MessageBox(ModalScreen):
Expand Down Expand Up @@ -137,6 +141,27 @@ async def handle_transfer_and_update_ui_when_complete(self) -> None:
self.app.show_modal_error_dialog(output)


class SearchingCentralForNextSubSesPopup(ModalScreen):
"""
A popup to show message and a loading indicator when awaiting search next sub/ses across
the folders present in both local and central machines. This search happens in a separate
thread so as to allow TUI to display the loading indicate without freezing.

Only displayed when the `include_central` flag is checked and the connection method is "ssh".
"""

def __init__(self, sub_or_ses: Prefix) -> None:
super().__init__()
self.message = f"Searching central for next {sub_or_ses}"

def compose(self) -> ComposeResult:
yield Container(
Label(self.message, id="searching_message_label"),
LoadingIndicator(id="searching_animated_indicator"),
id="searching_top_container",
)


class SelectDirectoryTreeScreen(ModalScreen):
"""
A modal screen that includes a DirectoryTree to browse
Expand Down Expand Up @@ -165,7 +190,7 @@ def __init__(
path_ = Path().home()
self.path_ = path_

self.prev_click_time = 0
self.click_info = ClickInfo()

def compose(self) -> ComposeResult:

Expand All @@ -186,11 +211,13 @@ def compose(self) -> ComposeResult:
)

@require_double_click
def on_directory_tree_directory_selected(self, node) -> None:
if node.path.is_file():
def on_directory_tree_directory_selected(
self, event: DirectoryTree.DirectorySelected
) -> None:
if event.path.is_file():
return
else:
self.dismiss(node.path)
self.dismiss(event.path)

def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "cancel_button":
Expand Down
Loading
Loading