diff --git a/lslautobids/convert_to_bids_and_upload.py b/lslautobids/convert_to_bids_and_upload.py index 2da8b19..3bc17fc 100644 --- a/lslautobids/convert_to_bids_and_upload.py +++ b/lslautobids/convert_to_bids_and_upload.py @@ -2,6 +2,7 @@ import os import shutil import sys +import re from pyxdf import match_streaminfos, resolve_streams from mnelab.io.xdf import read_raw_xdf @@ -92,101 +93,89 @@ def copy_source_files_to_bids(self,xdf_file,subject_id,session_id,other, logger) def _copy_behavioral_files(self, file_base, subject_id, session_id, logger): """ - Copy behavioral files to the BIDS structure. + Copy behavioral files to the BIDS structure based on regex patterns. + Iterates through patterns and matches files, copying them directly to target locations. Args: file_base (str): Base name of the file (without extension). subject_id (str): Subject ID. session_id (str): Session ID. + logger: Logger instance. """ + project_name = cli_args.project_name logger.info("Copying the behavioral files to BIDS...") + + # Get the TOML configuration + toml_path = os.path.join(project_root, cli_args.project_name, cli_args.project_name + '_config.toml') + data = read_toml_file(toml_path) + _expectedotherfiles = data["OtherFilesInfo"]["expectedOtherFiles"] + + if not isinstance(_expectedotherfiles, dict): + raise ValueError("expectedOtherFiles must be a dictionary with regex patterns. List format is no longer supported since v0.2.0 .") + # get the source path - behavioural_path = os.path.join(project_other_root,project_name,'data', subject_id,session_id,'beh') - # get the destination path - dest_dir = os.path.join(bids_root , project_name, subject_id , session_id , 'beh') - #check if the directory exists - os.makedirs(dest_dir, exist_ok=True) - - processed_files = [] + behavioural_path = os.path.join(project_other_root, project_name, 'data', subject_id, session_id, 'beh') + + if not os.path.exists(behavioural_path): + raise FileNotFoundError(f"Behavioral path does not exist: {behavioural_path} - did you forget to mount?") + return + # Extract the sub-xxx_ses-yyy part def extract_prefix(filename): parts = filename.split("_") sub = next((p for p in parts if p.startswith("sub-")), None) ses = next((p for p in parts if p.startswith("ses-")), None) if sub and ses: - return f"{sub}_{ses}_" + return f"{sub}_{ses}" return None prefix = extract_prefix(file_base) - - for file in os.listdir(behavioural_path): - # Skip non-files (like directories) - original_path = os.path.join(behavioural_path, file) - if not os.path.isfile(original_path): - continue - - if not file.startswith(prefix): - logger.info(f"Renaming {file} to include prefix {prefix}") - renamed_file = prefix + file - else: - renamed_file = file + processed_files = [] - processed_files.append(renamed_file) - dest_file = os.path.join(dest_dir, renamed_file) + # Get all files in source directory once + source_files = [f for f in os.listdir(behavioural_path) + if os.path.isfile(os.path.join(behavioural_path, f))] + # Iterate through patterns (not files) + for pattern, target_template in _expectedotherfiles.items(): + compiled_regex = re.compile(pattern) + + # Find matching files for this pattern + matched_files = [f for f in source_files if compiled_regex.match(f)] + + if not matched_files: + raise FileExistsError(f"No files matched pattern '{pattern}' in {behavioural_path}") + + if len(matched_files) > 1: + raise ValueError(f"Multiple files matched pattern '{pattern}': {matched_files}. Only one file per pattern is supported - manuall intervention required") + + # Process the first matching file + file = matched_files[0] + original_path = os.path.join(behavioural_path, file) + + # Format the target path with prefix + target_path = target_template.format(prefix=prefix) + dest_file = os.path.join(bids_root, project_name, subject_id, session_id, target_path) + + # Ensure destination directory exists + os.makedirs(os.path.dirname(dest_file), exist_ok=True) + + # Track the relative path for checking + processed_files.append(target_path) + if cli_args.redo_other_pc: - logger.info(f"Copying (overwriting if needed) {file} to {dest_file}") + logger.info(f"Copying (overwriting) {file} to {target_path}") shutil.copy(original_path, dest_file) else: if os.path.exists(dest_file): - logger.info(f"Behavioural file {file} already exists in BIDS. Skipping.") + logger.info(f"Behavioural file {target_path} already exists in BIDS. Skipping.") else: - logger.info(f"Copying new file {file} to {dest_file}") + logger.info(f"Copying {file} to {target_path}") shutil.copy(original_path, dest_file) - - - - unnecessary_files = self._check_required_behavioral_files(processed_files, prefix, logger) - - # remove the unnecessary files - for file in unnecessary_files: - file_path = os.path.join(dest_dir, file) - if os.path.exists(file_path): - logger.info(f"Removing unnecessary file: {file_path}") - os.remove(file_path) - else: - logger.warning(f"File to remove does not exist: {file_path}") - - - - def _check_required_behavioral_files(self, files, prefix, logger): - """ - Check for required behavioral files after copying. - - Args: - files (list): List of copied file names. - prefix (str): Expected prefix (e.g., "sub-001_ses-002_"). - """ - logger.info("Checking for required behavioral files...") - - # Get the expected file names from the toml file - toml_path = os.path.join(project_root, cli_args.project_name, cli_args.project_name + '_config.toml') - data = read_toml_file(toml_path) - - required_files = data["OtherFilesInfo"]["expectedOtherFiles"] - - - for required_file in required_files: - if not any(f.startswith(prefix) and f.endswith(required_file) for f in files): - raise FileNotFoundError(f"Missing required behavioral file: {required_file}") - unnecessary_files = [] - # remove everything except the required files - for file in files: - if not any(file.endswith(required_file) for required_file in required_files): - unnecessary_files.append(file) - return unnecessary_files + logger.info(f"Successfully processed {len(processed_files)} behavioral files") + def _copy_experiment_files(self, subject_id, session_id, logger): diff --git a/lslautobids/gen_project_config.py b/lslautobids/gen_project_config.py index bf4136a..e451cae 100644 --- a/lslautobids/gen_project_config.py +++ b/lslautobids/gen_project_config.py @@ -21,7 +21,19 @@ [OtherFilesInfo] otherFilesUsed = true # Set to true if you want to include other (non-eeg-files) files (experiment files, other modalities like eye tracking) in the dataset, else false - expectedOtherFiles = [".edf", ".csv", "_labnotebook.tsv", "_participantform.tsv"] # List of expected other file extensions. Only the expected files will be copied to the beh folder in BIDS dataset. Give an empty list [] if you don't want any other files to be in the dataset. In this case only experiment files will be zipeed and copied to the misc folder in BIDS dataset. + + # expectedOtherFiles: Dictionary format with regex patterns + # - The key is a regular expression to match source filenames in the project_other/.../beh/ folder + # - The value is a template path that includes {prefix} (e.g. sub-003_ses-002) and the target folder (beh/ or misc/) + # - Only files matching these patterns will be copied to the BIDS dataset + # the following is a sample configuration, you could also write it in short-hand notation: expectedOtherFiles={ ".*.edf"= "beh/{prefix}_physio.edf", ...} + + [OtherFilesInfo.expectedOtherFiles] + ".*.edf" = "beh/{prefix}_physio.edf" + ".*.csv" = "beh/{prefix}_beh.tsv" + ".*_labnotebook.tsv" = "misc/{prefix}_labnotebook.tsv" + ".*_participantform.tsv" = "misc/{prefix}_participantform.tsv" + [FileSelection] ignoreSubjects = ['sub-777'] # List of subjects to ignore during the conversion - Leave empty to include all subjects. Changing this value will not delete already existing subjects. diff --git a/tests/data/projects/test-project/test-project_config.toml b/tests/data/projects/test-project/test-project_config.toml deleted file mode 100644 index b1d78a6..0000000 --- a/tests/data/projects/test-project/test-project_config.toml +++ /dev/null @@ -1,27 +0,0 @@ - - # This is the project configuration file - This configuration can be customized for each project - - [AuthorsInfo] - authors = "John Doe, Lina Doe" # List of authors separated by commas - affiliation = "University of Stuttgart, University of Stuttgart" # Affiliation of the authors in the same order as authors - email = "john@gmail.com" # Contact email of the authors in the same order as authors - - [DataverseDataset] - title = "Convert XDF to BIDS" # Title of the Dataverse dataset. This gets updated automatically by the project name. - datasetDescription = "This is a test project to set up the pipeline to convert XDF to BIDS." # Description of the dataset. This description will appear in the dataset.json file which then eventually gets displayed in the dataverse metadata - license = "MIT License" # License for the dataset, e.g. "CC0", "CC-BY-4.0", "ODC-By-1.0", "PDDL-1.0", "ODC-PDDL-1.0", "MIT License" - subject = ["Medicine, Health and Life Sciences","Engineering"] # List of subjects related to the dataset required for dataverse metadata - pid = '' # Persistent identifier for the dataset, e.g. DOI or Handle. This will be updated automatically after creating the dataset in dataverse. - - [OtherFilesInfo] - otherFilesUsed = true # Set to true if you want to include other (non-eeg-files) files (experiment files, other modalities like eye tracking) in the dataset, else false - expectedOtherFiles = [".edf", ".csv", "_labnotebook.tsv", "_participantform.tsv"] # List of expected other file extensions. Only the expected files will be copied to the beh folder in BIDS dataset. Give an empty list [] if you don't want any other files to be in the dataset. In this case only experiment files will be zipeed and copied to the misc folder in BIDS dataset. - - [FileSelection] - ignoreSubjects = ['sub-777'] # List of subjects to ignore during the conversion - Leave empty to include all subjects. Changing this value will not delete already existing subjects. - excludeTasks = ['sampletask'] # List of tasks to exclude from the conversion for all subjects - Leave empty to include all tasks. Changing this value will not delete already existing tasks. - - [BidsConfig] - anonymizationNumber = 123 # This is an anomization number that will be added to the recording date of all subjects. - - \ No newline at end of file diff --git a/tests/test_main_functionality/data/projects/test-project/test-project_config.toml b/tests/test_main_functionality/data/projects/test-project/test-project_config.toml index 0c0d89e..54bf686 100644 --- a/tests/test_main_functionality/data/projects/test-project/test-project_config.toml +++ b/tests/test_main_functionality/data/projects/test-project/test-project_config.toml @@ -12,7 +12,6 @@ pid = "" [OtherFilesInfo] otherFilesUsed = true -expectedOtherFiles = [ ".edf", ".csv", "_labnotebook.tsv", "_participantform.tsv",] [FileSelection] ignoreSubjects = [ "sub-777",] @@ -20,3 +19,9 @@ excludeTasks = [ "sampletask",] [BidsConfig] anonymizationNumber = 123 + +[OtherFilesInfo.expectedOtherFiles] +".*.edf" = "beh/{prefix}_physio.edf" +".*.csv" = "beh/{prefix}_beh.tsv" +".*_labnotebook.tsv" = "misc/{prefix}_labnotebook.tsv" +".*_participantform.tsv" = "misc/{prefix}_participantform.tsv" diff --git a/tests/test_otherfiles-renaming/__init__.py b/tests/test_otherfiles-renaming/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/ignoreme.tsv b/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/ignoreme.tsv new file mode 100644 index 0000000..1a6c624 --- /dev/null +++ b/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/ignoreme.tsv @@ -0,0 +1 @@ +id age gender handedness dom_eye no_preex_conditions visual_acuity_test remarks diff --git a/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/participantform.tsv b/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/participantform.tsv new file mode 100644 index 0000000..1a6c624 --- /dev/null +++ b/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/participantform.tsv @@ -0,0 +1 @@ +id age gender handedness dom_eye no_preex_conditions visual_acuity_test remarks diff --git a/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/sub-666_ses-001_labnotebook.tsv b/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/sub-666_ses-001_labnotebook.tsv new file mode 100644 index 0000000..c68197a --- /dev/null +++ b/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/sub-666_ses-001_labnotebook.tsv @@ -0,0 +1,4 @@ +time event what +00:00 cap size selection +00:00 camera working y/n + diff --git a/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/sub_99.edf b/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/sub_99.edf new file mode 100644 index 0000000..86d8d1e Binary files /dev/null and b/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/sub_99.edf differ diff --git a/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/subject-99.csv b/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/subject-99.csv new file mode 100644 index 0000000..c18df11 --- /dev/null +++ b/tests/test_otherfiles-renaming/data/project_other/test-project/data/sub-099/ses-001/beh/subject-99.csv @@ -0,0 +1,4 @@ +"acc","accuracy","average_response_time","avg_rt","background","block_size","canvas_backend","clock_backend","coco_id","color_backend","correct","correct_instructions_sketchpad","correct_keyboard_response","correct_manual_pause_end_kbd","correct_manual_pause_kbd","correct_start_practice_trials_info","correct_welcome","count_PressSpacebar","count_block_sequence","count_break_sequence","count_break_sketchpad","count_end_of_exp","count_end_of_practice","count_endofinstructions","count_endofpractice","count_experiment","count_fixation1_sketchpad","count_fixation_jittered","count_fixation_loop","count_fixation_sequence","count_general_setup","count_getting_started","count_instructions","count_instructions_sketchpad","count_keyboard_response","count_log_all_variables","count_main_loop","count_manual_calibrate","count_manual_pause_end","count_manual_pause_end_kbd","count_manual_pause_kbd","count_manual_pause_start","count_new_1_form_text_display","count_new_1_inline_script","count_new_1_sketchpad","count_new_2_inline_script","count_new_2_sequence","count_new_3_inline_script","count_new_4_inline_script","count_new_5_inline_script","count_new_6_inline_script","count_new_7_inline_script","count_new_advanced_delay","count_new_feedback","count_new_form_text_display","count_new_inline_script","count_new_loop","count_new_pygaze_init","count_new_pygaze_log","count_new_pygaze_start_recording","count_new_pygaze_stop_recording","count_new_reset_feedback","count_new_sequence","count_practiceinstructions","count_practiceloop","count_priliminaryinstructions","count_send_trigger_breakend","count_send_trigger_breakstart","count_send_trigger_end_practice","count_send_trigger_end_stimulus","count_send_trigger_manual_pause_end","count_send_trigger_manual_pause_start","count_send_trigger_start_stimulus","count_setup","count_start_practice_trials_info","count_stimuli","count_stimulus_sequence","count_trial_sequence","count_trigger_setup","count_wait_for_centre_gaze","count_welcome","current_block_no","delay_new_advanced_delay","description","disable_garbage_collection","duration","empty_column","exp_imgdetails_file_name","experiment_file","experiment_path","file_name","file_path_coco_dataset","fixated","font_bold","font_family","font_italic","font_size","font_underline","foreground","form_clicks","fullscreen","gaze_timeout","height","keyboard_backend","license","live_row","live_row_practiceloop","logfile","mouse_backend","n_practicetrials","opensesame_codename","opensesame_version","psychopy_screen","repeat_cycle","response","response_break_sketchpad","response_end_of_exp","response_end_of_practice","response_instructions_sketchpad","response_keyboard_response","response_manual_pause_end_kbd","response_manual_pause_kbd","response_new_1_sketchpad","response_new_feedback","response_start_practice_trials_info","response_time","response_time_break_sketchpad","response_time_end_of_exp","response_time_end_of_practice","response_time_instructions_sketchpad","response_time_keyboard_response","response_time_manual_pause_end_kbd","response_time_manual_pause_kbd","response_time_new_1_sketchpad","response_time_new_feedback","response_time_start_practice_trials_info","response_time_welcome","response_welcome","round_decimals","sampler_backend","sound_buf_size","sound_channels","sound_freq","sound_sample_size","start","subject_nr","subject_parity","time_PressSpacebar","time_block_sequence","time_break_sequence","time_break_sketchpad","time_end_of_exp","time_end_of_practice","time_endofinstructions","time_endofpractice","time_experiment","time_fixation1_sketchpad","time_fixation_jittered","time_fixation_loop","time_fixation_sequence","time_general_setup","time_getting_started","time_instructions","time_instructions_sketchpad","time_keyboard_response","time_log_all_variables","time_main_loop","time_manual_calibrate","time_manual_pause_end","time_manual_pause_end_kbd","time_manual_pause_kbd","time_manual_pause_start","time_new_1_form_text_display","time_new_1_inline_script","time_new_1_sketchpad","time_new_2_inline_script","time_new_2_sequence","time_new_3_inline_script","time_new_4_inline_script","time_new_5_inline_script","time_new_6_inline_script","time_new_7_inline_script","time_new_advanced_delay","time_new_feedback","time_new_form_text_display","time_new_inline_script","time_new_loop","time_new_pygaze_init","time_new_pygaze_log","time_new_pygaze_start_recording","time_new_pygaze_stop_recording","time_new_reset_feedback","time_new_sequence","time_practiceinstructions","time_practiceloop","time_priliminaryinstructions","time_send_trigger_breakend","time_send_trigger_breakstart","time_send_trigger_end_practice","time_send_trigger_end_stimulus","time_send_trigger_manual_pause_end","time_send_trigger_manual_pause_start","time_send_trigger_start_stimulus","time_setup","time_start_practice_trials_info","time_stimuli","time_stimulus_sequence","time_trial_sequence","time_trigger_setup","time_wait_for_centre_gaze","time_welcome","title","tolerance","total_blocks","total_correct","total_response_time","total_responses","total_trials","width" +"undefined","undefined","810","810","#808080","50","psycho","psycho","304187","psycho","undefined","undefined","undefined","NA","NA","undefined","undefined","0","-3","0","0","0","0","NA","NA","0","0","NA","NA","0","0","NA","0","0","0","0","0","1","0","0","0","0","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","0","NA","NA","NA","NA","0","NA","0","0","NA","NA","NA","0","NA","0","0","0","0","0","0","0","0","0","0","0","NA","0","0","0","1","419","The main experiment item","False","650","NA","projects/2024FreeViewingMSCOCO/assets/images/experiment_images_info.csv","free_viewing_experiment.osexp","/home/stimulus/projects/2024FreeViewingMSCOCO/experiment","000000304187_MEG_size.jpg","/scratch/data/coco/coco-2017/train/data/000000304187.jpg","False","False","mono","False","18","no","white","no","yes","5000","1080","psycho","Attribution License","0","0","/home/stimulus/projects/2024FreeViewingMSCOCO/data/sub-099/ses-001/beh/subject-99.csv","psycho","3","Melodramatic Milgram","4.0.13","1","0","space","NA","NA","NA","space","space","NA","NA","NA","NA","space","810.305118560791","NA","NA","NA","588.4740352630615","810.305118560791","NA","NA","NA","NA","398.3440399169922","1945.7039833068848","space","2","psycho","1024","2","48000","-16","experiment","99","odd","14410.46404838562","14407.202959060669","NA","NA","NA","NA","NA","NA","1086.3871574401855","15227.399110794067","NA","NA","14407.315015792847","1647.9620933532715","NA","11160.964012145996","13112.376928329468","14410.552978515625","21306.498050689697","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","15885.131120681763","NA","NA","NA","NA","1086.4861011505127","NA","14100.979089736938","NA","NA","NA","NA","14311.726093292236","NA","NA","NA","NA","21306.37001991272","NA","NA","16304.228067398071","1086.4551067352295","13702.502012252808","16306.323051452637","15885.082006454468","NA","1643.4519290924072","15227.437973022461","11163.213014602661","free_viewing_experiment","44","8","0","810.305118560791","1","400","1920" +"undefined","undefined","671","671","#808080","50","psycho","psycho","303436","psycho","undefined","undefined","undefined","NA","NA","undefined","undefined","1","-2","1","1","0","0","NA","NA","0","1","NA","NA","1","0","NA","0","0","1","1","0","2","1","1","1","1","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","1","NA","NA","NA","NA","0","NA","0","0","NA","NA","NA","0","NA","1","1","0","1","1","1","1","0","0","1","1","NA","0","1","0","1","389","The main experiment item","False","650","NA","projects/2024FreeViewingMSCOCO/assets/images/experiment_images_info.csv","free_viewing_experiment.osexp","/home/stimulus/projects/2024FreeViewingMSCOCO/experiment","000000303436_MEG_size.jpg","/scratch/data/coco/coco-2017/train/data/000000303436.jpg","False","False","mono","False","18","no","white","no","yes","5000","1080","psycho","Attribution-NonCommercial-NoDerivs License","1","1","/home/stimulus/projects/2024FreeViewingMSCOCO/data/sub-099/ses-001/beh/subject-99.csv","psycho","3","Melodramatic Milgram","4.0.13","1","0","space","NA","NA","NA","space","space","NA","NA","NA","NA","space","533.376932144165","NA","NA","NA","588.4740352630615","533.376932144165","NA","NA","NA","NA","398.3440399169922","1945.7039833068848","space","2","psycho","1024","2","48000","-16","experiment","99","odd","21415.446996688843","21412.346124649048","NA","NA","NA","NA","NA","NA","1086.3871574401855","21951.735019683838","NA","NA","21412.39595413208","1647.9620933532715","NA","11160.964012145996","13112.376928329468","21415.497064590454","28000.17809867859","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","22608.845949172974","NA","NA","NA","NA","1086.4861011505127","NA","14100.979089736938","NA","NA","NA","NA","14311.726093292236","NA","NA","NA","NA","28000.051975250244","NA","NA","22997.878074645996","1086.4551067352295","13702.502012252808","22999.948978424072","22608.808040618896","NA","1643.4519290924072","21951.760053634644","11163.213014602661","free_viewing_experiment","44","8","0","1343.682050704956","2","400","1920" +"undefined","undefined","640","640","#808080","50","psycho","psycho","222921","psycho","undefined","undefined","undefined","NA","NA","undefined","undefined","2","-1","2","2","0","0","NA","NA","0","2","NA","NA","2","0","NA","0","0","2","2","0","3","2","2","2","2","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","2","NA","NA","NA","NA","0","NA","0","0","NA","NA","NA","0","NA","2","2","0","2","2","2","2","0","0","2","2","NA","0","2","0","1","375","The main experiment item","False","650","NA","projects/2024FreeViewingMSCOCO/assets/images/experiment_images_info.csv","free_viewing_experiment.osexp","/home/stimulus/projects/2024FreeViewingMSCOCO/experiment","000000222921_MEG_size.jpg","/scratch/data/coco/coco-2017/train/data/000000222921.jpg","False","False","mono","False","18","no","white","no","yes","5000","1080","psycho","Attribution-NonCommercial License","2","2","/home/stimulus/projects/2024FreeViewingMSCOCO/data/sub-099/ses-001/beh/subject-99.csv","psycho","3","Melodramatic Milgram","4.0.13","1","0","space","NA","NA","NA","space","space","NA","NA","NA","NA","space","576.591968536377","NA","NA","NA","588.4740352630615","576.591968536377","NA","NA","NA","NA","398.3440399169922","1945.7039833068848","space","2","psycho","1024","2","48000","-16","experiment","99","odd","28108.245134353638","28105.04913330078","NA","NA","NA","NA","NA","NA","1086.3871574401855","28687.549114227295","NA","NA","28105.098962783813","1647.9620933532715","NA","11160.964012145996","13112.376928329468","28108.291149139404","34722.15914726257","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","29344.93613243103","NA","NA","NA","NA","1086.4861011505127","NA","14100.979089736938","NA","NA","NA","NA","14311.726093292236","NA","NA","NA","NA","34722.04303741455","NA","NA","29719.972133636475","1086.4551067352295","13702.502012252808","29721.93193435669","29344.89893913269","NA","1643.4519290924072","28687.572956085205","11163.213014602661","free_viewing_experiment","44","8","0","1920.274019241333","3","400","1920" diff --git a/tests/test_otherfiles-renaming/data/project_other/test-project/experiment/fake_experiment.osexp b/tests/test_otherfiles-renaming/data/project_other/test-project/experiment/fake_experiment.osexp new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_otherfiles-renaming/data/projects/test-project/dataset.json b/tests/test_otherfiles-renaming/data/projects/test-project/dataset.json new file mode 100644 index 0000000..5e91c22 --- /dev/null +++ b/tests/test_otherfiles-renaming/data/projects/test-project/dataset.json @@ -0,0 +1,76 @@ +{ + "datasetVersion": { + "metadataBlocks": { + "citation": { + "fields": [ + { + "typeName": "title", + "multiple": false, + "typeClass": "primitive", + "value": "test-project" + }, + { + "typeName": "author", + "multiple": true, + "typeClass": "compound", + "value": [ + { + "authorName": { + "typeName": "author", + "multiple": false, + "typeClass": "primitive", + "value": "John Doe, Lina Doe" + } + } + ] + }, + { + "typeName": "datasetContact", + "multiple": true, + "typeClass": "compound", + "value": [ + { + "datasetContactName": { + "typeName": "datasetContactName", + "multiple": false, + "typeClass": "primitive", + "value": "John Doe, Lina Doe" + }, + "datasetContactEmail": { + "typeName": "datasetContactEmail", + "multiple": false, + "typeClass": "primitive", + "value": "john@gmail.com" + } + } + ] + }, + { + "typeName": "dsDescription", + "multiple": true, + "typeClass": "compound", + "value": [ + { + "dsDescriptionValue": { + "typeName": "dsDescriptionValue", + "multiple": false, + "typeClass": "primitive", + "value": "This is a test project to set up the pipeline to convert XDF to BIDS." + } + } + ] + }, + { + "typeName": "subject", + "multiple": true, + "typeClass": "controlledVocabulary", + "value": [ + "Medicine, Health and Life Sciences", + "Engineering" + ] + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/test_otherfiles-renaming/data/projects/test-project/sub-099/ses-001/eeg/sub-099_ses-001_task-freeviewing_run-002_eeg.xdf b/tests/test_otherfiles-renaming/data/projects/test-project/sub-099/ses-001/eeg/sub-099_ses-001_task-freeviewing_run-002_eeg.xdf new file mode 100644 index 0000000..6239d63 Binary files /dev/null and b/tests/test_otherfiles-renaming/data/projects/test-project/sub-099/ses-001/eeg/sub-099_ses-001_task-freeviewing_run-002_eeg.xdf differ diff --git a/tests/test_otherfiles-renaming/data/projects/test-project/test-project_config.toml b/tests/test_otherfiles-renaming/data/projects/test-project/test-project_config.toml new file mode 100644 index 0000000..52b9820 --- /dev/null +++ b/tests/test_otherfiles-renaming/data/projects/test-project/test-project_config.toml @@ -0,0 +1,27 @@ +[AuthorsInfo] +authors = "John Doe, Lina Doe" +affiliation = "University of Stuttgart, University of Stuttgart" +email = "john@gmail.com" + +[DataverseDataset] +title = "test-project" +datasetDescription = "This is a test project to set up the pipeline to convert XDF to BIDS." +license = "MIT License" +subject = [ "Medicine, Health and Life Sciences", "Engineering",] +pid = "" + +[OtherFilesInfo] +otherFilesUsed = true + +[FileSelection] +ignoreSubjects = [ "sub-777",] +excludeTasks = [ "sampletask",] + +[BidsConfig] +anonymizationNumber = 123 + +[OtherFilesInfo.expectedOtherFiles] +".*.edf" = "beh/{prefix}_physio.edf" +".*.csv" = "beh/{prefix}_beh.tsv" +".*_labnotebook.tsv" = "misc/{prefix}_labnotebook.tsv" +".*participantform.tsv" = "misc/{prefix}_participantform.tsv" diff --git a/tests/test_otherfiles-renaming/test_otherfiles-renaming.py b/tests/test_otherfiles-renaming/test_otherfiles-renaming.py new file mode 100644 index 0000000..01e8865 --- /dev/null +++ b/tests/test_otherfiles-renaming/test_otherfiles-renaming.py @@ -0,0 +1,50 @@ +import os +import sys +import pytest +import yaml +import shutil +#import lslautobids +import importlib +#import lslautobids.main +# Compute project root (two levels up from current test.py) +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if PROJECT_ROOT not in sys.path: + sys.path.insert(0, PROJECT_ROOT) + + +from path_config import get_root_paths,monkeypatch_paths +#from test_utils.path_config import DummyCLIArgs +# Print test file name for traceability +test_file_name = os.path.basename(__file__) +print(f" Running tests in {test_file_name}") + + + + +@pytest.mark.filterwarnings("ignore::DeprecationWarning") +def test_process_otherfiles_renaming(setup_project, monkeypatch): + """ + Expect the main pipeline to raise RuntimeError when duplicate files are found. + """ + project_name = setup_project # fixture via pytest + paths = get_root_paths(__file__) + + monkeypatch_paths(monkeypatch,paths) + + + # Reset sys.argv to something that lslautobids.main.main() expects + # this effectively removes the -c from setup_project + sys.argv = [ + "lslautobids.main", + "-p", project_name, + # other args expected by lslautobids.main.main + ] + + # run once + import lslautobids.main + lslautobids.main.main() + + # cleanup + shutil.rmtree(paths["bids_root"]) + +