From 9d60f83c91d4176f81519dff2d24c071c6204efb Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Wed, 3 Jan 2024 16:11:50 -0600 Subject: [PATCH 01/26] Add DevContainer + general updates --- .devcontainer/Dockerfile | 48 + .devcontainer/devcontainer.json | 30 + .devcontainer/docker-compose.yaml | 25 + .github/workflows/release.yaml | 27 + .github/workflows/test.yaml | 34 + .../workflows/u24_element_before_release.yaml | 17 - .../workflows/u24_element_release_call.yaml | 28 - .../workflows/u24_element_tag_to_release.yaml | 14 - .gitignore | 94 +- .markdownlint.yaml | 18 + .pre-commit-config.yaml | 4 +- CONTRIBUTING.md | 2 +- LICENSE | 2 +- docker-compose-db.yaml | 15 + notebooks/tutorial.ipynb | 2840 +++++++++++++++++ notebooks/tutorial_pipeline.py | 64 + requirements.txt | 1 - requirements_dev.txt | 1 - setup.py | 22 +- 19 files changed, 3190 insertions(+), 96 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/test.yaml delete mode 100644 .github/workflows/u24_element_before_release.yaml delete mode 100644 .github/workflows/u24_element_release_call.yaml delete mode 100644 .github/workflows/u24_element_tag_to_release.yaml create mode 100644 .markdownlint.yaml create mode 100644 docker-compose-db.yaml create mode 100644 notebooks/tutorial.ipynb create mode 100644 notebooks/tutorial_pipeline.py delete mode 100644 requirements.txt delete mode 100644 requirements_dev.txt diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..d58008f --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,48 @@ +FROM python:3.9-slim@sha256:5f0192a4f58a6ce99f732fe05e3b3d00f12ae62e183886bca3ebe3d202686c7f + +ENV PATH /usr/local/bin:$PATH +ENV PYTHON_VERSION 3.9.17 + +RUN \ + adduser --system --disabled-password --shell /bin/bash vscode && \ + # install docker + apt-get update && \ + apt-get install ca-certificates curl gnupg lsb-release -y && \ + mkdir -m 0755 -p /etc/apt/keyrings && \ + curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null && \ + apt-get update && \ + apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y && \ + usermod -aG docker vscode && \ + apt-get clean + +RUN \ + # dev setup + apt update && \ + apt-get install sudo git bash-completion graphviz default-mysql-client s3fs procps -y && \ + usermod -aG sudo vscode && \ + echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \ + pip install --no-cache-dir --upgrade black pip nbconvert && \ + echo '. /etc/bash_completion' >> /home/vscode/.bashrc && \ + echo 'export PS1="\[\e[32;1m\]\u\[\e[m\]@\[\e[34;1m\]\H\[\e[m\]:\[\e[33;1m\]\w\[\e[m\]$ "' >> /home/vscode/.bashrc && \ + apt-get clean + +COPY ./ /tmp/element-optogenetics/ + +RUN \ + # pipeline dependencies + apt-get install gcc g++ ffmpeg libsm6 libxext6 -y && \ + pip install --no-cache-dir -e /tmp/element-optogenetics[elements,tests] && \ + # clean up + rm -rf /tmp/element-optogenetics && \ + apt-get clean + +ENV DJ_HOST fakeservices.datajoint.io +ENV DJ_USER root +ENV DJ_PASS simple + +ENV EPHYS_ROOT_DATA_DIR /workspaces/element-optogenetics/example_data +ENV DATABASE_PREFIX neuro_ + +USER vscode +CMD bash -c "sudo rm /var/run/docker.pid; sudo dockerd" \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..bf939e8 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,30 @@ +{ + "name": "Environment + Data", + "dockerComposeFile": "docker-compose.yaml", + "service": "app", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "remoteEnv": { + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" + }, + "onCreateCommand": "mkdir -p ${EPHYS_ROOT_DATA_DIR} && pip install -e .", + "postStartCommand": "docker volume prune -f && s3fs ${DJ_PUBLIC_S3_LOCATION} ${EPHYS_ROOT_DATA_DIR} -o nonempty,multipart_size=530,endpoint=us-east-1,url=http://s3.amazonaws.com,public_bucket=1", + "hostRequirements": { + "cpus": 4, + "memory": "8gb", + "storage": "32gb" + }, + "forwardPorts": [ + 3306 + ], + "customizations": { + "settings": { + "python.pythonPath": "/usr/local/bin/python" + }, + "vscode": { + "extensions": [ + "ms-python.python@2023.8.0", + "ms-toolsai.jupyter@2023.3.1201040234" + ] + } + } +} \ No newline at end of file diff --git a/.devcontainer/docker-compose.yaml b/.devcontainer/docker-compose.yaml new file mode 100644 index 0000000..e45c881 --- /dev/null +++ b/.devcontainer/docker-compose.yaml @@ -0,0 +1,25 @@ +version: "3" +services: + app: + cpus: 4 + mem_limit: 8g + build: + context: .. + dockerfile: ./.devcontainer/Dockerfile + # image: datajoint/element_array_ephys:latest + extra_hosts: + - fakeservices.datajoint.io:127.0.0.1 + environment: + - DJ_PUBLIC_S3_LOCATION=djhub.vathes.datapub.elements:/workflow-array-ephys-benchmark/v2 + devices: + - /dev/fuse + cap_add: + - SYS_ADMIN + security_opt: + - apparmor:unconfined + volumes: + - ..:/workspaces/element-optogenetics:cached + - docker_data:/var/lib/docker # persist docker images + privileged: true # only because of dind +volumes: + docker_data: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..4a5f2cb --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,27 @@ +name: Release +on: + workflow_dispatch: +jobs: + make_github_release: + uses: datajoint/.github/.github/workflows/make_github_release.yaml@main + pypi_release: + needs: make_github_release + uses: datajoint/.github/.github/workflows/pypi_release.yaml@main + secrets: + TWINE_USERNAME: ${{secrets.TWINE_USERNAME}} + TWINE_PASSWORD: ${{secrets.TWINE_PASSWORD}} + with: + UPLOAD_URL: ${{needs.make_github_release.outputs.release_upload_url}} + mkdocs_release: + uses: datajoint/.github/.github/workflows/mkdocs_release.yaml@main + permissions: + contents: write + devcontainer-build: + uses: datajoint/.github/.github/workflows/devcontainer-build.yaml@main + devcontainer-publish: + needs: + - devcontainer-build + uses: datajoint/.github/.github/workflows/devcontainer-publish.yaml@main + secrets: + DOCKERHUB_USERNAME: ${{secrets.DOCKERHUB_USERNAME}} + DOCKERHUB_TOKEN: ${{secrets.DOCKERHUB_TOKEN_FOR_ELEMENTS}} \ No newline at end of file diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..f29dafe --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,34 @@ +name: Test +on: + push: + pull_request: + workflow_dispatch: +jobs: + devcontainer-build: + uses: datajoint/.github/.github/workflows/devcontainer-build.yaml@main + tests: + runs-on: ubuntu-latest + strategy: + matrix: + py_ver: ["3.9", "3.10"] + mysql_ver: ["8.0", "5.7"] + include: + - py_ver: "3.8" + mysql_ver: "5.7" + - py_ver: "3.7" + mysql_ver: "5.7" + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{matrix.py_ver}} + uses: actions/setup-python@v4 + with: + python-version: ${{matrix.py_ver}} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 "black[jupyter]" + - name: Run style tests + run: | + python_version=${{matrix.py_ver}} + black element_optogenetics --check --verbose --target-version py${python_version//.} + diff --git a/.github/workflows/u24_element_before_release.yaml b/.github/workflows/u24_element_before_release.yaml deleted file mode 100644 index 692cf82..0000000 --- a/.github/workflows/u24_element_before_release.yaml +++ /dev/null @@ -1,17 +0,0 @@ -name: u24_element_before_release -on: - pull_request: - push: - branches: - - '**' - tags-ignore: - - '**' - workflow_dispatch: -jobs: - call_context_check: - uses: dj-sciops/djsciops-cicd/.github/workflows/context_check.yaml@main - call_u24_elements_build_alpine: - uses: dj-sciops/djsciops-cicd/.github/workflows/u24_element_build.yaml@main - with: - py_ver: 3.9 - image: djbase diff --git a/.github/workflows/u24_element_release_call.yaml b/.github/workflows/u24_element_release_call.yaml deleted file mode 100644 index 4324cca..0000000 --- a/.github/workflows/u24_element_release_call.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: u24_element_release_call -on: - workflow_run: - workflows: ["u24_element_tag_to_release"] - types: - - completed -jobs: - call_context_check: - uses: dj-sciops/djsciops-cicd/.github/workflows/context_check.yaml@main - test_call_u24_elements_release_alpine: - if: >- - github.event.workflow_run.conclusion == 'success' && ( contains(github.event.workflow_run.head_branch, 'test') || (github.event.workflow_run.event == 'pull_request')) - uses: dj-sciops/djsciops-cicd/.github/workflows/u24_element_release.yaml@main - with: - py_ver: 3.9 - twine_repo: testpypi - secrets: - TWINE_USERNAME: ${{secrets.TWINE_TEST_USERNAME}} - TWINE_PASSWORD: ${{secrets.TWINE_TEST_PASSWORD}} - call_u24_elements_release_alpine: - if: >- - github.event.workflow_run.conclusion == 'success' && github.repository_owner == 'datajoint' && !contains(github.event.workflow_run.head_branch, 'test') - uses: dj-sciops/djsciops-cicd/.github/workflows/u24_element_release.yaml@main - with: - py_ver: 3.9 - secrets: - TWINE_USERNAME: ${{secrets.TWINE_USERNAME}} - TWINE_PASSWORD: ${{secrets.TWINE_PASSWORD}} diff --git a/.github/workflows/u24_element_tag_to_release.yaml b/.github/workflows/u24_element_tag_to_release.yaml deleted file mode 100644 index 57334e9..0000000 --- a/.github/workflows/u24_element_tag_to_release.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: u24_element_tag_to_release -on: - push: - tags: - - '*.*.*' - - 'test*.*.*' -jobs: - call_context_check: - uses: dj-sciops/djsciops-cicd/.github/workflows/context_check.yaml@main - call_u24_elements_build_alpine: - uses: dj-sciops/djsciops-cicd/.github/workflows/u24_element_build.yaml@main - with: - py_ver: 3.9 - image: djbase diff --git a/.gitignore b/.gitignore index 9e92e15..3f5c0d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,15 @@ +# User data +.DS_Store + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class -# Distribution, packaging, PyInstaller +# C extensions +*.so + +# Distribution / packaging .Python env/ build/ @@ -21,11 +27,17 @@ wheels/ *.egg-info/ .installed.cfg *.egg +.idea/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec + +# Installer logs pip-log.txt -pip-delete*.txt -.idea/ +pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ @@ -37,52 +49,82 @@ nosetests.xml coverage.xml *.cover .hypothesis/ -.pytest_cache/ -# C extension, Translations -*.so +# Translations *.mo *.pot -# editors: vscode, emacs, Mac -.vscode -**/*~ -**/#*# -**/.#* -.DS_Store - -# Django, Flask, Scrapy, Sphinx, mkdocs: -# PyBuilder, Jupyter, SageMath, celery beat +# Django stuff: *.log local_settings.py + +# Flask stuff: instance/ .webassets-cache + +# Scrapy stuff: .scrapy scratchpaper.* + +# Sphinx documentation docs/_build/ -/site + +# PyBuilder target/ + +# Jupyter Notebook .ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file celerybeat-schedule + +# SageMath parsed files *.sage.py -# dotenv, virtualenv, pyenv, mypy +# dotenv ./.env +.env + +# virtualenv .venv venv/ ENV/ -.python-version -.mypy_cache/ -# Spyder/Rope project settings +# Spyder project settings .spyderproject .spyproject + +# Rope project settings .ropeproject -# datajoint, notes, nwb export -dj_local_c*.json +# mkdocs documentation +docs/site +docs/src/tutorials/*ipynb + +# mypy +.mypy_cache/ + +# datajoint +dj_local_conf.json +dj_local_conf_old.json + +# emacs +**/*~ +**/#*# +**/.#* + + +# include +!docs/docker-compose.yaml + +# vscode settings +*.code-workspace + +# exports/notes temp* -temp/* -*nwb -/docs/site -/docs/src/tutorials/*ipynb + +# Codespaces +example_data/ \ No newline at end of file diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..ac52a8a --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,18 @@ +# Markdown Linter configuration for docs +# https://github.com/DavidAnson/markdownlint +# https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md +MD009: false # permit trailing spaces +MD007: false # List indenting - permit 4 spaces +MD013: + line_length: "88" # Line length limits + tables: false # disable for tables + headings: false # disable for headings +MD030: false # Number of spaces after a list +MD033: # HTML elements allowed + allowed_elements: + - "br" + - "figure" + - "figcaption" +MD034: false # Permit bare URLs +MD031: false # Spacing w/code blocks. Conflicts with `??? Note` and code tab styling +MD046: false # Spacing w/code blocks. Conflicts with `??? Note` and code tab styling diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c685f6..0d513df 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,7 +44,7 @@ repos: # these are errors that will be ignored by flake8 # https://www.flake8rules.com/rules/{code}.html - - "--ignore=E203,E501,W503,W605" + - "--ignore=E203,E501,W503,W605,E402" # E203 - Colons should not have any space before them. # Needed for list indexing # E501 - Line lengths are recommended to be no greater than 79 characters. @@ -54,3 +54,5 @@ repos: # W605 - a backslash-character pair that is not a valid escape sequence now # generates a DeprecationWarning. This will eventually become a SyntaxError. # Needed because we use \d as an escape sequence + # E402 - Place module level import at the top. + # Needed to prevent circular import error diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e04d170..2bd0f49 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ # Contribution Guidelines This project follows the -[DataJoint Contribution Guidelines](https://datajoint.com/docs/community/contribute/). +[DataJoint Contribution Guidelines](https://datajoint.com/docs/about/contribute/). Please reference the link for more full details. diff --git a/LICENSE b/LICENSE index 2f92789..6872305 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 DataJoint +Copyright (c) 2024 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/docker-compose-db.yaml b/docker-compose-db.yaml new file mode 100644 index 0000000..1d453c8 --- /dev/null +++ b/docker-compose-db.yaml @@ -0,0 +1,15 @@ +# MYSQL_VER=8.0 docker compose -f docker-compose-db.yaml up --build +version: "3" +services: + db: + restart: always + image: datajoint/mysql:${MYSQL_VER} + environment: + - MYSQL_ROOT_PASSWORD=${DJ_PASS} + ports: + - "3306:3306" + healthcheck: + test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ] + timeout: 15s + retries: 10 + interval: 15s diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb new file mode 100644 index 0000000..84247a3 --- /dev/null +++ b/notebooks/tutorial.ipynb @@ -0,0 +1,2840 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# DataJoint Elements for Optogenetics\n", + "\n", + "#### Open-source data pipeline for processing and analyzing optogenetics datasets.\n", + "\n", + "Welcome to the tutorial for the DataJoint Element for optogenetics. This\n", + "tutorial aims to provide a comprehensive understanding of the open-source data pipeline\n", + "created using `element-optogenetics`.\n", + "\n", + "This package is designed to seamlessly ingest and track optotgenetics data. By the end of this\n", + "tutorial you will have a clear grasp on setting up and integrating `element-optogenetics`\n", + "into your specific research projects and lab. \n", + "\n", + "![flowchart](../images/diagram_flowchart.svg)\n", + "\n", + "### Prerequisites\n", + "\n", + "Please see the [datajoint tutorials GitHub\n", + "repository](https://github.com/datajoint/datajoint-tutorials/tree/main) before\n", + "proceeding.\n", + "\n", + "A basic understanding of the following DataJoint concepts will be beneficial to your\n", + "understanding of this tutorial: \n", + "1. The `Imported` and `Computed` tables types in `datajoint-python`.\n", + "2. The functionality of the `.populate()` method. \n", + "\n", + "#### **Tutorial Overview**\n", + "\n", + "+ Setup\n", + "+ *Activate* the DataJoint pipeline.\n", + "+ *Insert* subject, session, and probe metadata.\n", + "+ *Populate* electrophysiology recording metadata.\n", + "+ Run the clustering task.\n", + "+ Curate the results (optional).\n", + "+ Visualize the results.\n", + "\n", + "### **Setup**\n", + "\n", + "This tutorial examines extracellular electrophysiology data acquired with `OpenEphys`\n", + "and spike-sorted using Kilosort 2.5. The goal is to store, track\n", + "and manage sessions of array electrophysiology data, including spike sorting results and\n", + "unit-level visualizations. \n", + "\n", + "The results of this Element can be combined with **other modalities** to create\n", + "a complete, customizable data pipeline for your specific lab or study. For instance, you\n", + "can combine `element-array-ephys` with `element-calcium-imaging` and\n", + "`element-deeplabcut` to characterize the neural activity along with markless\n", + "pose-estimation during behavior.\n", + "\n", + "Let's start this tutorial by importing the packages necessary to run the notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import datajoint as dj\n", + "import datetime\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If the tutorial is run in Codespaces, a private, local database server is created and\n", + "made available for you. This is where we will insert and store our processed results.\n", + "Let's connect to the database server." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[2023-11-28 19:33:47,134][INFO]: Connecting root@fakeservices.datajoint.io:3306\n", + "[2023-11-28 19:33:47,142][INFO]: Connected root@fakeservices.datajoint.io:3306\n" + ] + }, + { + "data": { + "text/plain": [ + "DataJoint connection (connected) root@fakeservices.datajoint.io:3306" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.conn()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Activate the DataJoint Pipeline**\n", + "\n", + "This tutorial activates the `ephys_acute.py` module from `element-array-ephys`, along\n", + "with upstream dependencies from `element-animal` and `element-session`. Please refer to the\n", + "[`tutorial_pipeline.py`](./tutorial_pipeline.py) for the source code." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[2023-11-28 19:33:49,428][WARNING]: lab.Project and related tables will be removed in a future version of Element Lab. Please use the project schema.\n" + ] + } + ], + "source": [ + "from tutorial_pipeline import lab, subject, session, probe, ephys" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can represent the tables in the `probe` and `ephys` schemas as well as some of the\n", + "upstream dependencies to `session` and `subject` schemas as a diagram." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "%3\n", + "\n", + "\n", + "\n", + "ephys.LFP\n", + "\n", + "\n", + "ephys.LFP\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.LFP.Electrode\n", + "\n", + "\n", + "ephys.LFP.Electrode\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.LFP->ephys.LFP.Electrode\n", + "\n", + "\n", + "\n", + "\n", + "ephys.EphysRecording.EphysFile\n", + "\n", + "\n", + "ephys.EphysRecording.EphysFile\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.QualityMetrics.Cluster\n", + "\n", + "\n", + "ephys.QualityMetrics.Cluster\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "probe.ProbeType.Electrode\n", + "\n", + "\n", + "probe.ProbeType.Electrode\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "probe.ElectrodeConfig.Electrode\n", + "\n", + "\n", + "probe.ElectrodeConfig.Electrode\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "probe.ProbeType.Electrode->probe.ElectrodeConfig.Electrode\n", + "\n", + "\n", + "\n", + "\n", + "ephys.Curation\n", + "\n", + "\n", + "ephys.Curation\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.CuratedClustering\n", + "\n", + "\n", + "ephys.CuratedClustering\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.Curation->ephys.CuratedClustering\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ProbeInsertion\n", + "\n", + "\n", + "ephys.ProbeInsertion\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.InsertionLocation\n", + "\n", + "\n", + "ephys.InsertionLocation\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ProbeInsertion->ephys.InsertionLocation\n", + "\n", + "\n", + "\n", + "\n", + "ephys.EphysRecording\n", + "\n", + "\n", + "ephys.EphysRecording\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ProbeInsertion->ephys.EphysRecording\n", + "\n", + "\n", + "\n", + "\n", + "probe.ElectrodeConfig\n", + "\n", + "\n", + "probe.ElectrodeConfig\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "probe.ElectrodeConfig->probe.ElectrodeConfig.Electrode\n", + "\n", + "\n", + "\n", + "\n", + "probe.ElectrodeConfig->ephys.EphysRecording\n", + "\n", + "\n", + "\n", + "\n", + "ephys.WaveformSet.PeakWaveform\n", + "\n", + "\n", + "ephys.WaveformSet.PeakWaveform\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.CuratedClustering.Unit\n", + "\n", + "\n", + "ephys.CuratedClustering.Unit\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.CuratedClustering.Unit->ephys.QualityMetrics.Cluster\n", + "\n", + "\n", + "\n", + "\n", + "ephys.CuratedClustering.Unit->ephys.WaveformSet.PeakWaveform\n", + "\n", + "\n", + "\n", + "\n", + "ephys.WaveformSet.Waveform\n", + "\n", + "\n", + "ephys.WaveformSet.Waveform\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.CuratedClustering.Unit->ephys.WaveformSet.Waveform\n", + "\n", + "\n", + "\n", + "\n", + "ephys.QualityMetrics.Waveform\n", + "\n", + "\n", + "ephys.QualityMetrics.Waveform\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.CuratedClustering.Unit->ephys.QualityMetrics.Waveform\n", + "\n", + "\n", + "\n", + "\n", + "ephys.WaveformSet\n", + "\n", + "\n", + "ephys.WaveformSet\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.WaveformSet->ephys.WaveformSet.PeakWaveform\n", + "\n", + "\n", + "\n", + "\n", + "ephys.WaveformSet->ephys.WaveformSet.Waveform\n", + "\n", + "\n", + "\n", + "\n", + "probe.ProbeType\n", + "\n", + "\n", + "probe.ProbeType\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "probe.ProbeType->probe.ProbeType.Electrode\n", + "\n", + "\n", + "\n", + "\n", + "probe.ProbeType->probe.ElectrodeConfig\n", + "\n", + "\n", + "\n", + "\n", + "probe.Probe\n", + "\n", + "\n", + "probe.Probe\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "probe.ProbeType->probe.Probe\n", + "\n", + "\n", + "\n", + "\n", + "probe.Probe->ephys.ProbeInsertion\n", + "\n", + "\n", + "\n", + "\n", + "ephys.AcquisitionSoftware\n", + "\n", + "\n", + "ephys.AcquisitionSoftware\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.AcquisitionSoftware->ephys.EphysRecording\n", + "\n", + "\n", + "\n", + "\n", + "ephys.QualityMetrics\n", + "\n", + "\n", + "ephys.QualityMetrics\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.QualityMetrics->ephys.QualityMetrics.Cluster\n", + "\n", + "\n", + "\n", + "\n", + "ephys.QualityMetrics->ephys.QualityMetrics.Waveform\n", + "\n", + "\n", + "\n", + "\n", + "ephys.CuratedClustering->ephys.CuratedClustering.Unit\n", + "\n", + "\n", + "\n", + "\n", + "ephys.CuratedClustering->ephys.WaveformSet\n", + "\n", + "\n", + "\n", + "\n", + "ephys.CuratedClustering->ephys.QualityMetrics\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject\n", + "\n", + "\n", + "subject.Subject\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "session.Session\n", + "\n", + "\n", + "session.Session\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->session.Session\n", + "\n", + "\n", + "\n", + "\n", + "probe.ElectrodeConfig.Electrode->ephys.CuratedClustering.Unit\n", + "\n", + "\n", + "\n", + "\n", + "probe.ElectrodeConfig.Electrode->ephys.WaveformSet.Waveform\n", + "\n", + "\n", + "\n", + "\n", + "probe.ElectrodeConfig.Electrode->ephys.LFP.Electrode\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ClusteringMethod\n", + "\n", + "\n", + "ephys.ClusteringMethod\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ClusteringParamSet\n", + "\n", + "\n", + "ephys.ClusteringParamSet\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ClusteringMethod->ephys.ClusteringParamSet\n", + "\n", + "\n", + "\n", + "\n", + "ephys.EphysRecording->ephys.LFP\n", + "\n", + "\n", + "\n", + "\n", + "ephys.EphysRecording->ephys.EphysRecording.EphysFile\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ClusteringTask\n", + "\n", + "\n", + "ephys.ClusteringTask\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.EphysRecording->ephys.ClusteringTask\n", + "\n", + "\n", + "\n", + "\n", + "session.Session->ephys.ProbeInsertion\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ClusteringParamSet->ephys.ClusteringTask\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ClusterQualityLabel\n", + "\n", + "\n", + "ephys.ClusterQualityLabel\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ClusterQualityLabel->ephys.CuratedClustering.Unit\n", + "\n", + "\n", + "\n", + "\n", + "ephys.Clustering\n", + "\n", + "\n", + "ephys.Clustering\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ephys.Clustering->ephys.Curation\n", + "\n", + "\n", + "\n", + "\n", + "ephys.ClusteringTask->ephys.Clustering\n", + "\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(\n", + " dj.Diagram(subject.Subject)\n", + " + dj.Diagram(session.Session)\n", + " + dj.Diagram(probe)\n", + " + dj.Diagram(ephys)\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As evident from the diagram, this data pipeline encompasses tables associated with\n", + "recording and probe metadata, results of clustering, and optional curation of clustering\n", + "results. A few tables, such as `subject.Subject` or `session.Session`,\n", + "while important for a complete pipeline, fall outside the scope of the `element-array-ephys`\n", + "tutorial, and will therefore, not be explored extensively here. The primary focus of\n", + "this tutorial will be on the `probe` and `ephys` schemas.\n", + "\n", + "### **Insert subject, session, and probe metadata**\n", + "\n", + "Let's start with the first table in the schema diagram (i.e. `subject.Subject` table).\n", + "\n", + "To know what data to insert into the table, we can view its dependencies and attributes using the `.describe()` and `.heading` methods." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

subject_nickname

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

subject_birth_date

\n", + " \n", + "
\n", + "

subject_description

\n", + " \n", + "
\n", + " \n", + "

Total: 0

\n", + " " + ], + "text/plain": [ + "*subject subject_nickna sex subject_birth_ subject_descri\n", + "+---------+ +------------+ +-----+ +------------+ +------------+\n", + "\n", + " (Total: 0)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subject.Subject()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "subject : varchar(8) \n", + "---\n", + "subject_nickname=\"\" : varchar(64) \n", + "sex : enum('M','F','U') \n", + "subject_birth_date : date \n", + "subject_description=\"\" : varchar(1024) \n", + "\n" + ] + } + ], + "source": [ + "print(subject.Subject.describe())" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "# \n", + "subject : varchar(8) # \n", + "---\n", + "subject_nickname=\"\" : varchar(64) # \n", + "sex : enum('M','F','U') # \n", + "subject_birth_date : date # \n", + "subject_description=\"\" : varchar(1024) # " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subject.Subject.heading" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The cells above show all attributes of the subject table.\n", + "We will insert data into the\n", + "`subject.Subject` table. " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

subject_nickname

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

subject_birth_date

\n", + " \n", + "
\n", + "

subject_description

\n", + " \n", + "
subject5U2023-01-01
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject subject_nickna sex subject_birth_ subject_descri\n", + "+----------+ +------------+ +-----+ +------------+ +------------+\n", + "subject5 U 2023-01-01 \n", + " (Total: 1)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subject.Subject.insert1(\n", + " dict(subject=\"subject5\", subject_birth_date=\"2023-01-01\", sex=\"U\")\n", + ")\n", + "subject.Subject()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's repeat the steps above for the `Session` table and see how the output varies between\n", + "`.describe` and `.heading`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-> subject.Subject\n", + "session_datetime : datetime \n", + "\n" + ] + } + ], + "source": [ + "print(session.Session.describe())" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "# \n", + "subject : varchar(8) # \n", + "session_datetime : datetime # " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "session.Session.heading" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice that `describe`, displays the table's structure and highlights its dependencies, such as its reliance on the `Subject` table. These dependencies represent foreign key references, linking data across tables.\n", + "\n", + "On the other hand, `heading` provides an exhaustive list of the table's attributes. This\n", + "list includes both the attributes declared in this table and any inherited from upstream\n", + "tables.\n", + "\n", + "With this understanding, let's move on to insert a session associated with our subject.\n", + "\n", + "We will insert into the `session.Session` table by passing a dictionary to the `insert1` method." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "session_key = dict(subject=\"subject5\", session_datetime=\"2023-01-01 00:00:00\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_datetime

\n", + " \n", + "
subject52023-01-01 00:00:00
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_datet\n", + "+----------+ +------------+\n", + "subject5 2023-01-01 00:\n", + " (Total: 1)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "session.Session.insert1(session_key)\n", + "session.Session()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Every experimental session produces a set of data files. The purpose of the `SessionDirectory` table is to locate these files. It references a directory path relative to a root directory, defined in `dj.config[\"custom\"]`. More information about `dj.config` is provided in the [documentation](https://datajoint.com/docs/elements/user-guide/)." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_datetime

\n", + " \n", + "
\n", + "

session_dir

\n", + " Path to the data directory for a session\n", + "
subject52023-01-01 00:00:00raw/subject5/session1
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_datet session_dir \n", + "+----------+ +------------+ +------------+\n", + "subject5 2023-01-01 00: raw/subject5/s\n", + " (Total: 1)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "session.SessionDirectory.insert1(\n", + " dict(**session_key, session_dir=\"raw/subject5/session1\")\n", + ")\n", + "session.SessionDirectory()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As the Diagram indicates, the tables in the `probe` schemas need to\n", + "contain data before the tables in the `ephys` schema accept any data. Let's\n", + "start by inserting into `probe.Probe`, a table containing metadata about a\n", + "multielectrode probe. " + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Represent a physical probe with unique identification\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + "

probe

\n", + " unique identifier for this model of probe (e.g. serial number)\n", + "
\n", + "

probe_type

\n", + " e.g. neuropixels_1.0\n", + "
\n", + "

probe_comment

\n", + " \n", + "
714000838neuropixels 1.0 - 3B
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*probe probe_type probe_comment \n", + "+-----------+ +------------+ +------------+\n", + "714000838 neuropixels 1. \n", + " (Total: 1)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "probe.Probe.insert1(\n", + " dict(probe=\"714000838\", probe_type=\"neuropixels 1.0 - 3B\")\n", + ") # this info could be achieve from neuropixels meta file.\n", + "probe.Probe()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The probe metadata is used by the downstream `ProbeInsertion` table which we\n", + "insert data into in the cells below:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# Probe insertion implanted into an animal for a given session.\n", + "-> session.Session\n", + "insertion_number : tinyint unsigned \n", + "---\n", + "-> probe.Probe\n", + "\n" + ] + } + ], + "source": [ + "print(ephys.ProbeInsertion.describe())" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "# Probe insertion implanted into an animal for a given session.\n", + "subject : varchar(8) # \n", + "session_datetime : datetime # \n", + "insertion_number : tinyint unsigned # \n", + "---\n", + "probe : varchar(32) # unique identifier for this model of probe (e.g. serial number)" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ephys.ProbeInsertion.heading" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Probe insertion implanted into an animal for a given session.\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_datetime

\n", + " \n", + "
\n", + "

insertion_number

\n", + " \n", + "
\n", + "

probe

\n", + " unique identifier for this model of probe (e.g. serial number)\n", + "
subject52023-01-01 00:00:001714000838
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_datet *insertion_num probe \n", + "+----------+ +------------+ +------------+ +-----------+\n", + "subject5 2023-01-01 00: 1 714000838 \n", + " (Total: 1)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ephys.ProbeInsertion.insert1(\n", + " dict(\n", + " session_key,\n", + " insertion_number=1,\n", + " probe=\"714000838\",\n", + " )\n", + ") # probe, subject, session_datetime needs to follow the restrictions of foreign keys.\n", + "ephys.ProbeInsertion()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Confirm the inserted data:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Probe insertion implanted into an animal for a given session.\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_datetime

\n", + " \n", + "
\n", + "

insertion_number

\n", + " \n", + "
\n", + "

probe

\n", + " unique identifier for this model of probe (e.g. serial number)\n", + "
subject52023-01-01 00:00:001714000838
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_datet *insertion_num probe \n", + "+----------+ +------------+ +------------+ +-----------+\n", + "subject5 2023-01-01 00: 1 714000838 \n", + " (Total: 1)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ephys.ProbeInsertion()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Populate electrophysiology recording metadata**\n", + "\n", + "In the upcoming cells, the `.populate()` method will automatically extract and store the\n", + "recording metadata for each experimental session in the `ephys.EphysRecording` table and its part table `ephys.EphysRecording.EphysFile`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Ephys recording from a probe insertion for a given session.\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_datetime

\n", + " \n", + "
\n", + "

insertion_number

\n", + " \n", + "
\n", + "

electrode_config_hash

\n", + " \n", + "
\n", + "

acq_software

\n", + " \n", + "
\n", + "

sampling_rate

\n", + " (Hz)\n", + "
\n", + "

recording_datetime

\n", + " datetime of the recording from this probe\n", + "
\n", + "

recording_duration

\n", + " (seconds) duration of the recording from this probe\n", + "
\n", + " \n", + "

Total: 0

\n", + " " + ], + "text/plain": [ + "*subject *session_datet *insertion_num electrode_conf acq_software sampling_rate recording_date recording_dura\n", + "+---------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+\n", + "\n", + " (Total: 0)" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ephys.EphysRecording()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Paths of files of a given EphysRecording round.\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_datetime

\n", + " \n", + "
\n", + "

insertion_number

\n", + " \n", + "
\n", + "

file_path

\n", + " filepath relative to root data directory\n", + "
\n", + " \n", + "

Total: 0

\n", + " " + ], + "text/plain": [ + "*subject *session_datet *insertion_num *file_path \n", + "+---------+ +------------+ +------------+ +-----------+\n", + "\n", + " (Total: 0)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ephys.EphysRecording.EphysFile()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "EphysRecording: 100%|██████████| 1/1 [00:01<00:00, 1.25s/it]\n" + ] + } + ], + "source": [ + "ephys.EphysRecording.populate(session_key, display_progress=True)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's view the information was entered into each of these tables:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Ephys recording from a probe insertion for a given session.\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_datetime

\n", + " \n", + "
\n", + "

insertion_number

\n", + " \n", + "
\n", + "

electrode_config_hash

\n", + " \n", + "
\n", + "

acq_software

\n", + " \n", + "
\n", + "

sampling_rate

\n", + " (Hz)\n", + "
\n", + "

recording_datetime

\n", + " datetime of the recording from this probe\n", + "
\n", + "

recording_duration

\n", + " (seconds) duration of the recording from this probe\n", + "
subject52023-01-01 00:00:0018d4cc6d8-a02d-42c8-bf27-7459c39ea0eeSpikeGLX30000.02018-07-03 20:32:28338.666
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_datet *insertion_num electrode_conf acq_software sampling_rate recording_date recording_dura\n", + "+----------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+\n", + "subject5 2023-01-01 00: 1 8d4cc6d8-a02d- SpikeGLX 30000.0 2018-07-03 20: 338.666 \n", + " (Total: 1)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ephys.EphysRecording()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Paths of files of a given EphysRecording round.\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_datetime

\n", + " \n", + "
\n", + "

insertion_number

\n", + " \n", + "
\n", + "

file_path

\n", + " filepath relative to root data directory\n", + "
subject52023-01-01 00:00:001raw/subject5/session1/probe_1/npx_g0_t0.imec.ap.meta
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_datet *insertion_num *file_path \n", + "+----------+ +------------+ +------------+ +------------+\n", + "subject5 2023-01-01 00: 1 raw/subject5/s\n", + " (Total: 1)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ephys.EphysRecording.EphysFile()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Run the Clustering Task**\n", + "\n", + "We're almost ready to spike sort the data with `kilosort`. An important step before\n", + "processing is managing the parameters which will be used in that step. To do so, we will\n", + "define the kilosort parameters in a dictionary and insert them into a DataJoint table\n", + "`ClusteringParamSet`. This table keeps track of all combinations of your spike sorting\n", + "parameters. You can choose which parameters are used during processing in a later step.\n", + "\n", + "Let's view the attributes and insert data into `ephys.ClusteringParamSet`." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "# Parameter set to be used in a clustering procedure\n", + "paramset_idx : smallint # \n", + "---\n", + "clustering_method : varchar(16) # \n", + "paramset_desc : varchar(128) # \n", + "param_set_hash : uuid # \n", + "params : longblob # dictionary of all applicable parameters" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ephys.ClusteringParamSet.heading" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Parameter set to be used in a clustering procedure\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

paramset_idx

\n", + " \n", + "
\n", + "

clustering_method

\n", + " \n", + "
\n", + "

paramset_desc

\n", + " \n", + "
\n", + "

param_set_hash

\n", + " \n", + "
\n", + "

params

\n", + " dictionary of all applicable parameters\n", + "
0kilosort2Spike sorting using Kilosort2de78cee1-526f-319e-b6d5-8a2ba04963d8=BLOB=
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*paramset_idx clustering_met paramset_desc param_set_hash params \n", + "+------------+ +------------+ +------------+ +------------+ +--------+\n", + "0 kilosort2 Spike sorting de78cee1-526f- =BLOB= \n", + " (Total: 1)" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# insert clustering task manually\n", + "params_ks = {\n", + " \"fs\": 30000,\n", + " \"fshigh\": 150,\n", + " \"minfr_goodchannels\": 0.1,\n", + " \"Th\": [10, 4],\n", + " \"lam\": 10,\n", + " \"AUCsplit\": 0.9,\n", + " \"minFR\": 0.02,\n", + " \"momentum\": [20, 400],\n", + " \"sigmaMask\": 30,\n", + " \"ThPr\": 8,\n", + " \"spkTh\": -6,\n", + " \"reorder\": 1,\n", + " \"nskip\": 25,\n", + " \"GPU\": 1,\n", + " \"Nfilt\": 1024,\n", + " \"nfilt_factor\": 4,\n", + " \"ntbuff\": 64,\n", + " \"whiteningRange\": 32,\n", + " \"nSkipCov\": 25,\n", + " \"scaleproc\": 200,\n", + " \"nPCs\": 3,\n", + " \"useRAM\": 0,\n", + "}\n", + "ephys.ClusteringParamSet.insert_new_params(\n", + " clustering_method=\"kilosort2\",\n", + " paramset_idx=0,\n", + " params=params_ks,\n", + " paramset_desc=\"Spike sorting using Kilosort2\",\n", + ")\n", + "ephys.ClusteringParamSet()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "DataJoint uses a `ClusteringTask` table to\n", + "manage which `EphysRecording` and `ClusteringParamSet` should be used during processing. \n", + "\n", + "This table is important for defining several important aspects of\n", + "downstream processing. Let's view the attributes to get a better understanding. " + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "# Manual table for defining a clustering task ready to be run\n", + "subject : varchar(8) # \n", + "session_datetime : datetime # \n", + "insertion_number : tinyint unsigned # \n", + "paramset_idx : smallint # \n", + "---\n", + "clustering_output_dir=\"\" : varchar(255) # clustering output directory relative to the clustering root data directory\n", + "task_mode=\"load\" : enum('load','trigger') # 'load': load computed analysis results, 'trigger': trigger computation" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ephys.ClusteringTask.heading" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `ClusteringTask` table contains two important attributes: \n", + "+ `paramset_idx` - Allows the user to choose the parameter set with which you want to\n", + " run spike sorting.\n", + "+ `task_mode` - Can be set to `load` or `trigger`. When set to `load`, running the\n", + " Clustering step initiates a search for existing output files of the spike sorting\n", + " algorithm defined in `ClusteringParamSet`. When set to `trigger`, the processing step\n", + " will run spike sorting on the raw data." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "ephys.ClusteringTask.insert1(\n", + " dict(\n", + " session_key,\n", + " insertion_number=1,\n", + " paramset_idx=0,\n", + " task_mode=\"load\", # load or trigger\n", + " clustering_output_dir=\"processed/subject5/session1/probe_1/kilosort2-5_1\",\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's call populate on the `Clustering` table which checks for kilosort results since `task_mode=load`." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Clustering: 100%|██████████| 1/1 [00:00<00:00, 3.27it/s]\n" + ] + } + ], + "source": [ + "ephys.Clustering.populate(session_key, display_progress=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Curate the results (Optional)**\n", + "\n", + "While spike sorting is completed in the above step, you can optionally curate\n", + "the output of image processing using the `Curation` table. For this demo, we\n", + "will simply use the results of the spike sorting output from the `Clustering` task." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "# Manual curation procedure\n", + "subject : varchar(8) # \n", + "session_datetime : datetime # \n", + "insertion_number : tinyint unsigned # \n", + "paramset_idx : smallint # \n", + "curation_id : int # \n", + "---\n", + "curation_time : datetime # time of generation of this set of curated clustering results\n", + "curation_output_dir : varchar(255) # output directory of the curated results, relative to root data directory\n", + "quality_control : tinyint # has this clustering result undergone quality control?\n", + "manual_curation : tinyint # has manual curation been performed on this clustering result?\n", + "curation_note=\"\" : varchar(2000) # " + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ephys.Curation.heading" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "clustering_key = (ephys.ClusteringTask & session_key).fetch1(\"KEY\")\n", + "ephys.Curation().create1_from_clustering_task(clustering_key)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once the `Curation` table receives an entry, we can populate the remaining\n", + "tables in the workflow including `CuratedClustering`, `WaveformSet`, and `LFP`. " + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "CuratedClustering: 0%| | 0/1 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(lfp_average)\n", + "plt.title(\"Average LFP Waveform for Insertion 1\")\n", + "plt.xlabel(\"Samples\")\n", + "plt.ylabel(\"microvolts (uV)\");" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "DataJoint queries are a highly flexible tool to manipulate and visualize your data.\n", + "After all, visualizing traces or generating rasters is likely just the start of\n", + "your analysis workflow. This can also make the queries seem more complex at\n", + "first. However, we'll walk through them slowly to simplify their content in this notebook. \n", + "\n", + "The examples below perform several operations using DataJoint queries:\n", + "- Fetch the primary key attributes of all units that are in `insertion_number=1`.\n", + "- Use **multiple restrictions** to fetch timestamps and create a raster plot.\n", + "- Use a **join** operation and **multiple restrictions** to fetch a waveform\n", + " trace, along with unit data to create a single waveform plot" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [], + "source": [ + "insert_key = (ephys.ProbeInsertion & \"insertion_number = '1'\").fetch1(\"KEY\")\n", + "units, unit_spiketimes = (\n", + " ephys.CuratedClustering.Unit\n", + " & insert_key\n", + " & 'unit IN (\"6\",\"7\",\"9\",\"14\",\"15\",\"17\",\"19\")'\n", + ").fetch(\"unit\", \"spike_times\")" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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V++//77mzJnjdd6MGTO0ZcsWuVyunuk8gG5HgQMgJEydOlW//vWvlZ6erqVLl2rgwIGKj4/XzTffrPT0dC1btkyVlZXasWOHJOk///M/lZmZqYceekhnnHGGMjMz9b//+79at26d8vPz/bZx4MAB9e/fXwkJCea2kpISDR48WP/yL/+i1NRUZWZm6o477vA6LykpSU1NTV4fvwEIbRQ4AELClClTzJ/79++vuLg4TZ482dyWmJgoSaqoqJB08svC69atM7/TExkZqTPOOEOStG/fPr9tHDt2THa73euL0BdddJFSU1M1evRo3XDDDXr55ZfNd4ncIiIiJKnVdgChiwIHQEgYMGCA13Obzea1zV2UtLS0SJLq6uqUnZ2tvLw8r0dBQUGrj5jc4uPj1dDQoKamJnNbVFSUtm/frldffVXDhw/XsmXLNHXqVFVXV5vHVFVVSZKGDh3aLX0F0PMocAD0SWeeeaZ27dqlUaNGaezYsV6PwYMH+z1n2rRpkqTdu3d7bQ8LC9O8efP06KOPaseOHdq/f78++eQTc//OnTs1cuRIxcfH91h/AHQvChwAfdKtt96qqqoqXXPNNfryyy+1b98+ffDBB7rxxhvV3Nzs95yhQ4fqzDPP1MaNG81t7733np566inl5eXpwIEDevHFF9XS0qJx48aZx3z22WeaP39+j/cJQPehwAHQJyUlJWnTpk1qbm7W/PnzNXnyZC1ZskQxMTHq1y/wS9tNN92kl19+2XweExOjN998UxdccIHGjx+vZ599Vq+++qomTpwoSWpsbNRbb72lm2++ucf7BKD7cKM/AKeVY8eOady4cXr99dc1c+bMdo9/5plntHr1an344Ye9EB2A7sI7OABOKxEREXrxxRfbvCGgpwEDBujpp5/u4agAdDfewQEAAJbDOzgAAMByKHAAAIDlUOAAAADLocABAACWQ4EDAAAshwIHAABYDgUOAACwHAocAABgORQ4AADAcv4/4rIQegvxjTEAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "x = np.hstack(unit_spiketimes)\n", + "y = np.hstack([np.full_like(s, u) for u, s in zip(units, unit_spiketimes)])\n", + "plt.plot(x, y, \"|\")\n", + "plt.xlabel(\"Time (s)\")\n", + "plt.ylabel(\"Unit\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Below we will use two queries to fetch *all* of the information about a single unit and\n", + "plot the unit waveform." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "unit_key = (ephys.CuratedClustering.Unit & insert_key & \"unit = '15'\").fetch1(\"KEY\")\n", + "unit_data = (\n", + " ephys.CuratedClustering.Unit * ephys.WaveformSet.PeakWaveform & unit_key\n", + ").fetch1()" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'subject': 'subject5',\n", + " 'session_datetime': datetime.datetime(2023, 1, 1, 0, 0),\n", + " 'insertion_number': 1,\n", + " 'paramset_idx': 0,\n", + " 'curation_id': 1,\n", + " 'unit': 15,\n", + " 'electrode_config_hash': UUID('8d4cc6d8-a02d-42c8-bf27-7459c39ea0ee'),\n", + " 'probe_type': 'neuropixels 1.0 - 3A',\n", + " 'electrode': 92,\n", + " 'cluster_quality_label': 'noise',\n", + " 'spike_count': 292,\n", + " 'spike_times': array([ 1.02606667, 1.19973333, 1.5044 , 1.52283333,\n", + " 1.86786667, 1.8688 , 1.8806 , 1.88553333,\n", + " 2.0581 , 2.76826667, 2.89186667, 2.9311 ,\n", + " 4.8753 , 5.2964 , 7.02266667, 9.65273333,\n", + " 9.81373333, 10.22443333, 11.96146667, 15.07173333,\n", + " 15.08983333, 15.78326667, 21.30936667, 22.7549 ,\n", + " 23.5582 , 23.6582 , 23.87043333, 24.16726667,\n", + " 24.25456667, 24.2671 , 25.0249 , 27.89116667,\n", + " 35.3036 , 36.0171 , 40.00396667, 40.0873 ,\n", + " 41.015 , 42.70086667, 45.8682 , 47.9291 ,\n", + " 48.90843333, 49.37996667, 49.39596667, 49.4058 ,\n", + " 49.65926667, 49.68203333, 49.74273333, 51.52213333,\n", + " 52.41486667, 55.27623333, 55.54576667, 55.81213333,\n", + " 56.0544 , 56.29426667, 56.36896667, 56.3743 ,\n", + " 56.47403333, 56.7147 , 56.71683333, 60.764 ,\n", + " 61.5317 , 61.54046667, 61.7721 , 62.10233333,\n", + " 62.10726667, 62.2118 , 62.81173333, 63.15896667,\n", + " 65.11126667, 65.495 , 67.18373333, 77.59256667,\n", + " 79.2709 , 80.46186667, 82.1178 , 85.6568 ,\n", + " 86.52613333, 89.12126667, 89.46963333, 89.64663333,\n", + " 90.19103333, 92.0923 , 92.99573333, 93.36923333,\n", + " 93.68086667, 95.2097 , 97.96296667, 98.1067 ,\n", + " 98.69713333, 99.26963333, 99.28013333, 101.04216667,\n", + " 101.2002 , 101.3843 , 101.3975 , 101.40656667,\n", + " 102.00996667, 102.07066667, 102.17033333, 103.5861 ,\n", + " 104.68523333, 104.71643333, 105.2595 , 105.5166 ,\n", + " 105.54723333, 107.0848 , 109.82746667, 110.14853333,\n", + " 110.92203333, 111.2481 , 112.26 , 113.09466667,\n", + " 113.09846667, 113.1005 , 113.33513333, 118.842 ,\n", + " 118.96803333, 119.32606667, 119.64796667, 119.96313333,\n", + " 119.97343333, 120.19573333, 120.19693333, 120.21163333,\n", + " 121.1879 , 121.233 , 121.9611 , 122.40203333,\n", + " 123.1745 , 124.6798 , 124.68196667, 124.97396667,\n", + " 125.2205 , 125.87786667, 125.957 , 125.95803333,\n", + " 127.7149 , 136.8558 , 136.86413333, 138.49966667,\n", + " 139.49553333, 139.93163333, 139.9819 , 142.57013333,\n", + " 142.70053333, 143.343 , 143.40796667, 148.46066667,\n", + " 148.47023333, 148.89893333, 152.79766667, 153.2198 ,\n", + " 153.39096667, 153.62456667, 153.82253333, 153.82693333,\n", + " 153.8364 , 153.98863333, 155.4047 , 157.52706667,\n", + " 157.53233333, 163.1177 , 163.12516667, 163.46916667,\n", + " 163.69406667, 163.7119 , 166.6946 , 166.7006 ,\n", + " 170.78053333, 170.7891 , 170.79653333, 180.36596667,\n", + " 180.43416667, 182.72556667, 182.73706667, 182.76576667,\n", + " 184.24713333, 185.5131 , 185.7329 , 186.4198 ,\n", + " 186.48443333, 186.72036667, 186.95926667, 187.1402 ,\n", + " 187.16683333, 189.4036 , 193.27583333, 195.40946667,\n", + " 200.2427 , 203.03376667, 203.04076667, 205.84043333,\n", + " 206.1151 , 207.3402 , 210.95773333, 217.73583333,\n", + " 220.35 , 221.1235 , 227.7719 , 227.946 ,\n", + " 228.02456667, 231.2108 , 231.80003333, 233.48253333,\n", + " 236.8342 , 241.78993333, 243.6285 , 245.61546667,\n", + " 245.95573333, 246.04586667, 246.06206667, 247.12933333,\n", + " 248.17143333, 249.16873333, 251.13613333, 252.4734 ,\n", + " 253.2007 , 254.50943333, 255.22563333, 255.2502 ,\n", + " 255.5314 , 255.53423333, 255.7684 , 256.81196667,\n", + " 256.99066667, 256.9928 , 257.0588 , 257.06206667,\n", + " 257.0801 , 257.15523333, 257.27496667, 257.291 ,\n", + " 257.99483333, 259.6955 , 259.7061 , 259.71746667,\n", + " 263.59203333, 266.7052 , 266.70576667, 267.0376 ,\n", + " 267.35913333, 267.87316667, 268.36156667, 268.94556667,\n", + " 269.01516667, 269.63623333, 269.88553333, 270.16176667,\n", + " 270.39123333, 273.41633333, 273.43763333, 274.2084 ,\n", + " 276.36113333, 278.68053333, 279.50626667, 281.0338 ,\n", + " 281.9869 , 283.69233333, 285.0663 , 287.0179 ,\n", + " 289.5541 , 291.78113333, 292.16493333, 292.85823333,\n", + " 292.8702 , 295.93823333, 295.9514 , 295.9679 ,\n", + " 296.00623333, 296.0391 , 296.04393333, 298.9717 ,\n", + " 299.01556667, 299.0859 , 299.7463 , 306.65153333,\n", + " 307.38243333, 307.7771 , 311.80163333, 311.83026667,\n", + " 313.37816667, 314.948 , 317.10726667, 317.98363333,\n", + " 319.25856667, 321.01533333, 321.16426667, 330.15466667,\n", + " 330.73633333, 330.91536667, 332.83243333, 332.86266667]),\n", + " 'spike_sites': array([92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", + " 92, 92, 92]),\n", + " 'spike_depths': array([ 917.68931051, 1002.6141662 , 893.1431346 , 912.33104931,\n", + " 902.87693568, 872.31105976, 875.26191363, 922.47333477,\n", + " 949.16252597, 801.99946108, 858.01692677, 921.7837487 ,\n", + " 897.25087548, 874.42894267, 914.89717798, 861.55454858,\n", + " 904.22082978, 900.71556806, 848.89880207, 871.55857472,\n", + " 849.40502424, 887.21898509, 957.57407264, 857.52950094,\n", + " 941.13752662, 931.03217518, 898.99527832, 962.92735677,\n", + " 915.56550832, 922.14542481, 894.27941318, 874.31413165,\n", + " 847.78323382, 960.73651726, 902.09721294, 899.97936727,\n", + " 936.76767365, 908.68471877, 931.97562897, 892.8563381 ,\n", + " 940.49920044, 925.87119487, 915.55667036, 786.01763842,\n", + " 867.61268444, 1007.66794229, 867.43220284, 941.51843395,\n", + " 955.66765617, 970.36344663, 894.82624021, 915.00922015,\n", + " 889.99169864, 878.28545616, 850.33714087, 928.43421969,\n", + " 939.52352775, 962.60336897, 903.36858487, 896.37548128,\n", + " 963.73199174, 789.82843798, 920.43722936, 828.40065648,\n", + " 943.98810503, 835.85766882, 841.76946105, 937.36801859,\n", + " 896.49642885, 967.15672796, 918.39713395, 906.36795986,\n", + " 919.40916519, 875.89694182, 908.3418033 , 972.05674531,\n", + " 927.07275902, 940.77150366, 918.04680759, 981.59648505,\n", + " 940.32229921, 938.36235287, 849.04038544, 893.3058527 ,\n", + " 913.09883832, 912.35625832, 989.78190796, 879.34780591,\n", + " 928.81644221, 892.97322993, 898.70158737, 906.91746418,\n", + " 946.42526162, 914.08702993, 859.34034454, 907.18338275,\n", + " 989.30138781, 926.06769221, 838.08269605, 929.50654863,\n", + " 827.39586238, 927.36188021, 910.17661856, 935.41822322,\n", + " 885.63661049, 914.48018584, 895.93753147, 911.13864214,\n", + " 881.60996972, 938.20931097, 1015.75963624, 938.56188955,\n", + " 909.09481672, 867.81417174, 912.15092578, 907.0519042 ,\n", + " 913.43873474, 913.23570042, 983.89208176, 899.44708324,\n", + " 910.55603061, 950.67199374, 916.27718038, 886.16272254,\n", + " 975.57372749, 882.46972449, 908.99151954, 893.41760103,\n", + " 938.6441373 , 885.19738826, 947.44382361, 918.75218854,\n", + " 942.78614663, 968.52291541, 915.78656008, 989.00683215,\n", + " 855.88223229, 976.62553529, 919.25644927, 958.63970635,\n", + " 934.03540249, 926.45247121, 961.36373273, 926.55709697,\n", + " 896.6375551 , 897.47943897, 907.74803191, 954.04187795,\n", + " 882.10295293, 1004.09432843, 892.73726557, 846.13198111,\n", + " 929.42733278, 894.24531402, 921.97988827, 860.601478 ,\n", + " 912.55635483, 997.22339505, 985.88635074, 938.45775184,\n", + " 944.63766895, 942.18376197, 984.23087354, 922.40370934,\n", + " 965.34813049, 921.32552393, 969.81367405, 917.74503135,\n", + " 833.76599428, 894.97963584, 878.49090123, 864.91049261,\n", + " 985.04808527, 927.18133162, 844.64492657, 913.88047009,\n", + " 928.03561194, 930.79686847, 930.13055052, 839.5761256 ,\n", + " 943.37875897, 956.44476838, 931.39781252, 989.20932655,\n", + " 1006.29928459, 942.03146682, 901.72518137, 932.53880871,\n", + " 935.96943144, 925.53161728, 874.67439465, 951.48997974,\n", + " 960. , 874.14717137, 921.49562818, 927.26749515,\n", + " 926.41158172, 896.61260138, 938.6103718 , 959.64828575,\n", + " 935.22630845, 955.63103852, 950.8495808 , 888.45366602,\n", + " 912.06814044, 862.81354284, 875.05713562, 875.4100677 ,\n", + " 923.5426824 , 839.78562837, 889.74471045, 867.8332007 ,\n", + " 933.88736839, 868.07155277, 883.34738777, 913.12741673,\n", + " 915.77804797, 962.99775026, 932.9485248 , 930.37415413,\n", + " 944.21567324, 776.98228821, 936.37311401, 941.58975024,\n", + " 941.55965056, 864.51860126, 930.38329188, 912.78502395,\n", + " 947.32712079, 1011.13494089, 937.99624501, 916.59535343,\n", + " 873.82437868, 901.49630725, 846.11674006, 998.64016883,\n", + " 978.32805846, 876.27655411, 905.78089027, 827.25234516,\n", + " 1002.18813125, 898.65291068, 920.350764 , 874.17361952,\n", + " 938.70381451, 945.81921622, 874.98921047, 915.97985629,\n", + " 920.14460695, 965.25282342, 977.01542391, 863.18856888,\n", + " 907.62393082, 906.19584978, 851.37149337, 956.85804045,\n", + " 969.94521463, 912.02588788, 953.60047228, 819.53086148,\n", + " 911.99611929, 845.66361521, 962.24964999, 921.43338903,\n", + " 982.04267741, 931.1841306 , 896.56744607, 926.6958426 ,\n", + " 841.56778533, 961.77261899, 943.15601639, 786.18136435,\n", + " 864.98026608, 950.29718422, 930.39820918, 983.57154574,\n", + " 909.55498598, 925.76591071, 906.07808244, 964.41923255,\n", + " 911.17276099, 919.34859876, 906.11721418, 905.96844161,\n", + " 911.53683431, 958.97512493, 953.15452691, 889.09240389,\n", + " 957.98473324, 993.29170991, 928.99175736, 908.10563408]),\n", + " 'peak_electrode_waveform': array([-503.33636558, -502.734375 , -501.32973031, -500.34246575,\n", + " -499.73244863, -498.66491866, -494.41887842, -491.6015625 ,\n", + " -486.59300086, -481.55233305, -474.36055223, -466.79152397,\n", + " -458.98169949, -451.10766267, -442.41491866, -431.03328339,\n", + " -420.81549658, -410.91074486, -402.25010702, -395.59610445,\n", + " -389.25513699, -384.02985873, -381.67005565, -379.81592466,\n", + " -378.79655394, -380.26541096, -384.54355736, -392.41759418,\n", + " -401.11033818, -410.67797517, -420.13324058, -430.6640625 ,\n", + " -442.34267979, -452.71297089, -461.42979452, -469.67305223,\n", + " -478.64672517, -486.40036387, -493.83294092, -498.87360873,\n", + " -504.82127568, -509.58101455, -511.55554366, -514.19627568,\n", + " -515.77750428, -516.41160103, -517.3828125 , -518.16941353,\n", + " -517.26241438, -518.46639555, -517.76808647, -517.3828125 ,\n", + " -516.61226455, -514.32470034, -512.5187286 , -511.66791524,\n", + " -510.65657106, -507.74293664, -504.84535531, -503.02333048,\n", + " -501.93172089, -499.3552012 , -498.77729024, -495.78339041,\n", + " -495.42219606, -495.78339041, -494.30650685, -492.30789812,\n", + " -491.83433219, -492.14736729, -490.16481164, -489.69124572,\n", + " -488.84043236, -488.5354238 , -489.24175942, -487.68461045,\n", + " -486.48062928, -485.50941781, -484.23319777, -484.24925086,\n", + " -484.26530394, -483.3984375 ])}" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "unit_data" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sampling_rate = (ephys.EphysRecording & insert_key).fetch1(\n", + " \"sampling_rate\"\n", + ") / 1000 # in kHz\n", + "plt.plot(\n", + " np.r_[: unit_data[\"peak_electrode_waveform\"].size] * 1 / sampling_rate,\n", + " unit_data[\"peak_electrode_waveform\"],\n", + ")\n", + "plt.xlabel(\"Time (ms)\")\n", + "plt.ylabel(r\"Voltage ($\\mu$V)\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "Following this tutorial, we have: \n", + "+ Covered the essential functionality of `element-array-ephys`.\n", + "+ Learned how to manually insert data into tables.\n", + "+ Executed and ingested results of spike sorting with Kilosort.\n", + "+ Visualized the results. \n", + "\n", + "#### Documentation and DataJoint Tutorials\n", + "\n", + "+ [Detailed documentation on\n", + " `element-array-ephys`.](https://datajoint.com/docs/elements/element-array-ephys/)\n", + "+ [General `datajoint-python`\n", + " tutorials.](https://github.com/datajoint/datajoint-tutorials) covering fundamentals,\n", + " such as table tiers, query operations, fetch operations, automated computations with the\n", + " make function, and more.\n", + "+ [Documentation for\n", + " `datajoint-python`.](https://datajoint.com/docs/core/datajoint-python/)\n", + "\n", + "##### Run this tutorial on your own data\n", + "\n", + "To run this tutorial notebook on your own data, please use the following steps:\n", + "+ Download the [mysql-docker image for\n", + " DataJoint](https://github.com/datajoint/mysql-docker) and run the container according\n", + " to the instructions provide in the repository.\n", + "+ Create a fork of this repository to your GitHub account.\n", + "+ Clone the repository and open the files using your IDE.\n", + "+ Add a code cell immediately after the first code cell in the notebook - we will setup\n", + " the local connection using this cell. In this cell, type in the following code. \n", + "\n", + "```python\n", + "import datajoint as dj\n", + "dj.config[\"database.host\"] = \"localhost\"\n", + "dj.config[\"database.user\"] = \"\"\n", + "dj.config[\"database.password\"] = \"\"\n", + "dj.config[\"custom\"] = {\"imaging_root_data_dir\": \"path/to/your/data/dir\",\n", + "\"database_prefix\": \"\"}\n", + "dj.config.save_local()\n", + "dj.conn()\n", + "```\n", + "\n", + "+ Run the code block above and proceed with the rest of the notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "python3p10", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.17" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "ff52d424e56dd643d8b2ec122f40a2e279e94970100b4e6430cb9025a65ba4cf" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/tutorial_pipeline.py b/notebooks/tutorial_pipeline.py new file mode 100644 index 0000000..a4c6383 --- /dev/null +++ b/notebooks/tutorial_pipeline.py @@ -0,0 +1,64 @@ +import os +import pathlib +import datajoint as dj +from element_animal import subject +from element_animal.subject import Subject +from element_lab import lab +from element_lab.lab import Lab, Location, Project, Protocol, Source, User +from element_lab.lab import Device as Equipment +from element_lab.lab import User as Experimenter +from element_session import session_with_datetime as session +from element_session.session_with_datetime import Session +from element_optogenetics import optogenetics + + +if "custom" not in dj.config: + dj.config["custom"] = {} + +# overwrite dj.config['custom'] values with environment variables if available + +dj.config["custom"]["database.prefix"] = os.getenv( + "DATABASE_PREFIX", dj.config["custom"].get("database.prefix", "") +) + +dj.config["custom"]["ephys_root_data_dir"] = os.getenv( + "EPHYS_ROOT_DATA_DIR", dj.config["custom"].get("ephys_root_data_dir", "") +) + +db_prefix = dj.config["custom"].get("database.prefix", "") + + +# Declare functions for retrieving data +def get_ephys_root_data_dir(): + """Retrieve ephys root data directory.""" + ephys_root_dirs = dj.config.get("custom", {}).get("ephys_root_data_dir", None) + if not ephys_root_dirs: + return None + elif isinstance(ephys_root_dirs, (str, pathlib.Path)): + return [ephys_root_dirs] + elif isinstance(ephys_root_dirs, list): + return ephys_root_dirs + else: + raise TypeError("`ephys_root_data_dir` must be a string, pathlib, or list") + + +# Activate schemas +lab.activate(db_prefix + "lab") +subject.activate(db_prefix + "subject", linking_module=__name__) +session.activate(db_prefix + "session", linking_module=__name__) + + +@lab.schema +class SkullReference(dj.Lookup): + definition = """ + skull_reference : varchar(60) + """ + contents = zip(["Bregma", "Lambda"]) + + +def get_session_directory(session_key): + session_directory = (session.SessionDirectory & session_key).fetch1("session_dir") + return pathlib.Path(session_directory) + + +optogenetics.activate(db_prefix + "optogenetics", linking_module=__name__) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index fc7a8e6..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -datajoint>=0.13.0 diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index 416634f..0000000 --- a/requirements_dev.txt +++ /dev/null @@ -1 +0,0 @@ -pre-commit diff --git a/setup.py b/setup.py index 6798877..e1f8af2 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,13 @@ from os import path - from setuptools import find_packages, setup -pkg_name = next(p for p in find_packages() if "." not in p) + +pkg_name = "element_optogenetics" 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()) @@ -27,5 +24,18 @@ keywords="neuroscience optogenetics science datajoint", packages=find_packages(exclude=["contrib", "docs", "tests*"]), scripts=[], - install_requires=requirements, + install_requires=[ + "datajoint>=0.13.0", + ], + extras_require={ + "elements": [ + "element-animal @ git+https://github.com/datajoint/element-animal.git", + "element-event @ git+https://github.com/datajoint/element-event.git", + "element-interface @ git+https://github.com/datajoint/element-interface.git", + "element-lab @ git+https://github.com/datajoint/element-lab.git", + "element-session @ git+https://github.com/datajoint/element-session.git", + "element-array-ephys @ git+https://github.com/datajoint/element-array-ephys.git", + ], + "tests": ["pre-commit", "pytest", "pytest-cov"], + }, ) From af9052030374f31a41939c56bb3741279062ed66 Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Thu, 4 Jan 2024 13:41:35 -0600 Subject: [PATCH 02/26] Update image names, notebook, tutorial pipeline --- ...wchart.drawio => diagram_flowchart.drawio} | 0 .../{flowchart.svg => diagram_flowchart.svg} | 0 notebooks/tutorial.ipynb | 284 +++++++++--------- notebooks/tutorial_pipeline.py | 46 +-- 4 files changed, 156 insertions(+), 174 deletions(-) rename images/{flowchart.drawio => diagram_flowchart.drawio} (100%) rename images/{flowchart.svg => diagram_flowchart.svg} (100%) diff --git a/images/flowchart.drawio b/images/diagram_flowchart.drawio similarity index 100% rename from images/flowchart.drawio rename to images/diagram_flowchart.drawio diff --git a/images/flowchart.svg b/images/diagram_flowchart.svg similarity index 100% rename from images/flowchart.svg rename to images/diagram_flowchart.svg diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb index 84247a3..0e2d543 100644 --- a/notebooks/tutorial.ipynb +++ b/notebooks/tutorial.ipynb @@ -13,7 +13,7 @@ "tutorial aims to provide a comprehensive understanding of the open-source data pipeline\n", "created using `element-optogenetics`.\n", "\n", - "This package is designed to seamlessly ingest and track optotgenetics data. By the end of this\n", + "This package is designed to seamlessly ingest and track optogenetics data. By the end of this\n", "tutorial you will have a clear grasp on setting up and integrating `element-optogenetics`\n", "into your specific research projects and lab. \n", "\n", @@ -23,35 +23,27 @@ "\n", "Please see the [datajoint tutorials GitHub\n", "repository](https://github.com/datajoint/datajoint-tutorials/tree/main) before\n", - "proceeding.\n", - "\n", - "A basic understanding of the following DataJoint concepts will be beneficial to your\n", - "understanding of this tutorial: \n", - "1. The `Imported` and `Computed` tables types in `datajoint-python`.\n", - "2. The functionality of the `.populate()` method. \n", + "proceeding. \n", "\n", "#### **Tutorial Overview**\n", "\n", "+ Setup\n", "+ *Activate* the DataJoint pipeline.\n", - "+ *Insert* subject, session, and probe metadata.\n", - "+ *Populate* electrophysiology recording metadata.\n", - "+ Run the clustering task.\n", - "+ Curate the results (optional).\n", - "+ Visualize the results.\n", + "+ *Insert* subject, session, surgery metadata.\n", + "+ *Insert* optogenetics recording data.\n", + "+ Query and view data\n", "\n", "### **Setup**\n", "\n", - "This tutorial examines extracellular electrophysiology data acquired with `OpenEphys`\n", - "and spike-sorted using Kilosort 2.5. The goal is to store, track\n", - "and manage sessions of array electrophysiology data, including spike sorting results and\n", - "unit-level visualizations. \n", + "This tutorial examines DataJoint tables that track optogenetics data. The goal is to store, track\n", + "and manage all metadata associated with optogenetics experiments, including surgical\n", + "implanation data. \n", "\n", "The results of this Element can be combined with **other modalities** to create\n", "a complete, customizable data pipeline for your specific lab or study. For instance, you\n", - "can combine `element-array-ephys` with `element-calcium-imaging` and\n", - "`element-deeplabcut` to characterize the neural activity along with markless\n", - "pose-estimation during behavior.\n", + "can combine `element-optogenetics` with `element-array-ephys` and\n", + "`element-event` to characterize the neural activity during specific optogenetic stimulus\n", + "events.\n", "\n", "Let's start this tutorial by importing the packages necessary to run the notebook." ] @@ -111,7 +103,7 @@ "source": [ "### **Activate the DataJoint Pipeline**\n", "\n", - "This tutorial activates the `ephys_acute.py` module from `element-array-ephys`, along\n", + "This tutorial activates the `optogenetics.py` module from `element-optogenetics`, along\n", "with upstream dependencies from `element-animal` and `element-session`. Please refer to the\n", "[`tutorial_pipeline.py`](./tutorial_pipeline.py) for the source code." ] @@ -130,14 +122,14 @@ } ], "source": [ - "from tutorial_pipeline import lab, subject, session, probe, ephys" + "from tutorial_pipeline import lab, subject, surgery, session, optogenetics, Device" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We can represent the tables in the `probe` and `ephys` schemas as well as some of the\n", + "We can represent the tables in the `optogenetics` schemas as well as some of the\n", "upstream dependencies to `session` and `subject` schemas as a diagram." ] }, @@ -600,9 +592,10 @@ "source": [ "(\n", " dj.Diagram(subject.Subject)\n", + " + dj.Diagram(surgery.Implantation),\n", " + dj.Diagram(session.Session)\n", - " + dj.Diagram(probe)\n", - " + dj.Diagram(ephys)\n", + " + dj.Diagram(Device)\n", + " + dj.Diagram(optogenetics)\n", ")" ] }, @@ -613,11 +606,11 @@ "As evident from the diagram, this data pipeline encompasses tables associated with\n", "recording and probe metadata, results of clustering, and optional curation of clustering\n", "results. A few tables, such as `subject.Subject` or `session.Session`,\n", - "while important for a complete pipeline, fall outside the scope of the `element-array-ephys`\n", + "while important for a complete pipeline, fall outside the scope of the `element-optogenetics`\n", "tutorial, and will therefore, not be explored extensively here. The primary focus of\n", - "this tutorial will be on the `probe` and `ephys` schemas.\n", + "this tutorial will be on the `optogenetics` schemas.\n", "\n", - "### **Insert subject, session, and probe metadata**\n", + "### **Insert subject, surgery, and session metadata**\n", "\n", "Let's start with the first table in the schema diagram (i.e. `subject.Subject` table).\n", "\n", @@ -887,7 +880,7 @@ ], "source": [ "subject.Subject.insert1(\n", - " dict(subject=\"subject5\", subject_birth_date=\"2023-01-01\", sex=\"U\")\n", + " dict(subject=\"subject1\", subject_birth_date=\"2023-01-01\", sex=\"U\")\n", ")\n", "subject.Subject()" ] @@ -1272,10 +1265,26 @@ } ], "source": [ - "probe.Probe.insert1(\n", - " dict(probe=\"714000838\", probe_type=\"neuropixels 1.0 - 3B\")\n", - ") # this info could be achieve from neuropixels meta file.\n", - "probe.Probe()" + "Device.insert1(\n", + " dict(\n", + " device=\"OPTG_8\",\n", + " modality=\"Optogenetics\",\n", + " description=\"8 channel pulse sequence device\",\n", + " )\n", + ")\n", + "Device()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lab.User.insert1(\n", + " dict(user=\"User1\")\n", + ")\n", + "lab.User()" ] }, { @@ -1306,7 +1315,7 @@ } ], "source": [ - "print(ephys.ProbeInsertion.describe())" + "surgery.CoordinateReference()" ] }, { @@ -1331,7 +1340,7 @@ } ], "source": [ - "ephys.ProbeInsertion.heading" + "surgery.Hemisphere()" ] }, { @@ -1432,124 +1441,127 @@ } ], "source": [ - "ephys.ProbeInsertion.insert1(\n", + "surgery.BrainRegion.insert1(\n", " dict(\n", - " session_key,\n", - " insertion_number=1,\n", - " probe=\"714000838\",\n", + " region_acronym=\"dHP\",\n", + " region_name=\"Dorsal Hippocampus\"\n", + " )\n", + ")\n", + "surgery.BrainRegion()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "surgery.Implantation.insert1(\n", + " dict(\n", + " subject=\"subject1\",\n", + " implant_date=\"2022-04-01 12:13:14\",\n", + " implate_type=\"opto\",\n", + " target_region=\"dHP\",\n", + " target_hemisphere=\"left\",\n", + " surgeon=\"User1\",\n", " )\n", - ") # probe, subject, session_datetime needs to follow the restrictions of foreign keys.\n", - "ephys.ProbeInsertion()" + ")\n", + "\n", + "surgery.Implantation.Coordinate.insert1(\n", + " dict(\n", + " subject=\"subject1\",\n", + " implant_date=\"2022-04-01 12:13:14\",\n", + " implate_type=\"opto\",\n", + " target_region=\"dHP\",\n", + " target_hemisphere=\"left\",\n", + " ap=\"-7.9\",\n", + " ap_ref=\"bregma\",\n", + " ml=\"-1.8\",\n", + " ml_ref=\"bregma\",\n", + " dv=\"5\",\n", + " dv_ref=\"skull_surface\",\n", + " theta=\"11.5\",\n", + " phi=\"0\",\n", + " beta=None\n", + " )\n", + ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Confirm the inserted data:" + "Confirm the inserted information:" ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " Probe insertion implanted into an animal for a given session.\n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "
\n", - "

subject

\n", - " \n", - "
\n", - "

session_datetime

\n", - " \n", - "
\n", - "

insertion_number

\n", - " \n", - "
\n", - "

probe

\n", - " unique identifier for this model of probe (e.g. serial number)\n", - "
subject52023-01-01 00:00:001714000838
\n", - " \n", - "

Total: 1

\n", - " " - ], - "text/plain": [ - "*subject *session_datet *insertion_num probe \n", - "+----------+ +------------+ +------------+ +-----------+\n", - "subject5 2023-01-01 00: 1 714000838 \n", - " (Total: 1)" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "ephys.ProbeInsertion()" + "surgery.Implantation()" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "surgery.Implantation.Coordinate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "optogenetics.OptoWaveform.insert1(\n", + " dict(\n", + " waveform_name=\"square_10\",\n", + " waveform_type=\"square\",\n", + " waveform_description=\"Square waveform: 10%/90% on/off cycle\",\n", + " )\n", + ")\n", + "\n", + "# Square is one part table of OptoWaveform.\n", + "# For sine and ramp waveforms, see the corresponding tables.\n", + "optogenetics.OptoWaveform.Square.insert1(\n", + " dict(\n", + " waveform_name=\"square_10\",\n", + " on_proportion=0.10,\n", + " off_proportion=0.90\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "optogenetics.OptoStimParams.insert1(\n", + " dict(\n", + " opto_params_id=1,\n", + " waveform_name=\"square_10\",\n", + " wavelength=470,\n", + " light_intensity=10.2,\n", + " frequency=1,\n", + " duration=241,\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/notebooks/tutorial_pipeline.py b/notebooks/tutorial_pipeline.py index a4c6383..ea5f124 100644 --- a/notebooks/tutorial_pipeline.py +++ b/notebooks/tutorial_pipeline.py @@ -1,14 +1,12 @@ -import os import pathlib import datajoint as dj -from element_animal import subject -from element_animal.subject import Subject +from element_animal import subject, surgery +from element_animal.subject import Subject # Dependency for session schema +from element_animal.surgery import Implantation # Dependency for optogenetics schema from element_lab import lab -from element_lab.lab import Lab, Location, Project, Protocol, Source, User -from element_lab.lab import Device as Equipment -from element_lab.lab import User as Experimenter -from element_session import session_with_datetime as session -from element_session.session_with_datetime import Session +from element_lab.lab import Lab, Project, Protocol, Source, User +from element_session import session_with_id as session +from element_session.session_with_id import Session from element_optogenetics import optogenetics @@ -21,44 +19,16 @@ "DATABASE_PREFIX", dj.config["custom"].get("database.prefix", "") ) -dj.config["custom"]["ephys_root_data_dir"] = os.getenv( - "EPHYS_ROOT_DATA_DIR", dj.config["custom"].get("ephys_root_data_dir", "") -) - db_prefix = dj.config["custom"].get("database.prefix", "") -# Declare functions for retrieving data -def get_ephys_root_data_dir(): - """Retrieve ephys root data directory.""" - ephys_root_dirs = dj.config.get("custom", {}).get("ephys_root_data_dir", None) - if not ephys_root_dirs: - return None - elif isinstance(ephys_root_dirs, (str, pathlib.Path)): - return [ephys_root_dirs] - elif isinstance(ephys_root_dirs, list): - return ephys_root_dirs - else: - raise TypeError("`ephys_root_data_dir` must be a string, pathlib, or list") - - # Activate schemas lab.activate(db_prefix + "lab") subject.activate(db_prefix + "subject", linking_module=__name__) -session.activate(db_prefix + "session", linking_module=__name__) - - -@lab.schema -class SkullReference(dj.Lookup): - definition = """ - skull_reference : varchar(60) - """ - contents = zip(["Bregma", "Lambda"]) +Experimenter = User +session.activate(db_prefix + "session", linking_module=__name__) -def get_session_directory(session_key): - session_directory = (session.SessionDirectory & session_key).fetch1("session_dir") - return pathlib.Path(session_directory) optogenetics.activate(db_prefix + "optogenetics", linking_module=__name__) From dd7e778df8d1362ffcaa5dffeb24647956fa2ee5 Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Thu, 4 Jan 2024 13:42:22 -0600 Subject: [PATCH 03/26] Black formatting --- element_optogenetics/optogenetics.py | 2 +- notebooks/tutorial.ipynb | 24 ++++++------------------ notebooks/tutorial_pipeline.py | 1 - 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/element_optogenetics/optogenetics.py b/element_optogenetics/optogenetics.py index 9b5e555..febc113 100644 --- a/element_optogenetics/optogenetics.py +++ b/element_optogenetics/optogenetics.py @@ -11,7 +11,7 @@ def activate( *, create_schema: bool = True, create_tables: bool = True, - linking_module: str = None + linking_module: str = None, ): """Activate this schema. diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb index 0e2d543..2b5e4d1 100644 --- a/notebooks/tutorial.ipynb +++ b/notebooks/tutorial.ipynb @@ -591,11 +591,8 @@ ], "source": [ "(\n", - " dj.Diagram(subject.Subject)\n", - " + dj.Diagram(surgery.Implantation),\n", - " + dj.Diagram(session.Session)\n", - " + dj.Diagram(Device)\n", - " + dj.Diagram(optogenetics)\n", + " dj.Diagram(subject.Subject) + dj.Diagram(surgery.Implantation),\n", + " +dj.Diagram(session.Session) + dj.Diagram(Device) + dj.Diagram(optogenetics),\n", ")" ] }, @@ -1281,9 +1278,7 @@ "metadata": {}, "outputs": [], "source": [ - "lab.User.insert1(\n", - " dict(user=\"User1\")\n", - ")\n", + "lab.User.insert1(dict(user=\"User1\"))\n", "lab.User()" ] }, @@ -1442,10 +1437,7 @@ ], "source": [ "surgery.BrainRegion.insert1(\n", - " dict(\n", - " region_acronym=\"dHP\",\n", - " region_name=\"Dorsal Hippocampus\"\n", - " )\n", + " dict(region_acronym=\"dHP\", region_name=\"Dorsal Hippocampus\")\n", ")\n", "surgery.BrainRegion()" ] @@ -1482,7 +1474,7 @@ " dv_ref=\"skull_surface\",\n", " theta=\"11.5\",\n", " phi=\"0\",\n", - " beta=None\n", + " beta=None,\n", " )\n", ")" ] @@ -1529,11 +1521,7 @@ "# Square is one part table of OptoWaveform.\n", "# For sine and ramp waveforms, see the corresponding tables.\n", "optogenetics.OptoWaveform.Square.insert1(\n", - " dict(\n", - " waveform_name=\"square_10\",\n", - " on_proportion=0.10,\n", - " off_proportion=0.90\n", - " )\n", + " dict(waveform_name=\"square_10\", on_proportion=0.10, off_proportion=0.90)\n", ")" ] }, diff --git a/notebooks/tutorial_pipeline.py b/notebooks/tutorial_pipeline.py index ea5f124..ddf5c58 100644 --- a/notebooks/tutorial_pipeline.py +++ b/notebooks/tutorial_pipeline.py @@ -30,5 +30,4 @@ session.activate(db_prefix + "session", linking_module=__name__) - optogenetics.activate(db_prefix + "optogenetics", linking_module=__name__) From 321bd65db1cd76092fb40dfd322faea7011bbad8 Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Thu, 4 Jan 2024 20:03:57 +0000 Subject: [PATCH 04/26] Update pipeline + notebook within codespace --- notebooks/tutorial.ipynb | 2061 +++----------------------------- notebooks/tutorial_pipeline.py | 24 +- 2 files changed, 204 insertions(+), 1881 deletions(-) diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb index 2b5e4d1..b80539a 100644 --- a/notebooks/tutorial.ipynb +++ b/notebooks/tutorial.ipynb @@ -78,8 +78,8 @@ "name": "stderr", "output_type": "stream", "text": [ - "[2023-11-28 19:33:47,134][INFO]: Connecting root@fakeservices.datajoint.io:3306\n", - "[2023-11-28 19:33:47,142][INFO]: Connected root@fakeservices.datajoint.io:3306\n" + "[2024-01-04 20:00:17,037][INFO]: Connecting root@fakeservices.datajoint.io:3306\n", + "[2024-01-04 20:00:17,044][INFO]: Connected root@fakeservices.datajoint.io:3306\n" ] }, { @@ -117,7 +117,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "[2023-11-28 19:33:49,428][WARNING]: lab.Project and related tables will be removed in a future version of Element Lab. Please use the project schema.\n" + "[2024-01-04 20:00:18,332][WARNING]: lab.Project and related tables will be removed in a future version of Element Lab. Please use the project schema.\n" ] } ], @@ -135,464 +135,200 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ - "\n", - "\n", + "\n", + "\n", "%3\n", - "\n", - "\n", + "\n", + "\n", "\n", - "ephys.LFP\n", - "\n", - "\n", - "ephys.LFP\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "ephys.LFP.Electrode\n", - "\n", - "\n", - "ephys.LFP.Electrode\n", + "optogenetics.OptoWaveform.Square\n", + "\n", + "\n", + "optogenetics.OptoWaveform.Square\n", "\n", "\n", "\n", - "\n", - "\n", - "ephys.LFP->ephys.LFP.Electrode\n", - "\n", - "\n", - "\n", + "\n", "\n", - "ephys.EphysRecording.EphysFile\n", - "\n", - "\n", - "ephys.EphysRecording.EphysFile\n", + "Device\n", + "\n", + "\n", + "Device\n", "\n", "\n", "\n", - "\n", - "\n", - "ephys.QualityMetrics.Cluster\n", - "\n", - "\n", - "ephys.QualityMetrics.Cluster\n", + "\n", + "\n", + "optogenetics.OptoProtocol\n", + "\n", + "\n", + "optogenetics.OptoProtocol\n", "\n", "\n", "\n", - "\n", - "\n", - "probe.ProbeType.Electrode\n", - "\n", - "\n", - "probe.ProbeType.Electrode\n", - "\n", - "\n", + "\n", + "\n", + "Device->optogenetics.OptoProtocol\n", + "\n", "\n", - "\n", - "\n", - "probe.ElectrodeConfig.Electrode\n", - "\n", - "\n", - "probe.ElectrodeConfig.Electrode\n", + "\n", + "\n", + "surgery.Implantation\n", + "\n", + "\n", + "surgery.Implantation\n", "\n", "\n", "\n", - "\n", + "\n", "\n", - "probe.ProbeType.Electrode->probe.ElectrodeConfig.Electrode\n", - "\n", + "surgery.Implantation->optogenetics.OptoProtocol\n", + "\n", "\n", - "\n", - "\n", - "ephys.Curation\n", - "\n", - "\n", - "ephys.Curation\n", + "\n", + "\n", + "subject.Subject\n", + "\n", + "\n", + "subject.Subject\n", "\n", "\n", "\n", - "\n", - "\n", - "ephys.CuratedClustering\n", - "\n", - "\n", - "ephys.CuratedClustering\n", + "\n", + "\n", + "subject.Subject->surgery.Implantation\n", + "\n", + "\n", + "\n", + "\n", + "session.Session\n", + "\n", + "\n", + "session.Session\n", "\n", "\n", "\n", - "\n", - "\n", - "ephys.Curation->ephys.CuratedClustering\n", - "\n", + "\n", + "\n", + "subject.Subject->session.Session\n", + "\n", "\n", - "\n", - "\n", - "ephys.ProbeInsertion\n", - "\n", - "\n", - "ephys.ProbeInsertion\n", + "\n", + "\n", + "optogenetics.OptoWaveformType\n", + "\n", + "\n", + "optogenetics.OptoWaveformType\n", "\n", "\n", "\n", - "\n", - "\n", - "ephys.InsertionLocation\n", - "\n", - "\n", - "ephys.InsertionLocation\n", + "\n", + "\n", + "optogenetics.OptoWaveform\n", + "\n", + "\n", + "optogenetics.OptoWaveform\n", "\n", "\n", "\n", - "\n", - "\n", - "ephys.ProbeInsertion->ephys.InsertionLocation\n", - "\n", + "\n", + "\n", + "optogenetics.OptoWaveformType->optogenetics.OptoWaveform\n", + "\n", "\n", - "\n", - "\n", - "ephys.EphysRecording\n", - "\n", - "\n", - "ephys.EphysRecording\n", + "\n", + "\n", + "optogenetics.OptoWaveform.Sine\n", + "\n", + "\n", + "optogenetics.OptoWaveform.Sine\n", "\n", "\n", "\n", - "\n", - "\n", - "ephys.ProbeInsertion->ephys.EphysRecording\n", - "\n", - "\n", - "\n", - "\n", - "probe.ElectrodeConfig\n", - "\n", - "\n", - "probe.ElectrodeConfig\n", + "\n", + "\n", + "optogenetics.OptoEvent\n", + "\n", + "\n", + "optogenetics.OptoEvent\n", "\n", "\n", "\n", - "\n", + "\n", "\n", - "probe.ElectrodeConfig->probe.ElectrodeConfig.Electrode\n", - "\n", - "\n", - "\n", - "\n", - "probe.ElectrodeConfig->ephys.EphysRecording\n", - "\n", + "optogenetics.OptoProtocol->optogenetics.OptoEvent\n", + "\n", "\n", - "\n", + "\n", "\n", - "ephys.WaveformSet.PeakWaveform\n", - "\n", - "\n", - "ephys.WaveformSet.PeakWaveform\n", + "optogenetics.OptoWaveform.Ramp\n", + "\n", + "\n", + "optogenetics.OptoWaveform.Ramp\n", "\n", "\n", "\n", - "\n", - "\n", - "ephys.CuratedClustering.Unit\n", - "\n", - "\n", - "ephys.CuratedClustering.Unit\n", - "\n", - "\n", + "\n", + "\n", + "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Square\n", + "\n", "\n", - "\n", + "\n", "\n", - "ephys.CuratedClustering.Unit->ephys.QualityMetrics.Cluster\n", - "\n", + "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Sine\n", + "\n", "\n", - "\n", + "\n", "\n", - "ephys.CuratedClustering.Unit->ephys.WaveformSet.PeakWaveform\n", - "\n", + "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Ramp\n", + "\n", "\n", - "\n", - "\n", - "ephys.WaveformSet.Waveform\n", - "\n", - "\n", - "ephys.WaveformSet.Waveform\n", + "\n", + "\n", + "optogenetics.OptoStimParams\n", + "\n", + "\n", + "optogenetics.OptoStimParams\n", "\n", "\n", "\n", - "\n", + "\n", "\n", - "ephys.CuratedClustering.Unit->ephys.WaveformSet.Waveform\n", - "\n", - "\n", - "\n", - "\n", - "ephys.QualityMetrics.Waveform\n", - "\n", - "\n", - "ephys.QualityMetrics.Waveform\n", - "\n", - "\n", + "optogenetics.OptoWaveform->optogenetics.OptoStimParams\n", + "\n", "\n", - "\n", + "\n", "\n", - "ephys.CuratedClustering.Unit->ephys.QualityMetrics.Waveform\n", - "\n", - "\n", - "\n", - "\n", - "ephys.WaveformSet\n", - "\n", - "\n", - "ephys.WaveformSet\n", - "\n", + "optogenetics.OptoStimParams->optogenetics.OptoProtocol\n", + "\n", "\n", - "\n", - "\n", + "\n", "\n", - "ephys.WaveformSet->ephys.WaveformSet.PeakWaveform\n", - "\n", - "\n", - "\n", - "\n", - "ephys.WaveformSet->ephys.WaveformSet.Waveform\n", - "\n", - "\n", - "\n", - "\n", - "probe.ProbeType\n", - "\n", - "\n", - "probe.ProbeType\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "probe.ProbeType->probe.ProbeType.Electrode\n", - "\n", - "\n", - "\n", - "\n", - "probe.ProbeType->probe.ElectrodeConfig\n", - "\n", - "\n", - "\n", - "\n", - "probe.Probe\n", - "\n", - "\n", - "probe.Probe\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "probe.ProbeType->probe.Probe\n", - "\n", - "\n", - "\n", - "\n", - "probe.Probe->ephys.ProbeInsertion\n", - "\n", - "\n", - "\n", - "\n", - "ephys.AcquisitionSoftware\n", - "\n", - "\n", - "ephys.AcquisitionSoftware\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "ephys.AcquisitionSoftware->ephys.EphysRecording\n", - "\n", - "\n", - "\n", - "\n", - "ephys.QualityMetrics\n", - "\n", - "\n", - "ephys.QualityMetrics\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "ephys.QualityMetrics->ephys.QualityMetrics.Cluster\n", - "\n", - "\n", - "\n", - "\n", - "ephys.QualityMetrics->ephys.QualityMetrics.Waveform\n", - "\n", - "\n", - "\n", - "\n", - "ephys.CuratedClustering->ephys.CuratedClustering.Unit\n", - "\n", - "\n", - "\n", - "\n", - "ephys.CuratedClustering->ephys.WaveformSet\n", - "\n", - "\n", - "\n", - "\n", - "ephys.CuratedClustering->ephys.QualityMetrics\n", - "\n", - "\n", - "\n", - "\n", - "subject.Subject\n", - "\n", - "\n", - "subject.Subject\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "session.Session\n", - "\n", - "\n", - "session.Session\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "subject.Subject->session.Session\n", - "\n", - "\n", - "\n", - "\n", - "probe.ElectrodeConfig.Electrode->ephys.CuratedClustering.Unit\n", - "\n", - "\n", - "\n", - "\n", - "probe.ElectrodeConfig.Electrode->ephys.WaveformSet.Waveform\n", - "\n", - "\n", - "\n", - "\n", - "probe.ElectrodeConfig.Electrode->ephys.LFP.Electrode\n", - "\n", - "\n", - "\n", - "\n", - "ephys.ClusteringMethod\n", - "\n", - "\n", - "ephys.ClusteringMethod\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "ephys.ClusteringParamSet\n", - "\n", - "\n", - "ephys.ClusteringParamSet\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "ephys.ClusteringMethod->ephys.ClusteringParamSet\n", - "\n", - "\n", - "\n", - "\n", - "ephys.EphysRecording->ephys.LFP\n", - "\n", - "\n", - "\n", - "\n", - "ephys.EphysRecording->ephys.EphysRecording.EphysFile\n", - "\n", - "\n", - "\n", - "\n", - "ephys.ClusteringTask\n", - "\n", - "\n", - "ephys.ClusteringTask\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "ephys.EphysRecording->ephys.ClusteringTask\n", - "\n", - "\n", - "\n", - "\n", - "session.Session->ephys.ProbeInsertion\n", - "\n", - "\n", - "\n", - "\n", - "ephys.ClusteringParamSet->ephys.ClusteringTask\n", - "\n", - "\n", - "\n", - "\n", - "ephys.ClusterQualityLabel\n", - "\n", - "\n", - "ephys.ClusterQualityLabel\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "ephys.ClusterQualityLabel->ephys.CuratedClustering.Unit\n", - "\n", - "\n", - "\n", - "\n", - "ephys.Clustering\n", - "\n", - "\n", - "ephys.Clustering\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "ephys.Clustering->ephys.Curation\n", - "\n", - "\n", - "\n", - "\n", - "ephys.ClusteringTask->ephys.Clustering\n", - "\n", + "session.Session->optogenetics.OptoProtocol\n", + "\n", "\n", "\n", "" ], "text/plain": [ - "" + "" ] }, - "execution_count": 4, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(\n", - " dj.Diagram(subject.Subject) + dj.Diagram(surgery.Implantation),\n", - " +dj.Diagram(session.Session) + dj.Diagram(Device) + dj.Diagram(optogenetics),\n", + " dj.Diagram(subject.Subject) + dj.Diagram(surgery.Implantation)\n", + " + dj.Diagram(session.Session) + dj.Diagram(Device) + dj.Diagram(optogenetics)\n", ")" ] }, @@ -616,150 +352,27 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "
\n", - "

subject

\n", - " \n", - "
\n", - "

subject_nickname

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
\n", - "

subject_birth_date

\n", - " \n", - "
\n", - "

subject_description

\n", - " \n", - "
\n", - " \n", - "

Total: 0

\n", - " " - ], - "text/plain": [ - "*subject subject_nickna sex subject_birth_ subject_descri\n", - "+---------+ +------------+ +-----+ +------------+ +------------+\n", - "\n", - " (Total: 0)" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "subject.Subject()" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "subject : varchar(8) \n", - "---\n", - "subject_nickname=\"\" : varchar(64) \n", - "sex : enum('M','F','U') \n", - "subject_birth_date : date \n", - "subject_description=\"\" : varchar(1024) \n", - "\n" - ] - } - ], + "outputs": [], "source": [ "print(subject.Subject.describe())" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "# \n", - "subject : varchar(8) # \n", - "---\n", - "subject_nickname=\"\" : varchar(64) # \n", - "sex : enum('M','F','U') # \n", - "subject_birth_date : date # \n", - "subject_description=\"\" : varchar(1024) # " - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "subject.Subject.heading" ] @@ -776,105 +389,9 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "

subject

\n", - " \n", - "
\n", - "

subject_nickname

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
\n", - "

subject_birth_date

\n", - " \n", - "
\n", - "

subject_description

\n", - " \n", - "
subject5U2023-01-01
\n", - " \n", - "

Total: 1

\n", - " " - ], - "text/plain": [ - "*subject subject_nickna sex subject_birth_ subject_descri\n", - "+----------+ +------------+ +-----+ +------------+ +------------+\n", - "subject5 U 2023-01-01 \n", - " (Total: 1)" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "subject.Subject.insert1(\n", " dict(subject=\"subject1\", subject_birth_date=\"2023-01-01\", sex=\"U\")\n", @@ -892,41 +409,18 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-> subject.Subject\n", - "session_datetime : datetime \n", - "\n" - ] - } - ], + "outputs": [], "source": [ "print(session.Session.describe())" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "# \n", - "subject : varchar(8) # \n", - "session_datetime : datetime # " - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "session.Session.heading" ] @@ -949,7 +443,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -958,93 +452,9 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "
\n", - "

subject

\n", - " \n", - "
\n", - "

session_datetime

\n", - " \n", - "
subject52023-01-01 00:00:00
\n", - " \n", - "

Total: 1

\n", - " " - ], - "text/plain": [ - "*subject *session_datet\n", - "+----------+ +------------+\n", - "subject5 2023-01-01 00:\n", - " (Total: 1)" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "session.Session.insert1(session_key)\n", "session.Session()" @@ -1059,97 +469,9 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "
\n", - "

subject

\n", - " \n", - "
\n", - "

session_datetime

\n", - " \n", - "
\n", - "

session_dir

\n", - " Path to the data directory for a session\n", - "
subject52023-01-01 00:00:00raw/subject5/session1
\n", - " \n", - "

Total: 1

\n", - " " - ], - "text/plain": [ - "*subject *session_datet session_dir \n", - "+----------+ +------------+ +------------+\n", - "subject5 2023-01-01 00: raw/subject5/s\n", - " (Total: 1)" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "session.SessionDirectory.insert1(\n", " dict(**session_key, session_dir=\"raw/subject5/session1\")\n", @@ -1170,97 +492,9 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " Represent a physical probe with unique identification\n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "
\n", - "

probe

\n", - " unique identifier for this model of probe (e.g. serial number)\n", - "
\n", - "

probe_type

\n", - " e.g. neuropixels_1.0\n", - "
\n", - "

probe_comment

\n", - " \n", - "
714000838neuropixels 1.0 - 3B
\n", - " \n", - "

Total: 1

\n", - " " - ], - "text/plain": [ - "*probe probe_type probe_comment \n", - "+-----------+ +------------+ +------------+\n", - "714000838 neuropixels 1. \n", - " (Total: 1)" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "Device.insert1(\n", " dict(\n", @@ -1293,148 +527,27 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "# Probe insertion implanted into an animal for a given session.\n", - "-> session.Session\n", - "insertion_number : tinyint unsigned \n", - "---\n", - "-> probe.Probe\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "surgery.CoordinateReference()" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "# Probe insertion implanted into an animal for a given session.\n", - "subject : varchar(8) # \n", - "session_datetime : datetime # \n", - "insertion_number : tinyint unsigned # \n", - "---\n", - "probe : varchar(32) # unique identifier for this model of probe (e.g. serial number)" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "surgery.Hemisphere()" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " Probe insertion implanted into an animal for a given session.\n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "
\n", - "

subject

\n", - " \n", - "
\n", - "

session_datetime

\n", - " \n", - "
\n", - "

insertion_number

\n", - " \n", - "
\n", - "

probe

\n", - " unique identifier for this model of probe (e.g. serial number)\n", - "
subject52023-01-01 00:00:001714000838
\n", - " \n", - "

Total: 1

\n", - " " - ], - "text/plain": [ - "*subject *session_datet *insertion_num probe \n", - "+----------+ +------------+ +------------+ +-----------+\n", - "subject5 2023-01-01 00: 1 714000838 \n", - " (Total: 1)" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "surgery.BrainRegion.insert1(\n", " dict(region_acronym=\"dHP\", region_name=\"Dorsal Hippocampus\")\n", @@ -1562,225 +675,27 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " Ephys recording from a probe insertion for a given session.\n", - "
\n", - " \n", - " \n", - " \n", - "
\n", - "

subject

\n", - " \n", - "
\n", - "

session_datetime

\n", - " \n", - "
\n", - "

insertion_number

\n", - " \n", - "
\n", - "

electrode_config_hash

\n", - " \n", - "
\n", - "

acq_software

\n", - " \n", - "
\n", - "

sampling_rate

\n", - " (Hz)\n", - "
\n", - "

recording_datetime

\n", - " datetime of the recording from this probe\n", - "
\n", - "

recording_duration

\n", - " (seconds) duration of the recording from this probe\n", - "
\n", - " \n", - "

Total: 0

\n", - " " - ], - "text/plain": [ - "*subject *session_datet *insertion_num electrode_conf acq_software sampling_rate recording_date recording_dura\n", - "+---------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+\n", - "\n", - " (Total: 0)" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ephys.EphysRecording()" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " Paths of files of a given EphysRecording round.\n", - "
\n", - " \n", - " \n", - " \n", - "
\n", - "

subject

\n", - " \n", - "
\n", - "

session_datetime

\n", - " \n", - "
\n", - "

insertion_number

\n", - " \n", - "
\n", - "

file_path

\n", - " filepath relative to root data directory\n", - "
\n", - " \n", - "

Total: 0

\n", - " " - ], - "text/plain": [ - "*subject *session_datet *insertion_num *file_path \n", - "+---------+ +------------+ +------------+ +-----------+\n", - "\n", - " (Total: 0)" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ephys.EphysRecording.EphysFile()" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "EphysRecording: 100%|██████████| 1/1 [00:01<00:00, 1.25s/it]\n" - ] - } - ], + "outputs": [], "source": [ "ephys.EphysRecording.populate(session_key, display_progress=True)" ] @@ -1795,218 +710,18 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " Ephys recording from a probe insertion for a given session.\n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "

subject

\n", - " \n", - "
\n", - "

session_datetime

\n", - " \n", - "
\n", - "

insertion_number

\n", - " \n", - "
\n", - "

electrode_config_hash

\n", - " \n", - "
\n", - "

acq_software

\n", - " \n", - "
\n", - "

sampling_rate

\n", - " (Hz)\n", - "
\n", - "

recording_datetime

\n", - " datetime of the recording from this probe\n", - "
\n", - "

recording_duration

\n", - " (seconds) duration of the recording from this probe\n", - "
subject52023-01-01 00:00:0018d4cc6d8-a02d-42c8-bf27-7459c39ea0eeSpikeGLX30000.02018-07-03 20:32:28338.666
\n", - " \n", - "

Total: 1

\n", - " " - ], - "text/plain": [ - "*subject *session_datet *insertion_num electrode_conf acq_software sampling_rate recording_date recording_dura\n", - "+----------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+\n", - "subject5 2023-01-01 00: 1 8d4cc6d8-a02d- SpikeGLX 30000.0 2018-07-03 20: 338.666 \n", - " (Total: 1)" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ephys.EphysRecording()" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " Paths of files of a given EphysRecording round.\n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "
\n", - "

subject

\n", - " \n", - "
\n", - "

session_datetime

\n", - " \n", - "
\n", - "

insertion_number

\n", - " \n", - "
\n", - "

file_path

\n", - " filepath relative to root data directory\n", - "
subject52023-01-01 00:00:001raw/subject5/session1/probe_1/npx_g0_t0.imec.ap.meta
\n", - " \n", - "

Total: 1

\n", - " " - ], - "text/plain": [ - "*subject *session_datet *insertion_num *file_path \n", - "+----------+ +------------+ +------------+ +------------+\n", - "subject5 2023-01-01 00: 1 raw/subject5/s\n", - " (Total: 1)" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ephys.EphysRecording.EphysFile()" ] @@ -2029,131 +744,18 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "# Parameter set to be used in a clustering procedure\n", - "paramset_idx : smallint # \n", - "---\n", - "clustering_method : varchar(16) # \n", - "paramset_desc : varchar(128) # \n", - "param_set_hash : uuid # \n", - "params : longblob # dictionary of all applicable parameters" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ephys.ClusteringParamSet.heading" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " Parameter set to be used in a clustering procedure\n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "

paramset_idx

\n", - " \n", - "
\n", - "

clustering_method

\n", - " \n", - "
\n", - "

paramset_desc

\n", - " \n", - "
\n", - "

param_set_hash

\n", - " \n", - "
\n", - "

params

\n", - " dictionary of all applicable parameters\n", - "
0kilosort2Spike sorting using Kilosort2de78cee1-526f-319e-b6d5-8a2ba04963d8=BLOB=
\n", - " \n", - "

Total: 1

\n", - " " - ], - "text/plain": [ - "*paramset_idx clustering_met paramset_desc param_set_hash params \n", - "+------------+ +------------+ +------------+ +------------+ +--------+\n", - "0 kilosort2 Spike sorting de78cee1-526f- =BLOB= \n", - " (Total: 1)" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# insert clustering task manually\n", "params_ks = {\n", @@ -2203,27 +805,9 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "# Manual table for defining a clustering task ready to be run\n", - "subject : varchar(8) # \n", - "session_datetime : datetime # \n", - "insertion_number : tinyint unsigned # \n", - "paramset_idx : smallint # \n", - "---\n", - "clustering_output_dir=\"\" : varchar(255) # clustering output directory relative to the clustering root data directory\n", - "task_mode=\"load\" : enum('load','trigger') # 'load': load computed analysis results, 'trigger': trigger computation" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ephys.ClusteringTask.heading" ] @@ -2244,7 +828,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -2268,17 +852,9 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Clustering: 100%|██████████| 1/1 [00:00<00:00, 3.27it/s]\n" - ] - } - ], + "outputs": [], "source": [ "ephys.Clustering.populate(session_key, display_progress=True)" ] @@ -2296,38 +872,16 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "# Manual curation procedure\n", - "subject : varchar(8) # \n", - "session_datetime : datetime # \n", - "insertion_number : tinyint unsigned # \n", - "paramset_idx : smallint # \n", - "curation_id : int # \n", - "---\n", - "curation_time : datetime # time of generation of this set of curated clustering results\n", - "curation_output_dir : varchar(255) # output directory of the curated results, relative to root data directory\n", - "quality_control : tinyint # has this clustering result undergone quality control?\n", - "manual_curation : tinyint # has manual curation been performed on this clustering result?\n", - "curation_note=\"\" : varchar(2000) # " - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ephys.Curation.heading" ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -2346,21 +900,9 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "CuratedClustering: 0%| | 0/1 [00:00" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "plt.plot(lfp_average)\n", "plt.title(\"Average LFP Waveform for Insertion 1\")\n", @@ -2455,7 +986,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -2469,20 +1000,9 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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V++//77mzJnjdd6MGTO0ZcsWuVyunuk8gG5HgQMgJEydOlW//vWvlZ6erqVLl2rgwIGKj4/XzTffrPT0dC1btkyVlZXasWOHJOk///M/lZmZqYceekhnnHGGMjMz9b//+79at26d8vPz/bZx4MAB9e/fXwkJCea2kpISDR48WP/yL/+i1NRUZWZm6o477vA6LykpSU1NTV4fvwEIbRQ4AELClClTzJ/79++vuLg4TZ482dyWmJgoSaqoqJB08svC69atM7/TExkZqTPOOEOStG/fPr9tHDt2THa73euL0BdddJFSU1M1evRo3XDDDXr55ZfNd4ncIiIiJKnVdgChiwIHQEgYMGCA13Obzea1zV2UtLS0SJLq6uqUnZ2tvLw8r0dBQUGrj5jc4uPj1dDQoKamJnNbVFSUtm/frldffVXDhw/XsmXLNHXqVFVXV5vHVFVVSZKGDh3aLX0F0PMocAD0SWeeeaZ27dqlUaNGaezYsV6PwYMH+z1n2rRpkqTdu3d7bQ8LC9O8efP06KOPaseOHdq/f78++eQTc//OnTs1cuRIxcfH91h/AHQvChwAfdKtt96qqqoqXXPNNfryyy+1b98+ffDBB7rxxhvV3Nzs95yhQ4fqzDPP1MaNG81t7733np566inl5eXpwIEDevHFF9XS0qJx48aZx3z22WeaP39+j/cJQPehwAHQJyUlJWnTpk1qbm7W/PnzNXnyZC1ZskQxMTHq1y/wS9tNN92kl19+2XweExOjN998UxdccIHGjx+vZ599Vq+++qomTpwoSWpsbNRbb72lm2++ucf7BKD7cKM/AKeVY8eOady4cXr99dc1c+bMdo9/5plntHr1an344Ye9EB2A7sI7OABOKxEREXrxxRfbvCGgpwEDBujpp5/u4agAdDfewQEAAJbDOzgAAMByKHAAAIDlUOAAAADLocABAACWQ4EDAAAshwIHAABYDgUOAACwHAocAABgORQ4AADAcv4/4rIQegvxjTEAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "x = np.hstack(unit_spiketimes)\n", "y = np.hstack([np.full_like(s, u) for u, s in zip(units, unit_spiketimes)])\n", @@ -2501,7 +1021,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -2513,235 +1033,18 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'subject': 'subject5',\n", - " 'session_datetime': datetime.datetime(2023, 1, 1, 0, 0),\n", - " 'insertion_number': 1,\n", - " 'paramset_idx': 0,\n", - " 'curation_id': 1,\n", - " 'unit': 15,\n", - " 'electrode_config_hash': UUID('8d4cc6d8-a02d-42c8-bf27-7459c39ea0ee'),\n", - " 'probe_type': 'neuropixels 1.0 - 3A',\n", - " 'electrode': 92,\n", - " 'cluster_quality_label': 'noise',\n", - " 'spike_count': 292,\n", - " 'spike_times': array([ 1.02606667, 1.19973333, 1.5044 , 1.52283333,\n", - " 1.86786667, 1.8688 , 1.8806 , 1.88553333,\n", - " 2.0581 , 2.76826667, 2.89186667, 2.9311 ,\n", - " 4.8753 , 5.2964 , 7.02266667, 9.65273333,\n", - " 9.81373333, 10.22443333, 11.96146667, 15.07173333,\n", - " 15.08983333, 15.78326667, 21.30936667, 22.7549 ,\n", - " 23.5582 , 23.6582 , 23.87043333, 24.16726667,\n", - " 24.25456667, 24.2671 , 25.0249 , 27.89116667,\n", - " 35.3036 , 36.0171 , 40.00396667, 40.0873 ,\n", - " 41.015 , 42.70086667, 45.8682 , 47.9291 ,\n", - " 48.90843333, 49.37996667, 49.39596667, 49.4058 ,\n", - " 49.65926667, 49.68203333, 49.74273333, 51.52213333,\n", - " 52.41486667, 55.27623333, 55.54576667, 55.81213333,\n", - " 56.0544 , 56.29426667, 56.36896667, 56.3743 ,\n", - " 56.47403333, 56.7147 , 56.71683333, 60.764 ,\n", - " 61.5317 , 61.54046667, 61.7721 , 62.10233333,\n", - " 62.10726667, 62.2118 , 62.81173333, 63.15896667,\n", - " 65.11126667, 65.495 , 67.18373333, 77.59256667,\n", - " 79.2709 , 80.46186667, 82.1178 , 85.6568 ,\n", - " 86.52613333, 89.12126667, 89.46963333, 89.64663333,\n", - " 90.19103333, 92.0923 , 92.99573333, 93.36923333,\n", - " 93.68086667, 95.2097 , 97.96296667, 98.1067 ,\n", - " 98.69713333, 99.26963333, 99.28013333, 101.04216667,\n", - " 101.2002 , 101.3843 , 101.3975 , 101.40656667,\n", - " 102.00996667, 102.07066667, 102.17033333, 103.5861 ,\n", - " 104.68523333, 104.71643333, 105.2595 , 105.5166 ,\n", - " 105.54723333, 107.0848 , 109.82746667, 110.14853333,\n", - " 110.92203333, 111.2481 , 112.26 , 113.09466667,\n", - " 113.09846667, 113.1005 , 113.33513333, 118.842 ,\n", - " 118.96803333, 119.32606667, 119.64796667, 119.96313333,\n", - " 119.97343333, 120.19573333, 120.19693333, 120.21163333,\n", - " 121.1879 , 121.233 , 121.9611 , 122.40203333,\n", - " 123.1745 , 124.6798 , 124.68196667, 124.97396667,\n", - " 125.2205 , 125.87786667, 125.957 , 125.95803333,\n", - " 127.7149 , 136.8558 , 136.86413333, 138.49966667,\n", - " 139.49553333, 139.93163333, 139.9819 , 142.57013333,\n", - " 142.70053333, 143.343 , 143.40796667, 148.46066667,\n", - " 148.47023333, 148.89893333, 152.79766667, 153.2198 ,\n", - " 153.39096667, 153.62456667, 153.82253333, 153.82693333,\n", - " 153.8364 , 153.98863333, 155.4047 , 157.52706667,\n", - " 157.53233333, 163.1177 , 163.12516667, 163.46916667,\n", - " 163.69406667, 163.7119 , 166.6946 , 166.7006 ,\n", - " 170.78053333, 170.7891 , 170.79653333, 180.36596667,\n", - " 180.43416667, 182.72556667, 182.73706667, 182.76576667,\n", - " 184.24713333, 185.5131 , 185.7329 , 186.4198 ,\n", - " 186.48443333, 186.72036667, 186.95926667, 187.1402 ,\n", - " 187.16683333, 189.4036 , 193.27583333, 195.40946667,\n", - " 200.2427 , 203.03376667, 203.04076667, 205.84043333,\n", - " 206.1151 , 207.3402 , 210.95773333, 217.73583333,\n", - " 220.35 , 221.1235 , 227.7719 , 227.946 ,\n", - " 228.02456667, 231.2108 , 231.80003333, 233.48253333,\n", - " 236.8342 , 241.78993333, 243.6285 , 245.61546667,\n", - " 245.95573333, 246.04586667, 246.06206667, 247.12933333,\n", - " 248.17143333, 249.16873333, 251.13613333, 252.4734 ,\n", - " 253.2007 , 254.50943333, 255.22563333, 255.2502 ,\n", - " 255.5314 , 255.53423333, 255.7684 , 256.81196667,\n", - " 256.99066667, 256.9928 , 257.0588 , 257.06206667,\n", - " 257.0801 , 257.15523333, 257.27496667, 257.291 ,\n", - " 257.99483333, 259.6955 , 259.7061 , 259.71746667,\n", - " 263.59203333, 266.7052 , 266.70576667, 267.0376 ,\n", - " 267.35913333, 267.87316667, 268.36156667, 268.94556667,\n", - " 269.01516667, 269.63623333, 269.88553333, 270.16176667,\n", - " 270.39123333, 273.41633333, 273.43763333, 274.2084 ,\n", - " 276.36113333, 278.68053333, 279.50626667, 281.0338 ,\n", - " 281.9869 , 283.69233333, 285.0663 , 287.0179 ,\n", - " 289.5541 , 291.78113333, 292.16493333, 292.85823333,\n", - " 292.8702 , 295.93823333, 295.9514 , 295.9679 ,\n", - " 296.00623333, 296.0391 , 296.04393333, 298.9717 ,\n", - " 299.01556667, 299.0859 , 299.7463 , 306.65153333,\n", - " 307.38243333, 307.7771 , 311.80163333, 311.83026667,\n", - " 313.37816667, 314.948 , 317.10726667, 317.98363333,\n", - " 319.25856667, 321.01533333, 321.16426667, 330.15466667,\n", - " 330.73633333, 330.91536667, 332.83243333, 332.86266667]),\n", - " 'spike_sites': array([92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,\n", - " 92, 92, 92]),\n", - " 'spike_depths': array([ 917.68931051, 1002.6141662 , 893.1431346 , 912.33104931,\n", - " 902.87693568, 872.31105976, 875.26191363, 922.47333477,\n", - " 949.16252597, 801.99946108, 858.01692677, 921.7837487 ,\n", - " 897.25087548, 874.42894267, 914.89717798, 861.55454858,\n", - " 904.22082978, 900.71556806, 848.89880207, 871.55857472,\n", - " 849.40502424, 887.21898509, 957.57407264, 857.52950094,\n", - " 941.13752662, 931.03217518, 898.99527832, 962.92735677,\n", - " 915.56550832, 922.14542481, 894.27941318, 874.31413165,\n", - " 847.78323382, 960.73651726, 902.09721294, 899.97936727,\n", - " 936.76767365, 908.68471877, 931.97562897, 892.8563381 ,\n", - " 940.49920044, 925.87119487, 915.55667036, 786.01763842,\n", - " 867.61268444, 1007.66794229, 867.43220284, 941.51843395,\n", - " 955.66765617, 970.36344663, 894.82624021, 915.00922015,\n", - " 889.99169864, 878.28545616, 850.33714087, 928.43421969,\n", - " 939.52352775, 962.60336897, 903.36858487, 896.37548128,\n", - " 963.73199174, 789.82843798, 920.43722936, 828.40065648,\n", - " 943.98810503, 835.85766882, 841.76946105, 937.36801859,\n", - " 896.49642885, 967.15672796, 918.39713395, 906.36795986,\n", - " 919.40916519, 875.89694182, 908.3418033 , 972.05674531,\n", - " 927.07275902, 940.77150366, 918.04680759, 981.59648505,\n", - " 940.32229921, 938.36235287, 849.04038544, 893.3058527 ,\n", - " 913.09883832, 912.35625832, 989.78190796, 879.34780591,\n", - " 928.81644221, 892.97322993, 898.70158737, 906.91746418,\n", - " 946.42526162, 914.08702993, 859.34034454, 907.18338275,\n", - " 989.30138781, 926.06769221, 838.08269605, 929.50654863,\n", - " 827.39586238, 927.36188021, 910.17661856, 935.41822322,\n", - " 885.63661049, 914.48018584, 895.93753147, 911.13864214,\n", - " 881.60996972, 938.20931097, 1015.75963624, 938.56188955,\n", - " 909.09481672, 867.81417174, 912.15092578, 907.0519042 ,\n", - " 913.43873474, 913.23570042, 983.89208176, 899.44708324,\n", - " 910.55603061, 950.67199374, 916.27718038, 886.16272254,\n", - " 975.57372749, 882.46972449, 908.99151954, 893.41760103,\n", - " 938.6441373 , 885.19738826, 947.44382361, 918.75218854,\n", - " 942.78614663, 968.52291541, 915.78656008, 989.00683215,\n", - " 855.88223229, 976.62553529, 919.25644927, 958.63970635,\n", - " 934.03540249, 926.45247121, 961.36373273, 926.55709697,\n", - " 896.6375551 , 897.47943897, 907.74803191, 954.04187795,\n", - " 882.10295293, 1004.09432843, 892.73726557, 846.13198111,\n", - " 929.42733278, 894.24531402, 921.97988827, 860.601478 ,\n", - " 912.55635483, 997.22339505, 985.88635074, 938.45775184,\n", - " 944.63766895, 942.18376197, 984.23087354, 922.40370934,\n", - " 965.34813049, 921.32552393, 969.81367405, 917.74503135,\n", - " 833.76599428, 894.97963584, 878.49090123, 864.91049261,\n", - " 985.04808527, 927.18133162, 844.64492657, 913.88047009,\n", - " 928.03561194, 930.79686847, 930.13055052, 839.5761256 ,\n", - " 943.37875897, 956.44476838, 931.39781252, 989.20932655,\n", - " 1006.29928459, 942.03146682, 901.72518137, 932.53880871,\n", - " 935.96943144, 925.53161728, 874.67439465, 951.48997974,\n", - " 960. , 874.14717137, 921.49562818, 927.26749515,\n", - " 926.41158172, 896.61260138, 938.6103718 , 959.64828575,\n", - " 935.22630845, 955.63103852, 950.8495808 , 888.45366602,\n", - " 912.06814044, 862.81354284, 875.05713562, 875.4100677 ,\n", - " 923.5426824 , 839.78562837, 889.74471045, 867.8332007 ,\n", - " 933.88736839, 868.07155277, 883.34738777, 913.12741673,\n", - " 915.77804797, 962.99775026, 932.9485248 , 930.37415413,\n", - " 944.21567324, 776.98228821, 936.37311401, 941.58975024,\n", - " 941.55965056, 864.51860126, 930.38329188, 912.78502395,\n", - " 947.32712079, 1011.13494089, 937.99624501, 916.59535343,\n", - " 873.82437868, 901.49630725, 846.11674006, 998.64016883,\n", - " 978.32805846, 876.27655411, 905.78089027, 827.25234516,\n", - " 1002.18813125, 898.65291068, 920.350764 , 874.17361952,\n", - " 938.70381451, 945.81921622, 874.98921047, 915.97985629,\n", - " 920.14460695, 965.25282342, 977.01542391, 863.18856888,\n", - " 907.62393082, 906.19584978, 851.37149337, 956.85804045,\n", - " 969.94521463, 912.02588788, 953.60047228, 819.53086148,\n", - " 911.99611929, 845.66361521, 962.24964999, 921.43338903,\n", - " 982.04267741, 931.1841306 , 896.56744607, 926.6958426 ,\n", - " 841.56778533, 961.77261899, 943.15601639, 786.18136435,\n", - " 864.98026608, 950.29718422, 930.39820918, 983.57154574,\n", - " 909.55498598, 925.76591071, 906.07808244, 964.41923255,\n", - " 911.17276099, 919.34859876, 906.11721418, 905.96844161,\n", - " 911.53683431, 958.97512493, 953.15452691, 889.09240389,\n", - " 957.98473324, 993.29170991, 928.99175736, 908.10563408]),\n", - " 'peak_electrode_waveform': array([-503.33636558, -502.734375 , -501.32973031, -500.34246575,\n", - " -499.73244863, -498.66491866, -494.41887842, -491.6015625 ,\n", - " -486.59300086, -481.55233305, -474.36055223, -466.79152397,\n", - " -458.98169949, -451.10766267, -442.41491866, -431.03328339,\n", - " -420.81549658, -410.91074486, -402.25010702, -395.59610445,\n", - " -389.25513699, -384.02985873, -381.67005565, -379.81592466,\n", - " -378.79655394, -380.26541096, -384.54355736, -392.41759418,\n", - " -401.11033818, -410.67797517, -420.13324058, -430.6640625 ,\n", - " -442.34267979, -452.71297089, -461.42979452, -469.67305223,\n", - " -478.64672517, -486.40036387, -493.83294092, -498.87360873,\n", - " -504.82127568, -509.58101455, -511.55554366, -514.19627568,\n", - " -515.77750428, -516.41160103, -517.3828125 , -518.16941353,\n", - " -517.26241438, -518.46639555, -517.76808647, -517.3828125 ,\n", - " -516.61226455, -514.32470034, -512.5187286 , -511.66791524,\n", - " -510.65657106, -507.74293664, -504.84535531, -503.02333048,\n", - " -501.93172089, -499.3552012 , -498.77729024, -495.78339041,\n", - " -495.42219606, -495.78339041, -494.30650685, -492.30789812,\n", - " -491.83433219, -492.14736729, -490.16481164, -489.69124572,\n", - " -488.84043236, -488.5354238 , -489.24175942, -487.68461045,\n", - " -486.48062928, -485.50941781, -484.23319777, -484.24925086,\n", - " -484.26530394, -483.3984375 ])}" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "unit_data" ] }, { "cell_type": "code", - "execution_count": 40, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "sampling_rate = (ephys.EphysRecording & insert_key).fetch1(\n", " \"sampling_rate\"\n", diff --git a/notebooks/tutorial_pipeline.py b/notebooks/tutorial_pipeline.py index ddf5c58..e6588f2 100644 --- a/notebooks/tutorial_pipeline.py +++ b/notebooks/tutorial_pipeline.py @@ -1,13 +1,13 @@ -import pathlib +import os import datajoint as dj from element_animal import subject, surgery from element_animal.subject import Subject # Dependency for session schema from element_animal.surgery import Implantation # Dependency for optogenetics schema from element_lab import lab from element_lab.lab import Lab, Project, Protocol, Source, User +from element_optogenetics import optogenetics from element_session import session_with_id as session from element_session.session_with_id import Session -from element_optogenetics import optogenetics if "custom" not in dj.config: @@ -25,9 +25,29 @@ # Activate schemas lab.activate(db_prefix + "lab") subject.activate(db_prefix + "subject", linking_module=__name__) +surgery.activate(db_prefix + "surgery", linking_module=__name__) Experimenter = User session.activate(db_prefix + "session", linking_module=__name__) +@lab.schema +class Device(dj.Lookup): + """Table for managing lab devices. + + Attributes: + device ( varchar(32) ): Device short name. + modality ( varchar(64) ): Modality for which this device is used. + description ( varchar(256), optional ): Description of device. + """ + + definition = """ + device : varchar(32) + --- + modality : varchar(64) + description='' : varchar(256) + """ + contents = [ + ["OPTG_4", "Optogenetics", "Doric Pulse Sequence Generator"], + ] optogenetics.activate(db_prefix + "optogenetics", linking_module=__name__) From cfa8a370a8869f271063500ebeffb7326c926534 Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Wed, 17 Jan 2024 05:44:53 -0600 Subject: [PATCH 05/26] Complete optogenetics tutorial notebook --- notebooks/tutorial.ipynb | 420 +++++++++------------------------------ 1 file changed, 99 insertions(+), 321 deletions(-) diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb index b80539a..c3e860c 100644 --- a/notebooks/tutorial.ipynb +++ b/notebooks/tutorial.ipynb @@ -337,8 +337,7 @@ "metadata": {}, "source": [ "As evident from the diagram, this data pipeline encompasses tables associated with\n", - "recording and probe metadata, results of clustering, and optional curation of clustering\n", - "results. A few tables, such as `subject.Subject` or `session.Session`,\n", + "optogenetic stimulation parameters, protocol, and events. A few tables, such as `subject.Subject` or `session.Session`,\n", "while important for a complete pipeline, fall outside the scope of the `element-optogenetics`\n", "tutorial, and will therefore, not be explored extensively here. The primary focus of\n", "this tutorial will be on the `optogenetics` schemas.\n", @@ -394,7 +393,12 @@ "outputs": [], "source": [ "subject.Subject.insert1(\n", - " dict(subject=\"subject1\", subject_birth_date=\"2023-01-01\", sex=\"U\")\n", + " dict(\n", + " subject=\"subject1\",\n", + " sex=\"F\",\n", + " subject_birth_date=\"2020-01-01\",\n", + " subject_description=\"Optogenetic pilot subject\",\n", + " )\n", ")\n", "subject.Subject()" ] @@ -461,10 +465,12 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Every experimental session produces a set of data files. The purpose of the `SessionDirectory` table is to locate these files. It references a directory path relative to a root directory, defined in `dj.config[\"custom\"]`. More information about `dj.config` is provided in the [documentation](https://datajoint.com/docs/elements/user-guide/)." + "As the Diagram indicates, the `OptoProtocol` table requires an entry in both `Session`\n", + "and `Device` tables. Let's insert into the `Device` table." ] }, { @@ -473,37 +479,31 @@ "metadata": {}, "outputs": [], "source": [ - "session.SessionDirectory.insert1(\n", - " dict(**session_key, session_dir=\"raw/subject5/session1\")\n", + "Device.insert1(\n", + " dict(\n", + " device=\"OPTG_8\",\n", + " modality=\"Optogenetics\",\n", + " description=\"8 channel pulse sequence device\",\n", + " )\n", ")\n", - "session.SessionDirectory()" + "Device()" ] }, { - "attachments": {}, - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "As the Diagram indicates, the tables in the `probe` schemas need to\n", - "contain data before the tables in the `ephys` schema accept any data. Let's\n", - "start by inserting into `probe.Probe`, a table containing metadata about a\n", - "multielectrode probe. " + "print(surgery.Implantation.describe())" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "Device.insert1(\n", - " dict(\n", - " device=\"OPTG_8\",\n", - " modality=\"Optogenetics\",\n", - " description=\"8 channel pulse sequence device\",\n", - " )\n", - ")\n", - "Device()" + "The `surgery.Implantation` table's attribute includes the `User` table. Let's insert\n", + "into the `User` table." ] }, { @@ -521,8 +521,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The probe metadata is used by the downstream `ProbeInsertion` table which we\n", - "insert data into in the cells below:" + "The `Implantation` table's attributes includes the `CoordinateReference` and `Hemisphere` tables. Let's view the contents of these lookup tables, which have default contents." ] }, { @@ -543,6 +542,13 @@ "surgery.Hemisphere()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Insert a new entry for the location of the optogenetics probe." + ] + }, { "cell_type": "code", "execution_count": null, @@ -565,29 +571,28 @@ " dict(\n", " subject=\"subject1\",\n", " implant_date=\"2022-04-01 12:13:14\",\n", - " implate_type=\"opto\",\n", + " implant_type=\"opto\",\n", " target_region=\"dHP\",\n", " target_hemisphere=\"left\",\n", " surgeon=\"User1\",\n", " )\n", ")\n", - "\n", "surgery.Implantation.Coordinate.insert1(\n", " dict(\n", " subject=\"subject1\",\n", " implant_date=\"2022-04-01 12:13:14\",\n", - " implate_type=\"opto\",\n", + " implant_type=\"opto\",\n", " target_region=\"dHP\",\n", " target_hemisphere=\"left\",\n", - " ap=\"-7.9\",\n", + " ap=\"-7.9\", # [mm] anterior-posterior distance\n", " ap_ref=\"bregma\",\n", - " ml=\"-1.8\",\n", + " ml=\"-1.8\", # [mm] medial axis distance\n", " ml_ref=\"bregma\",\n", - " dv=\"5\",\n", + " dv=\"5\", # [mm] dorso-ventral axis distance\n", " dv_ref=\"skull_surface\",\n", - " theta=\"11.5\",\n", - " phi=\"0\",\n", - " beta=None,\n", + " theta=\"11.5\", # [0, 180] degree rotation about ml-axis relative to z\n", + " phi=\"0\", # [0, 360] degree rotation about dv-axis relative to x\n", + " beta=None, # [-180, 180] degree rotation about shank relative to anterior\n", " )\n", ")" ] @@ -617,6 +622,13 @@ "surgery.Implantation.Coordinate()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We'll add information to describe the stimulus, including waveform shape and stimulation parameters." + ] + }, { "cell_type": "code", "execution_count": null, @@ -653,93 +665,15 @@ " frequency=1,\n", " duration=241,\n", " )\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Populate electrophysiology recording metadata**\n", - "\n", - "In the upcoming cells, the `.populate()` method will automatically extract and store the\n", - "recording metadata for each experimental session in the `ephys.EphysRecording` table and its part table `ephys.EphysRecording.EphysFile`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ephys.EphysRecording()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ephys.EphysRecording.EphysFile()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ephys.EphysRecording.populate(session_key, display_progress=True)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's view the information was entered into each of these tables:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ephys.EphysRecording()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ephys.EphysRecording.EphysFile()" + ")\n", + "optogenetics.OptoStimParams()" ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "### **Run the Clustering Task**\n", - "\n", - "We're almost ready to spike sort the data with `kilosort`. An important step before\n", - "processing is managing the parameters which will be used in that step. To do so, we will\n", - "define the kilosort parameters in a dictionary and insert them into a DataJoint table\n", - "`ClusteringParamSet`. This table keeps track of all combinations of your spike sorting\n", - "parameters. You can choose which parameters are used during processing in a later step.\n", - "\n", - "Let's view the attributes and insert data into `ephys.ClusteringParamSet`." + "Next, we'll describe the session in which these parameters are used in `OptoProtocol`." ] }, { @@ -748,59 +682,27 @@ "metadata": {}, "outputs": [], "source": [ - "ephys.ClusteringParamSet.heading" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# insert clustering task manually\n", - "params_ks = {\n", - " \"fs\": 30000,\n", - " \"fshigh\": 150,\n", - " \"minfr_goodchannels\": 0.1,\n", - " \"Th\": [10, 4],\n", - " \"lam\": 10,\n", - " \"AUCsplit\": 0.9,\n", - " \"minFR\": 0.02,\n", - " \"momentum\": [20, 400],\n", - " \"sigmaMask\": 30,\n", - " \"ThPr\": 8,\n", - " \"spkTh\": -6,\n", - " \"reorder\": 1,\n", - " \"nskip\": 25,\n", - " \"GPU\": 1,\n", - " \"Nfilt\": 1024,\n", - " \"nfilt_factor\": 4,\n", - " \"ntbuff\": 64,\n", - " \"whiteningRange\": 32,\n", - " \"nSkipCov\": 25,\n", - " \"scaleproc\": 200,\n", - " \"nPCs\": 3,\n", - " \"useRAM\": 0,\n", - "}\n", - "ephys.ClusteringParamSet.insert_new_params(\n", - " clustering_method=\"kilosort2\",\n", - " paramset_idx=0,\n", - " params=params_ks,\n", - " paramset_desc=\"Spike sorting using Kilosort2\",\n", + "optogenetics.OptoProtocol.insert1(\n", + " dict(\n", + " subject=\"subject1\",\n", + " session_id=\"1\",\n", + " protocol_id=\"1\",\n", + " opto_params_id=\"1\",\n", + " implant_date=\"2022-04-01 12:13:14\",\n", + " implant_type=\"opto\",\n", + " target_region=\"dHP\",\n", + " target_hemisphere=\"left\",\n", + " device=\"OPTG_4\",\n", + " )\n", ")\n", - "ephys.ClusteringParamSet()" + "optogenetics.OptoProtocol()" ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "DataJoint uses a `ClusteringTask` table to\n", - "manage which `EphysRecording` and `ClusteringParamSet` should be used during processing. \n", - "\n", - "This table is important for defining several important aspects of\n", - "downstream processing. Let's view the attributes to get a better understanding. " + "We can describe the timing of these stimulations in `OptoEvent`." ] }, { @@ -809,7 +711,16 @@ "metadata": {}, "outputs": [], "source": [ - "ephys.ClusteringTask.heading" + "optogenetics.OptoEvent.insert1(\n", + " dict(\n", + " subject=\"subject1\",\n", + " session_id=1,\n", + " protocol_id=1,\n", + " stim_start_time=241,\n", + " stim_end_time=482,\n", + " )\n", + ")\n", + "optogenetics.OptoEvent()" ] }, { @@ -817,13 +728,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The `ClusteringTask` table contains two important attributes: \n", - "+ `paramset_idx` - Allows the user to choose the parameter set with which you want to\n", - " run spike sorting.\n", - "+ `task_mode` - Can be set to `load` or `trigger`. When set to `load`, running the\n", - " Clustering step initiates a search for existing output files of the spike sorting\n", - " algorithm defined in `ClusteringParamSet`. When set to `trigger`, the processing step\n", - " will run spike sorting on the raw data." + "We can insert a second set of timing information for the stimulation." ] }, { @@ -832,42 +737,26 @@ "metadata": {}, "outputs": [], "source": [ - "ephys.ClusteringTask.insert1(\n", + "optogenetics.OptoEvent.insert1(\n", " dict(\n", - " session_key,\n", - " insertion_number=1,\n", - " paramset_idx=0,\n", - " task_mode=\"load\", # load or trigger\n", - " clustering_output_dir=\"processed/subject5/session1/probe_1/kilosort2-5_1\",\n", + " subject=\"subject1\",\n", + " session_id=1,\n", + " protocol_id=1,\n", + " stim_start_time=543,\n", + " stim_end_time=797,\n", " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's call populate on the `Clustering` table which checks for kilosort results since `task_mode=load`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ephys.Clustering.populate(session_key, display_progress=True)" + ")\n", + "optogenetics.OptoEvent()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### **Curate the results (Optional)**\n", + "### **Query and view data**\n", "\n", - "While spike sorting is completed in the above step, you can optionally curate\n", - "the output of image processing using the `Curation` table. For this demo, we\n", - "will simply use the results of the spike sorting output from the `Clustering` task." + "Queries allow you to view the contents of the database. The simplest query is the\n", + "instance of the table class." ] }, { @@ -876,26 +765,14 @@ "metadata": {}, "outputs": [], "source": [ - "ephys.Curation.heading" + "optogenetics.OptoEvent()" ] }, { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "clustering_key = (ephys.ClusteringTask & session_key).fetch1(\"KEY\")\n", - "ephys.Curation().create1_from_clustering_task(clustering_key)" - ] - }, - { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Once the `Curation` table receives an entry, we can populate the remaining\n", - "tables in the workflow including `CuratedClustering`, `WaveformSet`, and `LFP`. " + "With the `&` operator, we will restrict the contents of the `OptoEvent` table to those entries with a `stim_start_time` of 543." ] }, { @@ -904,9 +781,7 @@ "metadata": {}, "outputs": [], "source": [ - "ephys.CuratedClustering.populate(session_key, display_progress=True)\n", - "ephys.LFP.populate(session_key, display_progress=True)\n", - "ephys.WaveformSet.populate(session_key, display_progress=True)" + "optogenetics.OptoEvent & \"stim_start_time=543\"" ] }, { @@ -914,25 +789,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now that we've populated the tables in this DataJoint pipeline, there are one of\n", - "several next steps. If you have an existing pipeline for\n", - "aligning waveforms to behavior data or other stimuli, you can easily\n", - "invoke `element-event` or define your custom DataJoint tables to extend the\n", - "pipeline." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Visualize the results**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this tutorial, we will do some exploratory analysis by fetching the data from the database and creating a few plots." + "DataJoint queries can be a highly flexible tool with several [operators](https://datajoint.com/docs/core/concepts/query-lang/operators/). The next operator we will explore is `join` which combines matching information from tables." ] }, { @@ -941,7 +798,7 @@ "metadata": {}, "outputs": [], "source": [ - "lfp_average = (ephys.LFP & \"insertion_number = '1'\").fetch1(\"lfp_mean\")" + "optogenetics.OptoProtocol * optogenetics.OptoStimParams" ] }, { @@ -949,10 +806,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In the query above, we fetch a single `lfp_mean` attribute from the `LFP` table.\n", - "We also restrict the query to insertion number 1.\n", + "The `fetch` and `fetch1` methods download the data from the query object into the workspace.\n", "\n", - "Let's go ahead and plot the LFP mean." + "Below we will run `fetch()` without any arguments to return all attributes of all entries in the table." ] }, { @@ -961,41 +817,14 @@ "metadata": {}, "outputs": [], "source": [ - "plt.plot(lfp_average)\n", - "plt.title(\"Average LFP Waveform for Insertion 1\")\n", - "plt.xlabel(\"Samples\")\n", - "plt.ylabel(\"microvolts (uV)\");" + "optogenetics.OptoEvent.fetch(as_dict=True)" ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "DataJoint queries are a highly flexible tool to manipulate and visualize your data.\n", - "After all, visualizing traces or generating rasters is likely just the start of\n", - "your analysis workflow. This can also make the queries seem more complex at\n", - "first. However, we'll walk through them slowly to simplify their content in this notebook. \n", - "\n", - "The examples below perform several operations using DataJoint queries:\n", - "- Fetch the primary key attributes of all units that are in `insertion_number=1`.\n", - "- Use **multiple restrictions** to fetch timestamps and create a raster plot.\n", - "- Use a **join** operation and **multiple restrictions** to fetch a waveform\n", - " trace, along with unit data to create a single waveform plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "insert_key = (ephys.ProbeInsertion & \"insertion_number = '1'\").fetch1(\"KEY\")\n", - "units, unit_spiketimes = (\n", - " ephys.CuratedClustering.Unit\n", - " & insert_key\n", - " & 'unit IN (\"6\",\"7\",\"9\",\"14\",\"15\",\"17\",\"19\")'\n", - ").fetch(\"unit\", \"spike_times\")" + "Next, we will fetch the entry with a `stim_start_time` of 543 with the `fetch1` method, which returns a dictionary containing all attributes of one entry in the table." ] }, { @@ -1004,57 +833,7 @@ "metadata": {}, "outputs": [], "source": [ - "x = np.hstack(unit_spiketimes)\n", - "y = np.hstack([np.full_like(s, u) for u, s in zip(units, unit_spiketimes)])\n", - "plt.plot(x, y, \"|\")\n", - "plt.xlabel(\"Time (s)\")\n", - "plt.ylabel(\"Unit\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Below we will use two queries to fetch *all* of the information about a single unit and\n", - "plot the unit waveform." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "unit_key = (ephys.CuratedClustering.Unit & insert_key & \"unit = '15'\").fetch1(\"KEY\")\n", - "unit_data = (\n", - " ephys.CuratedClustering.Unit * ephys.WaveformSet.PeakWaveform & unit_key\n", - ").fetch1()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "unit_data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sampling_rate = (ephys.EphysRecording & insert_key).fetch1(\n", - " \"sampling_rate\"\n", - ") / 1000 # in kHz\n", - "plt.plot(\n", - " np.r_[: unit_data[\"peak_electrode_waveform\"].size] * 1 / sampling_rate,\n", - " unit_data[\"peak_electrode_waveform\"],\n", - ")\n", - "plt.xlabel(\"Time (ms)\")\n", - "plt.ylabel(r\"Voltage ($\\mu$V)\");" + "(optogenetics.OptoEvent & \"stim_start_time=543\").fetch1()" ] }, { @@ -1064,15 +843,14 @@ "## Summary\n", "\n", "Following this tutorial, we have: \n", - "+ Covered the essential functionality of `element-array-ephys`.\n", + "+ Covered the essential functionality of `element-optogenetics`.\n", "+ Learned how to manually insert data into tables.\n", - "+ Executed and ingested results of spike sorting with Kilosort.\n", - "+ Visualized the results. \n", + "+ Queried and viewed the data. \n", "\n", "#### Documentation and DataJoint Tutorials\n", "\n", "+ [Detailed documentation on\n", - " `element-array-ephys`.](https://datajoint.com/docs/elements/element-array-ephys/)\n", + " `element-optogenetics`.](https://datajoint.com/docs/elements/element-optogenetics/)\n", "+ [General `datajoint-python`\n", " tutorials.](https://github.com/datajoint/datajoint-tutorials) covering fundamentals,\n", " such as table tiers, query operations, fetch operations, automated computations with the\n", From d943684fa856b604311a05416dfc56c3e684f844 Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Wed, 17 Jan 2024 05:45:49 -0600 Subject: [PATCH 06/26] Apply black formatting --- notebooks/tutorial.ipynb | 19 +++++++++++-------- notebooks/tutorial_pipeline.py | 2 ++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb index c3e860c..efd021a 100644 --- a/notebooks/tutorial.ipynb +++ b/notebooks/tutorial.ipynb @@ -327,8 +327,11 @@ ], "source": [ "(\n", - " dj.Diagram(subject.Subject) + dj.Diagram(surgery.Implantation)\n", - " + dj.Diagram(session.Session) + dj.Diagram(Device) + dj.Diagram(optogenetics)\n", + " dj.Diagram(subject.Subject)\n", + " + dj.Diagram(surgery.Implantation)\n", + " + dj.Diagram(session.Session)\n", + " + dj.Diagram(Device)\n", + " + dj.Diagram(optogenetics)\n", ")" ] }, @@ -584,15 +587,15 @@ " implant_type=\"opto\",\n", " target_region=\"dHP\",\n", " target_hemisphere=\"left\",\n", - " ap=\"-7.9\", # [mm] anterior-posterior distance\n", + " ap=\"-7.9\", # [mm] anterior-posterior distance\n", " ap_ref=\"bregma\",\n", - " ml=\"-1.8\", # [mm] medial axis distance\n", + " ml=\"-1.8\", # [mm] medial axis distance\n", " ml_ref=\"bregma\",\n", - " dv=\"5\", # [mm] dorso-ventral axis distance\n", + " dv=\"5\", # [mm] dorso-ventral axis distance\n", " dv_ref=\"skull_surface\",\n", - " theta=\"11.5\", # [0, 180] degree rotation about ml-axis relative to z\n", - " phi=\"0\", # [0, 360] degree rotation about dv-axis relative to x\n", - " beta=None, # [-180, 180] degree rotation about shank relative to anterior\n", + " theta=\"11.5\", # [0, 180] degree rotation about ml-axis relative to z\n", + " phi=\"0\", # [0, 360] degree rotation about dv-axis relative to x\n", + " beta=None, # [-180, 180] degree rotation about shank relative to anterior\n", " )\n", ")" ] diff --git a/notebooks/tutorial_pipeline.py b/notebooks/tutorial_pipeline.py index e6588f2..f18c250 100644 --- a/notebooks/tutorial_pipeline.py +++ b/notebooks/tutorial_pipeline.py @@ -30,6 +30,7 @@ Experimenter = User session.activate(db_prefix + "session", linking_module=__name__) + @lab.schema class Device(dj.Lookup): """Table for managing lab devices. @@ -50,4 +51,5 @@ class Device(dj.Lookup): ["OPTG_4", "Optogenetics", "Doric Pulse Sequence Generator"], ] + optogenetics.activate(db_prefix + "optogenetics", linking_module=__name__) From 9819406f88d171d115c656c2b5759153b8430103 Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Wed, 17 Jan 2024 05:46:36 -0600 Subject: [PATCH 07/26] Remove PyPI release from GHA --- .github/workflows/release.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4a5f2cb..2eedb0e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,14 +4,6 @@ on: jobs: make_github_release: uses: datajoint/.github/.github/workflows/make_github_release.yaml@main - pypi_release: - needs: make_github_release - uses: datajoint/.github/.github/workflows/pypi_release.yaml@main - secrets: - TWINE_USERNAME: ${{secrets.TWINE_USERNAME}} - TWINE_PASSWORD: ${{secrets.TWINE_PASSWORD}} - with: - UPLOAD_URL: ${{needs.make_github_release.outputs.release_upload_url}} mkdocs_release: uses: datajoint/.github/.github/workflows/mkdocs_release.yaml@main permissions: From 93bb6d4cc0e7d97e6b1a81368786deb015cdb0e4 Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:10:59 +0000 Subject: [PATCH 08/26] Minor fixes to tutorial notebook --- notebooks/tutorial.ipynb | 2120 +++++++++++++++++++++++++++++++++++--- 1 file changed, 1963 insertions(+), 157 deletions(-) diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb index efd021a..6d3687a 100644 --- a/notebooks/tutorial.ipynb +++ b/notebooks/tutorial.ipynb @@ -54,10 +54,7 @@ "metadata": {}, "outputs": [], "source": [ - "import datajoint as dj\n", - "import datetime\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np" + "import datajoint as dj" ] }, { @@ -71,15 +68,15 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "[2024-01-04 20:00:17,037][INFO]: Connecting root@fakeservices.datajoint.io:3306\n", - "[2024-01-04 20:00:17,044][INFO]: Connected root@fakeservices.datajoint.io:3306\n" + "[2024-01-17 12:07:52,612][INFO]: Connecting root@fakeservices.datajoint.io:3306\n", + "[2024-01-17 12:07:52,620][INFO]: Connected root@fakeservices.datajoint.io:3306\n" ] }, { @@ -88,7 +85,7 @@ "DataJoint connection (connected) root@fakeservices.datajoint.io:3306" ] }, - "execution_count": 2, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -110,14 +107,14 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "[2024-01-04 20:00:18,332][WARNING]: lab.Project and related tables will be removed in a future version of Element Lab. Please use the project schema.\n" + "[2024-01-17 12:07:58,189][WARNING]: lab.Project and related tables will be removed in a future version of Element Lab. Please use the project schema.\n" ] } ], @@ -145,179 +142,179 @@ "\n", "%3\n", "\n", - "\n", - "\n", - "optogenetics.OptoWaveform.Square\n", - "\n", - "\n", - "optogenetics.OptoWaveform.Square\n", - "\n", - "\n", - "\n", "\n", - "\n", + "\n", "Device\n", - "\n", + "\n", "\n", "Device\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "optogenetics.OptoProtocol\n", - "\n", - "\n", - "optogenetics.OptoProtocol\n", + "\n", + "\n", + "optogenetics.OptoProtocol\n", "\n", "\n", "\n", "\n", "\n", "Device->optogenetics.OptoProtocol\n", - "\n", + "\n", "\n", - "\n", - "\n", - "surgery.Implantation\n", - "\n", - "\n", - "surgery.Implantation\n", + "\n", + "\n", + "subject.Subject\n", + "\n", + "\n", + "subject.Subject\n", "\n", "\n", "\n", - "\n", + "\n", + "\n", + "session.Session\n", + "\n", + "\n", + "session.Session\n", + "\n", + "\n", + "\n", + "\n", "\n", - "surgery.Implantation->optogenetics.OptoProtocol\n", - "\n", + "subject.Subject->session.Session\n", + "\n", "\n", - "\n", - "\n", - "subject.Subject\n", - "\n", - "\n", - "subject.Subject\n", + "\n", + "\n", + "surgery.Implantation\n", + "\n", + "\n", + "surgery.Implantation\n", "\n", "\n", "\n", "\n", "\n", "subject.Subject->surgery.Implantation\n", - "\n", + "\n", "\n", - "\n", - "\n", - "session.Session\n", - "\n", - "\n", - "session.Session\n", + "\n", + "\n", + "optogenetics.OptoEvent\n", + "\n", + "\n", + "optogenetics.OptoEvent\n", "\n", "\n", "\n", - "\n", + "\n", "\n", - "subject.Subject->session.Session\n", - "\n", + "optogenetics.OptoProtocol->optogenetics.OptoEvent\n", + "\n", "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveformType\n", - "\n", - "\n", - "optogenetics.OptoWaveformType\n", + "\n", + "\n", + "optogenetics.OptoWaveformType\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveform\n", - "\n", - "\n", - "optogenetics.OptoWaveform\n", + "\n", + "\n", + "optogenetics.OptoWaveform\n", "\n", "\n", "\n", "\n", "\n", "optogenetics.OptoWaveformType->optogenetics.OptoWaveform\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveform.Sine\n", - "\n", - "\n", - "optogenetics.OptoWaveform.Sine\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "optogenetics.OptoEvent\n", - "\n", - "\n", - "optogenetics.OptoEvent\n", + "\n", + "\n", + "optogenetics.OptoWaveform.Sine\n", "\n", "\n", "\n", - "\n", - "\n", - "optogenetics.OptoProtocol->optogenetics.OptoEvent\n", - "\n", - "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveform.Ramp\n", - "\n", - "\n", - "optogenetics.OptoWaveform.Ramp\n", + "\n", + "\n", + "optogenetics.OptoWaveform.Ramp\n", "\n", "\n", "\n", - "\n", - "\n", - "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Square\n", - "\n", - "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Sine\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Ramp\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "optogenetics.OptoWaveform.Square\n", + "\n", + "\n", + "optogenetics.OptoWaveform.Square\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Square\n", + "\n", "\n", "\n", - "\n", + "\n", "optogenetics.OptoStimParams\n", - "\n", + "\n", "\n", "optogenetics.OptoStimParams\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveform->optogenetics.OptoStimParams\n", - "\n", + "\n", "\n", - "\n", + "\n", + "\n", + "session.Session->optogenetics.OptoProtocol\n", + "\n", + "\n", + "\n", "\n", - "optogenetics.OptoStimParams->optogenetics.OptoProtocol\n", - "\n", + "surgery.Implantation->optogenetics.OptoProtocol\n", + "\n", "\n", - "\n", + "\n", "\n", - "session.Session->optogenetics.OptoProtocol\n", - "\n", + "optogenetics.OptoStimParams->optogenetics.OptoProtocol\n", + "\n", "\n", "\n", "" ], "text/plain": [ - "" + "" ] }, "execution_count": 5, @@ -354,27 +351,150 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

subject_nickname

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

subject_birth_date

\n", + " \n", + "
\n", + "

subject_description

\n", + " \n", + "
\n", + " \n", + "

Total: 0

\n", + " " + ], + "text/plain": [ + "*subject subject_nickna sex subject_birth_ subject_descri\n", + "+---------+ +------------+ +-----+ +------------+ +------------+\n", + "\n", + " (Total: 0)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "subject.Subject()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "subject : varchar(8) \n", + "---\n", + "subject_nickname=\"\" : varchar(64) \n", + "sex : enum('M','F','U') \n", + "subject_birth_date : date \n", + "subject_description=\"\" : varchar(1024) \n", + "\n" + ] + } + ], "source": [ "print(subject.Subject.describe())" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "# \n", + "subject : varchar(8) # \n", + "---\n", + "subject_nickname=\"\" : varchar(64) # \n", + "sex : enum('M','F','U') # \n", + "subject_birth_date : date # \n", + "subject_description=\"\" : varchar(1024) # " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "subject.Subject.heading" ] @@ -391,9 +511,105 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

subject_nickname

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

subject_birth_date

\n", + " \n", + "
\n", + "

subject_description

\n", + " \n", + "
subject1F2020-01-01Optogenetic pilot subject
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject subject_nickna sex subject_birth_ subject_descri\n", + "+----------+ +------------+ +-----+ +------------+ +------------+\n", + "subject1 F 2020-01-01 Optogenetic pi\n", + " (Total: 1)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "subject.Subject.insert1(\n", " dict(\n", @@ -416,18 +632,45 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-> subject.Subject\n", + "session_id : int \n", + "---\n", + "session_datetime=null : datetime \n", + "\n" + ] + } + ], "source": [ "print(session.Session.describe())" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "# \n", + "subject : varchar(8) # \n", + "session_id : int # \n", + "---\n", + "session_datetime=null : datetime # " + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "session.Session.heading" ] @@ -450,18 +693,106 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ - "session_key = dict(subject=\"subject5\", session_datetime=\"2023-01-01 00:00:00\")" + "session_key = dict(subject=\"subject1\", session_id=0, session_datetime=\"2023-01-01 00:00:00\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_id

\n", + " \n", + "
\n", + "

session_datetime

\n", + " \n", + "
subject102023-01-01 00:00:00
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_id session_dateti\n", + "+----------+ +------------+ +------------+\n", + "subject1 0 2023-01-01 00:\n", + " (Total: 1)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "session.Session.insert1(session_key)\n", "session.Session()" @@ -478,9 +809,100 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

device

\n", + " \n", + "
\n", + "

modality

\n", + " \n", + "
\n", + "

description

\n", + " \n", + "
OPTG_4OptogeneticsDoric Pulse Sequence Generator
OPTG_8Optogenetics8 channel pulse sequence device
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*device modality description \n", + "+--------+ +------------+ +------------+\n", + "OPTG_4 Optogenetics Doric Pulse Se\n", + "OPTG_8 Optogenetics 8 channel puls\n", + " (Total: 2)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "Device.insert1(\n", " dict(\n", @@ -494,9 +916,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-> subject.Subject\n", + "implant_date : datetime # surgery date\n", + "-> surgery.ImplantationType\n", + "-> surgery.BrainRegion.proj(target_region=\"region_acronym\")\n", + "-> surgery.Hemisphere.proj(target_hemisphere=\"hemisphere\")\n", + "---\n", + "-> lab.User.proj(surgeon=\"user\")\n", + "implant_comment=\"\" : varchar(1024) # Comments about the implant\n", + "\n" + ] + } + ], "source": [ "print(surgery.Implantation.describe())" ] @@ -511,9 +949,101 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Table for storing user information.\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "
\n", + "

user

\n", + " username, short identifier\n", + "
\n", + "

user_email

\n", + " \n", + "
\n", + "

user_cellphone

\n", + " \n", + "
\n", + "

user_fullname

\n", + " Full name used to uniquely identify an individual\n", + "
User1
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*user user_email user_cellphone user_fullname \n", + "+-------+ +------------+ +------------+ +------------+\n", + "User1 \n", + " (Total: 1)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "lab.User.insert1(dict(user=\"User1\"))\n", "lab.User()" @@ -529,18 +1059,185 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

reference

\n", + " \n", + "
bregma
dura
lambda
sagittal_suture
sinus
skull_surface
\n", + " \n", + "

Total: 6

\n", + " " + ], + "text/plain": [ + "*reference \n", + "+------------+\n", + "bregma \n", + "dura \n", + "lambda \n", + "sagittal_sutur\n", + "sinus \n", + "skull_surface \n", + " (Total: 6)" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "surgery.CoordinateReference()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

hemisphere

\n", + " Brain region hemisphere\n", + "
left
middle
right
\n", + " \n", + "

Total: 3

\n", + " " + ], + "text/plain": [ + "*hemisphere \n", + "+------------+\n", + "left \n", + "middle \n", + "right \n", + " (Total: 3)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "surgery.Hemisphere()" ] @@ -554,9 +1251,93 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "
\n", + "

region_acronym

\n", + " Brain region shorthand\n", + "
\n", + "

region_name

\n", + " Brain region full name\n", + "
dHPDorsal Hippocampus
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*region_acrony region_name \n", + "+------------+ +------------+\n", + "dHP Dorsal Hippoca\n", + " (Total: 1)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "surgery.BrainRegion.insert1(\n", " dict(region_acronym=\"dHP\", region_name=\"Dorsal Hippocampus\")\n", @@ -566,7 +1347,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -609,18 +1390,254 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

implant_date

\n", + " surgery date\n", + "
\n", + "

implant_type

\n", + " Short name for type of implanted device\n", + "
\n", + "

target_region

\n", + " Brain region shorthand\n", + "
\n", + "

target_hemisphere

\n", + " Brain region hemisphere\n", + "
\n", + "

surgeon

\n", + " username, short identifier\n", + "
\n", + "

implant_comment

\n", + " Comments about the implant\n", + "
subject12022-04-01 12:13:14optodHPleftUser1
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *implant_date *implant_type *target_region *target_hemisp surgeon implant_commen\n", + "+----------+ +------------+ +------------+ +------------+ +------------+ +---------+ +------------+\n", + "subject1 2022-04-01 12: opto dHP left User1 \n", + " (Total: 1)" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "surgery.Implantation()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

implant_date

\n", + " surgery date\n", + "
\n", + "

implant_type

\n", + " Short name for type of implanted device\n", + "
\n", + "

target_region

\n", + " Brain region shorthand\n", + "
\n", + "

target_hemisphere

\n", + " Brain region hemisphere\n", + "
\n", + "

ap

\n", + " (mm) anterior-posterior; ref is 0\n", + "
\n", + "

ap_ref

\n", + " \n", + "
\n", + "

ml

\n", + " (mm) medial axis; ref is 0\n", + "
\n", + "

ml_ref

\n", + " \n", + "
\n", + "

dv

\n", + " (mm) dorso-ventral axis; ventral negative\n", + "
\n", + "

dv_ref

\n", + " \n", + "
\n", + "

theta

\n", + " (deg) rot about ml-axis [0, 180] wrt z\n", + "
\n", + "

phi

\n", + " (deg) rot about dv-axis [0, 360] wrt x\n", + "
\n", + "

beta

\n", + " (deg) rot about shank [-180, 180] wrt anterior\n", + "
subject12022-04-01 12:13:14optodHPleft-7.9bregma-1.8bregma5.0skull_surface11.50.0nan
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *implant_date *implant_type *target_region *target_hemisp ap ap_ref ml ml_ref dv dv_ref theta phi beta \n", + "+----------+ +------------+ +------------+ +------------+ +------------+ +------+ +--------+ +------+ +--------+ +-----+ +------------+ +-------+ +-----+ +------+\n", + "subject1 2022-04-01 12: opto dHP left -7.9 bregma -1.8 bregma 5.0 skull_surface 11.5 0.0 nan \n", + " (Total: 1)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "surgery.Implantation.Coordinate()" ] @@ -634,7 +1651,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -655,9 +1672,113 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Defines a single optical stimulus that repeats.\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

opto_params_id

\n", + " \n", + "
\n", + "

waveform_name

\n", + " \n", + "
\n", + "

wavelength

\n", + " (nm) wavelength of optical stimulation light\n", + "
\n", + "

power

\n", + " (mW) total power from light source\n", + "
\n", + "

light_intensity

\n", + " (mW/mm2) power for given area\n", + "
\n", + "

frequency

\n", + " (Hz) frequency of the waveform\n", + "
\n", + "

duration

\n", + " (ms) duration of each optical stimulus\n", + "
1square_10470None10.201.0241.0
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*opto_params_i waveform_name wavelength power light_intensit frequency duration \n", + "+------------+ +------------+ +------------+ +-------+ +------------+ +-----------+ +----------+\n", + "1 square_10 470 None 10.20 1.0 241.0 \n", + " (Total: 1)" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "optogenetics.OptoStimParams.insert1(\n", " dict(\n", @@ -681,14 +1802,130 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_id

\n", + " \n", + "
\n", + "

protocol_id

\n", + " \n", + "
\n", + "

opto_params_id

\n", + " \n", + "
\n", + "

implant_date

\n", + " surgery date\n", + "
\n", + "

implant_type

\n", + " Short name for type of implanted device\n", + "
\n", + "

target_region

\n", + " Brain region shorthand\n", + "
\n", + "

target_hemisphere

\n", + " Brain region hemisphere\n", + "
\n", + "

device

\n", + " \n", + "
\n", + "

protocol_description

\n", + " description of optogenetics protocol\n", + "
subject10112022-04-01 12:13:14optodHPleftOPTG_4
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_id *protocol_id opto_params_id implant_date implant_type target_region target_hemisph device protocol_descr\n", + "+----------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+ +--------+ +------------+\n", + "subject1 0 1 1 2022-04-01 12: opto dHP left OPTG_4 \n", + " (Total: 1)" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "optogenetics.OptoProtocol.insert1(\n", " dict(\n", " subject=\"subject1\",\n", - " session_id=\"1\",\n", + " session_id=\"0\",\n", " protocol_id=\"1\",\n", " opto_params_id=\"1\",\n", " implant_date=\"2022-04-01 12:13:14\",\n", @@ -710,14 +1947,110 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_id

\n", + " \n", + "
\n", + "

protocol_id

\n", + " \n", + "
\n", + "

stim_start_time

\n", + " (s) stimulus start time relative to session start\n", + "
\n", + "

stim_end_time

\n", + " (s) stimulus end time relative session start\n", + "
subject101241.0482.0
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_id *protocol_id *stim_start_ti stim_end_time \n", + "+----------+ +------------+ +------------+ +------------+ +------------+\n", + "subject1 0 1 241.0 482.0 \n", + " (Total: 1)" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "optogenetics.OptoEvent.insert1(\n", " dict(\n", " subject=\"subject1\",\n", - " session_id=1,\n", + " session_id=0,\n", " protocol_id=1,\n", " stim_start_time=241,\n", " stim_end_time=482,\n", @@ -736,14 +2069,115 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_id

\n", + " \n", + "
\n", + "

protocol_id

\n", + " \n", + "
\n", + "

stim_start_time

\n", + " (s) stimulus start time relative to session start\n", + "
\n", + "

stim_end_time

\n", + " (s) stimulus end time relative session start\n", + "
subject101241.0482.0
subject101543.0797.0
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*subject *session_id *protocol_id *stim_start_ti stim_end_time \n", + "+----------+ +------------+ +------------+ +------------+ +------------+\n", + "subject1 0 1 241.0 482.0 \n", + "subject1 0 1 543.0 797.0 \n", + " (Total: 2)" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "optogenetics.OptoEvent.insert1(\n", " dict(\n", " subject=\"subject1\",\n", - " session_id=1,\n", + " session_id=0,\n", " protocol_id=1,\n", " stim_start_time=543,\n", " stim_end_time=797,\n", @@ -764,9 +2198,110 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_id

\n", + " \n", + "
\n", + "

protocol_id

\n", + " \n", + "
\n", + "

stim_start_time

\n", + " (s) stimulus start time relative to session start\n", + "
\n", + "

stim_end_time

\n", + " (s) stimulus end time relative session start\n", + "
subject101241.0482.0
subject101543.0797.0
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*subject *session_id *protocol_id *stim_start_ti stim_end_time \n", + "+----------+ +------------+ +------------+ +------------+ +------------+\n", + "subject1 0 1 241.0 482.0 \n", + "subject1 0 1 543.0 797.0 \n", + " (Total: 2)" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "optogenetics.OptoEvent()" ] @@ -780,9 +2315,105 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_id

\n", + " \n", + "
\n", + "

protocol_id

\n", + " \n", + "
\n", + "

stim_start_time

\n", + " (s) stimulus start time relative to session start\n", + "
\n", + "

stim_end_time

\n", + " (s) stimulus end time relative session start\n", + "
subject101543.0797.0
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_id *protocol_id *stim_start_ti stim_end_time \n", + "+----------+ +------------+ +------------+ +------------+ +------------+\n", + "subject1 0 1 543.0 797.0 \n", + " (Total: 1)" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "optogenetics.OptoEvent & \"stim_start_time=543\"" ] @@ -797,9 +2428,149 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject

\n", + " \n", + "
\n", + "

session_id

\n", + " \n", + "
\n", + "

protocol_id

\n", + " \n", + "
\n", + "

opto_params_id

\n", + " \n", + "
\n", + "

implant_date

\n", + " surgery date\n", + "
\n", + "

implant_type

\n", + " Short name for type of implanted device\n", + "
\n", + "

target_region

\n", + " Brain region shorthand\n", + "
\n", + "

target_hemisphere

\n", + " Brain region hemisphere\n", + "
\n", + "

device

\n", + " \n", + "
\n", + "

protocol_description

\n", + " description of optogenetics protocol\n", + "
\n", + "

waveform_name

\n", + " \n", + "
\n", + "

wavelength

\n", + " (nm) wavelength of optical stimulation light\n", + "
\n", + "

power

\n", + " (mW) total power from light source\n", + "
\n", + "

light_intensity

\n", + " (mW/mm2) power for given area\n", + "
\n", + "

frequency

\n", + " (Hz) frequency of the waveform\n", + "
\n", + "

duration

\n", + " (ms) duration of each optical stimulus\n", + "
subject10112022-04-01 12:13:14optodHPleftOPTG_4square_10470None10.201.0241.0
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*subject *session_id *protocol_id *opto_params_i implant_date implant_type target_region target_hemisph device protocol_descr waveform_name wavelength power light_intensit frequency duration \n", + "+----------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+ +------------+ +--------+ +------------+ +------------+ +------------+ +-------+ +------------+ +-----------+ +----------+\n", + "subject1 0 1 1 2022-04-01 12: opto dHP left OPTG_4 square_10 470 None 10.20 1.0 241.0 \n", + " (Total: 1)" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "optogenetics.OptoProtocol * optogenetics.OptoStimParams" ] @@ -816,9 +2587,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[{'subject': 'subject1',\n", + " 'session_id': 0,\n", + " 'protocol_id': 1,\n", + " 'stim_start_time': 241.0,\n", + " 'stim_end_time': 482.0},\n", + " {'subject': 'subject1',\n", + " 'session_id': 0,\n", + " 'protocol_id': 1,\n", + " 'stim_start_time': 543.0,\n", + " 'stim_end_time': 797.0}]" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "optogenetics.OptoEvent.fetch(as_dict=True)" ] @@ -832,9 +2623,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'subject': 'subject1',\n", + " 'session_id': 0,\n", + " 'protocol_id': 1,\n", + " 'stim_start_time': 543.0,\n", + " 'stim_end_time': 797.0}" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "(optogenetics.OptoEvent & \"stim_start_time=543\").fetch1()" ] From ae9cf1496de34795892e5e22c89b9e61590d8fba Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:18:36 +0000 Subject: [PATCH 09/26] Test removing s3fs for devcontainer --- .devcontainer/Dockerfile | 1 - .devcontainer/devcontainer.json | 2 +- .devcontainer/docker-compose.yaml | 4 +--- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d58008f..a50f210 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -41,7 +41,6 @@ ENV DJ_HOST fakeservices.datajoint.io ENV DJ_USER root ENV DJ_PASS simple -ENV EPHYS_ROOT_DATA_DIR /workspaces/element-optogenetics/example_data ENV DATABASE_PREFIX neuro_ USER vscode diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bf939e8..e82fc62 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,7 @@ "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }, "onCreateCommand": "mkdir -p ${EPHYS_ROOT_DATA_DIR} && pip install -e .", - "postStartCommand": "docker volume prune -f && s3fs ${DJ_PUBLIC_S3_LOCATION} ${EPHYS_ROOT_DATA_DIR} -o nonempty,multipart_size=530,endpoint=us-east-1,url=http://s3.amazonaws.com,public_bucket=1", + "postStartCommand": "docker volume prune -f", "hostRequirements": { "cpus": 4, "memory": "8gb", diff --git a/.devcontainer/docker-compose.yaml b/.devcontainer/docker-compose.yaml index e45c881..af1047e 100644 --- a/.devcontainer/docker-compose.yaml +++ b/.devcontainer/docker-compose.yaml @@ -6,11 +6,9 @@ services: build: context: .. dockerfile: ./.devcontainer/Dockerfile - # image: datajoint/element_array_ephys:latest + # image: datajoint/element_optogenetics:latest extra_hosts: - fakeservices.datajoint.io:127.0.0.1 - environment: - - DJ_PUBLIC_S3_LOCATION=djhub.vathes.datapub.elements:/workflow-array-ephys-benchmark/v2 devices: - /dev/fuse cap_add: From 195c60e1deb0a7076847fc9758155033107c2d53 Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:29:04 +0000 Subject: [PATCH 10/26] Fix extras_require and create devcontainer command --- .devcontainer/devcontainer.json | 2 +- setup.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e82fc62..ec9b835 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,7 @@ "remoteEnv": { "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }, - "onCreateCommand": "mkdir -p ${EPHYS_ROOT_DATA_DIR} && pip install -e .", + "onCreateCommand": "pip install -e .", "postStartCommand": "docker volume prune -f", "hostRequirements": { "cpus": 4, diff --git a/setup.py b/setup.py index e1f8af2..4d5c096 100644 --- a/setup.py +++ b/setup.py @@ -31,10 +31,8 @@ "elements": [ "element-animal @ git+https://github.com/datajoint/element-animal.git", "element-event @ git+https://github.com/datajoint/element-event.git", - "element-interface @ git+https://github.com/datajoint/element-interface.git", "element-lab @ git+https://github.com/datajoint/element-lab.git", "element-session @ git+https://github.com/datajoint/element-session.git", - "element-array-ephys @ git+https://github.com/datajoint/element-array-ephys.git", ], "tests": ["pre-commit", "pytest", "pytest-cov"], }, From 3f7b15bb51382db7c7cdd05ce2178a303ca5c3e9 Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:37:39 +0000 Subject: [PATCH 11/26] Minor fixes to tutorial notebook --- notebooks/tutorial.ipynb | 337 ++++++++++++++++++++------------------- 1 file changed, 169 insertions(+), 168 deletions(-) diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb index 6d3687a..118f42b 100644 --- a/notebooks/tutorial.ipynb +++ b/notebooks/tutorial.ipynb @@ -68,15 +68,15 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "[2024-01-17 12:07:52,612][INFO]: Connecting root@fakeservices.datajoint.io:3306\n", - "[2024-01-17 12:07:52,620][INFO]: Connected root@fakeservices.datajoint.io:3306\n" + "[2024-01-17 12:35:14,558][INFO]: Connecting root@fakeservices.datajoint.io:3306\n", + "[2024-01-17 12:35:14,571][INFO]: Connected root@fakeservices.datajoint.io:3306\n" ] }, { @@ -85,7 +85,7 @@ "DataJoint connection (connected) root@fakeservices.datajoint.io:3306" ] }, - "execution_count": 3, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -107,14 +107,14 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "[2024-01-17 12:07:58,189][WARNING]: lab.Project and related tables will be removed in a future version of Element Lab. Please use the project schema.\n" + "[2024-01-17 12:35:14,756][WARNING]: lab.Project and related tables will be removed in a future version of Element Lab. Please use the project schema.\n" ] } ], @@ -132,192 +132,192 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ - "\n", + "\n", "\n", "%3\n", - "\n", - "\n", + "\n", + "\n", "\n", - "Device\n", - "\n", - "\n", - "Device\n", + "optogenetics.OptoStimParams\n", + "\n", + "\n", + "optogenetics.OptoStimParams\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "optogenetics.OptoProtocol\n", - "\n", - "\n", - "optogenetics.OptoProtocol\n", + "\n", + "\n", + "optogenetics.OptoProtocol\n", "\n", "\n", "\n", - "\n", + "\n", "\n", - "Device->optogenetics.OptoProtocol\n", - "\n", + "optogenetics.OptoStimParams->optogenetics.OptoProtocol\n", + "\n", "\n", - "\n", + "\n", "\n", - "subject.Subject\n", - "\n", - "\n", - "subject.Subject\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "session.Session\n", - "\n", - "\n", - "session.Session\n", + "Device\n", + "\n", + "\n", + "Device\n", "\n", "\n", "\n", - "\n", + "\n", "\n", - "subject.Subject->session.Session\n", - "\n", - "\n", - "\n", - "\n", - "surgery.Implantation\n", - "\n", - "\n", - "surgery.Implantation\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "subject.Subject->surgery.Implantation\n", - "\n", - "\n", - "\n", - "\n", - "optogenetics.OptoEvent\n", - "\n", - "\n", - "optogenetics.OptoEvent\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "optogenetics.OptoProtocol->optogenetics.OptoEvent\n", - "\n", - "\n", - "\n", - "\n", - "optogenetics.OptoWaveformType\n", - "\n", - "\n", - "optogenetics.OptoWaveformType\n", - "\n", - "\n", + "Device->optogenetics.OptoProtocol\n", + "\n", "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveform\n", - "\n", - "\n", - "optogenetics.OptoWaveform\n", + "\n", + "\n", + "optogenetics.OptoWaveform\n", "\n", "\n", "\n", - "\n", - "\n", - "optogenetics.OptoWaveformType->optogenetics.OptoWaveform\n", - "\n", + "\n", + "\n", + "optogenetics.OptoWaveform->optogenetics.OptoStimParams\n", + "\n", "\n", "\n", "\n", "optogenetics.OptoWaveform.Sine\n", "\n", - "\n", - "optogenetics.OptoWaveform.Sine\n", + "\n", + "optogenetics.OptoWaveform.Sine\n", "\n", "\n", "\n", + "\n", + "\n", + "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Sine\n", + "\n", + "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveform.Ramp\n", - "\n", - "\n", - "optogenetics.OptoWaveform.Ramp\n", + "\n", + "\n", + "optogenetics.OptoWaveform.Ramp\n", "\n", "\n", "\n", - "\n", - "\n", - "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Sine\n", - "\n", - "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Ramp\n", - "\n", + "\n", "\n", "\n", "\n", "optogenetics.OptoWaveform.Square\n", "\n", - "\n", - "optogenetics.OptoWaveform.Square\n", + "\n", + "optogenetics.OptoWaveform.Square\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "optogenetics.OptoWaveform->optogenetics.OptoWaveform.Square\n", - "\n", + "\n", "\n", - "\n", - "\n", - "optogenetics.OptoStimParams\n", - "\n", - "\n", - "optogenetics.OptoStimParams\n", + "\n", + "\n", + "optogenetics.OptoEvent\n", + "\n", + "\n", + "optogenetics.OptoEvent\n", "\n", "\n", "\n", - "\n", - "\n", - "optogenetics.OptoWaveform->optogenetics.OptoStimParams\n", - "\n", + "\n", + "\n", + "surgery.Implantation\n", + "\n", + "\n", + "surgery.Implantation\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "surgery.Implantation->optogenetics.OptoProtocol\n", + "\n", + "\n", + "\n", + "\n", + "session.Session\n", + "\n", + "\n", + "session.Session\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "session.Session->optogenetics.OptoProtocol\n", - "\n", + "\n", "\n", - "\n", + "\n", + "\n", + "optogenetics.OptoWaveformType\n", + "\n", + "\n", + "optogenetics.OptoWaveformType\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "optogenetics.OptoWaveformType->optogenetics.OptoWaveform\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject\n", + "\n", + "\n", + "subject.Subject\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->surgery.Implantation\n", + "\n", + "\n", + "\n", "\n", - "surgery.Implantation->optogenetics.OptoProtocol\n", - "\n", + "subject.Subject->session.Session\n", + "\n", "\n", - "\n", + "\n", "\n", - "optogenetics.OptoStimParams->optogenetics.OptoProtocol\n", - "\n", + "optogenetics.OptoProtocol->optogenetics.OptoEvent\n", + "\n", "\n", "\n", "" ], "text/plain": [ - "" + "" ] }, - "execution_count": 5, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -351,7 +351,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -441,7 +441,7 @@ " (Total: 0)" ] }, - "execution_count": 6, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -452,7 +452,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -475,7 +475,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -490,7 +490,7 @@ "subject_description=\"\" : varchar(1024) # " ] }, - "execution_count": 8, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -511,7 +511,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -605,7 +605,7 @@ " (Total: 1)" ] }, - "execution_count": 9, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -632,7 +632,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -653,7 +653,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -666,7 +666,7 @@ "session_datetime=null : datetime # " ] }, - "execution_count": 11, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -693,7 +693,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -702,7 +702,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -788,7 +788,7 @@ " (Total: 1)" ] }, - "execution_count": 17, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -809,7 +809,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -898,7 +898,7 @@ " (Total: 2)" ] }, - "execution_count": 18, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -916,7 +916,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -949,7 +949,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -1039,7 +1039,7 @@ " (Total: 1)" ] }, - "execution_count": 20, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -1059,7 +1059,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -1142,7 +1142,7 @@ " (Total: 6)" ] }, - "execution_count": 21, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -1153,7 +1153,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -1233,7 +1233,7 @@ " (Total: 3)" ] }, - "execution_count": 22, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -1251,7 +1251,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -1333,7 +1333,7 @@ " (Total: 1)" ] }, - "execution_count": 23, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -1347,7 +1347,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -1390,7 +1390,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -1492,7 +1492,7 @@ " (Total: 1)" ] }, - "execution_count": 25, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -1503,7 +1503,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -1633,7 +1633,7 @@ " (Total: 1)" ] }, - "execution_count": 26, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -1651,7 +1651,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -1672,7 +1672,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -1774,7 +1774,7 @@ " (Total: 1)" ] }, - "execution_count": 28, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -1797,12 +1797,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "### **Insert optogenetics recording data**\n", "Next, we'll describe the session in which these parameters are used in `OptoProtocol`." ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -1916,7 +1917,7 @@ " (Total: 1)" ] }, - "execution_count": 30, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -1947,7 +1948,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -2041,7 +2042,7 @@ " (Total: 1)" ] }, - "execution_count": 32, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -2069,7 +2070,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -2168,7 +2169,7 @@ " (Total: 2)" ] }, - "execution_count": 34, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -2198,7 +2199,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -2297,7 +2298,7 @@ " (Total: 2)" ] }, - "execution_count": 35, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -2315,7 +2316,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -2409,7 +2410,7 @@ " (Total: 1)" ] }, - "execution_count": 36, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -2428,7 +2429,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -2566,7 +2567,7 @@ " (Total: 1)" ] }, - "execution_count": 37, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -2587,7 +2588,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -2605,7 +2606,7 @@ " 'stim_end_time': 797.0}]" ] }, - "execution_count": 38, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -2623,7 +2624,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -2636,7 +2637,7 @@ " 'stim_end_time': 797.0}" ] }, - "execution_count": 39, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } From 109e2e822831f78482b8fe48be71a798d2627782 Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Wed, 17 Jan 2024 06:42:08 -0600 Subject: [PATCH 12/26] Update README --- README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8297015..9807c49 100644 --- a/README.md +++ b/README.md @@ -4,28 +4,76 @@ DataJoint Element for managing data from optogenetics experiments. DataJoint Ele collectively standardize and automate data collection and analysis for neuroscience experiments. Each Element is a modular pipeline for data storage and processing with corresponding database tables that can be combined with other Elements to assemble a -fully functional pipeline. +fully functional pipeline. This repository also provides a tutorial +environment and notebooks to learn the pipeline. ## Experiment Flowchart ![flowchart](https://raw.githubusercontent.com/datajoint/element-optogenetics/main/images/flowchart.svg) -## Data Pipeline +## Data Pipeline Diagram ![pipeline](https://raw.githubusercontent.com/datajoint/element-optogenetics/main/images/pipeline.svg) ## Getting Started -+ Install from PyPI ++ Please fork this repository. + ++ Clone the repository to your computer. + +```bash + git clone https://github.com//element-optogenetics.git +``` + ++ Install with `pip`: ```bash - pip install element-optogenetics + pip install -e . ``` - -+ [Interactive tutorial on GitHub Codespaces](https://github.com/datajoint/workflow-optogenetics#interactive-tutorial) + ++ [Interactive tutorial on GitHub + Codespaces](https://github.com/datajoint/element-optogenetics#interactive-tutorial) + [Documentation](https://datajoint.com/docs/elements/element-optogenetics) ## Support -+ If you need help getting started or run into any errors, please contact our team by email at support@datajoint.com. ++ If you need help getting started or run into any errors, please contact our team by + email at support@datajoint.com. + +## Interactive Tutorial + ++ The easiest way to learn about DataJoint Elements is to use the tutorial notebooks within the included interactive environment configured using [Dev Container](https://containers.dev/). + +### Launch Environment + +Here are some options that provide a great experience: + +- (*recommended*) Cloud-based Environment + - Launch using [GitHub Codespaces](https://github.com/features/codespaces) using the `+` option which will `Create codespace on main` in the codebase repository on your fork with default options. For more control, see the `...` where you may create `New with options...`. + - Build time for a codespace is a few minutes. This is done infrequently and cached for convenience. + - Start time for a codespace is less than 1 minute. This will pull the built codespace from cache when you need it. + - *Tip*: Each month, GitHub renews a [free-tier](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces#monthly-included-storage-and-core-hours-for-personal-accounts) quota of compute and storage. Typically we run into the storage limits before anything else since Codespaces consume storage while stopped. It is best to delete Codespaces when not actively in use and recreate when needed. We'll soon be creating prebuilds to avoid larger build times. Once any portion of your quota is reached, you will need to wait for it to be reset at the end of your cycle or add billing info to your GitHub account to handle overages. + - *Tip*: GitHub auto names the codespace but you can rename the codespace so that it is easier to identify later. + +- Local Environment + > *Note: Access to example data is currently limited to MacOS and Linux due to the s3fs utility. Windows users are recommended to use the above environment.* + - Install [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) + - Install [Docker](https://docs.docker.com/get-docker/) + - Install [VSCode](https://code.visualstudio.com/) + - Install the VSCode [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) + - `git clone` the codebase repository and open it in VSCode + - Use the `Dev Containers extension` to `Reopen in Container` (More info is in the `Getting started` included with the extension.) + +You will know your environment has finished loading once you either see a terminal open related to `Running postStartCommand` with a final message of `Done` or the `README.md` is opened in `Preview`. + +Once the environment has launched, please run the following command in the terminal: +``` +MYSQL_VER=8.0 docker compose -f docker-compose-db.yaml up --build -d +``` + +### Instructions + +1. We recommend you start by navigating to the `notebooks` directory on the left panel and go through the `tutorial.ipynb` Jupyter notebook. Execute the cells in the notebook to begin your walk through of the tutorial. + +2. Once you are done, see the options available to you in the menu in the bottom-left corner. For example, in Codespace you will have an option to `Stop Current Codespace` but when running Dev Container on your own machine the equivalent option is `Reopen folder locally`. By default, GitHub will also automatically stop the Codespace after 30 minutes of inactivity. Once the Codespace is no longer being used, we recommend deleting the Codespace. From 1ba3a623beeac4a18bdcce42be13242544b845da Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 06:49:26 -0600 Subject: [PATCH 13/26] Update diagram_flowchart.drawio --- images/diagram_flowchart.drawio | 126 ++++++++++++++++---------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/images/diagram_flowchart.drawio b/images/diagram_flowchart.drawio index dc4b997..8f6c2a2 100644 --- a/images/diagram_flowchart.drawio +++ b/images/diagram_flowchart.drawio @@ -1,63 +1,63 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 730c1ec58fc533d13e229df498d540c574b300be Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Wed, 17 Jan 2024 07:00:08 -0600 Subject: [PATCH 14/26] Change `diagram_flowchart` to `diagram` --- images/{diagram_flowchart.drawio => flowchart.drawio} | 0 images/{diagram_flowchart.svg => flowchart.svg} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename images/{diagram_flowchart.drawio => flowchart.drawio} (100%) rename images/{diagram_flowchart.svg => flowchart.svg} (100%) diff --git a/images/diagram_flowchart.drawio b/images/flowchart.drawio similarity index 100% rename from images/diagram_flowchart.drawio rename to images/flowchart.drawio diff --git a/images/diagram_flowchart.svg b/images/flowchart.svg similarity index 100% rename from images/diagram_flowchart.svg rename to images/flowchart.svg From 06730de7196251f59b9f62eadb24e83d0d329cdc Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Wed, 17 Jan 2024 07:00:29 -0600 Subject: [PATCH 15/26] Update reference to diagram in tutorial notebook --- notebooks/tutorial.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb index 118f42b..d832dc6 100644 --- a/notebooks/tutorial.ipynb +++ b/notebooks/tutorial.ipynb @@ -17,7 +17,7 @@ "tutorial you will have a clear grasp on setting up and integrating `element-optogenetics`\n", "into your specific research projects and lab. \n", "\n", - "![flowchart](../images/diagram_flowchart.svg)\n", + "![flowchart](../images/flowchart.svg)\n", "\n", "### Prerequisites\n", "\n", From 07e4b2dd0108d466addc0c9275ff68ed57dd0a65 Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:02:00 -0600 Subject: [PATCH 16/26] Update diagram.drawio --- images/diagram_flowchart.drawio | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 images/diagram_flowchart.drawio diff --git a/images/diagram_flowchart.drawio b/images/diagram_flowchart.drawio new file mode 100644 index 0000000..a07cf60 --- /dev/null +++ b/images/diagram_flowchart.drawio @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From bbbc9bd997a8fe0228b0e160ed1b14a4379f0069 Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:03:44 -0600 Subject: [PATCH 17/26] Added diagram_flowchart.svg --- images/diagram_flowchart.svg | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 images/diagram_flowchart.svg diff --git a/images/diagram_flowchart.svg b/images/diagram_flowchart.svg new file mode 100644 index 0000000..04d095b --- /dev/null +++ b/images/diagram_flowchart.svg @@ -0,0 +1,4 @@ + + + +
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file From f8275dd93a7476f51f86a32dbf503b481676decc Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Wed, 17 Jan 2024 07:04:42 -0600 Subject: [PATCH 18/26] Update diagrams --- images/diagram_flowchart.drawio | 63 --------------------------------- images/diagram_flowchart.svg | 4 --- images/flowchart.drawio | 4 +-- images/flowchart.svg | 61 +++---------------------------- 4 files changed, 6 insertions(+), 126 deletions(-) delete mode 100644 images/diagram_flowchart.drawio delete mode 100644 images/diagram_flowchart.svg diff --git a/images/diagram_flowchart.drawio b/images/diagram_flowchart.drawio deleted file mode 100644 index a07cf60..0000000 --- a/images/diagram_flowchart.drawio +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/images/diagram_flowchart.svg b/images/diagram_flowchart.svg deleted file mode 100644 index 04d095b..0000000 --- a/images/diagram_flowchart.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - -
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/images/flowchart.drawio b/images/flowchart.drawio index 8f6c2a2..a07cf60 100644 --- a/images/flowchart.drawio +++ b/images/flowchart.drawio @@ -1,4 +1,4 @@ - + @@ -22,7 +22,7 @@ - + diff --git a/images/flowchart.svg b/images/flowchart.svg index 9a9692e..04d095b 100644 --- a/images/flowchart.svg +++ b/images/flowchart.svg @@ -1,57 +1,4 @@ -
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol

 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Viewer does not support full SVG 1.1
\ No newline at end of file + + + +
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file From 0991c99a1b6a1532cd8d26ba084b2ce8d07c905b Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:07:15 -0600 Subject: [PATCH 19/26] Added flowchart.svg --- images/flowchart.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/flowchart.svg b/images/flowchart.svg index 04d095b..bbe9dca 100644 --- a/images/flowchart.svg +++ b/images/flowchart.svg @@ -1,4 +1,4 @@ -
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file +
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file From 6612c3bff7d430ff4bb354de9c434109677cf893 Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:07:41 -0600 Subject: [PATCH 20/26] Save flowchart.svg --- images/flowchart.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/flowchart.svg b/images/flowchart.svg index bbe9dca..3daa9b2 100644 --- a/images/flowchart.svg +++ b/images/flowchart.svg @@ -1,4 +1,4 @@ -
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file +
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file From 444ec91b69dff295bacc482627a50e8e5f879cbf Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:09:17 -0600 Subject: [PATCH 21/26] Update flowchart.svg --- images/flowchart.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/flowchart.svg b/images/flowchart.svg index 3daa9b2..8283066 100644 --- a/images/flowchart.svg +++ b/images/flowchart.svg @@ -1,4 +1,4 @@ -
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file +
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file From 54d134a27c90386b41a46e0ffd44a4e81cca2992 Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:12:09 -0600 Subject: [PATCH 22/26] Update flowchart.svg --- images/flowchart.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/flowchart.svg b/images/flowchart.svg index 8283066..6a86f83 100644 --- a/images/flowchart.svg +++ b/images/flowchart.svg @@ -1,4 +1,4 @@ -
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file +
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file From 7729e98d6debe35be28c1245385e03da87e9dbde Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:16:12 -0600 Subject: [PATCH 23/26] Update flowchart.drawio --- images/flowchart.drawio | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/flowchart.drawio b/images/flowchart.drawio index a07cf60..67e5766 100644 --- a/images/flowchart.drawio +++ b/images/flowchart.drawio @@ -1,6 +1,6 @@ - + - + From 17554a685eb4538eaac2ca96b2ba60b539f2b10d Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:19:19 -0600 Subject: [PATCH 24/26] Update flowchart.drawio --- images/flowchart.drawio | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/images/flowchart.drawio b/images/flowchart.drawio index 67e5766..49a5ff8 100644 --- a/images/flowchart.drawio +++ b/images/flowchart.drawio @@ -1,4 +1,4 @@ - + @@ -7,51 +7,51 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + From f8339d45df14bea59aefd48a141e1fc2d5a87adb Mon Sep 17 00:00:00 2001 From: Kushal Bakshi <52367253+kushalbakshi@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:19:43 -0600 Subject: [PATCH 25/26] Added flowchart.svg --- images/flowchart.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/flowchart.svg b/images/flowchart.svg index 6a86f83..97322b5 100644 --- a/images/flowchart.svg +++ b/images/flowchart.svg @@ -1,4 +1,4 @@ -
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol


 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file +
Synchronize data modalities & exploratory analysis
Synchronize data...
Visualize Stimulus
Waveform and
Neurophysiology
Recording
Visualize Stimulus...

 Export & publish

 
 
Export & publish...
Create project &
stimulation protocol

 
Create project &...
Enter metadata
into pipeline
Enter metadata...
Collect session 
recordings
Collect session...
Enter metadata of
 fiber location
Enter metadata of...
Text is not SVG - cannot display
\ No newline at end of file From 93c3208e9364703552ea50d9a30e02cbd356d4ff Mon Sep 17 00:00:00 2001 From: kushalbakshi Date: Wed, 17 Jan 2024 07:25:02 -0600 Subject: [PATCH 26/26] Update CHANGELOG and version --- CHANGELOG.md | 9 +++++++++ element_optogenetics/version.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37a00c8..ee444b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ 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.2.0] - 2024-01-17 + ++ Add - devcontainer configuration ++ Add - tutorial notebook for Codespaces ++ Update - `setup.py` ++ Update - GitHub Actions do not release to PyPI ++ Update - drawio diagrams + ## [0.1.3] - 2023-05-12 + Fix - Docs @@ -20,6 +28,7 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and + Add - Table structure and basic docs (changelog, contribution guidelines, etc.) +[0.2.0]: https://github.com/datajoint/element-optogenetics/releases/tag/0.2.0 [0.1.3]: https://github.com/datajoint/element-optogenetics/releases/tag/0.1.3 [0.1.2]: https://github.com/datajoint/element-optogenetics/releases/tag/0.1.2 [0.1.1]: https://github.com/datajoint/element-optogenetics/releases/tag/0.1.1 diff --git a/element_optogenetics/version.py b/element_optogenetics/version.py index 29d8a5d..bc510a2 100644 --- a/element_optogenetics/version.py +++ b/element_optogenetics/version.py @@ -1,2 +1,2 @@ """Package metadata""" -__version__ = "0.1.3" +__version__ = "0.2.0"