Skip to content

Commit 833f75d

Browse files
committed
Add option to select a Presentation source table
1 parent 6dead00 commit 833f75d

File tree

4 files changed

+45
-23
lines changed

4 files changed

+45
-23
lines changed

bidscoin/bids.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,18 @@
6262
class EventsParser(ABC):
6363
"""Parser for stimulus presentation logfiles"""
6464

65-
def __init__(self, sourcefile: Path, eventsdata: dict):
65+
def __init__(self, sourcefile: Path, eventsdata: dict, options: dict):
6666
"""
6767
Reads the events table from the events logfile
6868
6969
:param sourcefile: The full filepath of the raw logfile
7070
:param eventsdata: The run['events'] data (from a bidsmap)
71+
:param options: The plugin options
7172
"""
7273

7374
self.sourcefile = sourcefile
7475
self._data = eventsdata
75-
# TODO: Check if edits in self.start/timecols propagate back to the bidsmap data
76+
self.options = options
7677

7778
def __repr__(self):
7879

@@ -121,6 +122,7 @@ def eventstable(self) -> pd.DataFrame:
121122

122123
# Loop over the row groups to filter/edit the rows
123124
rows = pd.Series([len(self.rows) == 0] * len(df)).astype(bool) # Series with True values if no row expressions were specified
125+
rows.index = df.index # Make sure the indices align
124126
for group in self.rows:
125127

126128
for column, regex in group['include'].items():
@@ -822,7 +824,7 @@ def eventsparser(self) -> EventsParser:
822824

823825
for name in self.plugins:
824826
if plugin := bcoin.import_plugin(name, (f"{self.dataformat}Events",)):
825-
return getattr(plugin, f"{self.dataformat}Events")(self.provenance, self.events)
827+
return getattr(plugin, f"{self.dataformat}Events")(self.provenance, self.events, self.plugins[name])
826828

827829

828830
class DataType:

bidscoin/bidseditor.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,7 +1281,7 @@ def fill_table(self, table: MyQTable, data: list):
12811281

12821282
elif header:
12831283
if i == 0: # The first/header row of the data has the column names
1284-
table.setHorizontalHeaderLabels(item.get('value') for item in row)
1284+
table.setHorizontalHeaderLabels(str(item.get('value')) for item in row)
12851285
continue
12861286
i -= 1 # Account for the header row
12871287

@@ -1310,7 +1310,7 @@ def fill_table(self, table: MyQTable, data: list):
13101310
myitem.setToolTip(get_entityhelp(key))
13111311
elif tablename == 'meta' and j == 0:
13121312
myitem.setToolTip(get_metahelp(key))
1313-
elif tablename == 'events_columns' and i == 1:
1313+
elif tablename == 'events_columns' and j == 1:
13141314
myitem.setToolTip(get_eventshelp(itemvalue))
13151315
table.setItem(i, j, myitem)
13161316

@@ -1423,18 +1423,18 @@ def run2data(self) -> tuple:
14231423
events_data['columns'] = [[{'value': 'input', 'editable': False}, {'value': 'output', 'editable': False}]]
14241424
for mapping in runitem.events.get('columns') or []:
14251425
for key, value in mapping.items():
1426-
events_data['columns'].append([{'value': value, 'editable': True}, {'value': key, 'editable': True}])
1426+
events_data['columns'].append([{'value': value, 'editable': True}, {'value': key, 'editable': key not in ('onset','duration')}])
14271427

14281428
# Set up the data for the events table
14291429
parser = runitem.eventsparser()
14301430
if parser:
14311431
df = parser.logtable
1432-
events_data['log_table'] = [[{'value': name, 'editable': False} for name in df.columns]]
1432+
events_data['log_table'] = [[{'value': name, 'editable': False} for name in df.columns]] if len(df) else []
14331433
for i in range(len(df)):
14341434
events_data['log_table'].append([{'value': value, 'editable': False} for value in df.iloc[i]])
14351435

14361436
df = parser.eventstable
1437-
events_data['table'] = [[{'value': name, 'editable': False} for name in df.columns]]
1437+
events_data['table'] = [[{'value': name, 'editable': False} for name in df.columns]] if len(df) else []
14381438
for i in range(len(df)):
14391439
events_data['table'].append([{'value': value, 'editable': False} for value in df.iloc[i]])
14401440
else:

bidscoin/heuristics/bidsmap_dccn.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ Options:
3939
meta: [.json, .tsv, .tsv.gz] # The file extensions of the equally named metadata sourcefiles that are copied over to the BIDS sidecar files
4040
fallback: y # Appends unhandled dcm2niix suffixes to the `acq` label if 'y' (recommended, else the suffix data is discarded)
4141
events2bids:
42-
meta: [.json, .tsv, .tsv.gz]
42+
table: event # The table that is used to generate the output table (https://www.neurobs.com/pres_docs/html/03_presentation/07_data_reporting/01_logfiles/index.html)
43+
meta: [.json, .tsv]
4344

4445

4546
DICOM:

bidscoin/plugins/events2bids.py

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
LOGGER = logging.getLogger(__name__)
1414

1515
# The default/fallback options that are set when installing/using the plugin
16-
OPTIONS = Plugin({'meta': ['.json', '.tsv']}) # The file extensions of the equally named metadata sourcefiles that are copied over as BIDS sidecar files
16+
OPTIONS = Plugin({'table': 'event', 'meta': ['.json', '.tsv']}) # The file extensions of the equally named metadata sourcefiles that are copied over as BIDS sidecar files
1717

1818

1919
def test(options: Plugin=OPTIONS) -> int:
@@ -228,28 +228,47 @@ def bidscoiner_plugin(session: Path, bidsmap: BidsMap, bidsses: Path) -> None:
228228
class PresentationEvents(EventsParser):
229229
"""Parser for Presentation (Neurobs) logfiles"""
230230

231-
def __init__(self, sourcefile: Path, _data):
231+
def __init__(self, sourcefile: Path, _data: dict, options: dict):
232232
"""
233233
Reads the event table from the Presentation logfile
234234
235235
:param sourcefile: The full filepath of the logfile
236236
:param data: The run['events'] data (from a bidsmap)
237+
:param options: The plugin options
237238
"""
238239

239-
super().__init__(sourcefile, _data)
240+
super().__init__(sourcefile, _data, options)
240241

241-
# Read the event table from the Presentation logfile
242-
self.sourcetable = df = pd.read_csv(self.sourcefile, sep='\t', skiprows=3, skip_blank_lines=True)
243-
"""The Presentation event table (https://www.neurobs.com/pres_docs/html/03_presentation/07_data_reporting/01_logfiles/index.html)"""
244-
245-
# Drop the stimulus, video and survey tables
246-
endoftable = df['Subject'].isin(['Event Type', 'filename', 'Time']).idxmax()
247-
if endoftable:
248-
LOGGER.bcdebug(f"Dropping sourcetable data at row: {endoftable}")
249-
self.sourcetable = df.iloc[:endoftable]
242+
# Read the log-tables from the Presentation logfile
243+
self._sourcetable = pd.read_csv(self.sourcefile, sep='\t', skiprows=3, skip_blank_lines=True)
244+
"""The Presentation log-tables (https://www.neurobs.com/pres_docs/html/03_presentation/07_data_reporting/01_logfiles/index.html)"""
250245

251246
@property
252247
def logtable(self) -> pd.DataFrame:
253-
"""Returns the source logging data"""
248+
"""Returns a Presentation log-table"""
249+
250+
nrows = len(self._sourcetable)
251+
stimulus_start = (self._sourcetable.iloc[:, 0] == 'Event Type').idxmax() or nrows
252+
video_start = (self._sourcetable.iloc[:, 0] == 'filename').idxmax() or nrows
253+
survey_start = (self._sourcetable.iloc[:, 0] == 'Time').idxmax() or nrows
254+
255+
# Drop the stimulus, video and survey tables
256+
if self.options['table'] == 'event':
257+
begin = 0
258+
end = min(stimulus_start, video_start, survey_start)
259+
elif self.options['table'] == 'stimulus':
260+
self._sourcetable.columns = self._sourcetable.iloc[stimulus_start]
261+
begin = stimulus_start + 1
262+
end = min(video_start, survey_start)
263+
elif self.options['table'] == 'video':
264+
self._sourcetable.columns = self._sourcetable.iloc[video_start]
265+
begin = video_start + 1
266+
end = survey_start
267+
else:
268+
begin = 0
269+
end = nrows
270+
LOGGER.error(f"NOT IMPLEMENTED TABLE: {self.options['table']}")
271+
272+
LOGGER.bcdebug(f"Slicing '{self.options['table']}' sourcetable[{begin}:{end}]")
254273

255-
return self.sourcetable
274+
return self._sourcetable.iloc[begin:end]

0 commit comments

Comments
 (0)