Module xelo2.api.frontend

Expand source code
from logging import getLogger
from PyQt5.QtSql import QSqlQuery
from numpy import (
    array,
    )

from .backend import Table_with_files, NumpyTable
from .utils import (
    find_subject_id,
    get_dtypes,
    out_datetime,
    list_channels_electrodes,
    recording_attach,
    recording_get,
    sort_starttime,
    sort_subjects_alphabetical,
    sort_subjects_date,
    )

lg = getLogger(__name__)


def list_subjects(db, alphabetical=False, reverse=False):
    """List of the subjects in the currently open database, sorted based on
    the date of their first run.

    Parameters
    ----------
    alphabetical : bool
        False -> sort by date of first run
        True -> sort alphabetically

    reverse : bool
        False -> oldest to newest, True -> newest to oldest
        False -> A to Z, True -> Z to A

    Returns
    -------
    list of instances of Subject
        list of subjects in the database
    """
    query = QSqlQuery(db['db'])
    query.exec('SELECT id FROM subjects')

    list_of_subjects = []
    while query.next():
        list_of_subjects.append(Subject(db, id=query.value('id')))

    if alphabetical:
        _sort_subjects = sort_subjects_alphabetical
    else:
        _sort_subjects = sort_subjects_date

    return sorted(list_of_subjects, key=_sort_subjects, reverse=reverse)


class Subject(Table_with_files):
    t = 'subject'

    def __init__(self, db, code=None, id=None):
        if code is not None:
            id = find_subject_id(db, code)
            if id is None:
                raise ValueError(f'There is no "{code}" in "subject_codes" table')

        super().__init__(db, id)

    def __str__(self):
        codes = self.codes
        if len(codes) == 0:
            return '(subject without code)'
        elif len(codes) == 1:
            return codes[0]
        else:
            return ', '.join(codes)

    @classmethod
    def add(cls, db, code=None):
        """You can create an empty subject, with no code, but it's a bad idea
        """
        if code is not None:
            id = find_subject_id(db, code)
            if id is not None:
                raise ValueError(f'Subject "{code}" already exists')

        # add empty value to get new id
        query = QSqlQuery(db['db'])
        query.prepare("INSERT INTO subjects () VALUES () ")
        if query.exec():
            id = query.lastInsertId()
        else:
            raise ValueError(query.lastError().text())

        if code is not None:
            query = QSqlQuery(db['db'])
            query.prepare("INSERT INTO subject_codes (`subject_id`, `code`) VALUES (:subject_id, :code)")
            query.bindValue(':subject_id', id)
            query.bindValue(':code', code)
            if not query.exec():
                raise SyntaxError(query.lastError().text())

        return Subject(db, id=id)

    @property
    def codes(self):
        """Get the codes associated with this subjects"""
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT code FROM subject_codes WHERE subject_codes.subject_id = :id")
        query.bindValue(':id', self.id)
        if not query.exec():
            lg.warning(query.lastError().text())

        list_of_codes = []
        while query.next():
            list_of_codes.append(query.value('code'))

        # put RESP at the end
        list_of_codes.sort()
        list_of_codes.sort(key=lambda s: s.startswith('RESP'))
        return list_of_codes

    @codes.setter
    def codes(self, codes):

        query = QSqlQuery(self.db['db'])
        query.prepare('DELETE FROM subject_codes WHERE subject_id = :id')
        query.bindValue(':id', self.id)
        if not query.exec():
            lg.warning(query.lastError().text())

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO subject_codes (`subject_id`, `code`) VALUES (:id, :code)")
        query.bindValue(':id', self.id)
        for code in set(codes):
            query.bindValue(':code', code)
            if not query.exec():
                raise SyntaxError(query.lastError().text())

    def add_session(self, name):

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO sessions (`subject_id`, `name`) VALUES (:id, :name)")
        query.bindValue(':id', self.id)
        query.bindValue(':name', name)
        if not query.exec():
            raise ValueError(query.lastError().text())

        session_id = query.lastInsertId()
        if session_id is None:
            raise SyntaxError(query.lastError().text())

        return Session(self.db, session_id, subject=self)

    def list_sessions(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT sessions.id, name FROM sessions WHERE sessions.subject_id = :id")
        query.bindValue(':id', self.id)
        assert query.exec()

        list_of_sessions = []
        while query.next():
            list_of_sessions.append(
                Session(self.db, id=query.value('id'), subject=self))
        return sorted(list_of_sessions, key=sort_starttime)

    def add_protocol(self, METC):

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO protocols (`subject_id`, `metc`) VALUES (:id, :metc)")
        query.bindValue(':id', self.id)
        query.bindValue(':metc', METC)

        if not query.exec():
            raise ValueError(query.lastError().text())

        protocol_id = query.lastInsertId()
        return Protocol(self.db, protocol_id, subject=self)

    def list_protocols(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT id FROM protocols WHERE subject_id = :id")
        query.bindValue(':id', self.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_protocols = []
        while query.next():
            list_of_protocols.append(
                Protocol(self.db, id=query.value('id'), subject=self))

        return sorted(list_of_protocols, key=lambda obj: obj.metc)


class Protocol(Table_with_files):
    t = 'protocol'

    def __init__(self, db, id, subject=None):
        super().__init__(db, id)
        self.subject = subject


class Session(Table_with_files):
    t = 'session'
    subject = None

    def __init__(self, db, id, subject=None):
        super().__init__(db, id)
        self.subject = subject

    def __str__(self):
        return f'<{self.t} {self.name} (#{self.id})>'

    @property
    def start_time(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT MIN(runs.start_time) FROM runs WHERE runs.session_id = :id")
        query.bindValue(':id', self.id)
        assert query.exec()

        if query.next():
            return out_datetime(self.db['db'].driverName(), query.value(0))

    def list_runs(self):
        """List runs which were acquired during session"""

        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT runs.id FROM runs WHERE runs.session_id = :id")
        query.bindValue(':id', self.id)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_runs = []
        while query.next():
            list_of_runs.append(
                Run(self.db, id=query.value('id'), session=self))
        return sorted(list_of_runs, key=sort_starttime)

    def list_channels(self):

        chan_ids = list_channels_electrodes(self.db, self.id, name='channel')
        return [Channels(self.db, id=id_) for id_ in chan_ids]

    def list_electrodes(self):

        elec_ids = list_channels_electrodes(self.db, self.id, name='electrode')
        return [Electrodes(self.db, id=id_) for id_ in elec_ids]

    def add_run(self, task_name):

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO runs (`session_id`, `task_name`) VALUES (:id, :task_name)")
        query.bindValue(':id', self.id)
        query.bindValue(':task_name', task_name)
        if not query.exec():
            raise ValueError(query.lastError().text())

        run_id = query.lastInsertId()
        return Run(self.db, run_id, session=self)


class Run(Table_with_files):
    t = 'run'
    session = None

    def __init__(self, db, id, session=None):
        self.session = session
        super().__init__(db, id)

    def __str__(self):
        return f'<{self.t} (#{self.id})>'

    def list_recordings(self):

        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT recordings.id FROM recordings WHERE recordings.run_id = :id")
        query.bindValue(':id', self.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_recordings = []
        while query.next():
            list_of_recordings.append(
                Recording(self.db, id=query.value('id'), run=self))
        return sorted(list_of_recordings, key=lambda obj: obj.modality)

    def add_recording(self, modality):

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO recordings (`run_id`, `modality`) VALUES (:id, :modality)")
        query.bindValue(':id', self.id)
        query.bindValue(':modality', modality)

        if not query.exec():
            raise ValueError(query.lastError().text())

        recording_id = query.lastInsertId()
        recording = Recording(self.db, recording_id, run=self)
        return recording

    @property
    def events(self):
        dtypes = get_dtypes(self.db['tables']['events'])

        query_str = ', '.join(f"`{x}`" for x in dtypes.names)
        query = QSqlQuery(self.db['db'])
        query.prepare(f"SELECT {query_str} FROM events WHERE run_id = :id")
        query.bindValue(':id', self.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

        values = []
        while query.next():
            values.append(
                tuple(query.value(name) for name in dtypes.names)
                )
        return array(values, dtype=dtypes)

    @events.setter
    def events(self, values):
        """If values is None, it deletes all the events.
        """
        query = QSqlQuery(self.db['db'])
        query.prepare('DELETE FROM events WHERE run_id = :id')
        query.bindValue(':id', self.id)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

        if values is None:
            return

        query_str = ', '.join(f"`{x}`" for x in values.dtype.names)

        for row in values:
            values_str = ', '.join([f"'{x}'" for x in row])
            query = QSqlQuery(self.db['db'])
            sql_cmd = f"""\
                INSERT INTO events (`run_id`, {query_str})
                VALUES ('{self.id}', {values_str})
                """
            if not query.exec(sql_cmd):
                raise SyntaxError(query.lastError().text())

    @property
    def experimenters(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT name FROM experimenters JOIN runs_experimenters ON experimenters.id = runs_experimenters.experimenter_id WHERE run_id = :id")
        query.bindValue(':id', self.id)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_experimenters = []
        while query.next():
            list_of_experimenters.append(query.value('name'))
        return sorted(list_of_experimenters)

    @experimenters.setter
    def experimenters(self, experimenters):

        query = QSqlQuery(self.db['db'])
        query.prepare('DELETE FROM runs_experimenters WHERE run_id = :id')
        query.bindValue(':id', self.id)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

        query_select = QSqlQuery(self.db['db'])
        query_select.prepare("SELECT id FROM experimenters WHERE name = :experimenter")

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO runs_experimenters (`run_id`, `experimenter_id`) VALUES (:id, :exp_id)")
        query.bindValue(':id', self.id)

        for exp in experimenters:
            query_select.bindValue(':experimenter', exp)
            if not query_select.exec():
                raise SyntaxError(query_select.lastError().text())

            if query_select.next():
                exp_id = query_select.value('id')
                query.bindValue(':exp_id', exp_id)
                if not query.exec():
                    raise SyntaxError(query.lastError().text())

            else:
                lg.warning(f'Could not find Experimenter called "{exp}". You should add it to "Experimenters" table')

    @property
    def intendedfor(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT target FROM intended_for WHERE run_id = :id")
        query.bindValue(':id', self.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_intendedfor = []
        while query.next():
            list_of_intendedfor.append(
                Run(self.db, query.value('target')))
        return list_of_intendedfor

    @intendedfor.setter
    def intendedfor(self, runs):
        query = QSqlQuery(self.db['db'])
        query.prepare('DELETE FROM intended_for WHERE run_id = :id')
        query.bindValue(':id', self.id)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO intended_for (`run_id`, `target`) VALUES (:id, :target_id)")
        query.bindValue(':id', self.id)

        for one_run in runs:
            query.bindValue(':target_id', one_run.id)
            if not query.exec():
                raise SyntaxError(query.lastError().text())

    def attach_protocol(self, protocol):
        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO runs_protocols (`run_id`, `protocol_id`) VALUES (:id, :protocol_id)")
        query.bindValue(':id', self.id)
        query.bindValue(':protocol_id', protocol.id)

        if not query.exec():
            raise ValueError(query.lastError().text())

    def detach_protocol(self, protocol):
        query = QSqlQuery(self.db['db'])
        query.prepare("DELETE FROM runs_protocols WHERE run_id = :id AND protocol_id = :protocol_id")
        query.bindValue(':id', self.id)
        query.bindValue(':protocol_id', protocol.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

    def list_protocols(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT protocol_id FROM runs_protocols WHERE run_id = :id")
        query.bindValue(':id', self.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_protocols = []
        while query.next():
            list_of_protocols.append(
                Protocol(self.db, query.value('protocol_id')))
        return list_of_protocols


class Recording(Table_with_files):
    t = 'recording'
    run = None

    def __init__(self, db, id, run=None):
        self.run = run
        super().__init__(db, id)

    @property
    def electrodes(self):
        electrode_id = recording_get(self.db, 'electrode', self.id)
        if electrode_id is None:
            return None
        return Electrodes(self.db, id=electrode_id)

    @property
    def channels(self):
        channel_id = recording_get(self.db, 'channel', self.id)
        if channel_id is None:
            return None
        return Channels(self.db, id=channel_id)

    def attach_electrodes(self, electrodes):
        """Only recording_ephys"""
        recording_attach(self.db, 'electrode', self.id, group_id=electrodes.id)

    def attach_channels(self, channels):
        """Only recording_ephys"""
        recording_attach(self.db, 'channel', self.id, group_id=channels.id)

    def detach_electrodes(self):
        """Only recording_ephys"""
        recording_attach(self.db, 'electrode', self.id, group_id=None)

    def detach_channels(self):
        """Only recording_ephys"""
        recording_attach(self.db, 'channel', self.id, group_id=None)


class Channels(NumpyTable):
    t = 'channel_group'  # for Table.__getattr__

    @classmethod
    def add(cls, db):
        # add empty value to get new id
        query = QSqlQuery(db['db'])
        query.prepare("INSERT INTO channel_groups (`Reference`) VALUES (NULL) ")
        if query.exec():
            id = query.lastInsertId()
        else:
            raise SyntaxError(query.lastError().text())

        return cls(db, id)


class Electrodes(NumpyTable):
    t = 'electrode_group'  # for Table.__getattr__

    @classmethod
    def add(cls, db):
        """Use ID if provided, otherwise create a new electrode_group with
        reasonable parameters"""
        query = QSqlQuery(db['db'])
        query.prepare("INSERT INTO electrode_groups (`CoordinateSystem`, `CoordinateUnits`) VALUES ('ACPC', 'mm')")
        if query.exec():
            id = query.lastInsertId()
        else:
            raise SyntaxError(query.lastError().text())

        return cls(db, id)


def list_experimenters(db):
    query = QSqlQuery(db['db'])
    query.prepare("SELECT name FROM experimenters ORDER BY name")

    if not query.exec():
        raise SyntaxError(query.lastError().text())

    out = []
    while query.next():
        out.append(query.value('name'))

    return out

Functions

def list_experimenters(db)
Expand source code
def list_experimenters(db):
    query = QSqlQuery(db['db'])
    query.prepare("SELECT name FROM experimenters ORDER BY name")

    if not query.exec():
        raise SyntaxError(query.lastError().text())

    out = []
    while query.next():
        out.append(query.value('name'))

    return out
def list_subjects(db, alphabetical=False, reverse=False)

List of the subjects in the currently open database, sorted based on the date of their first run.

Parameters

alphabetical : bool
False -> sort by date of first run True -> sort alphabetically
reverse : bool
False -> oldest to newest, True -> newest to oldest False -> A to Z, True -> Z to A

Returns

list of instances of Subject
list of subjects in the database
Expand source code
def list_subjects(db, alphabetical=False, reverse=False):
    """List of the subjects in the currently open database, sorted based on
    the date of their first run.

    Parameters
    ----------
    alphabetical : bool
        False -> sort by date of first run
        True -> sort alphabetically

    reverse : bool
        False -> oldest to newest, True -> newest to oldest
        False -> A to Z, True -> Z to A

    Returns
    -------
    list of instances of Subject
        list of subjects in the database
    """
    query = QSqlQuery(db['db'])
    query.exec('SELECT id FROM subjects')

    list_of_subjects = []
    while query.next():
        list_of_subjects.append(Subject(db, id=query.value('id')))

    if alphabetical:
        _sort_subjects = sort_subjects_alphabetical
    else:
        _sort_subjects = sort_subjects_date

    return sorted(list_of_subjects, key=_sort_subjects, reverse=reverse)

Classes

class Channels (db, id)

Note that self.id points to the ID of the group

Expand source code
class Channels(NumpyTable):
    t = 'channel_group'  # for Table.__getattr__

    @classmethod
    def add(cls, db):
        # add empty value to get new id
        query = QSqlQuery(db['db'])
        query.prepare("INSERT INTO channel_groups (`Reference`) VALUES (NULL) ")
        if query.exec():
            id = query.lastInsertId()
        else:
            raise SyntaxError(query.lastError().text())

        return cls(db, id)

Ancestors

Class variables

var t

Static methods

def add(db)
Expand source code
@classmethod
def add(cls, db):
    # add empty value to get new id
    query = QSqlQuery(db['db'])
    query.prepare("INSERT INTO channel_groups (`Reference`) VALUES (NULL) ")
    if query.exec():
        id = query.lastInsertId()
    else:
        raise SyntaxError(query.lastError().text())

    return cls(db, id)

Inherited members

class Electrodes (db, id)

Note that self.id points to the ID of the group

Expand source code
class Electrodes(NumpyTable):
    t = 'electrode_group'  # for Table.__getattr__

    @classmethod
    def add(cls, db):
        """Use ID if provided, otherwise create a new electrode_group with
        reasonable parameters"""
        query = QSqlQuery(db['db'])
        query.prepare("INSERT INTO electrode_groups (`CoordinateSystem`, `CoordinateUnits`) VALUES ('ACPC', 'mm')")
        if query.exec():
            id = query.lastInsertId()
        else:
            raise SyntaxError(query.lastError().text())

        return cls(db, id)

Ancestors

Class variables

var t

Static methods

def add(db)

Use ID if provided, otherwise create a new electrode_group with reasonable parameters

Expand source code
@classmethod
def add(cls, db):
    """Use ID if provided, otherwise create a new electrode_group with
    reasonable parameters"""
    query = QSqlQuery(db['db'])
    query.prepare("INSERT INTO electrode_groups (`CoordinateSystem`, `CoordinateUnits`) VALUES ('ACPC', 'mm')")
    if query.exec():
        id = query.lastInsertId()
    else:
        raise SyntaxError(query.lastError().text())

    return cls(db, id)

Inherited members

class Protocol (db, id, subject=None)

This class (which should be used by end-users) is useful when handling objects which might be associated with files.

Expand source code
class Protocol(Table_with_files):
    t = 'protocol'

    def __init__(self, db, id, subject=None):
        super().__init__(db, id)
        self.subject = subject

Ancestors

Class variables

var t

Inherited members

class Recording (db, id, run=None)

This class (which should be used by end-users) is useful when handling objects which might be associated with files.

Expand source code
class Recording(Table_with_files):
    t = 'recording'
    run = None

    def __init__(self, db, id, run=None):
        self.run = run
        super().__init__(db, id)

    @property
    def electrodes(self):
        electrode_id = recording_get(self.db, 'electrode', self.id)
        if electrode_id is None:
            return None
        return Electrodes(self.db, id=electrode_id)

    @property
    def channels(self):
        channel_id = recording_get(self.db, 'channel', self.id)
        if channel_id is None:
            return None
        return Channels(self.db, id=channel_id)

    def attach_electrodes(self, electrodes):
        """Only recording_ephys"""
        recording_attach(self.db, 'electrode', self.id, group_id=electrodes.id)

    def attach_channels(self, channels):
        """Only recording_ephys"""
        recording_attach(self.db, 'channel', self.id, group_id=channels.id)

    def detach_electrodes(self):
        """Only recording_ephys"""
        recording_attach(self.db, 'electrode', self.id, group_id=None)

    def detach_channels(self):
        """Only recording_ephys"""
        recording_attach(self.db, 'channel', self.id, group_id=None)

Ancestors

Class variables

var run
var t

Instance variables

var channels
Expand source code
@property
def channels(self):
    channel_id = recording_get(self.db, 'channel', self.id)
    if channel_id is None:
        return None
    return Channels(self.db, id=channel_id)
var electrodes
Expand source code
@property
def electrodes(self):
    electrode_id = recording_get(self.db, 'electrode', self.id)
    if electrode_id is None:
        return None
    return Electrodes(self.db, id=electrode_id)

Methods

def attach_channels(self, channels)

Only recording_ephys

Expand source code
def attach_channels(self, channels):
    """Only recording_ephys"""
    recording_attach(self.db, 'channel', self.id, group_id=channels.id)
def attach_electrodes(self, electrodes)

Only recording_ephys

Expand source code
def attach_electrodes(self, electrodes):
    """Only recording_ephys"""
    recording_attach(self.db, 'electrode', self.id, group_id=electrodes.id)
def detach_channels(self)

Only recording_ephys

Expand source code
def detach_channels(self):
    """Only recording_ephys"""
    recording_attach(self.db, 'channel', self.id, group_id=None)
def detach_electrodes(self)

Only recording_ephys

Expand source code
def detach_electrodes(self):
    """Only recording_ephys"""
    recording_attach(self.db, 'electrode', self.id, group_id=None)

Inherited members

class Run (db, id, session=None)

This class (which should be used by end-users) is useful when handling objects which might be associated with files.

Expand source code
class Run(Table_with_files):
    t = 'run'
    session = None

    def __init__(self, db, id, session=None):
        self.session = session
        super().__init__(db, id)

    def __str__(self):
        return f'<{self.t} (#{self.id})>'

    def list_recordings(self):

        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT recordings.id FROM recordings WHERE recordings.run_id = :id")
        query.bindValue(':id', self.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_recordings = []
        while query.next():
            list_of_recordings.append(
                Recording(self.db, id=query.value('id'), run=self))
        return sorted(list_of_recordings, key=lambda obj: obj.modality)

    def add_recording(self, modality):

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO recordings (`run_id`, `modality`) VALUES (:id, :modality)")
        query.bindValue(':id', self.id)
        query.bindValue(':modality', modality)

        if not query.exec():
            raise ValueError(query.lastError().text())

        recording_id = query.lastInsertId()
        recording = Recording(self.db, recording_id, run=self)
        return recording

    @property
    def events(self):
        dtypes = get_dtypes(self.db['tables']['events'])

        query_str = ', '.join(f"`{x}`" for x in dtypes.names)
        query = QSqlQuery(self.db['db'])
        query.prepare(f"SELECT {query_str} FROM events WHERE run_id = :id")
        query.bindValue(':id', self.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

        values = []
        while query.next():
            values.append(
                tuple(query.value(name) for name in dtypes.names)
                )
        return array(values, dtype=dtypes)

    @events.setter
    def events(self, values):
        """If values is None, it deletes all the events.
        """
        query = QSqlQuery(self.db['db'])
        query.prepare('DELETE FROM events WHERE run_id = :id')
        query.bindValue(':id', self.id)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

        if values is None:
            return

        query_str = ', '.join(f"`{x}`" for x in values.dtype.names)

        for row in values:
            values_str = ', '.join([f"'{x}'" for x in row])
            query = QSqlQuery(self.db['db'])
            sql_cmd = f"""\
                INSERT INTO events (`run_id`, {query_str})
                VALUES ('{self.id}', {values_str})
                """
            if not query.exec(sql_cmd):
                raise SyntaxError(query.lastError().text())

    @property
    def experimenters(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT name FROM experimenters JOIN runs_experimenters ON experimenters.id = runs_experimenters.experimenter_id WHERE run_id = :id")
        query.bindValue(':id', self.id)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_experimenters = []
        while query.next():
            list_of_experimenters.append(query.value('name'))
        return sorted(list_of_experimenters)

    @experimenters.setter
    def experimenters(self, experimenters):

        query = QSqlQuery(self.db['db'])
        query.prepare('DELETE FROM runs_experimenters WHERE run_id = :id')
        query.bindValue(':id', self.id)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

        query_select = QSqlQuery(self.db['db'])
        query_select.prepare("SELECT id FROM experimenters WHERE name = :experimenter")

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO runs_experimenters (`run_id`, `experimenter_id`) VALUES (:id, :exp_id)")
        query.bindValue(':id', self.id)

        for exp in experimenters:
            query_select.bindValue(':experimenter', exp)
            if not query_select.exec():
                raise SyntaxError(query_select.lastError().text())

            if query_select.next():
                exp_id = query_select.value('id')
                query.bindValue(':exp_id', exp_id)
                if not query.exec():
                    raise SyntaxError(query.lastError().text())

            else:
                lg.warning(f'Could not find Experimenter called "{exp}". You should add it to "Experimenters" table')

    @property
    def intendedfor(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT target FROM intended_for WHERE run_id = :id")
        query.bindValue(':id', self.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_intendedfor = []
        while query.next():
            list_of_intendedfor.append(
                Run(self.db, query.value('target')))
        return list_of_intendedfor

    @intendedfor.setter
    def intendedfor(self, runs):
        query = QSqlQuery(self.db['db'])
        query.prepare('DELETE FROM intended_for WHERE run_id = :id')
        query.bindValue(':id', self.id)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO intended_for (`run_id`, `target`) VALUES (:id, :target_id)")
        query.bindValue(':id', self.id)

        for one_run in runs:
            query.bindValue(':target_id', one_run.id)
            if not query.exec():
                raise SyntaxError(query.lastError().text())

    def attach_protocol(self, protocol):
        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO runs_protocols (`run_id`, `protocol_id`) VALUES (:id, :protocol_id)")
        query.bindValue(':id', self.id)
        query.bindValue(':protocol_id', protocol.id)

        if not query.exec():
            raise ValueError(query.lastError().text())

    def detach_protocol(self, protocol):
        query = QSqlQuery(self.db['db'])
        query.prepare("DELETE FROM runs_protocols WHERE run_id = :id AND protocol_id = :protocol_id")
        query.bindValue(':id', self.id)
        query.bindValue(':protocol_id', protocol.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

    def list_protocols(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT protocol_id FROM runs_protocols WHERE run_id = :id")
        query.bindValue(':id', self.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_protocols = []
        while query.next():
            list_of_protocols.append(
                Protocol(self.db, query.value('protocol_id')))
        return list_of_protocols

Ancestors

Class variables

var session
var t

Instance variables

var events
Expand source code
@property
def events(self):
    dtypes = get_dtypes(self.db['tables']['events'])

    query_str = ', '.join(f"`{x}`" for x in dtypes.names)
    query = QSqlQuery(self.db['db'])
    query.prepare(f"SELECT {query_str} FROM events WHERE run_id = :id")
    query.bindValue(':id', self.id)

    if not query.exec():
        raise SyntaxError(query.lastError().text())

    values = []
    while query.next():
        values.append(
            tuple(query.value(name) for name in dtypes.names)
            )
    return array(values, dtype=dtypes)
var experimenters
Expand source code
@property
def experimenters(self):
    query = QSqlQuery(self.db['db'])
    query.prepare("SELECT name FROM experimenters JOIN runs_experimenters ON experimenters.id = runs_experimenters.experimenter_id WHERE run_id = :id")
    query.bindValue(':id', self.id)
    if not query.exec():
        raise SyntaxError(query.lastError().text())

    list_of_experimenters = []
    while query.next():
        list_of_experimenters.append(query.value('name'))
    return sorted(list_of_experimenters)
var intendedfor
Expand source code
@property
def intendedfor(self):
    query = QSqlQuery(self.db['db'])
    query.prepare("SELECT target FROM intended_for WHERE run_id = :id")
    query.bindValue(':id', self.id)

    if not query.exec():
        raise SyntaxError(query.lastError().text())

    list_of_intendedfor = []
    while query.next():
        list_of_intendedfor.append(
            Run(self.db, query.value('target')))
    return list_of_intendedfor

Methods

def add_recording(self, modality)
Expand source code
def add_recording(self, modality):

    query = QSqlQuery(self.db['db'])
    query.prepare("INSERT INTO recordings (`run_id`, `modality`) VALUES (:id, :modality)")
    query.bindValue(':id', self.id)
    query.bindValue(':modality', modality)

    if not query.exec():
        raise ValueError(query.lastError().text())

    recording_id = query.lastInsertId()
    recording = Recording(self.db, recording_id, run=self)
    return recording
def attach_protocol(self, protocol)
Expand source code
def attach_protocol(self, protocol):
    query = QSqlQuery(self.db['db'])
    query.prepare("INSERT INTO runs_protocols (`run_id`, `protocol_id`) VALUES (:id, :protocol_id)")
    query.bindValue(':id', self.id)
    query.bindValue(':protocol_id', protocol.id)

    if not query.exec():
        raise ValueError(query.lastError().text())
def detach_protocol(self, protocol)
Expand source code
def detach_protocol(self, protocol):
    query = QSqlQuery(self.db['db'])
    query.prepare("DELETE FROM runs_protocols WHERE run_id = :id AND protocol_id = :protocol_id")
    query.bindValue(':id', self.id)
    query.bindValue(':protocol_id', protocol.id)

    if not query.exec():
        raise SyntaxError(query.lastError().text())
def list_protocols(self)
Expand source code
def list_protocols(self):
    query = QSqlQuery(self.db['db'])
    query.prepare("SELECT protocol_id FROM runs_protocols WHERE run_id = :id")
    query.bindValue(':id', self.id)

    if not query.exec():
        raise SyntaxError(query.lastError().text())

    list_of_protocols = []
    while query.next():
        list_of_protocols.append(
            Protocol(self.db, query.value('protocol_id')))
    return list_of_protocols
def list_recordings(self)
Expand source code
def list_recordings(self):

    query = QSqlQuery(self.db['db'])
    query.prepare("SELECT recordings.id FROM recordings WHERE recordings.run_id = :id")
    query.bindValue(':id', self.id)

    if not query.exec():
        raise SyntaxError(query.lastError().text())

    list_of_recordings = []
    while query.next():
        list_of_recordings.append(
            Recording(self.db, id=query.value('id'), run=self))
    return sorted(list_of_recordings, key=lambda obj: obj.modality)

Inherited members

class Session (db, id, subject=None)

This class (which should be used by end-users) is useful when handling objects which might be associated with files.

Expand source code
class Session(Table_with_files):
    t = 'session'
    subject = None

    def __init__(self, db, id, subject=None):
        super().__init__(db, id)
        self.subject = subject

    def __str__(self):
        return f'<{self.t} {self.name} (#{self.id})>'

    @property
    def start_time(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT MIN(runs.start_time) FROM runs WHERE runs.session_id = :id")
        query.bindValue(':id', self.id)
        assert query.exec()

        if query.next():
            return out_datetime(self.db['db'].driverName(), query.value(0))

    def list_runs(self):
        """List runs which were acquired during session"""

        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT runs.id FROM runs WHERE runs.session_id = :id")
        query.bindValue(':id', self.id)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_runs = []
        while query.next():
            list_of_runs.append(
                Run(self.db, id=query.value('id'), session=self))
        return sorted(list_of_runs, key=sort_starttime)

    def list_channels(self):

        chan_ids = list_channels_electrodes(self.db, self.id, name='channel')
        return [Channels(self.db, id=id_) for id_ in chan_ids]

    def list_electrodes(self):

        elec_ids = list_channels_electrodes(self.db, self.id, name='electrode')
        return [Electrodes(self.db, id=id_) for id_ in elec_ids]

    def add_run(self, task_name):

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO runs (`session_id`, `task_name`) VALUES (:id, :task_name)")
        query.bindValue(':id', self.id)
        query.bindValue(':task_name', task_name)
        if not query.exec():
            raise ValueError(query.lastError().text())

        run_id = query.lastInsertId()
        return Run(self.db, run_id, session=self)

Ancestors

Class variables

var subject
var t

Instance variables

var start_time
Expand source code
@property
def start_time(self):
    query = QSqlQuery(self.db['db'])
    query.prepare("SELECT MIN(runs.start_time) FROM runs WHERE runs.session_id = :id")
    query.bindValue(':id', self.id)
    assert query.exec()

    if query.next():
        return out_datetime(self.db['db'].driverName(), query.value(0))

Methods

def add_run(self, task_name)
Expand source code
def add_run(self, task_name):

    query = QSqlQuery(self.db['db'])
    query.prepare("INSERT INTO runs (`session_id`, `task_name`) VALUES (:id, :task_name)")
    query.bindValue(':id', self.id)
    query.bindValue(':task_name', task_name)
    if not query.exec():
        raise ValueError(query.lastError().text())

    run_id = query.lastInsertId()
    return Run(self.db, run_id, session=self)
def list_channels(self)
Expand source code
def list_channels(self):

    chan_ids = list_channels_electrodes(self.db, self.id, name='channel')
    return [Channels(self.db, id=id_) for id_ in chan_ids]
def list_electrodes(self)
Expand source code
def list_electrodes(self):

    elec_ids = list_channels_electrodes(self.db, self.id, name='electrode')
    return [Electrodes(self.db, id=id_) for id_ in elec_ids]
def list_runs(self)

List runs which were acquired during session

Expand source code
def list_runs(self):
    """List runs which were acquired during session"""

    query = QSqlQuery(self.db['db'])
    query.prepare("SELECT runs.id FROM runs WHERE runs.session_id = :id")
    query.bindValue(':id', self.id)
    if not query.exec():
        raise SyntaxError(query.lastError().text())

    list_of_runs = []
    while query.next():
        list_of_runs.append(
            Run(self.db, id=query.value('id'), session=self))
    return sorted(list_of_runs, key=sort_starttime)

Inherited members

class Subject (db, code=None, id=None)

This class (which should be used by end-users) is useful when handling objects which might be associated with files.

Expand source code
class Subject(Table_with_files):
    t = 'subject'

    def __init__(self, db, code=None, id=None):
        if code is not None:
            id = find_subject_id(db, code)
            if id is None:
                raise ValueError(f'There is no "{code}" in "subject_codes" table')

        super().__init__(db, id)

    def __str__(self):
        codes = self.codes
        if len(codes) == 0:
            return '(subject without code)'
        elif len(codes) == 1:
            return codes[0]
        else:
            return ', '.join(codes)

    @classmethod
    def add(cls, db, code=None):
        """You can create an empty subject, with no code, but it's a bad idea
        """
        if code is not None:
            id = find_subject_id(db, code)
            if id is not None:
                raise ValueError(f'Subject "{code}" already exists')

        # add empty value to get new id
        query = QSqlQuery(db['db'])
        query.prepare("INSERT INTO subjects () VALUES () ")
        if query.exec():
            id = query.lastInsertId()
        else:
            raise ValueError(query.lastError().text())

        if code is not None:
            query = QSqlQuery(db['db'])
            query.prepare("INSERT INTO subject_codes (`subject_id`, `code`) VALUES (:subject_id, :code)")
            query.bindValue(':subject_id', id)
            query.bindValue(':code', code)
            if not query.exec():
                raise SyntaxError(query.lastError().text())

        return Subject(db, id=id)

    @property
    def codes(self):
        """Get the codes associated with this subjects"""
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT code FROM subject_codes WHERE subject_codes.subject_id = :id")
        query.bindValue(':id', self.id)
        if not query.exec():
            lg.warning(query.lastError().text())

        list_of_codes = []
        while query.next():
            list_of_codes.append(query.value('code'))

        # put RESP at the end
        list_of_codes.sort()
        list_of_codes.sort(key=lambda s: s.startswith('RESP'))
        return list_of_codes

    @codes.setter
    def codes(self, codes):

        query = QSqlQuery(self.db['db'])
        query.prepare('DELETE FROM subject_codes WHERE subject_id = :id')
        query.bindValue(':id', self.id)
        if not query.exec():
            lg.warning(query.lastError().text())

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO subject_codes (`subject_id`, `code`) VALUES (:id, :code)")
        query.bindValue(':id', self.id)
        for code in set(codes):
            query.bindValue(':code', code)
            if not query.exec():
                raise SyntaxError(query.lastError().text())

    def add_session(self, name):

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO sessions (`subject_id`, `name`) VALUES (:id, :name)")
        query.bindValue(':id', self.id)
        query.bindValue(':name', name)
        if not query.exec():
            raise ValueError(query.lastError().text())

        session_id = query.lastInsertId()
        if session_id is None:
            raise SyntaxError(query.lastError().text())

        return Session(self.db, session_id, subject=self)

    def list_sessions(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT sessions.id, name FROM sessions WHERE sessions.subject_id = :id")
        query.bindValue(':id', self.id)
        assert query.exec()

        list_of_sessions = []
        while query.next():
            list_of_sessions.append(
                Session(self.db, id=query.value('id'), subject=self))
        return sorted(list_of_sessions, key=sort_starttime)

    def add_protocol(self, METC):

        query = QSqlQuery(self.db['db'])
        query.prepare("INSERT INTO protocols (`subject_id`, `metc`) VALUES (:id, :metc)")
        query.bindValue(':id', self.id)
        query.bindValue(':metc', METC)

        if not query.exec():
            raise ValueError(query.lastError().text())

        protocol_id = query.lastInsertId()
        return Protocol(self.db, protocol_id, subject=self)

    def list_protocols(self):
        query = QSqlQuery(self.db['db'])
        query.prepare("SELECT id FROM protocols WHERE subject_id = :id")
        query.bindValue(':id', self.id)

        if not query.exec():
            raise SyntaxError(query.lastError().text())

        list_of_protocols = []
        while query.next():
            list_of_protocols.append(
                Protocol(self.db, id=query.value('id'), subject=self))

        return sorted(list_of_protocols, key=lambda obj: obj.metc)

Ancestors

Class variables

var t

Static methods

def add(db, code=None)

You can create an empty subject, with no code, but it's a bad idea

Expand source code
@classmethod
def add(cls, db, code=None):
    """You can create an empty subject, with no code, but it's a bad idea
    """
    if code is not None:
        id = find_subject_id(db, code)
        if id is not None:
            raise ValueError(f'Subject "{code}" already exists')

    # add empty value to get new id
    query = QSqlQuery(db['db'])
    query.prepare("INSERT INTO subjects () VALUES () ")
    if query.exec():
        id = query.lastInsertId()
    else:
        raise ValueError(query.lastError().text())

    if code is not None:
        query = QSqlQuery(db['db'])
        query.prepare("INSERT INTO subject_codes (`subject_id`, `code`) VALUES (:subject_id, :code)")
        query.bindValue(':subject_id', id)
        query.bindValue(':code', code)
        if not query.exec():
            raise SyntaxError(query.lastError().text())

    return Subject(db, id=id)

Instance variables

var codes

Get the codes associated with this subjects

Expand source code
@property
def codes(self):
    """Get the codes associated with this subjects"""
    query = QSqlQuery(self.db['db'])
    query.prepare("SELECT code FROM subject_codes WHERE subject_codes.subject_id = :id")
    query.bindValue(':id', self.id)
    if not query.exec():
        lg.warning(query.lastError().text())

    list_of_codes = []
    while query.next():
        list_of_codes.append(query.value('code'))

    # put RESP at the end
    list_of_codes.sort()
    list_of_codes.sort(key=lambda s: s.startswith('RESP'))
    return list_of_codes

Methods

def add_protocol(self, METC)
Expand source code
def add_protocol(self, METC):

    query = QSqlQuery(self.db['db'])
    query.prepare("INSERT INTO protocols (`subject_id`, `metc`) VALUES (:id, :metc)")
    query.bindValue(':id', self.id)
    query.bindValue(':metc', METC)

    if not query.exec():
        raise ValueError(query.lastError().text())

    protocol_id = query.lastInsertId()
    return Protocol(self.db, protocol_id, subject=self)
def add_session(self, name)
Expand source code
def add_session(self, name):

    query = QSqlQuery(self.db['db'])
    query.prepare("INSERT INTO sessions (`subject_id`, `name`) VALUES (:id, :name)")
    query.bindValue(':id', self.id)
    query.bindValue(':name', name)
    if not query.exec():
        raise ValueError(query.lastError().text())

    session_id = query.lastInsertId()
    if session_id is None:
        raise SyntaxError(query.lastError().text())

    return Session(self.db, session_id, subject=self)
def list_protocols(self)
Expand source code
def list_protocols(self):
    query = QSqlQuery(self.db['db'])
    query.prepare("SELECT id FROM protocols WHERE subject_id = :id")
    query.bindValue(':id', self.id)

    if not query.exec():
        raise SyntaxError(query.lastError().text())

    list_of_protocols = []
    while query.next():
        list_of_protocols.append(
            Protocol(self.db, id=query.value('id'), subject=self))

    return sorted(list_of_protocols, key=lambda obj: obj.metc)
def list_sessions(self)
Expand source code
def list_sessions(self):
    query = QSqlQuery(self.db['db'])
    query.prepare("SELECT sessions.id, name FROM sessions WHERE sessions.subject_id = :id")
    query.bindValue(':id', self.id)
    assert query.exec()

    list_of_sessions = []
    while query.next():
        list_of_sessions.append(
            Session(self.db, id=query.value('id'), subject=self))
    return sorted(list_of_sessions, key=sort_starttime)

Inherited members