From ddcd11ca1312b90df01e90ff5b6a4ffc92de86ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20Cl=C3=A9net?= <117362283+bclenet@users.noreply.github.com> Date: Mon, 24 Jul 2023 15:43:46 +0200 Subject: [PATCH] Continuous Integration - part 2 (#47) * [LINT] cleaning some files following PEP8 * [REFAC] moving description and results to narps_open/data/ [REFAC] creating narps_open.data.participants * Rebasing branch to main * [TEST] modifying helpers for pipeline testing & correlation values comparison * [ENH] runner can use a number of subjects from a fixed list * [ENH] sorting participant list so that it alternates participants from the equalRange and the equalIndifference groups * [BUG] artifacts in pylint action * [REFAC] participants infos from TSV * [TEST] 2T6S test_execution * [TEST] new pipeline test helper, with a number of trials * [ENH] adding a configuration parameter for results using the neurovault naming * [TEST] import issue in test * [TEST] consider unthresholded maps only [skip ci] * [2T6S] issue with hypo names [skip ci] * [TEST] consider unthresholded maps only [skip ci] * Rebasing branch on main * [TEST] refac + change hypothesis order + change correlation score thresholds [skip ci] * [TEST] refac + change hypothesis order + change correlation score thresholds [skip ci] * [TEST] execution with 20 subjects [skip ci] * [TEST] test pipeline output file formatting [skip ci] * [TEST][CI] displaying test results in GitHub Actions summary [skip ci] * [TEST] remove previous results before testing pipeline [skip ci] * [TEST] issue with keys of results [skip ci] * [2T6S] adjusting use of contrasts [skip ci] * [2T6S] adjusting use of contrasts [skip ci] * [2T6S] adjusting use of contrasts [skip ci] * [2T6S] adjusting use of contrasts [skip ci] * [TEST] correlation values are now configurable [skip ci] * [LINT] conform to PEP8 * [CI] git diff exclude deleted files * [CI] some actions run on self-hosted runner * [CI] installing python and cache mechanism are not needed in the self-hosted runner * [CI] Regex for pipeline files search * Resolving conflicts * Resolving conflicts * [CI] self-hosted runner config * [CI] self-hosted runner config * [CI] self-hosted runner config * [CI] pipeline_tests and change_tests only run on PR events * [CI] pipeline_tests and change_tests only run on PR events * [CI] pipeline_tests and change_tests only run on PR events * [CI] pipeline_tests and change_tests only run on PR events * [CI] skip testing if test list is empty * [CI] skip testing if test list is empty * [CI] git checkout strategy * [CI] git checkout strategy * [CI] git checkout strategy * [CI] git checkout strategy * [CI] git checkout strategy * [CI] git checkout strategy * [CI] git checkout strategy --- .github/workflows/code_quality.yml | 2 +- .github/workflows/pipeline_tests.yml | 67 ++- .github/workflows/test_changes.yml | 45 +- .gitignore | 3 +- docs/description.md | 14 +- narps_open/data/__init__.py | 0 .../{utils => data}/description/__init__.py | 4 +- .../{utils => data}/description/__main__.py | 4 +- ...nalysis_pipelines_derived_descriptions.tsv | 0 .../analysis_pipelines_full_descriptions.tsv | 0 narps_open/data/participants.py | 51 ++ narps_open/data/results/__init__.py | 106 ++++ narps_open/data/results/results.json | 537 ++++++++++++++++++ narps_open/pipelines/__init__.py | 24 + narps_open/pipelines/team_2T6S.py | 99 ++-- narps_open/runner.py | 24 +- narps_open/utils/__init__.py | 71 +-- .../utils/configuration/default_config.toml | 9 +- .../utils/configuration/testing_config.toml | 17 +- narps_open/utils/results.py | 90 --- narps_open/utils/status.py | 2 +- setup.py | 5 +- tests/conftest.py | 150 +++-- tests/{utils => data}/test_description.py | 4 +- tests/data/test_participants.py | 89 +++ tests/data/test_results.py | 60 ++ tests/pipelines/test_pipelines.py | 4 + tests/pipelines/test_team_2T6S.py | 26 +- tests/test_runner.py | 15 +- tests/utils/test_results.py | 58 -- tests/utils/test_utils.py | 37 +- 31 files changed, 1194 insertions(+), 423 deletions(-) create mode 100644 narps_open/data/__init__.py rename narps_open/{utils => data}/description/__init__.py (98%) rename narps_open/{utils => data}/description/__main__.py (94%) rename narps_open/{utils => data}/description/analysis_pipelines_derived_descriptions.tsv (100%) rename narps_open/{utils => data}/description/analysis_pipelines_full_descriptions.tsv (100%) create mode 100644 narps_open/data/participants.py create mode 100644 narps_open/data/results/__init__.py create mode 100644 narps_open/data/results/results.json delete mode 100644 narps_open/utils/results.py rename tests/{utils => data}/test_description.py (96%) create mode 100644 tests/data/test_participants.py create mode 100644 tests/data/test_results.py delete mode 100644 tests/utils/test_results.py diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml index 8ac51cd3..2804e813 100644 --- a/.github/workflows/code_quality.yml +++ b/.github/workflows/code_quality.yml @@ -45,7 +45,7 @@ jobs: - name: Analyse the code with pylint run: | pylint --exit-zero narps_open > pylint_report_narps_open.txt - pylint --exit-zero tests > pylint_report_narps_open.txt + pylint --exit-zero tests > pylint_report_tests.txt - name: Archive pylint results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/pipeline_tests.yml b/.github/workflows/pipeline_tests.yml index 6fb9e30d..9d782326 100644 --- a/.github/workflows/pipeline_tests.yml +++ b/.github/workflows/pipeline_tests.yml @@ -5,9 +5,6 @@ name: pipeline_tests # Define when it runs on: - push: - paths: - - 'narps_open/pipelines/team**' pull_request: paths: - 'narps_open/pipelines/team**' @@ -15,18 +12,14 @@ on: # Jobs that define the workflow jobs: + # A job to list the tests to be run identify-tests: runs-on: ubuntu-latest outputs: + teams: ${{ steps.identify.outputs.teams }} tests: ${{ steps.identify.outputs.tests }} steps: - - name: Checkout main branch for comparison - uses: actions/checkout@v3 - with: - ref: main - fetch-depth: 0 - - name: Checkout current branch uses: actions/checkout@v3 with: @@ -35,48 +28,64 @@ jobs: - name: Create a list of tests for changed pipelines id: identify run: | - # Loop through modified files - for file in $(git diff --name-only remotes/origin/main...$GITHUB_SHA) + # Loop through modified files between PR base and last head + for file in $(git diff --name-only --diff-filter=d remotes/origin/main...$GITHUB_SHA) do # echo each file echo $file # List team id corresponding to team_* files - if [[ "$file" =~ .*"narps_open/pipelines/team_".* ]]; then + if [[ "$file" =~ narps_open/pipelines/team_[A-Z0-9]{4}.py ]]; then echo "Modified pipeline = $file" tmp=${file#*"team_"} # remove prefix ending in "team_" team_id=${tmp%".py"*} # remove suffix starting with ".py" - # Populate the list of test files + # Populate the lists of test files and teams test_files="$test_files tests/pipelines/test_team_$team_id.py" + teams="$teams $team_id" fi done # Send the test list as job output echo $test_files echo "tests=$test_files" >> $GITHUB_OUTPUT + echo "teams=$teams" >> $GITHUB_OUTPUT - # A job to run the identified tests + # A job to identify and run the tests pytest: needs: identify-tests - runs-on: ubuntu-latest + runs-on: self-hosted steps: - - name: Checkout repository + - name: Checkout PR branch uses: actions/checkout@v3 - - name: Set up Python 3.9 - uses: actions/setup-python@v3 - with: - python-version: 3.9 - - - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }} - restore-keys: | - ${{ runner.os }}-pip- + - name: Load configuration for self-hosted runner + run: cp /home/neuro/local_testing_config.toml narps_open/utils/configuration/testing_config.toml - name: Install dependencies run: | python -m pip install --upgrade pip pip install .[tests] - - name: Collect tests with pytest - run: pytest --collect-only -q -m "pipeline_test" ${{ needs.identify-tests.outputs.tests }} + - name: Remove test reports if any existing + run: rm -f test_pipeline-*.txt + + - name: Execute tests with pytest + run: | + if [[ "${{ needs.identify-tests.outputs.tests }}" != "" ]]; then + pytest -q -m "pipeline_test" ${{ needs.identify-tests.outputs.tests }} + fi + + - name: Report results on GitHub + run: | + # Start report + echo "# Correlation values" >> $GITHUB_STEP_SUMMARY + echo "Unthresholded maps, reproduced vs. results" >> $GITHUB_STEP_SUMMARY + echo "Correlation values are sorted from hypotheses 1 to 9" >> $GITHUB_STEP_SUMMARY + + # Start report table + echo "| Team | Number of subjects | Test status | Correlation values |" >> $GITHUB_STEP_SUMMARY + echo "| -------- | ------- | ------- | ------- |" >> $GITHUB_STEP_SUMMARY + + # Loop through test report files + for team in ${{ needs.identify-tests.outputs.teams }} + do + cat test_pipeline-$team.txt >> $GITHUB_STEP_SUMMARY + done diff --git a/.github/workflows/test_changes.yml b/.github/workflows/test_changes.yml index a92d6d68..c6187079 100644 --- a/.github/workflows/test_changes.yml +++ b/.github/workflows/test_changes.yml @@ -5,9 +5,6 @@ name: test_changes # Define when it runs on: - push: - paths: - - 'tests/**/test_*.py' pull_request: paths: - 'tests/**/test_*.py' @@ -16,37 +13,21 @@ on: jobs: # A job to list the tests to be run - pytest: + identify-tests: runs-on: ubuntu-latest + outputs: + tests: ${{ steps.identify.outputs.tests }} steps: - name: Checkout main branch for comparison uses: actions/checkout@v3 with: - ref: main fetch-depth: 0 - - name: Checkout current branch - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set up Python 3.9 - uses: actions/setup-python@v3 - with: - python-version: 3.9 - - - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }} - restore-keys: | - ${{ runner.os }}-pip- - - name: Create a list of tests for changed tests id: identify run: | - # Loop through modified files - for file in $(git diff --name-only remotes/origin/main...$GITHUB_SHA) + # Loop through modified files between PR base and last head + for file in $(git diff --name-only --diff-filter=d remotes/origin/main...$GITHUB_SHA) do # List files corresponding to tests/**/test_**.py if [[ "$file" =~ .*"tests".*"test_".*".py" ]]; then @@ -57,10 +38,24 @@ jobs: echo $test_files echo "tests=$test_files" >> $GITHUB_OUTPUT + # A job to list the tests to be run + pytest: + needs: identify-tests + runs-on: self-hosted + steps: + - name: Checkout PR branch + uses: actions/checkout@v3 + + - name: Load configuration for self-hosted runner + run: cp /home/neuro/local_testing_config.toml narps_open/utils/configuration/testing_config.toml + - name: Install dependencies run: | python -m pip install --upgrade pip pip install .[tests] - name: Collect tests with pytest - run: pytest --collect-only -q ${{ steps.identify.outputs.tests }} + run: | + if [[ "${{ needs.identify-tests.outputs.tests }}" != "" ]]; then + pytest -q ${{ needs.identify-tests.outputs.tests }} + fi diff --git a/.gitignore b/.gitignore index ae5b5b9e..bcf9e07a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # to avoid commiting data -data/ -tests/data/ +./data/ # neuro user in docker image neuro diff --git a/docs/description.md b/docs/description.md index 15719b29..f090cc5f 100644 --- a/docs/description.md +++ b/docs/description.md @@ -1,17 +1,17 @@ # Access the descriptions of NARPS teams pipelines -The file `narps_open/utils/description/analysis_pipelines_full_descriptions.tsv` contains the description provided by each team participating to NARPS. +The file `narps_open/data/description/analysis_pipelines_full_descriptions.tsv` contains the description provided by each team participating to NARPS. It is a convertion into tsv format (tab-separated values) of the [original .xlsx file published in NARPS](https://github.com/poldrack/narps/blob/1.0.1/ImageAnalyses/metadata_files/analysis_pipelines_for_analysis.xlsx ), which allows easier parsing with python. -The file `narps_open/utils/description/analysis_pipelines_derived_descriptions.tsv` contains for each team a set of programatically usable data based on the textual descriptions of the previous file. This data is available in the `derived` sub dictionary (see examples hereafter). +The file `narps_open/data/description/analysis_pipelines_derived_descriptions.tsv` contains for each team a set of programatically usable data based on the textual descriptions of the previous file. This data is available in the `derived` sub dictionary (see examples hereafter). -The class `TeamDescription` of module `narps_open.utils.description` acts as a parser for these two files. +The class `TeamDescription` of module `narps_open.data.description` acts as a parser for these two files. You can also use the command-line tool as so. Option `-t` is for the team id, option `-d` allows to print only one of the sub parts of the description among : `general`, `exclusions`, `preprocessing`, `analysis`, and `categorized_for_analysis`. ```bash -python narps_open/utils/description -h +python narps_open/data/description -h # usage: __init__.py [-h] -t TEAM [-d {general,exclusions,preprocessing,analysis,categorized_for_analysis,derived}] # # Get description of a NARPS pipeline. @@ -22,7 +22,7 @@ python narps_open/utils/description -h # -d {general,exclusions,preprocessing,analysis,categorized_for_analysis,derived}, --dictionary {general,exclusions,preprocessing,analysis,categorized_for_analysis,derived} # the sub dictionary of team description -python narps_open/utils/description -t 2T6S -d general +python narps_open/data/description -t 2T6S -d general # { # "teamID": "2T6S", # "NV_collection_link": "https://neurovault.org/collections/4881/", @@ -35,10 +35,10 @@ python narps_open/utils/description -t 2T6S -d general # } ``` -Of course the `narps_open.utils.description` module is accessible programmatically, here is an example on how to use it: +Of course the `narps_open.data.description` module is accessible programmatically, here is an example on how to use it: ```python -from narps_open.utils.description import TeamDescription +from narps_open.data.description import TeamDescription description = TeamDescription('2T6S') # Set the id of the team here # Access the object as a dict print(description) diff --git a/narps_open/data/__init__.py b/narps_open/data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/narps_open/utils/description/__init__.py b/narps_open/data/description/__init__.py similarity index 98% rename from narps_open/utils/description/__init__.py rename to narps_open/data/description/__init__.py index 7d83a9c2..000e1b65 100644 --- a/narps_open/utils/description/__init__.py +++ b/narps_open/data/description/__init__.py @@ -14,10 +14,10 @@ class TeamDescription(dict): """ description_file = join( - files('narps_open.utils.description'), + files('narps_open.data.description'), 'analysis_pipelines_full_descriptions.tsv') derived_description_file = join( - files('narps_open.utils.description'), + files('narps_open.data.description'), 'analysis_pipelines_derived_descriptions.tsv') def __init__(self, team_id): diff --git a/narps_open/utils/description/__main__.py b/narps_open/data/description/__main__.py similarity index 94% rename from narps_open/utils/description/__main__.py rename to narps_open/data/description/__main__.py index a09d0008..226249c5 100644 --- a/narps_open/utils/description/__main__.py +++ b/narps_open/data/description/__main__.py @@ -1,12 +1,12 @@ #!/usr/bin/python # coding: utf-8 -""" Provide a command-line interface for the package narps_open.utils.description """ +""" Provide a command-line interface for the package narps_open.data.description """ from argparse import ArgumentParser from json import dumps -from narps_open.utils.description import TeamDescription +from narps_open.data.description import TeamDescription # Parse arguments parser = ArgumentParser(description='Get description of a NARPS pipeline.') diff --git a/narps_open/utils/description/analysis_pipelines_derived_descriptions.tsv b/narps_open/data/description/analysis_pipelines_derived_descriptions.tsv similarity index 100% rename from narps_open/utils/description/analysis_pipelines_derived_descriptions.tsv rename to narps_open/data/description/analysis_pipelines_derived_descriptions.tsv diff --git a/narps_open/utils/description/analysis_pipelines_full_descriptions.tsv b/narps_open/data/description/analysis_pipelines_full_descriptions.tsv similarity index 100% rename from narps_open/utils/description/analysis_pipelines_full_descriptions.tsv rename to narps_open/data/description/analysis_pipelines_full_descriptions.tsv diff --git a/narps_open/data/participants.py b/narps_open/data/participants.py new file mode 100644 index 00000000..a9cc65a5 --- /dev/null +++ b/narps_open/data/participants.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +# coding: utf-8 + +""" A set of functions to get the participants data for the narps_open package """ + +from os.path import join + +from pandas import read_csv + +from narps_open.data.description import TeamDescription +from narps_open.utils.configuration import Configuration + +def get_participants_information(): + """ Get a list of participants information from the tsv file from NARPS """ + return read_csv(join(Configuration()['directories']['dataset'], 'participants.tsv'), sep='\t') + +def get_all_participants() -> list: + """ Return a list of all participants included in NARPS. + This list is ordered so that subsets of 20, 40, 60, 80, 108 participants + are balanced in terms of belonging to the equal indifference and equal + range groups. + """ + return [ + '020', '001', '070', '013', '120', '109', '118', '035', '002', '025', + '018', '053', '046', '073', '066', '121', '098', '011', '116', '087', + '008', '069', '106', '095', '004', '113', '104', '115', '092', '089', + '090', '045', '016', '117', '124', '093', '088', '021', '094', '041', + '062', '017', '040', '083', '084', '107', '056', '119', '064', '103', + '044', '057', '060', '061', '112', '085', '050', '027', '082', '059', + '022', '019', '052', '047', '030', '039', '100', '029', '108', '067', + '096', '009', '058', '055', '024', '015', '080', '077', '006', '003', + '076', '072', '014', '102', '010', '074', '038', '114', '026', '079', + '054', '071', '032', '051', '110', '081', '036', '037', '068', '099', + '105', '063', '075', '033', '049', '123', '043', '005'] + +def get_participants(team_id: str) -> list: + """ Return a list of participants that were taken into account by a given team + + Args: + team_id: str, the ID of the team. + + Returns: a list of participants labels + """ + description = TeamDescription(team_id) + excluded_participants = description.derived['excluded_participants'].replace(' ','').split(',') + + return [p for p in get_all_participants() if p not in excluded_participants] + +def get_participants_subset(nb_participants: int = 108) -> list: + """ Return a list of participants of length nb_participants """ + return get_all_participants()[0:nb_participants] diff --git a/narps_open/data/results/__init__.py b/narps_open/data/results/__init__.py new file mode 100644 index 00000000..a750ad8a --- /dev/null +++ b/narps_open/data/results/__init__.py @@ -0,0 +1,106 @@ +#!/usr/bin/python +# coding: utf-8 + +""" This module allows to get Neurovault collections corresponding + to results from teams involed in NARPS +""" + +from os import remove, makedirs +from os.path import join +from json import loads +from zipfile import ZipFile +from urllib.request import urlretrieve +from argparse import ArgumentParser +from importlib_resources import files + +from narps_open.utils.configuration import Configuration +from narps_open.data.description import TeamDescription +from narps_open.pipelines import implemented_pipelines +from narps_open.utils import show_download_progress + +class ResultsCollection(): + """ Represents a Neurovault collections corresponding + to results from teams involed in NARPS. + """ + + def __init__(self, team_id: str): + + # Initialize attributes + self.team_id = team_id + description = TeamDescription(team_id = self.team_id) + self.uid = description.general['NV_collection_link'].split('/')[-2] + self.url = description.general['NV_collection_link'] + 'download' + self.directory = join( + Configuration()['directories']['narps_results'], + 'orig', + self.uid + '_' + self.team_id + ) + self.files = { + 'hypo1_thresh.nii.gz' : 'hypo1_thresh.nii.gz', + 'hypo1_unthresh.nii.gz' : 'hypo1_unthresh.nii.gz', + 'hypo2_thresh.nii.gz' : 'hypo2_thresh.nii.gz', + 'hypo2_unthresh.nii.gz' : 'hypo2_unthresh.nii.gz', + 'hypo3_thresh.nii.gz' : 'hypo3_thresh.nii.gz', + 'hypo3_unthresh.nii.gz' : 'hypo3_unthresh.nii.gz', + 'hypo4_thresh.nii.gz' : 'hypo4_thresh.nii.gz', + 'hypo4_unthresh.nii.gz' : 'hypo4_unthresh.nii.gz', + 'hypo5_thresh.nii.gz' : 'hypo5_thresh.nii.gz', + 'hypo5_unthresh.nii.gz' : 'hypo5_unthresh.nii.gz', + 'hypo6_thresh.nii.gz' : 'hypo6_thresh.nii.gz', + 'hypo6_unthresh.nii.gz' : 'hypo6_unthresh.nii.gz', + 'hypo7_thresh.nii.gz' : 'hypo7_thresh.nii.gz', + 'hypo7_unthresh.nii.gz' : 'hypo7_unthresh.nii.gz', + 'hypo8_thresh.nii.gz' : 'hypo8_thresh.nii.gz', + 'hypo8_unthresh.nii.gz' : 'hypo8_unthresh.nii.gz', + 'hypo9_thresh.nii.gz' : 'hypo9_thresh.nii.gz', + 'hypo9_unthresh.nii.gz' : 'hypo9_unthresh.nii.gz' + } + + # Make correspondences between the names given by the + # team in neurovault collections, and the expected names of the hypotheses files. + if Configuration()['results']['neurovault_naming']: + with open( + join(files('narps_open.data.results'),'results.json'), + 'r', encoding = 'utf-8' + ) as file: + neurovault_files = loads(file.read())[self.team_id] + + if neurovault_files: + self.files = neurovault_files + + def download(self): + """ Download the collection, unzip it and remove zip file. """ + + # Create download directory if not existing + makedirs(self.directory, exist_ok = True) + + # Download dataset + print('Collecting results for team', self.team_id) + zip_filename = join(self.directory, 'NARPS-'+self.team_id+'.zip') + urlretrieve(self.url, zip_filename, show_download_progress) + + # Unzip files directly in the download directory + with ZipFile(zip_filename, 'r') as zip_file: + for zip_info in zip_file.infolist(): + zip_info.filename = zip_info.filename.split('/')[-1] + zip_info.filename = join(self.directory, zip_info.filename) + zip_file.extract(zip_info) + + # Remove zip file + remove(zip_filename) + +if __name__ == '__main__': + # Parse arguments + parser = ArgumentParser(description='Get Neurovault collection of results from NARPS teams.') + group = parser.add_mutually_exclusive_group(required = True) + group.add_argument('-t', '--teams', nargs='+', type=str, action='extend', + help='a list of team IDs') + group.add_argument('-a', '--all', action='store_true', help='download results from all teams') + arguments = parser.parse_args() + + if arguments.all: + for team_id, _ in implemented_pipelines.items(): + ResultsCollection(team_id).download() + else: + for team in arguments.teams: + ResultsCollection(team).download() diff --git a/narps_open/data/results/results.json b/narps_open/data/results/results.json new file mode 100644 index 00000000..9151b7c4 --- /dev/null +++ b/narps_open/data/results/results.json @@ -0,0 +1,537 @@ +{ + "50GV": {}, + "9Q6R": {}, + "3C6G": {}, + "O21U": { + "hypo1_thresh.nii.gz" : "hypo1_thresh.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthresh.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresh.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresh.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresh.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresh.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresh.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresh.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresh.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thresh.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresh.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thresh.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthresh.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thresh.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresh.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thresh2.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresh2.nii.gz" + }, + "E3B6": { + "hypo1_thresh.nii.gz" : "hypo1_thresh.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthres.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresh.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthres.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthres.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thres.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthres.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresh.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthres.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thres.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthres.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thres.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthres.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thres.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthres.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thres.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthres.nii.gz" + }, + "R9K3": {}, + "0JO0": {}, + "C88N": {}, + "U26C": {}, + "UI76": {}, + "43FJ": { + "hypo1_thresh.nii.gz" : "Zstat_Thresholded_Positive_Parametric_Effect_of_Gain_Equal_Indifference.nii.gz", + "hypo1_unthresh.nii.gz" : "Zstat_Unthresholded_Positive_Effect_of_Gain_Equal_Indifference.nii.gz", + "hypo2_thresh.nii.gz" : "Zstat_Thresholded_Positive_Effect_of_Gain_Equal_Range.nii.gz", + "hypo2_unthresh.nii.gz" : "Zstat_Unthresholded_Positive_Effect_of_Gain_Equal_Range.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresh.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresh.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresh.nii.gz", + "hypo5_thresh.nii.gz" : "Zstat_Thresholded_Negative_Effect_of_Loss_Equal_Indifference.nii.gz", + "hypo5_unthresh.nii.gz" : "Zstat_Unthresholded_Negative_Effect_of_Loss_Equal_Indifference.nii.gz", + "hypo6_thresh.nii.gz" : "Zstat_Thresholded_Negative_Effect_of_Loss_Equal_Range.nii.gz", + "hypo6_unthresh.nii.gz" : "Zstat_Unthresholded_Negative_Effect_of_Loss_Equal_Range.nii.gz", + "hypo7_thresh.nii.gz" : "Zstat_Thresholded_Positive_Effect_of_Loss_Equal_Indifference.nii.gz", + "hypo7_unthresh.nii.gz" : "Zstat_Unthresholded_Positive_Effect_of_Loss_Equal_Indifference.nii.gz", + "hypo8_thresh.nii.gz" : "Zstat_Thresholded_Positive_Effect_of_Loss_Equal_Range.nii.gz", + "hypo8_unthresh.nii.gz" : "Zstat_Unthresholded_Positive_Effect_of_Loss_Equal_Range.nii.gz", + "hypo9_thresh.nii.gz" : "Zstat_Thresholded_Positive_Effect_of_Loss_Equal_Range_vs_Equal_Indifference.nii.gz", + "hypo9_unthresh.nii.gz" : "Zstat_Unthresholded_Positive_Effect_of_Loss_Equal_Range_vs_Equal_Indifference.nii.gz" + }, + "L7J7": { + "hypo1_thresh.nii.gz" : "hypothesis_1_and_3_thresholded_1.nii.gz", + "hypo1_unthresh.nii.gz" : "hypothesis_1_and_3_unthresholded_1.nii.gz", + "hypo2_thresh.nii.gz" : "hypothesis_2_and_4_thresholded_1.nii.gz", + "hypo2_unthresh.nii.gz" : "hypothesis_2_and_4_unthresholded_1.nii.gz", + "hypo3_thresh.nii.gz" : "hypothesis_1_and_3_thresholded_1.nii.gz", + "hypo3_unthresh.nii.gz" : "hypothesis_1_and_3_unthresholded_1.nii.gz", + "hypo4_thresh.nii.gz" : "hypothesis_2_and_4_thresholded_1.nii.gz", + "hypo4_unthresh.nii.gz" : "hypothesis_2_and_4_unthresholded_1.nii.gz", + "hypo5_thresh.nii.gz" : "hypothesis_5_and_7_thresholded_1.nii.gz", + "hypo5_unthresh.nii.gz" : "hypothesis_5_and_7_unthresholded_1.nii.gz", + "hypo6_thresh.nii.gz" : "hypothesis_6_and_8_thresholded_1.nii.gz", + "hypo6_unthresh.nii.gz" : "hypothesis_6_and_8_unthresholded_1.nii.gz", + "hypo7_thresh.nii.gz" : "hypothesis_5_and_7_thresholded_1.nii.gz", + "hypo7_unthresh.nii.gz" : "hypothesis_5_and_7_unthresholded_1.nii.gz", + "hypo8_thresh.nii.gz" : "hypothesis_6_and_8_thresholded_1.nii.gz", + "hypo8_unthresh.nii.gz" : "hypothesis_6_and_8_unthresholded_1.nii.gz", + "hypo9_thresh.nii.gz" : "hypothesis_9_thresholded.nii.gz", + "hypo9_unthresh.nii.gz" : "hypothesis_9_unthresholded.nii.gz" + }, + "4TQ6": {}, + "9T8E": {}, + "T54A": { + "hypo1_thresh.nii.gz" : "hypo1_thresh_TFCE.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthresh_Z.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresh_TFCE.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresh_Z.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh_TFCE.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresh_Z.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresh_TFCE.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresh_Z.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresh_TFCE.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresh_Z.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thresh_TFCE.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresh_Z.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thresh_TFCE.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthresh_Z.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thresh_TFCE.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresh_Z.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thresh_TFCE.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresh_Z.nii.gz" + }, + "2T6S": { + "hypo1_thresh.nii.gz" : "hypo1_thresholded_revised.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthresholded.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresholded_revised.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresholded.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresholded_revised.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresholded.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresholded_revised.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresholded.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresholded_revised.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresholded_revised.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thresholded_revised.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresholded_revised.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thresholded_revised.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthresholded_revised.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thresholded_revised.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresholded_revised_1.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thresholded_revised.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresholded.nii.gz" + }, + "6VV2": { + "hypo1_thresh.nii.gz" : "hypo1_thresh1.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthresh.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresh1.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresh.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh1.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresh.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresh1.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresh.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresh1.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresh.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thresh1.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresh.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thresh1.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthresh.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thresh1.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresh.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thresh1.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresh.nii.gz" + }, + "L3V8": { + "hypo1_thresh.nii.gz" : "Hypo1_thresh.nii.gz", + "hypo1_unthresh.nii.gz" : "Hypo1 unthresh.nii.gz", + "hypo2_thresh.nii.gz" : "Hypo2 thresh.nii.gz", + "hypo2_unthresh.nii.gz" : "Hypo2 unthresh.nii.gz", + "hypo3_thresh.nii.gz" : "Hypo3_thresh.nii.gz", + "hypo3_unthresh.nii.gz" : "Hypo3 unthresh.nii.gz", + "hypo4_thresh.nii.gz" : "Hypo4 thresh.nii.gz", + "hypo4_unthresh.nii.gz" : "Hypo4 unthresh.nii.gz", + "hypo5_thresh.nii.gz" : "Hypo5 thresh.nii.gz", + "hypo5_unthresh.nii.gz" : "Hypo5 uthresh.nii.gz", + "hypo6_thresh.nii.gz" : "Hypo6 thresh.nii.gz", + "hypo6_unthresh.nii.gz" : "Hypo6 unthresh.nii.gz", + "hypo7_thresh.nii.gz" : "Hypo7 thresh.nii.gz", + "hypo7_unthresh.nii.gz" : "Hypo7 unthresh.nii.gz", + "hypo8_thresh.nii.gz" : "Hypo8 thresh.nii.gz", + "hypo8_unthresh.nii.gz" : "Hypo8 unthresh.nii.gz", + "hypo9_thresh.nii.gz" : "Hypo9 thresh.nii.gz", + "hypo9_unthresh.nii.gz" : "Hypo9 unthresh.nii.gz" + }, + "80GC": {}, + "X1Y5": {}, + "3PQ2": {}, + "O6R6": {}, + "UK24": {}, + "2T7P": { + "hypo1_unthresh.nii.gz" : "hypo1_unthresholded.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresholded.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresholded.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresholded.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresholded_flip.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresholded_flip.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthresholded.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresholded.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresholded.nii.gz" + }, + "V55J": {}, + "5G9K": {}, + "16IN": {}, + "AO86": { + "hypo1_thresh.nii.gz" : "hypo1_thresh.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthresh.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresh.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresh.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresh.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresh.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresh.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresh.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresh.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thresh.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresh.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thresh_1.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthresh_1.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thresh_1.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresh_1.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thresh.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresh.nii.gz" + }, + "I52Y": { + "hypo1_thresh.nii.gz" : "hypo1_thresh_tstat.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthresh_tstat.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresh_tstat.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresh_tstat.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh_tstat.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresh_tstat.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresh_tstat.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresh_tstat.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresh_tstat.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresh_tstat.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thresh_tstat.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresh_tstat.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thresh_tstat.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthresh_tstat.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thresh_tstat.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresh_tstat.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thresh_tstat.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresh_tstat.nii.gz" + }, + "0H5E": { + "hypo1_thresh.nii.gz" : "hypotheses_1_and_3_1.nii.gz", + "hypo1_unthresh.nii.gz" : "hypotheses_1_and_3_unthresh_1.nii.gz", + "hypo2_thresh.nii.gz" : "hypotheses_2_and_4_1.nii.gz", + "hypo2_unthresh.nii.gz" : "hypotheses_2_and_4_unthresh_1.nii.gz", + "hypo3_thresh.nii.gz" : "hypotheses_1_and_3_1.nii.gz", + "hypo3_unthresh.nii.gz" : "hypotheses_1_and_3_unthresh_1.nii.gz", + "hypo4_thresh.nii.gz" : "hypotheses_2_and_4_1.nii.gz", + "hypo4_unthresh.nii.gz" : "hypotheses_2_and_4_unthresh_1.nii.gz", + "hypo5_thresh.nii.gz" : "hypothesis_5.nii.gz", + "hypo5_unthresh.nii.gz" : "hypothesis_5_unthresh.nii.gz", + "hypo6_thresh.nii.gz" : "hypothesis_6.nii.gz", + "hypo6_unthresh.nii.gz" : "hypothesis_6_unthresh.nii.gz", + "hypo7_thresh.nii.gz" : "hypothesis_7.nii.gz", + "hypo7_unthresh.nii.gz" : "hypothesis_7_unthresh.nii.gz", + "hypo8_thresh.nii.gz" : "hypothesis_8.nii.gz", + "hypo8_unthresh.nii.gz" : "hypothesis_8_unthresh.nii.gz", + "hypo9_thresh.nii.gz" : "hypothesis_9.nii.gz", + "hypo9_unthresh.nii.gz" : "hypothesis_9_unthresh.nii.gz" + }, + "0I4U": {}, + "B5I6": {}, + "1KB2": {}, + "X19V": {}, + "J7F9": {}, + "R5K7": { + "hypo1_thresh.nii.gz" : "hypo1_thresh.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthresh_1.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresh.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresh_1.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresh_1.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresh.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresh_1.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresh.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresh_1.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thresh.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresh_1.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thresh.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthresh_1.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thresh.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresh_1.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thresh.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresh_1.nii.gz" + }, + "X1Z4": {}, + "08MQ": {}, + "R7D1": {}, + "E6R3": {}, + "K9P0": {}, + "DC61": {}, + "9U7M": {}, + "3TR7": { + "hypo1_thresh.nii.gz" : "hypo1_thresh_1.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthresh_1.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresh_1.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresh_1.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh_1.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresh_1.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresh_1.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresh_1.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresh.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresh.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thresh.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresh.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thresh_1.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthresh_1.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thresh_1.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresh_1.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thresh.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresh.nii.gz" + }, + "P5F3": { + "hypo1_thresh.nii.gz" : "thresh_zstat1_6.nii.gz", + "hypo1_unthresh.nii.gz" : "zstat1_6.nii.gz", + "hypo2_thresh.nii.gz" : "thresh_zstat1_1.nii.gz", + "hypo2_unthresh.nii.gz" : "zstat1_1.nii.gz", + "hypo3_thresh.nii.gz" : "thresh_zstat1.nii.gz", + "hypo3_unthresh.nii.gz" : "zstat1.nii.gz", + "hypo4_thresh.nii.gz" : "thresh_zstat1_3.nii.gz", + "hypo4_unthresh.nii.gz" : "zstat1_3.nii.gz", + "hypo5_thresh.nii.gz" : "thresh_zstat2.nii.gz", + "hypo5_unthresh.nii.gz" : "zstat2.nii.gz", + "hypo6_thresh.nii.gz" : "thresh_zstat2_1.nii.gz", + "hypo6_unthresh.nii.gz" : "zstat2_1.nii.gz", + "hypo7_thresh.nii.gz" : "thresh_zstat1_4.nii.gz", + "hypo7_unthresh.nii.gz" : "zstat1_4.nii.gz", + "hypo8_thresh.nii.gz" : "thresh_zstat1_5.nii.gz", + "hypo8_unthresh.nii.gz" : "zstat1_5.nii.gz", + "hypo9_thresh.nii.gz" : "thresh_zstat3.nii.gz", + "hypo9_unthresh.nii.gz" : "zstat3.nii.gz" + }, + "Q6O0": { + "hypo1_thresh.nii.gz" : "gain_positive.nii.gz", + "hypo1_unthresh.nii.gz" : "spmT_0001.nii.gz", + "hypo2_thresh.nii.gz" : "gain_positive_1.nii.gz", + "hypo2_unthresh.nii.gz" : "spmT_0001_1.nii.gz", + "hypo3_thresh.nii.gz" : "gain_positive_2.nii.gz", + "hypo3_unthresh.nii.gz" : "spmT_0001_2.nii.gz", + "hypo4_thresh.nii.gz" : "gain_positive_3.nii.gz", + "hypo4_unthresh.nii.gz" : "spmT_0001_3.nii.gz", + "hypo5_thresh.nii.gz" : "loss_negative.nii.gz", + "hypo5_unthresh.nii.gz" : "spmT_0002.nii.gz", + "hypo6_thresh.nii.gz" : "loss_negative_1.nii.gz", + "hypo6_unthresh.nii.gz" : "spmT_0002_1.nii.gz", + "hypo7_thresh.nii.gz" : "loss_positive.nii.gz", + "hypo7_unthresh.nii.gz" : "spmT_0001_4.nii.gz", + "hypo8_thresh.nii.gz" : "loss_positive_1.nii.gz", + "hypo8_unthresh.nii.gz" : "spmT_0001_5.nii.gz", + "hypo9_thresh.nii.gz" : "loss_positive_difference.nii.gz", + "hypo9_unthresh.nii.gz" : "spmT_0001_6.nii.gz" + }, + "O03M": { + "hypo1_thresh.nii.gz" : "hypo_1_thresh.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo_1_unthresh.nii.gz", + "hypo2_thresh.nii.gz" : "hypo_2_thresh.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo_2_unthresh.nii.gz", + "hypo3_thresh.nii.gz" : "hypo_3_thresh.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo_3_unthresh.nii.gz", + "hypo4_thresh.nii.gz" : "hypo_4_thresh.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo_4_unthresh.nii.gz", + "hypo5_thresh.nii.gz" : "hypo_5_thresh.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo_5_unthresh.nii.gz", + "hypo6_thresh.nii.gz" : "hypo_6_thresh.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo_6_unthresh.nii.gz", + "hypo7_thresh.nii.gz" : "hypo_7_thresh.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo_7_unthresh.nii.gz", + "hypo8_thresh.nii.gz" : "hypo_8_thresh.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo_8_unthresh.nii.gz", + "hypo9_thresh.nii.gz" : "hypo_9_thresh.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo_9_unthresh.nii.gz" + }, + "1K0E": [ + "hypo1_thresh_LR.nii.gz", + "hypo1_thresh_subcortex.nii.gz", + "hypo1_unthresh_LR.nii.gz", + "hypo1_unthresh_subcortex.nii.gz", + "hypo2_thresh_LR.nii.gz", + "hypo2_thresh_subcortex.nii.gz", + "hypo2_unthresh_LR.nii.gz", + "hypo2_unthresh_subcortex.nii.gz", + "hypo3_thresh_LR.nii.gz", + "hypo3_thresh_subcortex.nii.gz", + "hypo3_unthresh_LR.nii.gz", + "hypo3_unthresh_subcortex.nii.gz", + "hypo4_thresh_LR.nii.gz", + "hypo4_thresh_subcortex.nii.gz", + "hypo4_unthresh_LR.nii.gz", + "hypo4_unthresh_subcortex.nii.gz", + "hypo5_thresh_LR.nii.gz", + "hypo5_thresh_subcortex.nii.gz", + "hypo5_unthresh_LR.nii.gz", + "hypo5_unthresh_subcortex.nii.gz", + "hypo6_thresh_LR.nii.gz", + "hypo6_thresh_subcortex.nii.gz", + "hypo6_unthresh_LR.nii.gz", + "hypo6_unthresh_subcortex.nii.gz", + "hypo7_thresh_LR.nii.gz", + "hypo7_thresh_subcortex.nii.gz", + "hypo7_unthresh_LR.nii.gz", + "hypo7_unthresh_subcortex.nii.gz", + "hypo8_thresh_LR.nii.gz", + "hypo8_thresh_subcortex.nii.gz", + "hypo8_unthresh_LR.nii.gz", + "hypo8_unthresh_subcortex.nii.gz", + "hypo9_thresh_LR.nii.gz", + "hypo9_thresh_subcortex.nii.gz", + "hypo9_unthresh_LR.nii.gz", + "hypo9_unthresh_subcortex.nii.gz" + ], + "27SS": {}, + "I9D6": {}, + "IZ20": { + "hypo1_thresh.nii.gz" : "group1_gain_pos.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo_1.nii.gz", + "hypo2_thresh.nii.gz" : "group_0_gain_pos.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo_2.nii.gz", + "hypo3_thresh.nii.gz" : "group1_gain_pos_1.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo_1_1.nii.gz", + "hypo4_thresh.nii.gz" : "group_0_gain_pos_1.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo_2_1.nii.gz", + "hypo5_thresh.nii.gz" : "group1_loss_neg.nii.gz", + "hypo5_unthresh.nii.gz" : "spmT_0002.nii.gz", + "hypo6_thresh.nii.gz" : "group0_loss_neg.nii.gz", + "hypo6_unthresh.nii.gz" : "spmT_0002_1.nii.gz", + "hypo7_thresh.nii.gz" : "group1_loss_pos.nii.gz", + "hypo7_unthresh.nii.gz" : "spmT_0001.nii.gz", + "hypo8_thresh.nii.gz" : "group0_loss_pos.nii.gz", + "hypo8_unthresh.nii.gz" : "spmT_0001_1.nii.gz", + "hypo9_thresh.nii.gz" : "threshold.nii.gz", + "hypo9_unthresh.nii.gz" : "spmT_0001_2.nii.gz" + }, + "B23O": {}, + "98BT": {}, + "XU70": {}, + "0ED6": {}, + "I07H": {}, + "Q58J": { + "hypo1_thresh.nii.gz" : "hypo1_thresh.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthresh.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresh.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresh.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresh.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresh.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresh.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresh.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresh.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thresh.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresh.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thresh.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7unthresh_.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thresh.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresh.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thresh.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresh.nii.gz" + }, + "51PW": { + "hypo1_thresh.nii.gz" : "hypo1_final_shadowreg_thresh_zstat1.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_final_shadowreg_zstat1.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_final_shadowreg_thresh_zstat2.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_final_shadowreg_zstat2.nii.gz", + "hypo3_thresh.nii.gz" : "hypo1_thresh_zstat1_1.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo1_zstat1_1.nii.gz", + "hypo4_thresh.nii.gz" : "hypo2_final_shadowreg_thresh_zstat2_1.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo2_final_shadowreg_zstat2_1.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_shadowreg_thresh_zstat1.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_shadowreg_zstat1.nii.gz", + "hypo6_thresh.nii.gz" : "hypo5_shadowreg_thresh_zstat2.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo5_shadowreg_zstat2.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_shadowreg_thresh_zstat1.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_shadowreg_hypo1_unthresh_zstat.nii.gz", + "hypo8_thresh.nii.gz" : "hypo7_shadowreg_thresh_zstat2.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo7_shadowreg_zstat2.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_shadowreg_thresh_zstat3.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_shadowreg_zstat3.nii.gz" + }, + "L9G5": {}, + "VG39": {}, + "R42Q": {}, + "94GU": {}, + "46CD": { + "hypo1_thresh.nii.gz" : "hypo1_thresh_indifference_gain_0.005p_398fdrc.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_unthresh_indifference_gain_unthresholded.nii.gz", + "hypo2_thresh.nii.gz" : "hypo2_thresh_range_gain_0.005p_392fdrc.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_unthresh_gain_range_unthresholded.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh_indifference_gain_0.005p_398fdrc.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_unthresh_indifference_gain_unthresholded.nii.gz", + "hypo4_thresh.nii.gz" : "hypo4_thresh_range_gain_0.005p_392fdrc.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_unthresh_gain_range_unthresholded.nii.gz", + "hypo5_thresh.nii.gz" : "hypo5_thresh_neg_indiff_0.005p_390fdrc.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_unthresh_indifference_loss_unthresholded.nii.gz", + "hypo6_thresh.nii.gz" : "hypo6_thresh_neg_range_loss_0.005p_0fdrc.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_unthresh_range_loss_unthresholded.nii.gz", + "hypo7_thresh.nii.gz" : "hypo7_thresh_indiff_loss_0.005p_435fdrc.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_unthresh_indifference_loss_unthresholded.nii.gz", + "hypo8_thresh.nii.gz" : "hypo8_thresh_range_loss_0.005p_416fdrc.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_unthresh_range_loss_unthresholded.nii.gz", + "hypo9_thresh.nii.gz" : "hypo9_thresh_2_rangegindiff_0.005p_0fdrc.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_unthresh_2_rangegindiff_unthresholded.nii.gz" + }, + "1P0Y": { + "hypo1_thresh.nii.gz" : "Hypothesis1_3_thresh_T_1.nii.gz", + "hypo1_unthresh.nii.gz" : "Hypothesis1_and3_spmT_0001_1.nii.gz", + "hypo2_thresh.nii.gz" : "Hypothesis2_4_thresh_t_1.nii.gz", + "hypo2_unthresh.nii.gz" : "Hypothesis2_and4_spmT_0001_1.nii.gz", + "hypo3_thresh.nii.gz" : "Hypothesis1_3_thresh_T_1.nii.gz", + "hypo3_unthresh.nii.gz" : "Hypothesis1_and3_spmT_0001_1.nii.gz", + "hypo4_thresh.nii.gz" : "Hypothesis2_4_thresh_t_1.nii.gz", + "hypo4_unthresh.nii.gz" : "Hypothesis2_and4_spmT_0001_1.nii.gz", + "hypo5_unthresh.nii.gz" : "Hypothesis5_spmT_0003.nii.gz", + "hypo5_thresh.nii.gz" : "Hypothesis5_threshT.nii.gz", + "hypo6_unthresh.nii.gz" : "Hypothesis6_spmT_0003.nii.gz", + "hypo6_thresh.nii.gz" : "Hypothesis6_ThreshT.nii.gz", + "hypo7_unthresh.nii.gz" : "Hypothesis7_spmT_0001.nii.gz", + "hypo7_thresh.nii.gz" : "Hypothesis7_ThreshT.nii.gz", + "hypo8_unthresh.nii.gz" : "Hypothesis8_spmT_0001.nii.gz", + "hypo8_thresh.nii.gz" : "Hypothesis8_ThreshT.nii.gz", + "hypo9_unthresh.nii.gz" : "Hypothesis9_spmT_0001.nii.gz", + "hypo9_thresh.nii.gz" : "Hypothesis9_ThreshT.nii.gz" + }, + "0C7Q": {}, + "C22U": {}, + "6FH5": {}, + "4SZ2": { + "hypo1_thresh.nii.gz" : "hypo1_thresh_MNI.nii.gz", + "hypo1_unthresh.nii.gz" : "hypo1_untresh_MNI.nii.gz", + "hypo2_tresh.nii.gz" : "hypo2_tresh_MNI.nii.gz", + "hypo2_unthresh.nii.gz" : "hypo2_untresh_MNI.nii.gz", + "hypo3_thresh.nii.gz" : "hypo3_thresh_MNI.nii.gz", + "hypo3_unthresh.nii.gz" : "hypo3_untresh_MNI.nii.gz", + "hypo4_tresh.nii.gz" : "hypo4_tresh_MNI.nii.gz", + "hypo4_unthresh.nii.gz" : "hypo4_untresh_MNI.nii.gz", + "hypo5_tresh.nii.gz" : "hypo5_tresh_MNI.nii.gz", + "hypo5_unthresh.nii.gz" : "hypo5_untresh_MNI.nii.gz", + "hypo6_tresh.nii.gz" : "hypo6_tresh_MNI.nii.gz", + "hypo6_unthresh.nii.gz" : "hypo6_untresh_MNI.nii.gz", + "hypo7_tresh.nii.gz" : "hypo7_tresh_MNI.nii.gz", + "hypo7_unthresh.nii.gz" : "hypo7_untresh_MNI.nii.gz", + "hypo8_tresh.nii.gz" : "hypo8_tresh_MNI.nii.gz", + "hypo8_unthresh.nii.gz" : "hypo8_untresh_MNI.nii.gz", + "hypo9_tresh.nii.gz" : "hypo9_2_tresh_MNI.nii.gz", + "hypo9_unthresh.nii.gz" : "hypo9_2_untresh_MNI.nii.gz" + }, + "SM54": {}, + "L1A8": {} +} diff --git a/narps_open/pipelines/__init__.py b/narps_open/pipelines/__init__.py index 7915b852..966df03f 100644 --- a/narps_open/pipelines/__init__.py +++ b/narps_open/pipelines/__init__.py @@ -295,3 +295,27 @@ def get_subject_level_outputs(self): def get_group_level_outputs(self): """ Return the names of the files the group level analysis is supposed to generate. """ return [] + + @abstractmethod + def get_hypotheses_outputs(self): + """ Return the names of the files used by the team to answer the hypotheses of NARPS. + Files must be in the following order: + hypo1_thresh.nii.gz + hypo1_unthresh.nii.gz + hypo2_thresh.nii.gz + hypo2_unthresh.nii.gz + hypo3_thresh.nii.gz + hypo3_unthresh.nii.gz + hypo4_thresh.nii.gz + hypo4_unthresh.nii.gz + hypo5_thresh.nii.gz + hypo5_unthresh.nii.gz + hypo6_thresh.nii.gz + hypo6_unthresh.nii.gz + hypo7_thresh.nii.gz + hypo7_unthresh.nii.gz + hypo8_thresh.nii.gz + hypo8_unthresh.nii.gz + hypo9_thresh.nii.gz + hypo9_unthresh.nii.gz + """ diff --git a/narps_open/pipelines/team_2T6S.py b/narps_open/pipelines/team_2T6S.py index e1d61efb..a533095e 100755 --- a/narps_open/pipelines/team_2T6S.py +++ b/narps_open/pipelines/team_2T6S.py @@ -26,7 +26,7 @@ def __init__(self): super().__init__() self.fwhm = 8.0 self.team_id = '2T6S' - self.contrast_list = ['0001', '0002', '0003', '0004'] + self.contrast_list = ['0001', '0002', '0003'] def get_preprocessing(self): """ No preprocessing has been done by team 2T6S """ @@ -123,11 +123,10 @@ def get_contrasts(): # Create contrasts trial = ('trial', 'T', conditions, [1, 0, 0]) effect_gain = ('effect_of_gain', 'T', conditions, [0, 1, 0]) - positive_effect_loss = ('positive_effect_of_loss', 'T', conditions, [0, 0, 1]) - negative_effect_loss = ('negative_effect_of_loss', 'T', conditions, [0, 0, -1]) + effect_loss = ('effect_of_loss', 'T', conditions, [0, 0, 1]) # Return contrast list - return [trial, effect_gain, positive_effect_loss, negative_effect_loss] + return [trial, effect_gain, effect_loss] # @staticmethod # Starting python 3.10, staticmethod should be used here # Otherwise it produces a TypeError: 'staticmethod' object is not callable @@ -428,56 +427,6 @@ def get_subset_contrasts(file_list, subject_list, participants_file): return equal_indifference_id, equal_range_id, equal_indifference_files, equal_range_files - # @staticmethod # Starting python 3.10, staticmethod should be used here - # Otherwise it produces a TypeError: 'staticmethod' object is not callable - def reorganize_results(team_id, nb_sub, output_dir, results_dir): - """ Reorganize the results to analyze them. """ - from os import mkdir - from os.path import join, isdir - from shutil import copyfile - - hypotheses = [ - join(output_dir, f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_02'), - join(output_dir, f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_02'), - join(output_dir, f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_02'), - join(output_dir, f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_02'), - join(output_dir, f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_04'), - join(output_dir, f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_04'), - join(output_dir, f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_04'), - join(output_dir, f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_04'), - join(output_dir, f'l2_analysis_groupComp_nsub_{nb_sub}', '_contrast_id_03') - ] - - # Build lists of files for unthresholded and thresholded maps - repro_unthresh = [] - repro_thresh = [] - for file_id, filename in enumerate(hypotheses): - if file_id in [4,5]: - repro_unthresh.append(join(filename, 'spmT_0002.nii')) - repro_thresh.append(join(filename, '_threshold1', 'spmT_0002_thr.nii')) - else: - repro_unthresh.append(join(filename, 'spmT_0001.nii')) - repro_thresh.append(join(filename, '_threshold0', 'spmT_0001_thr.nii')) - - if not isdir(join(results_dir, 'NARPS-reproduction')): - mkdir(join(results_dir, 'NARPS-reproduction')) - - for file_id, filename in enumerate(repro_unthresh): - f_in = filename - f_out = join(results_dir, - 'NARPS-reproduction', - f'team-{team_id}_nsub-{nb_sub}_hypo-{file_id + 1}_unthresholded.nii') - copyfile(f_in, f_out) - - for file_id, filename in enumerate(repro_thresh): - f_in = filename - f_out = join(results_dir, - 'NARPS-reproduction', - f'team-{team_id}_nsub-{nb_sub}_hypo-{file_id + 1}_thresholded.nii') - copyfile(f_in, f_out) - - print(f'Results files of team {team_id} reorganized.') - def get_group_level_analysis(self): """ Return all workflows for the group level analysis. @@ -529,16 +478,6 @@ def get_group_level_analysis_sub_workflow(self, method): ), name = 'datasink_groupanalysis') - # Function node reorganize_results - organize results once computed - reorganize_res = Node(Function( - function = self.reorganize_results, - input_names = ['team_id', 'nb_subjects', 'results_dir', 'output_dir']), - name = 'reorganize_res') - reorganize_res.inputs.team_id = self.team_id - reorganize_res.inputs.nb_subjects = nb_subjects - reorganize_res.inputs.results_dir = self.directories.results_dir - reorganize_res.inputs.output_dir = self.directories.output_dir - # Function node get_subset_contrasts - select subset of contrasts sub_contrasts = Node(Function( function = self.get_subset_contrasts, @@ -635,7 +574,7 @@ def get_group_level_outputs(self): # Handle equalRange and equalIndifference parameters = { - 'contrast_id': ['0001', '0002', '0003', '0004'], + 'contrast_id': self.contrast_list, 'method': ['equalRange', 'equalIndifference'], 'file': [ 'con_0001.nii', 'con_0002.nii', 'mask.nii', 'SPM.mat', @@ -657,7 +596,7 @@ def get_group_level_outputs(self): # Handle groupComp parameters = { - 'contrast_id': ['0001', '0002', '0003', '0004'], + 'contrast_id': self.contrast_list, 'method': ['groupComp'], 'file': [ 'con_0001.nii', 'mask.nii', 'SPM.mat', 'spmT_0001.nii', @@ -677,3 +616,31 @@ def get_group_level_outputs(self): for parameter_values in parameter_sets] return return_list + + def get_hypotheses_outputs(self): + """ Return all hypotheses output file names. + Note that hypotheses 5 to 8 correspond to the maps given by the team in their results ; + but they are not fully consistent with the hypotheses definitions as expected by NARPS. + """ + nb_sub = len(self.subject_list) + files = [ + join(f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_0002', '_threshold0', 'spmT_0001_thr.nii'), + join(f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_0002', 'spmT_0001.nii'), + join(f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_0002', '_threshold0', 'spmT_0001_thr.nii'), + join(f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_0002', 'spmT_0001.nii'), + join(f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_0002', '_threshold0', 'spmT_0001_thr.nii'), + join(f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_0002', 'spmT_0001.nii'), + join(f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_0002', '_threshold0', 'spmT_0001_thr.nii'), + join(f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_0002', 'spmT_0001.nii'), + join(f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_0003', '_threshold1', 'spmT_0002_thr.nii'), + join(f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_0003', 'spmT_0002.nii'), + join(f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_0003', '_threshold1', 'spmT_0001_thr.nii'), + join(f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_0003', 'spmT_0001.nii'), + join(f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_0003', '_threshold0', 'spmT_0001_thr.nii'), + join(f'l2_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_0003', 'spmT_0001.nii'), + join(f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_0003', '_threshold0', 'spmT_0002_thr.nii'), + join(f'l2_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_0003', 'spmT_0002.nii'), + join(f'l2_analysis_groupComp_nsub_{nb_sub}', '_contrast_id_0003', '_threshold0', 'spmT_0001_thr.nii'), + join(f'l2_analysis_groupComp_nsub_{nb_sub}', '_contrast_id_0003', 'spmT_0001.nii') + ] + return [join(self.directories.output_dir, f) for f in files] diff --git a/narps_open/runner.py b/narps_open/runner.py index 53312140..4a6d4289 100644 --- a/narps_open/runner.py +++ b/narps_open/runner.py @@ -11,7 +11,11 @@ from nipype import Workflow from narps_open.pipelines import Pipeline, implemented_pipelines -from narps_open.utils import get_all_participants, get_participants +from narps_open.data.participants import ( + get_all_participants, + get_participants, + get_participants_subset + ) from narps_open.utils.configuration import Configuration class PipelineRunner(): @@ -53,6 +57,12 @@ def random_nb_subjects(self, value: int) -> None: # Generate a random list of subjects self._pipeline.subject_list = choices(get_participants(self.team_id), k = value) + @subjects.setter + def nb_subjects(self, value: int) -> None: + """ Setter for property nb_subjects """ + # Get a subset of participants + self._pipeline.subject_list = get_participants_subset(value) + @property def team_id(self) -> str: """ Getter for property team_id """ @@ -149,10 +159,12 @@ def get_missing_group_level_outputs(self): parser.add_argument('-t', '--team', type=str, required=True, help='the team ID') subjects = parser.add_mutually_exclusive_group(required=True) - subjects.add_argument('-r', '--random', type=str, + subjects.add_argument('-r', '--rsubjects', type=str, help='the number of subjects to be randomly selected') subjects.add_argument('-s', '--subjects', nargs='+', type=str, action='extend', help='a list of subjects') + subjects.add_argument('-n', '--nsubjects', type=str, + help='the number of subjects to be randomly selected') levels = parser.add_mutually_exclusive_group(required=False) levels.add_argument('-g', '--group', action='store_true', default=False, help='run the group level only') @@ -172,8 +184,10 @@ def get_missing_group_level_outputs(self): # Handle subject if arguments.subjects is not None: runner.subjects = arguments.subjects + elif arguments.rsubjects is not None: + runner.random_nb_subjects = int(arguments.rsubjects) else: - runner.random_nb_subjects = int(arguments.random) + runner.nb_subjects = int(arguments.nsubjects) # Check data if arguments.check: @@ -184,7 +198,7 @@ def get_missing_group_level_outputs(self): print('First level:', runner.get_missing_first_level_outputs()) if not arguments.first: print('Group level:', runner.get_missing_group_level_outputs()) - - # Start the runner + + # Start the runner else: runner.start(arguments.first, arguments.group) diff --git a/narps_open/utils/__init__.py b/narps_open/utils/__init__.py index 8cab0fd1..54198316 100644 --- a/narps_open/utils/__init__.py +++ b/narps_open/utils/__init__.py @@ -5,47 +5,38 @@ from os.path import join, abspath, dirname, realpath -import pandas as pd - -from narps_open.utils.description import TeamDescription - -def participants_tsv(): - - participants_tsv = join(directories()["exp"], "participants.tsv") - - return pd.read_csv(participants_tsv, sep="\t") - -def get_all_participants() -> list: - """ Return a list of all participants included in NARPS """ - # TODO : parse participants.tsv instead - return [ - '001', '002', '003', '004', '005', '006', '008', '009', - '010', '011', '013', '014', '015', '016', '017', '018', '019', - '020', '021', '022', '024', '025', '026', '027', '029', - '030', '032', '033', '035', '036', '037', '038', '039', - '040', '041', '043', '044', '045', '046', '047', '049', - '050', '051', '052', '053', '054', '055', '056', '057', '058', '059', - '060', '061', '062', '063', '064', '066', '067', '068', '069', - '070', '071', '072', '073', '074', '075', '076', '077', '079', - '080', '081', '082', '083', '084', '085', '087', '088', '089', - '090', '092', '093', '094', '095', '096', '098', '099', - '100', '102', '103', '104', '105', '106', '107', '108', '109', - '110', '112', '113', '114', '115', '116', '117', '118', '119', - '120', '121', '123', '124' - ] - -def get_participants(team_id: str) -> list: - """ Return a list of participants that were taken into account by a given team - - Args: - team_id: str, the ID of the team. - - Returns: a list of participants labels +def show_download_progress(count, block_size, total_size): + """ A hook function to be passed to urllib.request.urlretrieve in order to + print the progress of a download. + + Arguments: + - count: int - the number of blocks already downloaded + - block_size: int - the size in bytes of a block + - total_size: int - the total size in bytes of the download. -1 if not provided. + """ + if total_size != -1: + # Display a percentage + display_value = str(int(count * block_size * 100 / total_size))+' %' + else: + # Draw a pretty cursor + cursor = ['⣾','⣽','⣻','⢿','⡿','⣟','⣯','⣷'] + display_value = cursor[int(count)%len(cursor)] + + # Showing download progress + print('Downloading', display_value, end='\r') + +def get_subject_id(file_name: str) -> str: + """ Return the id of the subject corresponding to the passed file name. + Return None if the file name is not associated with any subject. + TODO : a feature to be handled globaly to parse data in a file name. """ - description = TeamDescription(team_id) - excluded_participants = description.derived['excluded_participants'].replace(' ','').split(',') + key = 'subject_id' + if key not in file_name: + return None - return [p for p in get_all_participants() if p not in excluded_participants] + position = file_name.find(key) + len(key) + 1 + + return file_name[position:position+3] def directories(team_id: str) -> dict: """ @@ -89,7 +80,6 @@ def directories(team_id: str) -> dict: "result": result_dir, } - def raw_data_template() -> dict: """ Returns: @@ -129,7 +119,6 @@ def raw_data_template() -> dict: "phasediff": phasediff_file, } - def fmriprep_data_template() -> dict: """ Returns: diff --git a/narps_open/utils/configuration/default_config.toml b/narps_open/utils/configuration/default_config.toml index dad45ea9..fdc572a7 100644 --- a/narps_open/utils/configuration/default_config.toml +++ b/narps_open/utils/configuration/default_config.toml @@ -3,9 +3,12 @@ title = "Default configuration for the NARPS open pipelines project" config_type = "default" [directories] -dataset = "data/original/ds001734/" -reproduced_results = "data/reproduced/" +dataset = "/home/bclenet/data/ds001734/" +reproduced_results = "/home/bclenet/output/" narps_results = "data/results/" [runner] -nb_procs = 8 # Maximum number of threads executed by the runner \ No newline at end of file +nb_procs = 8 # Maximum number of threads executed by the runner + +[results] +neurovault_naming = true # true if results files are saved using the neurovault naming, false if they use naming of narps diff --git a/narps_open/utils/configuration/testing_config.toml b/narps_open/utils/configuration/testing_config.toml index 595c22c3..ec0ab8d1 100644 --- a/narps_open/utils/configuration/testing_config.toml +++ b/narps_open/utils/configuration/testing_config.toml @@ -3,11 +3,20 @@ title = "Testing configuration for the NARPS open pipelines project" config_type = "testing" [directories] -dataset = "tests/data/original/ds001734/" -reproduced_results = "tests/data/reproduced/" -narps_results = "tests/data/results/" +dataset = "run/data/ds001734/" +reproduced_results = "run/data/reproduced/" +narps_results = "run/data/results/" test_data = "tests/test_data/" test_runs = "run/" [runner] -nb_procs = 8 # Maximum number of threads executed by the runner \ No newline at end of file +nb_procs = 8 # Maximum number of threads executed by the runner +nb_trials = 3 # Maximum number of executions to have the pipeline executed completely + +[results] +neurovault_naming = true # true if results files are saved using the neurovault naming, false if they use naming of narps + +[testing] + +[testing.pipelines] +correlation_thresholds = [0.30, 0.70, 0.80, 0.85, 0.93] # Correlation between reproduced hypotheses files and results, respectively for [20, 40, 60, 80, 108] subjects. diff --git a/narps_open/utils/results.py b/narps_open/utils/results.py deleted file mode 100644 index 0fd3ddb3..00000000 --- a/narps_open/utils/results.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 - -""" This module allows to get Neurovault corresponding to results from teams involed in NARPS """ - -from os import remove, makedirs -from os.path import join -from zipfile import ZipFile -from urllib.request import urlretrieve -from argparse import ArgumentParser - -from narps_open.utils.configuration import Configuration -from narps_open.utils.description import TeamDescription -from narps_open.pipelines import implemented_pipelines - -def show_progress(count, block_size, total_size): - """ A hook function to be passed to urllib.request.urlretrieve in order to - print the progress of a download. - - Arguments: - - count: int - the number of blocks already downloaded - - block_size: int - the size in bytes of a block - - total_size: int - the total size in bytes of the download. -1 if not provided. - """ - if total_size != -1: - # Display a percentage - display_value = str(int(count * block_size * 100 / total_size))+' %' - else: - # Draw a pretty cursor - cursor = ['⣾','⣽','⣻','⢿','⡿','⣟','⣯','⣷'] - display_value = cursor[int(count)%len(cursor)] - - # Showing download progress - print('Downloading', display_value, end='\r') - -def download_result_collection(team_id: str): - """ Download a Neurovault collection corresponding to results from a team involed in NARPS. - Unzip it and remove zip file. - - Arguments: - - team_id: team corresponding to the requested collection - """ - # Get collection url and id - description = TeamDescription(team_id = team_id) - collection_id = description.general['NV_collection_link'].split('/')[-2] - collection_url = description.general['NV_collection_link'] + 'download' - - # Create download directory if not existing - download_directory = join( - Configuration()['directories']['narps_results'], - 'orig', - collection_id+'_'+team_id - ) - makedirs(download_directory, exist_ok = True) - - # Download dataset - print('Collecting results for team', team_id) - zip_filename = join(download_directory, 'NARPS-'+team_id+'.zip') - urlretrieve(collection_url, zip_filename, show_progress) - - # Unzip files directly in the download directory - with ZipFile(zip_filename, 'r') as zip_file: - for zip_info in zip_file.infolist(): - zip_info.filename = zip_info.filename.split('/')[-1] - zip_info.filename = join(download_directory, zip_info.filename) - zip_file.extract(zip_info) - - # Remove zip file - remove(zip_filename) - -def download_all_result_collections(): - """ Download all Neurovault collections corresponding to results from teams involed in NARPS. - """ - for team_id, _ in implemented_pipelines.items(): - download_result_collection(team_id) - -if __name__ == '__main__': - # Parse arguments - parser = ArgumentParser(description='Get Neurovault collection of results from NARPS teams.') - group = parser.add_mutually_exclusive_group(required = True) - group.add_argument('-t', '--teams', nargs='+', type=str, action='extend', - help='a list of team IDs') - group.add_argument('-a', '--all', action='store_true', help='download results from all teams') - arguments = parser.parse_args() - - if arguments.all: - download_all_result_collections() - else: - for team in arguments.teams: - download_result_collection(team) diff --git a/narps_open/utils/status.py b/narps_open/utils/status.py index b562302d..4e294d76 100644 --- a/narps_open/utils/status.py +++ b/narps_open/utils/status.py @@ -12,7 +12,7 @@ from requests import get from importlib_resources import files -from narps_open.utils.description import TeamDescription +from narps_open.data.description import TeamDescription from narps_open.pipelines import implemented_pipelines def get_opened_issues(): diff --git a/setup.py b/setup.py index dc87a529..bc185a4e 100644 --- a/setup.py +++ b/setup.py @@ -59,7 +59,8 @@ data_files = [ ('narps_open/utils/configuration', ['narps_open/utils/configuration/default_config.toml']), ('narps_open/utils/configuration', ['narps_open/utils/configuration/testing_config.toml']), - ('narps_open/utils/description', ['narps_open/utils/description/analysis_pipelines_derived_descriptions.tsv']), - ('narps_open/utils/description', ['narps_open/utils/description/analysis_pipelines_full_descriptions.tsv']) + ('narps_open/data/description', ['narps_open/data/description/analysis_pipelines_derived_descriptions.tsv']), + ('narps_open/data/description', ['narps_open/data/description/analysis_pipelines_full_descriptions.tsv']), + ('narps_open/data/results', ['narps_open/data/results/results.json']) ] ) diff --git a/tests/conftest.py b/tests/conftest.py index cae9639c..d740eeac 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,32 +6,30 @@ pytest on (a) test file(s) in the same directory. """ -from os.path import join, exists +from os import remove +from os.path import join, isfile +from shutil import rmtree from pytest import helpers from narps_open.runner import PipelineRunner +from narps_open.utils import get_subject_id from narps_open.utils.correlation import get_correlation_coefficient from narps_open.utils.configuration import Configuration +from narps_open.data.results import ResultsCollection # Init configuration, to ensure it is in testing mode Configuration(config_type='testing') @helpers.register -def test_pipeline( +def test_pipeline_execution( team_id: str, - references_dir: str, - dataset_dir: str, - results_dir: str, nb_subjects: int = 4 ): """ This pytest helper allows to launch a pipeline over a given number of subjects Arguments: - team_id: str, the ID of the team (allows to identify which pipeline to run) - - references_dir: str, the path to the directory where results from the teams are - - dataset_dir: str, the path to the ds001734 dataset - - results_dir: str, the path where to store the results - nb_subjects: int, the number of subject to run the pipeline with Returns: @@ -39,54 +37,120 @@ def test_pipeline( (reference and computed) files: This function can be used as follows: - results = pytest.helpers.test_pipeline('2T6S', '/references/', '/data/', '/output/', 4) + results = pytest.helpers.test_pipeline('2T6S', 4) assert statistics.mean(results) > .003 TODO : how to keep intermediate files of the low level for the next numbers of subjects ? - keep intermediate levels : boolean in PipelineRunner """ - # Initialize the pipeline runner = PipelineRunner(team_id) - runner.random_nb_subjects = nb_subjects - runner.pipeline.directories.dataset_dir = dataset_dir - runner.pipeline.directories.results_dir = results_dir + runner.nb_subjects = nb_subjects + runner.pipeline.directories.dataset_dir = Configuration()['directories']['dataset'] + runner.pipeline.directories.results_dir = Configuration()['directories']['reproduced_results'] runner.pipeline.directories.set_output_dir_with_team_id(team_id) runner.pipeline.directories.set_working_dir_with_team_id(team_id) - runner.start() - - # Retrieve the paths to the computed files - output_files = [ - join( - runner.pipeline.directories.output_dir, - 'NARPS-reproduction', - f'team-2T6S_nsub-{nb_subjects}_hypo-{hypothesis}_unthresholded.nii' - ) - for hypothesis in range(1, 10) - ] - # Retrieve the paths to the reference files - reference_files = [ - join( - references_dir, - f'NARPS-{team_id}', - f'hypo{hypothesis}_unthresholded.nii.gz' - ) - for hypothesis in range(1, 10) - ] + # Run as long as there are missing files after first level (with a max number of trials) + # TODO : this is a workaround + for _ in range(Configuration()['runner']['nb_trials']): + + # Get missing subjects + missing_subjects = set() + for file in runner.get_missing_first_level_outputs(): + missing_subjects.add(get_subject_id(file)) + + # Leave if no missing subjects + if not missing_subjects: + break + + # Start pipeline + runner.subjects = missing_subjects + runner.start(True, False) - # Add 'revised' to files when needed - for index, reference_file in enumerate(reference_files): - if not exists(reference_file): - reference_files[index] = reference_file.replace('.nii.gz', '_revised.nii.gz') - if not exists(reference_files[index]): - raise FileNotFoundError(reference_files[index] + ' does not exist.') + # Check missing files for the last time + missing_files = runner.get_missing_first_level_outputs() + if missing_files: + print('Missing files:', missing_files) + raise Exception('There are missing files for first level analysis.') - # Example paths for reference data 2T6S - # 'https://neurovault.org/collections/4881/NARPS-2T6S/hypo1_unthresh' + # Start pipeline for the group level only + runner.nb_subjects = nb_subjects + runner.start(False, True) + + # Indices and keys to the unthresholded maps + indices = list(range(1, 18, 2)) + keys = [f'hypo{i}_unthresh.nii.gz' for i in range(1, 10)] + + # Retrieve the paths to the reproduced files + reproduced_files = runner.pipeline.get_hypotheses_outputs() + reproduced_files = [reproduced_files[i] for i in indices] + + # Retrieve the paths to the results files + collection = ResultsCollection(team_id) + results_files = [join(collection.directory, collection.files[k]) for k in keys] # Compute the correlation coefficients return [ - get_correlation_coefficient(output_file, reference_file) - for output_file, reference_file in zip(output_files, reference_files) + get_correlation_coefficient(reproduced_file, results_file) + for reproduced_file, results_file in zip(reproduced_files, results_files) ] + +@helpers.register +def test_correlation_results(values: list, nb_subjects: int) -> bool: + """ This pytest helper returns True if all values in `values` are greater than + expected values. It returns False otherwise. + + Arguments: + - values, list of 9 floats: a list of correlation values for the 9 hypotheses of NARPS + - nb_subjects, int: the number of subject used to compute the correlation values + """ + scores = Configuration()['testing']['pipelines']['correlation_thresholds'] + if nb_subjects < 21: + expected = [scores[0] for _ in range(9)] + elif nb_subjects < 41: + expected = [scores[1] for _ in range(9)] + elif nb_subjects < 61: + expected = [scores[2] for _ in range(9)] + elif nb_subjects < 81: + expected = [scores[3] for _ in range(9)] + else: + expected = [scores[4] for _ in range(9)] + + return False not in [v > e for v, e in zip(values, expected)] + +@helpers.register +def test_pipeline_evaluation(team_id: str): + """ Test the execution of a Pipeline and compare with results. + Arguments: + - team_id, str: the id of the team for which to test the pipeline + + Return: True if the correlation coefficients between reproduced data and results + meet the expectations, False otherwise. + """ + + # Remove previous computations + reproduced_dir = join( + Configuration()['directories']['reproduced_results'], + f'NARPS-{team_id}-reproduced' + ) + rmtree(reproduced_dir, ignore_errors=True) + + file_name = f'test_pipeline-{team_id}.txt' + if isfile(file_name): + remove(file_name) + + for subjects in [20, 40, 60, 80, 108]: + # Execute pipeline + results = helpers.test_pipeline_execution(team_id, subjects) + + # Compute correlation with results + passed = helpers.test_correlation_results(results, subjects) + + # Write values in a file + with open(file_name, 'a', encoding = 'utf-8') as file: + file.write(f'| {team_id} | {subjects} subjects | ') + file.write('success' if passed else 'failure') + file.write(f' | {[round(i, 2) for i in results]} |\n') + + assert passed diff --git a/tests/utils/test_description.py b/tests/data/test_description.py similarity index 96% rename from tests/utils/test_description.py rename to tests/data/test_description.py index e15aeb59..8bacabb9 100644 --- a/tests/utils/test_description.py +++ b/tests/data/test_description.py @@ -1,7 +1,7 @@ #!/usr/bin/python # coding: utf-8 -""" Tests of the 'narps_open.utils.description' module. +""" Tests of the 'narps_open.data.description' module. Launch this test with PyTest @@ -13,7 +13,7 @@ from pytest import raises, mark -from narps_open.utils.description import TeamDescription +from narps_open.data.description import TeamDescription class TestUtilsDescription: """ A class that contains all the unit tests for the description module.""" diff --git a/tests/data/test_participants.py b/tests/data/test_participants.py new file mode 100644 index 00000000..d30cd23e --- /dev/null +++ b/tests/data/test_participants.py @@ -0,0 +1,89 @@ +#!/usr/bin/python +# coding: utf-8 + +""" Tests of the 'narps_open.data.participants' module. + +Launch this test with PyTest + +Usage: +====== + pytest -q test_participants.py + pytest -q test_participants.py -k +""" + +from pytest import mark + +import narps_open.data.participants as part + +class TestParticipants: + """ A class that contains all the unit tests for the participants module.""" + + @staticmethod + @mark.unit_test + def test_get_participants_information(): + """ Test the get_participants_information function """ + """p_info = part.get_participants_information() + assert len(p_info) == 108 + assert p_info.at[5, 'participant_id'] == 'sub-006' + assert p_info.at[5, 'group'] == 'equalRange' + assert p_info.at[5, 'gender'] == 'M' + assert p_info.at[5, 'age'] == 30 + assert p_info.at[12, 'participant_id'] == 'sub-015' + assert p_info.at[12, 'group'] == 'equalIndifference' + assert p_info.at[12, 'gender'] == 'F' + assert p_info.at[12, 'age'] == 26""" + + @staticmethod + @mark.unit_test + def test_get_all_participants(): + """ Test the get_all_participants function """ + participants_list = part.get_all_participants() + assert len(participants_list) == 108 + assert '001' in participants_list + assert '105' in participants_list + assert '123' in participants_list + + @staticmethod + @mark.unit_test + def test_get_participants(): + """ Test the get_participants function """ + + # Team 2T6S includes all participants + participants_list = part.get_participants('2T6S') + assert len(participants_list) == 108 + + # Team C88N excludes some participants + participants_list = part.get_participants('C88N') + assert len(participants_list) == 106 + assert '001' in participants_list + assert '076' not in participants_list + assert '117' not in participants_list + + @staticmethod + @mark.unit_test + def test_get_participants_subset(): + """ Test the get_participants_subset function """ + participants_list = part.get_participants_subset() + assert len(participants_list) == 108 + assert participants_list[0] == '020' + assert participants_list[-1] == '005' + + participants_list = part.get_participants_subset(20) + assert len(participants_list) == 20 + assert participants_list[0] == '020' + assert participants_list[-1] == '087' + + participants_list = part.get_participants_subset(40) + assert len(participants_list) == 40 + assert participants_list[0] == '020' + assert participants_list[-1] == '041' + + participants_list = part.get_participants_subset(60) + assert len(participants_list) == 60 + assert participants_list[0] == '020' + assert participants_list[-1] == '059' + + participants_list = part.get_participants_subset(80) + assert len(participants_list) == 80 + assert participants_list[0] == '020' + assert participants_list[-1] == '003' diff --git a/tests/data/test_results.py b/tests/data/test_results.py new file mode 100644 index 00000000..a23fcaa5 --- /dev/null +++ b/tests/data/test_results.py @@ -0,0 +1,60 @@ +#!/usr/bin/python +# coding: utf-8 + +""" Tests of the 'narps_open.data.results' module. + +Launch this test with PyTest + +Usage: +====== + pytest -q test_results.py + pytest -q test_results.py -k +""" + +from os.path import isdir, join +from shutil import rmtree + +from checksumdir import dirhash +from pytest import mark + +from narps_open.data.results import ResultsCollection +from narps_open.utils.configuration import Configuration + +class TestResultsCollection: + """ A class that contains all the unit tests for the ResultsCollection class.""" + + @staticmethod + @mark.unit_test + def test_create(): + """ Test the creation of a ResultsCollection object """ + + collection = ResultsCollection('2T6S') + assert collection.team_id == '2T6S' + assert collection.uid == '4881' + assert collection.url == 'https://neurovault.org/collections/4881/download' + assert 'results/orig/4881_2T6S' in collection.directory + assert collection.files['hypo3_thresh.nii.gz'] == 'hypo3_thresholded_revised.nii.gz' + + collection = ResultsCollection('C88N') + assert collection.team_id == 'C88N' + assert collection.uid == '4812' + assert collection.url == 'https://neurovault.org/collections/4812/download' + assert 'results/orig/4812_C88N' in collection.directory + assert collection.files['hypo3_thresh.nii.gz'] == 'hypo3_thresh.nii.gz' + + @staticmethod + @mark.unit_test + def test_download(): + """ Test the download method """ + + ResultsCollection('2T6S').download() + + # Collection should be downloaded in the results directory + expected_dir = join(Configuration()['directories']['narps_results'], 'orig', '4881_2T6S') + + # Test presence of the download + assert isdir(expected_dir) + assert dirhash(expected_dir) == '26af20dc7847bcb14d4452239ea458e8' + + # Remove folder + rmtree(expected_dir) diff --git a/tests/pipelines/test_pipelines.py b/tests/pipelines/test_pipelines.py index 09cabf0b..a9001832 100644 --- a/tests/pipelines/test_pipelines.py +++ b/tests/pipelines/test_pipelines.py @@ -43,6 +43,9 @@ def get_subject_level_analysis(self): def get_group_level_analysis(self): return 'c' + def get_hypotheses_outputs(self): + return ['h1', 'h2'] + class TestPipelineDirectories: """ A class that contains all the unit tests for the PipelineDirectories class.""" @@ -123,6 +126,7 @@ def test_create(): assert pipeline.get_preprocessing() == 'a' assert pipeline.get_subject_level_analysis() == 'b' assert pipeline.get_group_level_analysis() == 'c' + assert pipeline.get_hypotheses_outputs()[0] == 'h1' class TestUtils: """ A class that contains all the unit tests for the utils in module pipelines.""" diff --git a/tests/pipelines/test_team_2T6S.py b/tests/pipelines/test_team_2T6S.py index 5bc206fb..43c41773 100644 --- a/tests/pipelines/test_team_2T6S.py +++ b/tests/pipelines/test_team_2T6S.py @@ -11,9 +11,7 @@ pytest -q test_team_2T6S.py -k """ -from statistics import mean - -from pytest import raises, helpers, mark +from pytest import helpers, mark from nipype import Workflow from narps_open.pipelines.team_2T6S import PipelineTeam2T6S @@ -47,28 +45,24 @@ def test_create(): def test_outputs(): """ Test the expected outputs of a PipelineTeam2T6S object """ pipeline = PipelineTeam2T6S() - # 1 - 1 suject outputs + # 1 - 1 subject outputs pipeline.subject_list = ['001'] assert len(pipeline.get_preprocessing_outputs()) == 0 assert len(pipeline.get_run_level_outputs()) == 0 - assert len(pipeline.get_subject_level_outputs()) == 9 - assert len(pipeline.get_group_level_outputs()) == 84 + assert len(pipeline.get_subject_level_outputs()) == 7 + assert len(pipeline.get_group_level_outputs()) == 63 + assert len(pipeline.get_hypotheses_outputs()) == 18 - # 2 - 1 suject outputs + # 2 - 4 subjects outputs pipeline.subject_list = ['001', '002', '003', '004'] assert len(pipeline.get_preprocessing_outputs()) == 0 assert len(pipeline.get_run_level_outputs()) == 0 - assert len(pipeline.get_subject_level_outputs()) == 36 - assert len(pipeline.get_group_level_outputs()) == 84 + assert len(pipeline.get_subject_level_outputs()) == 28 + assert len(pipeline.get_group_level_outputs()) == 63 + assert len(pipeline.get_hypotheses_outputs()) == 18 @staticmethod @mark.pipeline_test def test_execution(): """ Test the execution of a PipelineTeam2T6S and compare results """ - results_4_subjects = helpers.test_pipeline( - '2T6S', - '/references/', - '/data/', - '/output/', - 4) - assert mean(results_4_subjects) > .003 + helpers.test_pipeline_evaluation('2T6S') diff --git a/tests/test_runner.py b/tests/test_runner.py index a60d0aa2..0079905d 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -41,6 +41,7 @@ def __del__(self): if isfile(self.test_file): remove(self.test_file) + # @staticmethod def write_to_file(_, text_to_write: str, file_path: str): """ Method used inside a nipype Node, to write a line in a test file """ with open(file_path, 'a', encoding = 'utf-8') as file: @@ -128,6 +129,12 @@ def get_group_level_outputs(self): ] return [t.format(nb_subjects = len(self.subject_list)) for t in templates] + def get_hypotheses_outputs(self): + """ Return the names of the files used by the team to answer the hypotheses of NARPS. + """ + template = join(Configuration()['directories']['test_runs'], 'hypothesis_{id}.md') + return [template.format(id = i) for i in range(1,18)] + class MockupWrongPipeline(Pipeline): """ A simple Pipeline class for test purposes """ @@ -146,6 +153,9 @@ def get_subject_level_analysis(self): def get_group_level_analysis(self): return None + def get_hypotheses_outputs(self): + return None + class MockupWrongPipeline2(Pipeline): """ A simple Pipeline class for test purposes """ @@ -164,6 +174,9 @@ def get_subject_level_analysis(self): def get_group_level_analysis(self): return None + def get_hypotheses_outputs(self): + return None + class TestPipelineRunner: """ A class that contains all the unit tests for the PipelineRunner class.""" @@ -186,7 +199,7 @@ def test_create(): # 4 - Instanciate a runner with an implemented team id runner = PipelineRunner('2T6S') - assert isinstance(runner._pipeline, PipelineTeam2T6S) + assert isinstance(runner.pipeline, PipelineTeam2T6S) assert runner.team_id == '2T6S' # 5 - Modify team id for an existing runner (with a not implemented team id) diff --git a/tests/utils/test_results.py b/tests/utils/test_results.py deleted file mode 100644 index e252045b..00000000 --- a/tests/utils/test_results.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 - -""" Tests of the 'narps_open.utils.results' module. - -Launch this test with PyTest - -Usage: -====== - pytest -q test_results.py - pytest -q test_results.py -k -""" - -from os.path import isdir, join -from shutil import rmtree - -from checksumdir import dirhash -from pytest import raises, mark - -from narps_open.utils.results import show_progress, download_result_collection -from narps_open.utils.configuration import Configuration - -class TestUtilsResults: - """ A class that contains all the unit tests for the results module.""" - - @staticmethod - @mark.unit_test - def test_show_progress(capfd): # using pytest's capfd fixture to get stdout - """ Test the show_progress function """ - - show_progress(25,1,100) - captured = capfd.readouterr() - assert captured.out == 'Downloading 25 %\r' - - show_progress(26,2,200) - captured = capfd.readouterr() - assert captured.out == 'Downloading 26 %\r' - - show_progress(25,50,-1) - captured = capfd.readouterr() - assert captured.out == 'Downloading ⣽\r' - - @staticmethod - @mark.unit_test - def test_download_result_collection(): - """ Test the download_result_collection function """ - - download_result_collection('2T6S') - - # Collection should be downloaded in the results directory - expected_dir = join(Configuration()['directories']['narps_results'], 'orig', '4881_2T6S') - - # Test presence of the download - assert isdir(expected_dir) - assert dirhash(expected_dir) == '26af20dc7847bcb14d4452239ea458e8' - - # Remove folder - rmtree(expected_dir) diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index 552d7b71..74778744 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -13,33 +13,24 @@ from pytest import mark -from narps_open.utils import get_all_participants, get_participants +from narps_open.utils import show_download_progress class TestUtils: """ A class that contains all the unit tests for the utils module.""" @staticmethod @mark.unit_test - def test_get_all_participants(): - """ Test the get_all_participants function """ - participants_list = get_all_participants() - assert len(participants_list) == 108 - assert '001' in participants_list - assert '105' in participants_list - assert '123' in participants_list + def test_show_download_progress(capfd): # using pytest's capfd fixture to get stdout + """ Test the show_download_progress function """ - @staticmethod - @mark.unit_test - def test_get_participants(): - """ Test the get_participants function """ - - # Team 2T6S includes all participants - participants_list = get_participants('2T6S') - assert len(participants_list) == 108 - - # Team C88N excludes some participants - participants_list = get_participants('C88N') - assert len(participants_list) == 106 - assert '001' in participants_list - assert '076' not in participants_list - assert '117' not in participants_list + show_download_progress(25,1,100) + captured = capfd.readouterr() + assert captured.out == 'Downloading 25 %\r' + + show_download_progress(26,2,200) + captured = capfd.readouterr() + assert captured.out == 'Downloading 26 %\r' + + show_download_progress(25,50,-1) + captured = capfd.readouterr() + assert captured.out == 'Downloading ⣽\r'