Skip to content

Commit aef36f9

Browse files
Support alphanumeric sub or ses labels. (#575)
* Support alphanumeric sub or ses labels. * Fix linting. * Fix accidentally included dash in check. * Fix tests. * Fixing tests. * Small fixes, add first tests. * Add tests. * Rename placeholder variable. * Add tooltips. * Tidying up TUI, trying to get create settings page nice. * Fix tests. * Fix tests again. * Update docs text. * Update tcss. * Update documentation. * Small tidy ups. * Turn off validation during data transfers, add test. * Tidy up validation. * Explain better the leading zero case, do not mention this generally because inconsnstent zeros are not allowed anyway. * small fix to doc. * Small fixes to docs and docstrings. * Small changes to tcss. * Add test docstring. * Fix after rebase. * Rename alphanuermic to letters in in argument name. * Update tests/tests_tui/test_tui_widgets_and_defaults.py Co-authored-by: Shrey Singh <96627769+cs7-shrey@users.noreply.github.com> * Remove 'off'. * Use allow_letters_in_sub_ses_values in test. * Get allow_letters_in_sub_ses_values from tui_settings in interface. * Add comment to check_and_format_names. * Typo fix. --------- Co-authored-by: Shrey Singh <96627769+cs7-shrey@users.noreply.github.com>
1 parent 8cfdec7 commit aef36f9

30 files changed

Lines changed: 666 additions & 152 deletions

datashuttle/configs/canonical_configs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ def get_tui_config_defaults() -> Dict:
279279
"overwrite_existing_files": "never",
280280
"dry_run": False,
281281
"suggest_next_sub_ses_central": False,
282+
"allow_letters_in_sub_ses_values": False,
282283
}
283284
}
284285

datashuttle/datashuttle_class.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ def create_folders(
124124
ses_names: Optional[Union[str, List[str]]] = None,
125125
datatype: Union[str, List[str]] = "",
126126
bypass_validation: bool = False,
127+
allow_letters_in_sub_ses_values: bool = False,
127128
log: bool = True,
128129
) -> Dict[str, List[Path]]:
129130
"""Create a folder tree in the project folder.
@@ -159,6 +160,13 @@ def create_folders(
159160
If `True`, folders will be created even if they are not
160161
valid to NeuroBlueprint style.
161162
163+
allow_letters_in_sub_ses_values
164+
If `True`, any alphanumeric character are allowed for the values associated
165+
with sub- or ses- keys. Otherwise, values must be integer
166+
and the following additional checks are performed:
167+
168+
- Labels must be the same length (e.g. sub-01 and sub-002 is invalid).
169+
162170
log
163171
If `True`, details of folder creation will be logged.
164172
@@ -217,6 +225,7 @@ def create_folders(
217225
ses_names,
218226
name_templates,
219227
bypass_validation,
228+
allow_letters_in_sub_ses_values,
220229
log=True,
221230
)
222231

@@ -253,16 +262,25 @@ def _format_and_validate_names(
253262
ses_names: Optional[Union[str, List[str]]],
254263
name_templates: Dict,
255264
bypass_validation: bool,
265+
allow_letters_in_sub_ses_values: bool,
256266
log: bool = True,
257267
) -> Tuple[List[str], List[str]]:
258268
"""Central method to format and validate subject and session names."""
259269
format_sub = formatting.check_and_format_names(
260-
sub_names, "sub", name_templates, bypass_validation
270+
sub_names,
271+
"sub",
272+
name_templates,
273+
bypass_validation,
274+
allow_letters_in_sub_ses_values,
261275
)
262276

263277
if ses_names is not None:
264278
format_ses = formatting.check_and_format_names(
265-
ses_names, "ses", name_templates, bypass_validation
279+
ses_names,
280+
"ses",
281+
name_templates,
282+
bypass_validation,
283+
allow_letters_in_sub_ses_values,
266284
)
267285
else:
268286
format_ses = []
@@ -277,6 +295,7 @@ def _format_and_validate_names(
277295
display_mode="error",
278296
log=log,
279297
name_templates=name_templates,
298+
allow_letters_in_sub_ses_values=allow_letters_in_sub_ses_values,
280299
)
281300

282301
return format_sub, format_ses
@@ -1277,6 +1296,7 @@ def validate_project(
12771296
display_mode: DisplayMode,
12781297
include_central: bool = False,
12791298
strict_mode: bool = False,
1299+
allow_letters_in_sub_ses_values: bool = False,
12801300
) -> List[str]:
12811301
"""Perform validation on the project.
12821302
@@ -1306,6 +1326,13 @@ def validate_project(
13061326
any folder not prefixed with sub-, ses- or a valid datatype will
13071327
raise a validation issue.
13081328
1329+
allow_letters_in_sub_ses_values
1330+
If `True`, any alphanumeric character are allowed for the values associated
1331+
with sub- or ses- keys. Otherwise, values must be integer
1332+
and the following additional checks are performed:
1333+
1334+
- Labels must be the same length (e.g. sub-01 and sub-002 is invalid).
1335+
13091336
Returns
13101337
-------
13111338
error_messages
@@ -1344,14 +1371,19 @@ def validate_project(
13441371
display_mode=display_mode,
13451372
name_templates=name_templates,
13461373
strict_mode=strict_mode,
1374+
allow_letters_in_sub_ses_values=allow_letters_in_sub_ses_values,
13471375
)
13481376

13491377
ds_logger.close_log_filehandler()
13501378

13511379
return error_messages
13521380

13531381
@staticmethod
1354-
def check_name_formatting(names: Union[str, list], prefix: Prefix) -> None:
1382+
def check_name_formatting(
1383+
names: Union[str, list],
1384+
prefix: Prefix,
1385+
allow_letters_in_sub_ses_values: bool = False,
1386+
) -> None:
13551387
"""Format a list of subject or session names.
13561388
13571389
Pass list of names to check how these will be auto-formatted,
@@ -1369,6 +1401,13 @@ def check_name_formatting(names: Union[str, list], prefix: Prefix) -> None:
13691401
The relevant subject or session prefix,
13701402
e.g. ``"sub-"`` or ``"ses-"``
13711403
1404+
allow_letters_in_sub_ses_values
1405+
If `True`, any alphanumeric character are allowed for the values associated
1406+
with sub- or ses- keys. Otherwise, values must be integer
1407+
and the following additional checks are performed:
1408+
1409+
- Labels must be the same length (e.g. sub-01 and sub-002 is invalid).
1410+
13721411
"""
13731412
if prefix not in ["sub", "ses"]:
13741413
utils.log_and_raise_error(
@@ -1379,7 +1418,11 @@ def check_name_formatting(names: Union[str, list], prefix: Prefix) -> None:
13791418
if isinstance(names, str):
13801419
names = [names]
13811420

1382-
formatted_names = formatting.check_and_format_names(names, prefix)
1421+
formatted_names = formatting.check_and_format_names(
1422+
names,
1423+
prefix,
1424+
allow_letters_in_sub_ses_values=allow_letters_in_sub_ses_values,
1425+
)
13831426
utils.print_message_to_user(formatted_names)
13841427

13851428
# -------------------------------------------------------------------------
@@ -1636,6 +1679,7 @@ def _update_settings_with_new_canonical_keys(self, settings: Dict) -> None:
16361679
"overwrite_existing_files",
16371680
"dry_run",
16381681
"suggest_next_sub_ses_central",
1682+
"allow_letters_in_sub_ses_values",
16391683
]:
16401684
if key not in settings["tui"]:
16411685
settings["tui"][key] = canonical_tui_configs["tui"][key]

datashuttle/datashuttle_functions.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def validate_project_from_path(
2828
display_mode: DisplayMode = "warn",
2929
strict_mode: bool = False,
3030
name_templates: Optional[Dict] = None,
31+
allow_letters_in_sub_ses_values: bool = False,
3132
) -> List[str]:
3233
"""Perform validation on a NeuroBlueprint-formatted project.
3334
@@ -58,6 +59,13 @@ def validate_project_from_path(
5859
to validate against. See ``DataShuttle.set_name_templates()``
5960
for details.
6061
62+
allow_letters_in_sub_ses_values
63+
If `True`, any alphanumeric character are allowed for the values associated
64+
with sub- or ses- keys. Otherwise, values must be integer
65+
and the following additional checks are performed:
66+
67+
- Labels must be the same length (e.g. sub-01 and sub-002 is invalid).
68+
6169
Returns
6270
-------
6371
error_messages
@@ -95,6 +103,7 @@ def validate_project_from_path(
95103
display_mode=display_mode,
96104
name_templates=name_templates,
97105
strict_mode=strict_mode,
106+
allow_letters_in_sub_ses_values=allow_letters_in_sub_ses_values,
98107
)
99108

100109
return error_messages

datashuttle/tui/css/tui_menu.tcss

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -314,26 +314,43 @@ config label and button align center */
314314
#validate_top_container {
315315
align: center top;
316316
content-align: center top;
317-
margin: 2 0 0 0;
317+
margin: 2 0 1 0;
318318
layout: vertical;
319319
overflow: hidden auto;
320320
}
321321

322-
323322
#validate_arguments_horizontal{
324323
height: auto;
325-
margin: 0 1 1 1;
324+
margin: 0 1 1 0;
326325
}
327326

328327
#validate_top_level_folder_select {
329328
width: 30%;
329+
margin: 0 0 0 1;
330+
}
331+
332+
#validate_path_label {
333+
margin: 0 0 0 2;
330334
}
331335

332336
#validate_path_input {
333337
width: 80%;
334338
margin: 1 1 1 1;
335339
}
336340

341+
#validate_allow_letters_in_sub_ses_values_checkbox {
342+
margin: 0 0 0 6;
343+
}
344+
345+
#validate_strict_mode_checkbox {
346+
margin: 0 0 0 1;
347+
}
348+
349+
#validate_include_central_checkbox {
350+
margin: 0 6 0 1
351+
}
352+
353+
337354
#validate_select_button {
338355
margin: 1 1 1 1;
339356
}
@@ -387,12 +404,8 @@ DisplayedDatatypesScreen {
387404
CreateFoldersSettingsScreen {
388405
align: center middle;
389406
}
390-
391-
#labelTESTEST {
392-
padding: 1 0 1 2;
393-
}
394-
#tabscreen_toplevel_select {
395-
padding: 0 0 0 1;
407+
#create_folders_settings_toplevel_label {
408+
margin: 1 0 0 2;
396409
}
397410
#create_tab_settings_outer_container {
398411
height: 80%;
@@ -401,26 +414,30 @@ CreateFoldersSettingsScreen {
401414
border: tall $panel-lighten-3;
402415
}
403416
#checkbox_container {
404-
height: 65%;
417+
height: 80%;
405418
overflow: hidden auto;
406419
}
407-
#toplevel_folder_select_container {
408-
height: 15%;
420+
#create_folders_settings_allow_letters_in_checkbox {
421+
padding: 0 6;
422+
}
423+
#create_folders_settings_checkbox_outer_container {
424+
margin: 0 0 0 0;
409425
}
410426
#template_top_container {
411-
height: 70%;
427+
height: 75%;
412428
background: $primary-background;
413429
border: tall $panel-lighten-3;
414430
overflow: hidden auto;
415-
}
431+
}
432+
416433
#template_inner_horizontal_container{
417434
height: 3;
418435
}
419436

420-
#template_inner_container {
437+
#template_inner_container {
421438
height: 15;
422439
overflow: auto;
423-
}
440+
}
424441

425442
#template_message_label {
426443
width: auto;
@@ -470,7 +487,7 @@ CreateFoldersSettingsScreen {
470487
background: $boost;
471488
}
472489

473-
#template_settings_radioset:light:disabled {
490+
#template_settings_radioset:light:disabled {
474491
color: $panel-darken-2;
475492
}
476493

@@ -483,6 +500,10 @@ CreateFoldersSettingsScreen {
483500
color: $panel-darken-2;
484501
}
485502

503+
#create_folders_settings_close_button {
504+
margin: 1 0 0 0;
505+
}
506+
486507
/* Confirmation Screen --------------------------------------------------------------------- */
487508

488509
ConfirmAndAwaitTransferPopup {

datashuttle/tui/interface.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,28 @@ def create_folders(
130130
]
131131
bypass_validation = self.tui_settings["bypass_validation"]
132132

133+
allow_letters_in_sub_ses_values = self.tui_settings[
134+
"allow_letters_in_sub_ses_values"
135+
]
136+
133137
try:
134138
self.project.create_folders(
135139
top_level_folder,
136140
sub_names=sub_names,
137141
ses_names=ses_names,
138142
datatype=datatype,
139143
bypass_validation=bypass_validation,
144+
allow_letters_in_sub_ses_values=allow_letters_in_sub_ses_values,
140145
)
141146
return True, None
142147

143148
except BaseException as e:
144149
return False, str(e)
145150

146151
def validate_names(
147-
self, sub_names: List[str], ses_names: Optional[List[str]]
152+
self,
153+
sub_names: List[str],
154+
ses_names: Optional[List[str]],
148155
) -> InterfaceOutput:
149156
"""Validate a list of subject / session names.
150157
@@ -166,13 +173,18 @@ def validate_names(
166173
"create_tab"
167174
]
168175

176+
allow_letters_in_sub_ses_values = self.tui_settings[
177+
"allow_letters_in_sub_ses_values"
178+
]
179+
169180
try:
170181
format_sub, format_ses = self.project._format_and_validate_names(
171182
top_level_folder,
172183
sub_names,
173184
ses_names,
174185
self.get_name_templates(),
175186
bypass_validation=False,
187+
allow_letters_in_sub_ses_values=allow_letters_in_sub_ses_values,
176188
)
177189

178190
return True, {
@@ -188,6 +200,7 @@ def validate_project(
188200
top_level_folder: list[str] | None,
189201
include_central: bool,
190202
strict_mode: bool,
203+
allow_letters_in_sub_ses_values: bool,
191204
) -> tuple[bool, list[str] | str]:
192205
"""Wrap the validate project function.
193206
@@ -202,6 +215,8 @@ def validate_project(
202215
If `True`, the central project is also validated.
203216
strict_mode
204217
If `True`, validation will be run in strict mode.
218+
allow_letters_in_sub_ses_values
219+
If `True`, alphanumeric values will not raise an error.
205220
206221
Returns
207222
-------
@@ -218,6 +233,7 @@ def validate_project(
218233
display_mode="print", # unused
219234
include_central=include_central,
220235
strict_mode=strict_mode,
236+
allow_letters_in_sub_ses_values=allow_letters_in_sub_ses_values,
221237
)
222238
return True, results
223239

0 commit comments

Comments
 (0)