diff --git a/.gitignore b/.gitignore index 27a6378..e13e4fa 100644 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,4 @@ docker-compose.y*ml # notes temp* +*/temp* diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d8569..4ea2ef8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,8 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention. -## [0.1.0c0] - 2021-11-03 -### Added -+ First draft begins - -## [0.1.0b0] - 2021-00-00 +## 0.1.0b0 - Unreleased ### Added + First beta release + ++ First draft based on [Cajal](https://github.com/cajal/pipeline) and [Kavli Institute](https://github.com/kavli-ntnu/dj-docs) precursor projects diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..dd14131 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contribution Guidelines + +This project follows the [DataJoint Contribution Guidelines](https://docs.datajoint.org/python/community/02-Contribute.html). Please reference the link for more full details. diff --git a/LICENSE b/LICENSE index 6bf141b..2f92789 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 DataJoint +Copyright (c) 2022 DataJoint Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index b0ba2c1..8831670 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,65 @@ -# element-trial-behavior -This repository is a work in progress. It serves as a draft of a DataJoints element for trial-based behavior for our U24 itiative. - -## Notes: -I looked at the structure for `element-array-ephys` for general principle on how to call and load files. I mirrored the main DataJoint implementation as split from 'readers'. I incorporated feedback from project-specific `behavior.py` elsewhere in table development. - -## To do: -- [ ] Support functions - - [ ] Other elements/workflows pull `find_full_path` and `find_root_directory` either from their own `__init__.py` files or from `element-data-loader.utils`. Which is best practice? - - [ ] `workflow-array-ephys` relies on the linking module for functions to get root and session directories, but the MAP project defines these internally. Which is best practice? -- [ ] Table definitions: Discuss table structure -- [ ] Decide supported filetypes - - [ ] BPOD - - [ ] Kepec standard, TBD - - [ ] Generalizable CSV with user-determined column name to DJ variable name correspondence? -- [ ] Contact the [BPod team](https://github.com/sanworks/) - - [ ] Already an implementation of loading to Python? - - [ ] Create joint sustainability roadmap -- [ ] Contact Kepec team - joint sustainability roadmap -- [ ] Analysis package - - [ ] Load processed data to table structure - - [ ] Trigger analysis on raw data import -- [ ] Quality control metrics -- [ ] GitHub Actions for PyPI release -- [ ] example workflow - - [ ] Integration tests with pytest - - [ ] Tutorials in text format (i.e. Jupyter notebook) - - [ ] Tutorial in video format - - [ ] Docker for tests - - [ ] Example dataset(s) for public release, in DJ Archive - - [ ] NWB export - - [ ] README -- [ ] RRID +# DataJoint Element - Experimental trials +This repository is a work in progress not yet ready for public release. +It serves as a draft of a DataJoint element for trialized experiments behavior +for our U24 itiative. + +## Element architecture + +In both of the following diagrams, the trial table starts immediately downstream from +***Session***. In one case, Sessions are first segmented into trials, and then +segmented into events. This might be appropriate, for example, in a paradigm with +repeated conditions and response behaviors associated with different conditions. In the +next, Sessions are directly upstream from both Trials and Events. This might be appropropriate for a paradigm that recorded events within naturalistic free behavior. We provide an +[example workflow](https://github.com/datajoint/workflow-trial/) with a +[pipeline script](https://github.com/datajoint/workflow-trial/blob/main/workflow_trial/pipeline.py) +that models combining this Element with the corresponding +[Element-Session](https://github.com/datajoint/element-session). + +### Trial Schema + +![trial schema](./images/diagram_trial.svg) + +### Event Schema +![event schema](./images/diagram_event.svg) + +## Installation + ++ Install `element-trial` + ``` + pip install element-trial + ``` + ++ Upgrade `element-trial` previously installed with `pip` + ``` + pip install --upgrade element-trial + ``` + + + +## Usage + +### Element activation + +To activate the `element-trial`, one need to provide: + +1. Schema names for the event or trial module +2. Upstream Session table: A set of keys identifying a recording session (see [ +Element-Session](https://github.com/datajoint/element-session)). +3. Utility functions. See +[example definitions here](https://github.com/datajoint/workflow-trial/blob/main/workflow_trial/paths.py) + +For more detail, check the docstring of the `element-trial`: +```python +from element_trial import event, trial +help(event.activate) +help(trial.activate) +``` diff --git a/dj_local_conf_example.json b/dj_local_conf_example.json deleted file mode 100644 index 498909d..0000000 --- a/dj_local_conf_example.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "database.host": "", - "database.password": "", - "database.user": "", - "database.port": 3306, - "database.reconnect": true, - "connection.init_function": null, - "connection.charset": "", - "loglevel": "INFO", - "safemode": true, - "fetch_format": "array", - "display.limit": 12, - "display.width": 14, - "display.show_tuple_count": true, - "database.use_tls": null, - "enable_python_native_blobs": true, - "database.ingest_filename_short": "", - "database.ingest_filename_full": "", - "custom": { - "database.prefix": "YourPrefix_", - "beh_root_dir": [ - "/Abolute/Path/Here/", - "/Abolute/Other/Path/" - ] - } -} \ No newline at end of file diff --git a/element_trial/__init__.py b/element_trial/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/element_trial/event.py b/element_trial/event.py new file mode 100644 index 0000000..59ba5a6 --- /dev/null +++ b/element_trial/event.py @@ -0,0 +1,156 @@ +"""Events are linked directly to session""" + +import datajoint as dj +import inspect +import importlib + +schema = dj.schema() + +_linking_module = None + + +def activate(schema_name, *, create_schema=True, create_tables=True, + linking_module=None): + """ + activate(schema_name, *, create_schema=True, create_tables=True, + linking_module=None) + :param schema_name: schema name on the database server to activate + the `behavior` element + :param create_schema: when True (default), create schema in the + database if it does not yet exist. + :param create_tables: when True (default), create tables in the + database if they do not yet exist. + :param linking_module: a module (or name) containing the required + dependencies to activate the `session` element: + Upstream tables: + + Session: parent table to ProbeInsertion, typically + identifying a recording session. + Functions: + + get_trial_root_data_dir() -> list + Retrieve the root data director(y/ies) with behavioral + recordings (e.g., bpod files) for all subject/sessions. + :return: a string for full path to the root data directory + + get_trial_sess_dir(session_key: dict) -> str + Retrieve the session directory containing the recording(s) + for a given Session + :param session_key: a dictionary of one Session `key` + :return: a string for full path to the session directory + """ + if isinstance(linking_module, str): + linking_module = importlib.import_module(linking_module) + assert inspect.ismodule(linking_module), "The argument 'dependency' must"\ + + " be a module or module name" + + schema.activate(schema_name, create_schema=create_schema, + create_tables=create_tables, + add_objects=linking_module.__dict__) + +# -------------- Functions required by the element-trial --------------- + + +def get_trial_root_data_dir() -> list: + """ + All data paths, directories in DataJoint Elements are recommended to be + stored as relative paths, with respect to some user-configured "root" + directory, which varies from machine to machine + + get_trial_root_data_dir() -> list + This user-provided function retrieves the list of possible root data + directories containing the behavioral data for all subjects/sessions + :return: a string for full path to the behavioral root data directory, + or list of strings for possible root data directories + """ + return _linking_module.get_trial_root_data_dir() + + +def get_trial_sess_dir(session_key: dict) -> str: + """ + get_trial_sess_dir(session_key: dict) -> str + Retrieve the session directory, with all recordings for a given Session + :param session_key: a dictionary of one Session `key` + :return: a string for full path to the session directory + """ + return _linking_module.get_trial_sess_dir(session_key) + +# ----------------------------- Table declarations ---------------------- + + +@schema +class BehaviorRecording(dj.Manual): + definition = """ + -> Session + recording_id : varchar(16) + --- + recording_notes : varchar(256) + """ + + class BehaviorFile(dj.Part): + definition = """ + -> master + filepath : varchar(16) + """ + + +@schema +class TrialType(dj.Lookup): + definition = """ + trial_type : varchar(16) + --- + trial_type_description : varchar(256) + """ + + +@schema +class Trial(dj.Imported): + definition = """ + -> Session + trial : smallint # trial number (1-based indexing) + --- + -> TrialType + start_time : float # (second) relative to recording start + stop_time : float # (second) relative to recording start + """ + + class TrialVariable(dj.Part): + definition = """ + -> master + variable_name: varchar(16) + --- + variable_value: varchar(2000) + """ + + +@schema +class EventType(dj.Lookup): + definition = """ + event_type : varchar(16) + --- + event_type_description='': varchar(256) + """ + + +@schema +class Event(dj.Imported): + definition = """ # + -> Session + event_start_time : float # (second) relative to recording start + --- + -> EventType + event_end_time=null: float # (second) relative to recording start + """ + + class EventVariable(dj.Part): + definition = """ + -> master + variable_name: varchar(16) + --- + variable_value: varchar(2000) + """ + + +@schema +class TrialEvent(dj.Imported): + definition = """ + -> Trial + -> Event + """ diff --git a/element_trial/export/__init__.py b/element_trial/export/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/element_trial/trial.py b/element_trial/trial.py new file mode 100644 index 0000000..b50fb78 --- /dev/null +++ b/element_trial/trial.py @@ -0,0 +1,148 @@ +"""Events are linked to Trials""" + +import datajoint as dj +import inspect +import importlib + +schema = dj.schema() + +_linking_module = None + + +def activate(schema_name, *, create_schema=True, create_tables=True, + linking_module=None): + """ + activate(schema_name, *, create_schema=True, create_tables=True, + linking_module=None) + :param schema_name: schema name on the database server to activate + the `behavior` element + :param create_schema: when True (default), create schema in the + database if it does not yet exist. + :param create_tables: when True (default), create tables in the + database if they do not yet exist. + :param linking_module: a module (or name) containing the required + dependencies to activate the `session` element: + Upstream tables: + + Session: parent table to ProbeInsertion, typically + identifying a recording session. + Functions: + + get_trial_root_data_dir() -> list + Retrieve the root data director(y/ies) with behavioral + recordings (e.g., bpod files) for all subject/sessions. + :return: a string for full path to the root data directory + + get_trial_sess_dir(session_key: dict) -> str + Retrieve the session directory containing the recording(s) + for a given Session + :param session_key: a dictionary of one Session `key` + :return: a string for full path to the session directory + """ + if isinstance(linking_module, str): + linking_module = importlib.import_module(linking_module) + assert inspect.ismodule(linking_module), "The argument 'dependency' must"\ + + " be a module or module name" + + schema.activate(schema_name, create_schema=create_schema, + create_tables=create_tables, + add_objects=linking_module.__dict__) + +# -------------- Functions required by the element-trial --------------- + + +def get_trial_root_data_dir() -> list: + """ + All data paths, directories in DataJoint Elements are recommended to be + stored as relative paths, with respect to some user-configured "root" + directory, which varies from machine to machine + + get_trial_root_data_dir() -> list + This user-provided function retrieves the list of possible root data + directories containing the behavioral data for all subjects/sessions + :return: a string for full path to the behavioral root data directory, + or list of strings for possible root data directories + """ + return _linking_module.get_trial_root_data_dir() + + +def get_trial_sess_dir(session_key: dict) -> str: + """ + get_trial_sess_dir(session_key: dict) -> str + Retrieve the session directory, with all recordings for a given Session + :param session_key: a dictionary of one Session `key` + :return: a string for full path to the session directory + """ + return _linking_module.get_trial_sess_dir(session_key) + +# ----------------------------- Table declarations ---------------------- + + +@schema +class BehaviorRecording(dj.Manual): + definition = """ + -> Session + recording_id : varchar(16) + --- + recording_notes : varchar(256) + """ + + class BehaviorFile(dj.Part): + definition = """ + -> master + filepath : varchar(16) + """ + + +@schema +class TrialType(dj.Lookup): + definition = """ + trial_type : varchar(16) + --- + trial_type_description : varchar(256) + """ + + +@schema +class Trial(dj.Imported): + definition = """ + -> Session + trial : smallint # trial number (1-based indexing) + --- + -> TrialType + start_time : float # (second) relative to recording start + stop_time : float # (second) relative to recording start + """ + + class TrialVariable(dj.Part): + definition = """ + -> master + variable_name: varchar(16) + --- + variable_value: varchar(2000) + """ + + +@schema +class EventType(dj.Lookup): + definition = """ + event_type : varchar(16) + --- + event_type_description='': varchar(256) + """ + + +@schema +class Event(dj.Imported): + definition = """ + -> Trial + event_start_time : float # (second) relative to recording start + --- + -> EventType + event_end_time=null: float # (second) relative to recording start + """ + + class EventVariable(dj.Part): + definition = """ + -> master + variable_name: varchar(16) + --- + variable_value: varchar(2000) + """ diff --git a/element_trial_behavior/version.py b/element_trial/version.py similarity index 50% rename from element_trial_behavior/version.py rename to element_trial/version.py index 5677c7a..5613088 100644 --- a/element_trial_behavior/version.py +++ b/element_trial/version.py @@ -1,2 +1,2 @@ """Package metadata.""" -__version__ = '0.1.0c0' \ No newline at end of file +__version__ = '0.0.0a0' diff --git a/element_trial_behavior/__init__.py b/element_trial_behavior/__init__.py deleted file mode 100644 index 5f3c8ce..0000000 --- a/element_trial_behavior/__init__.py +++ /dev/null @@ -1,68 +0,0 @@ - -## Much of contents ported over from element_array_ephys/__init__.py -## functions duplicated in element init.py and dataloader utils -## Should we reduce duplication, always point to utils? - -__author__ = "DataJoint" -__date__ = "November, 2021" -__version__ = "0.1.0c0" - -__all__ = ['__author__', '__version__', '__date__'] - - -import datajoint as dj -import pathlib - - -dj.config['enable_python_native_blobs'] = True - - -def find_full_path(root_directories, relative_path): - """ - Given a relative path, search and return the full-path - from provided potential root directories (in the given order) - :param root_directories: potential root directories - :param relative_path: the relative path to find the valid root directory - :return: root_directory (pathlib.Path object) - """ - relative_path = pathlib.Path(relative_path) - - if relative_path.exists(): - return relative_path - - # turn to list if only a single root directory is provided - if isinstance(root_directories, (str, pathlib.Path)): - root_directories = [root_directories] - - for root_dir in root_directories: - if (pathlib.Path(root_dir) / relative_path).exists(): - return pathlib.Path(root_dir) / relative_path - - raise FileNotFoundError('No valid full-path found (from {})' - ' for {}'.format(root_directories, relative_path)) - - -def find_root_directory(root_directories, full_path): - """ - Given multiple potential root directories and a full-path, - search and return one directory that is the parent of the given path - :param root_directories: potential root directories - :param full_path: the relative path to search the root directory - :return: full-path (pathlib.Path object) - """ - full_path = pathlib.Path(full_path) - - if not full_path.exists(): - raise FileNotFoundError(f'{full_path} does not exist!') - - # turn to list if only a single root directory is provided - if isinstance(root_directories, (str, pathlib.Path)): - root_directories = [root_directories] - - try: - return next(pathlib.Path(root_dir) for root_dir in root_directories - if pathlib.Path(root_dir) in set(full_path.parents)) - - except StopIteration: - raise FileNotFoundError('No valid root directory found (from {})' - ' for {}'.format(root_directories, full_path)) \ No newline at end of file diff --git a/element_trial_behavior/behavior_trials.py b/element_trial_behavior/behavior_trials.py deleted file mode 100644 index 783f8ea..0000000 --- a/element_trial_behavior/behavior_trials.py +++ /dev/null @@ -1,169 +0,0 @@ -import datajoint as dj -import inspect -import importlib - -schema = dj.schema() - -def activate(schema_name, *, create_schema=True, create_tables=True, linking_module=None): - """ - activate(schema_name, *, create_schema=True, create_tables=True, linking_module=None) - :param schema_name: schema name on the database server to activate the `behavior` element - :param create_schema: when True (default), create schema in the database if it does not yet exist. - :param create_tables: when True (default), create tables in the database if they do not yet exist. - :param linking_module: a module name or a module containing the required - dependencies to activate the `session` element: - Upstream tables: - + Session: parent table to ProbeInsertion, typically identifying a recording session. - Functions: - + get_beh_root_dir() -> list - Retrieve the root data directory/directories containing behavioral recordings - (e.g., bpod files) for all subject/sessions. - :return: a string for full path to the root data directory - + get_beh_sess_dir(session_key: dict) -> str - Retrieve the session directory containing the recording(s) for a given Session - :param session_key: a dictionary of one Session `key` - :return: a string for full path to the session directory - """ - if isinstance(linking_module, str): - linking_module = importlib.import_module(linking_module) - assert inspect.ismodule(linking_module), "The argument 'dependency' must be a module's name or a module" - - schema.activate(schema_name, create_schema=create_schema, - create_tables=create_tables, add_objects=linking_module.__dict__) - -# -------------- Functions required by the elements-ephys --------------- - -# CB: Ephys pulls from linking module, MAP defines internally - default to ephys style? - -def get_beh_root_dir() -> list: - """ - All data paths, directories in DataJoint Elements are recommended to be stored as - relative paths, with respect to some user-configured "root" directory, - which varies from machine to machine (e.g. different mounted drive locations) - - get_beh_root_dir() -> list - This user-provided function retrieves the list of possible root data directories - containing the behavioral data for all subjects/sessions (e.g., bpod files) - :return: a string for full path to the behavioral root data directory, - or list of strings for possible root data directories - """ - return _linking_module.get_beh_root_dir() - - -def get_beh_sess_dir(session_key: dict) -> str: - """ - get_beh_sess_dir(session_key: dict) -> str - Retrieve the session directory containing the recordings for a given Session - :param session_key: a dictionary of one Session `key` - :return: a string for full path to the session directory - """ - return _linking_module.get_beh_sess_dir(session_key) - -# ----------------------------- Table declarations ---------------------- - -@schema -class TrialType(dj.Lookup): - definition = """ - trial_type: varchar(16) - --- - trial_type_description: varchar(256) - """ - -@schema -class Trial(dj.Imported): - definition = """ - -> Session - trial : smallint # trial number (1-based indexing) - --- - start_time : float # (second) relative to the start of the recording - stop_time : float # (second) relative to the start of the recording - """ - -@schema -class EventType(dj.Lookup): - definition = """ - event_type: varchar(16) - --- - event_type_description='': varchar(256) - """ - -@schema -class Event(dj.Imported): - definition = """ - -> Event - -> EventType - event_start_time: decimal(8, 4) # (s) from recording start - --- - event_end_time=null: decimal(8, 4) # (s) from recording start - """ - -@schema -class TrialEvent(dj.Imported): - definition = """ - -> Trial - -> Event - """ - -@schema -class BehaviorTrial(dj.Imported): - definition = """ - -> Trial - --- - -> TrialType - """ - - class TrialVariable(dj.Part): - definition = """ - -> master - variable_name: varchar(16) - --- - variable_value: varchar(2000) - """ - -@schema -class BehaviorRecording(dj.Imported) - definition = """ - -> session.SessionDirectory - recording_id: varchar(16) - --- - recording_notes: varchar(256) - """ - - class BehFile(dj.Part): - definition = """ - -> master - filetype: varchar(16) - """ - - def make(self, key): - beh_root_dir = pathlib.Path(get_beh_root_dir(key)) - beh_sess_dir = pathlib.Path(get_beh_sess_dir(key)) - beh_dir_full = find_full_path(beh_root_dir,beh_sess_dir) - - for beh_pattern, beh_file_type in zip(['*mat','*XXX'], - ['bpod','Kepecs Standard']) - # CB: critique - assumes .mat is bpod - # I wanted to save trying to open the file, so the bpod init - # checks and errors out if not. Are there better ways? - beh_filepaths = [fp for fp in beh_dir_full.rglob(beh_pattern)] - if beh_filepaths: - filetype = beh_file_type - break - else: - raise FileNotFoundError( - f'Neither bpod mat file nor csv file found in {beh_sess_dir}') - - if filetype == 'bpod': - for filepath in beh_filepaths: - bpod_data = bpod.BPod(filepath) - ''' - key['property'] = bpod_data.property - ''' - elif filetype == 'Kepecs Standard': - for filepath in beh_filepaths: - pass - # Waiting on more info from Kepecs Lab - # elif filetype == "other standard??" what others? - else: - raise NotImplementedError(f'Behavioral file of type {filetype}' - ' is not yet implemented') diff --git a/element_trial_behavior/readers/bpod.py b/element_trial_behavior/readers/bpod.py deleted file mode 100644 index 390133f..0000000 --- a/element_trial_behavior/readers/bpod.py +++ /dev/null @@ -1,49 +0,0 @@ -import os -import pathlib - -import datajoint as dj -from . import find_full_path, find_root_directory - -import scipy.io as spio -import numpy as np - -class BPod: - - def __init__(self, full_dir): - # check for file - try: - self.filepath=next(pathlib.Path(full_dir).glob('*.mat')) - except StopIteration: - raise FileNotFoundError(f'No bpod .mat file found at: {full_dir}') - # check that .mat file found is bpod, by checking that it has a SessionData section - try: self.file=load_bpod_matfile(self.file) - except StopIteration: - raise FileNotFoundError(f'.mat file missing SessionData field at: {full_dir}') - - @property - def trialnumber(self): - if self._trialnumber is None: - self._trialnumber = self.file.nTrials - return self._trialnumber - - ''' - @property - def [each relevant bpod property](self)]: - if self.[relevant property] is None: - self.[relevant property] = self.file.[specific structure] - ''' - -# --------------------- HELPER LOADER FUNCTIONS ----------------- - -# see full example here: -# https://github.com/mesoscale-activity-map/map-ephys/blob/master/pipeline/ingest/behavior.py - -def load_bpod_matfile(dir, matlab_filepath): - """ - Loading routine for behavioral file, bpod .mat - """ - #Loading the file - SessionData = spio.loadmat(matlab_filepath.as_posix(), - squeeze_me=True, - struct_as_record=False)['SessionData'] - return SessionData \ No newline at end of file diff --git a/images/diagram_event.svg b/images/diagram_event.svg new file mode 100644 index 0000000..405f8e9 --- /dev/null +++ b/images/diagram_event.svg @@ -0,0 +1,145 @@ + + + + + +event.Trial.TrialVariable + + +event.Trial.TrialVariable + + + + + +event.Trial + + +event.Trial + + + + + +event.Trial->event.Trial.TrialVariable + + + + +event.TrialEvent + + +event.TrialEvent + + + + + +event.Trial->event.TrialEvent + + + + +event.EventType + + +event.EventType + + + + + +event.Event + + +event.Event + + + + + +event.EventType->event.Event + + + + +event.Event.EventVariable + + +event.Event.EventVariable + + + + + +event.TrialType + + +event.TrialType + + + + + +event.TrialType->event.Trial + + + + +event.BehaviorRecording + + +event.BehaviorRecording + + + + + +event.BehaviorRecording.BehaviorFile + + +event.BehaviorRecording.BehaviorFile + + + + + +event.BehaviorRecording->event.BehaviorRecording.BehaviorFile + + + + +event.Event->event.TrialEvent + + + + +event.Event->event.Event.EventVariable + + + + +session.Session + + +session.Session + + + + + +session.Session->event.Trial + + + + +session.Session->event.BehaviorRecording + + + + +session.Session->event.Event + + + + \ No newline at end of file diff --git a/images/diagram_trial.svg b/images/diagram_trial.svg new file mode 100644 index 0000000..dd77e26 --- /dev/null +++ b/images/diagram_trial.svg @@ -0,0 +1,126 @@ + + + + + +trial.TrialType + + +trial.TrialType + + + + + +trial.Trial + + +trial.Trial + + + + + +trial.TrialType->trial.Trial + + + + +session.Session + + +session.Session + + + + + +trial.BehaviorRecording + + +trial.BehaviorRecording + + + + + +session.Session->trial.BehaviorRecording + + + + +session.Session->trial.Trial + + + + +trial.Event.EventVariable + + +trial.Event.EventVariable + + + + + +trial.BehaviorRecording.BehaviorFile + + +trial.BehaviorRecording.BehaviorFile + + + + + +trial.Event + + +trial.Event + + + + + +trial.Event->trial.Event.EventVariable + + + + +trial.EventType + + +trial.EventType + + + + + +trial.EventType->trial.Event + + + + +trial.Trial.TrialVariable + + +trial.Trial.TrialVariable + + + + + +trial.BehaviorRecording->trial.BehaviorRecording.BehaviorFile + + + + +trial.Trial->trial.Event + + + + +trial.Trial->trial.Trial.TrialVariable + + + + \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..40cda4c --- /dev/null +++ b/setup.py @@ -0,0 +1,30 @@ +from setuptools import setup, find_packages +from os import path + +pkg_name = next(p for p in find_packages() if '.' not in p) +here = path.abspath(path.dirname(__file__)) + +with open(path.join(here, 'README.md'), 'r') as f: + long_description = f.read() + +with open(path.join(here, 'requirements.txt')) as f: + requirements = f.read().splitlines() + +with open(path.join(here, pkg_name, 'version.py')) as f: + exec(f.read()) + +setup( + name=pkg_name.replace('_', '-'), + version=__version__, + description="DataJoint Elements for Trialized Experiments", + long_description=long_description, + long_description_content_type='text/markdown', + author='DataJoint', + author_email='info@datajoint.com', + license='MIT', + url=f'https://github.com/datajoint/{pkg_name.replace("_", "-")}', + keywords='neuroscience behavior bpod trials datajoint', + packages=find_packages(exclude=['contrib', 'docs', 'tests*']), + scripts=[], + install_requires=requirements, +)