Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Generate participants.tsv if it doesn't exist or update it if subject is missing in the file #244

Merged
merged 22 commits into from
Jun 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 73 additions & 1 deletion phys2bids/bids.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import os
import logging

from csv import reader
from pathlib import Path

import yaml
eurunuela marked this conversation as resolved.
Show resolved Hide resolved

from phys2bids import utils

LGR = logging.getLogger(__name__)
Expand Down Expand Up @@ -170,3 +172,73 @@ def use_heuristic(heur_file, sub, ses, filename, outdir, record_label=''):
heurpath = os.path.join(fldr, f'{name}physio')

return heurpath


def participants_file(outdir, yml, sub):
"""
Create participants.tsv file if it does not exist.
If it exists and the subject is missing, then add it.
Otherwise, do nothing.
Parameters
----------
outdir: path
Full path to the output directory.
yml: path
Full path to the yaml file.
sub: str
Subject ID.
"""
LGR.info('Updating participants.tsv ...')
file_path = os.path.join(outdir, 'participants.tsv')
if not os.path.exists(file_path):
LGR.warning('phys2bids could not find participants.tsv')
# Read yaml info if file exists
if '.yml' in yml and os.path.exists(yml):
LGR.info('Using yaml data to populate participants.tsv')
with open(yml) as f:
yaml_data = yaml.load(f, Loader=yaml.FullLoader)
p_id = f'sub-{sub}'
p_age = yaml_data['participant']['age']
p_sex = yaml_data['participant']['sex']
p_handedness = yaml_data['participant']['handedness']
else:
LGR.info('No yaml file was provided. Using phys2bids data to '
'populate participants.tsv')
# Fill in with data from phys2bids
eurunuela marked this conversation as resolved.
Show resolved Hide resolved
p_id = f'sub-{sub}'
p_age = 'n/a'
smoia marked this conversation as resolved.
Show resolved Hide resolved
p_sex = 'n/a'
p_handedness = 'n/a'

# Write to participants.tsv file
header = ['participant_id', 'age', 'sex', 'handedness']
utils.append_list_as_row(file_path, header)

participants_data = [p_id, p_age, p_sex, p_handedness]
utils.append_list_as_row(file_path, participants_data)

else: # If participants.tsv exists only update when subject is not there
LGR.info('phys2bids found participants.tsv. Updating if needed...')
# Find participant_id column in header
pf = open(file_path, 'r')
header = pf.readline().split("\t")
header_length = len(header)
pf.close()
p_id_idx = header.index('participant_id')

# Check if subject is already in the file
sub_exists = False
with open(file_path) as pf:
tsvreader = reader(pf, delimiter="\t")
for line in tsvreader:
if sub in line[p_id_idx]:
sub_exists = True
break
# Only append to file if subject is not in the file
if not sub_exists:
LGR.info(f'Appending subjet sub-{sub} to participants.tsv ...')
participants_data = ['n/a'] * header_length
participants_data[p_id_idx] = f'sub-{sub}'
utils.append_list_as_row(file_path, participants_data)
6 changes: 6 additions & 0 deletions phys2bids/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ def _get_parser():
type=str,
help='full path to store channels plot ',
default='')
optional.add_argument('-yml', '--participant-yml',
smoia marked this conversation as resolved.
Show resolved Hide resolved
dest='yml',
type=str,
help='full path to file with info needed to generate '
'participant.tsv file ',
default='')
optional.add_argument('-debug', '--debug',
dest='debug',
action='store_true',
Expand Down
5 changes: 5 additions & 0 deletions phys2bids/heuristics/participant.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
participant:
participant_id: # Required
age: n/a
sex: n/a
handedness: n/a
9 changes: 7 additions & 2 deletions phys2bids/phys2bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from numpy import savetxt

from phys2bids import utils, viz, _version
from phys2bids.bids import bidsify_units, use_heuristic
from phys2bids.bids import bidsify_units, use_heuristic, participants_file
from phys2bids.cli.run import _get_parser
from phys2bids.physio_obj import BlueprintOutput

Expand Down Expand Up @@ -112,7 +112,8 @@ def print_json(outfile, samp_freq, time_offset, ch_name):

def phys2bids(filename, info=False, indir='.', outdir='.', heur_file=None,
sub=None, ses=None, chtrig=0, chsel=None, num_timepoints_expected=0,
tr=1, thr=None, ch_name=[], chplot='', debug=False, quiet=False):
tr=1, thr=None, ch_name=[], chplot='', debug=False, quiet=False,
yml=''):
"""
Main workflow of phys2bids.
Expand Down Expand Up @@ -264,6 +265,10 @@ def phys2bids(filename, info=False, indir='.', outdir='.', heur_file=None,

if heur_file and sub:
LGR.info(f'Preparing BIDS output using {heur_file}')
# Generate participants.tsv file if it doesn't exist already.
# Update the file if the subject is not in the file.
# Do not update if the subject is already in the file.
participants_file(outdir, yml, sub)
elif heur_file and not sub:
LGR.warning('While "-heur" was specified, option "-sub" was not.\n'
'Skipping BIDS formatting.')
Expand Down
14 changes: 14 additions & 0 deletions phys2bids/tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import re
import subprocess
from csv import reader
from pkg_resources import resource_filename

from phys2bids._version import get_versions
Expand Down Expand Up @@ -336,6 +337,19 @@ def test_integration_heuristic(samefreq_short_txt_file):
assert math.isclose(json_data['StartTime'], -189.6,)
assert json_data['Columns'] == ['time', 'RESP - RSP100C', 'MR TRIGGER - Custom, HLT100C - A 5']

# Check that participant.tsv gets updated
phys2bids(filename=test_full_path, chtrig=test_chtrig, outdir=test_outdir,
num_timepoints_expected=test_ntp, tr=test_tr, thr=test_thr, sub='002',
ses='01', heur_file=test_heur)

counter = 0
subject_list = ['participant_id', '006', '002']
with open(os.path.join(test_path, 'participants.tsv')) as pf:
tsvreader = reader(pf, delimiter="\t")
for line in tsvreader:
assert subject_list[counter] in line[0]
counter += 1

# Remove generated files
for filename in glob.glob(os.path.join(test_path, 'phys2bids*')):
os.remove(filename)
Expand Down
10 changes: 10 additions & 0 deletions phys2bids/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import os
import sys
from csv import writer
from pathlib import Path

LGR = logging.getLogger(__name__)
Expand Down Expand Up @@ -280,3 +281,12 @@ def load_heuristic(heuristic):
except Exception as exc:
raise ImportError(f'Failed to import heuristic {heuristic}: {exc}')
return mod


def append_list_as_row(file_name, list_of_elem):
# Open file in append mode
with open(file_name, 'a+', newline='') as write_obj:
# Create a writer object from csv module
csv_writer = writer(write_obj, delimiter='\t')
# Add contents of list as last row in the csv file
csv_writer.writerow(list_of_elem)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
numpy>=1.9.3
matplotlib>=3.1.1
PyYAML
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ python_requires = >=3.6.1
install_requires =
numpy >=1.9.3
matplotlib >=3.1.1
PyYAML
tests_require =
pytest >=3.6
test_suite = pytest
Expand All @@ -47,8 +48,8 @@ test =
interfaces =
%(acq)s
all =
%(interfaces)s
%(doc)s
%(interfaces)s
%(style)s
%(test)s

Expand Down