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 16 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
63 changes: 63 additions & 0 deletions phys2bids/bids.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from csv import reader
import os
import logging

import yaml
eurunuela marked this conversation as resolved.
Show resolved Hide resolved
from pathlib import Path

from phys2bids import utils
Expand Down Expand Up @@ -170,3 +172,64 @@ def use_heuristic(heur_file, sub, ses, filename, outdir, record_label=''):
heurpath = os.path.join(fldr, f'{name}physio')

return heurpath


def participants_file(indir, outdir, yml, sub):
"""[summary]
eurunuela marked this conversation as resolved.
Show resolved Hide resolved

Args:
indir ([type]): [description]
outdir ([type]): [description]
yml ([type]): [description]
sub ([type]): [description]
"""
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 is not None and os.path.exists(os.path.join(indir, yml)):
LGR.info('Using yaml data to populate participants.tsv')
with open(os.path.join(indir, yml)) as f:
yaml_data = yaml.load(f, Loader=yaml.FullLoader)
p_id = 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 = sub
smoia marked this conversation as resolved.
Show resolved Hide resolved
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")
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} to participants.tsv ...')
participants_data = [sub, 'n/a', 'n/a', 'n/a']
smoia marked this conversation as resolved.
Show resolved Hide resolved
utils.append_list_as_row(file_path, participants_data)
5 changes: 5 additions & 0 deletions phys2bids/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ 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='file with info needed to generate participant.tsv file ',
default=None)
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=None):
"""
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(indir, outdir, yml, sub)
smoia marked this conversation as resolved.
Show resolved Hide resolved
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