Commit b4dd293f authored by Ronny Eichler's avatar Ronny Eichler
Browse files

Get Open Ephys info from xml file

parent 53f3ba22
......@@ -13,12 +13,31 @@ EXT_SOUND = ['.wav', '.mp3', '.snd', '.wma']
EXT_IMAGE = ['.png', '.bmp', '.jpg', '.jpeg', '.pgm']
EXT_DOC = ['.md', '.toml', '.xml', '.tsv', '.csv', '.txt', '.doc', '.rst']
DEFAULT_COLS = ['fname', 'size', 'num_files', 'num_vid', 'num_img', 'num_snd', 'num_doc', 'data_fmt']
COLUMNS = {'fname': ['F']}
class Column:
def __init__(self, name, width=3, fmt=':', align='^', *args, **kwargs): = name
self.w = width
self.align = align
self.fmt = '{'+fmt+align+str(width)+'}'
def header(self):
if in COLUMNS:
return self.fmt.format(COLUMNS[])
return self.fmt.format('Unknown')
def row(self, data):
return self.fmt.format(data)
table_hdr = "{:^28}{sep}{:^6}{sep}{:>3}{sep}{:>3}{sep}{:>3}{sep}{:>3}{sep}{:>3}{sep}{:^10}{sep}".format(
"Folder name", "size", "#fil", "#vid", "#img", "#snd", '#doc', "format", sep=" ")
_row = "{0:<28}{1}{2:>4}{3:>4}{4:>4}{5:>4}{6:>10}"
def contains_dataset(root, dirs=None, files=None):
def contains_data(root, dirs=None, files=None):
"""Check if directory or list of files contains a dataset of known format (OE, Kwik, etc.)"""
if None in [dirs, files]:
_, dirs, files = dir_content(root)
......@@ -41,12 +60,17 @@ def dir_details(path):
num_img= len([f for f in files if fext(f) in EXT_IMAGE])
num_snd = len([f for f in files if fext(f) in EXT_SOUND])
num_doc = len([f for f in files if fext(f) in EXT_DOC])
data_fmt = contains_dataset(path)
data_fmt = contains_data(path)
return dict(fname=name, size=size, num_files=num_files, num_vid=num_vid,
num_img=num_img, num_snd=num_snd, num_doc=num_doc,
def data_stats(path):
fmt = contains_data(path)
def gather(path):
"""Gather details on the path and its subdirectories.
......@@ -82,9 +106,7 @@ def fit_str(string, max_len=10, weight=0.7):
tail = int((max_len-len(indicator))*weight)
return string[:head]+indicator+string[-tail:]
def mk_row(row, colorized=True, cols=['fname', 'size', 'num_files',
'num_vid', 'num_img', 'num_snd', 'num_doc',
'data_fmt'], sepr='|'):
def mk_row(row, colorized=True, cols=DEFAULT_COLS, sepr='|'):
row_str = ''
for c in cols:
if c == 'fname':
......@@ -3,9 +3,10 @@
from __future__ import print_function
import os
import xml.etree.ElementTree as etree
import xml.etree.ElementTree as ET
from .tools import fext, dir_content
def detect(root=None, dirs=None, files=None):
"""Checks for existence of an open ephys formatted data set in the root directory.
......@@ -19,24 +20,72 @@ def detect(root=None, dirs=None, files=None):
# TODO: Make all three optional and work with either
if dirs is None or files is None:
_, dirs, files = dir_content(root)
_, dirs, files = dir_content(root)
for f in files:
if fext(f) in ['.continuous']:
fv = format_version(root, dirs, files)
settings_xml = _find_settings_xml(root)
if settings_xml is None:
fv = None
fv = format_version(settings_xml)
# print(_fpga_node(settings_xml))
return "OE_v{}".format(fv if fv else '???')
return False
def format_version(root, dirs=None, files=None):
def _fpga_node(path):
chain = _config(path)['SIGNALCHAIN']
nodes = [p['attrib']['NodeId'] for p in chain if p['type']=='PROCESSOR' and 'FPGA' in p['attrib']['name']]
if len(nodes) == 1:
return nodes[0]
raise BaseException('Node ID not found in xml file {}'.format(path))
def format_version(path):
settings = _config(path)
return settings['INFO']['VERSION']
def _find_settings_xml(base, dirs=None, files=None):
if dirs is None or files is None:
_, dirs, files = dir_content(root)
_, dirs, files = dir_content(base)
if "settings.xml" in files:
root = etree.parse(os.path.join(root, 'settings.xml'))
version = root.findall("INFO/VERSION")
if not len(version):
return None
return version[0].text
return os.path.join(base, 'settings.xml')
return None
def _config(path):
"""Reads Open Ephys XML settings file and returns dictionary with relevant information.
- Info field
Dict(GUI version, date, OS, machine),
- Signal chain
List(Processor or Switch dict(name, nodeID). Signal chain is returned in ORDER OF OCCURRENCE
in the xml file, this represents the order in the signal chain. The nodeID does NOT reflect this order, but
but the order of assembling the chain.
- Audio
Int bufferSize
path: Path to settings.xml file
root = ET.parse(path).getroot()
info = dict(
VERSION = root.find('INFO/VERSION').text,
DATE = root.find('INFO/DATE').text,
OS = root.find('INFO/OS').text,
MACHINE = root.find('INFO/VERSION').text
sc = root.find('SIGNALCHAIN')
chain = [dict(type=e.tag, attrib=e.attrib) for e in sc.getchildren()]
audio = root.find('AUDIO').attrib
return dict(INFO=info,
[ ] dm ls should return number of datasets on subfolders
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment