Skip to content

Commit

Permalink
Merge pull request #423 from int-brain-lab/hotfix/6.6.3
Browse files Browse the repository at this point in the history
Hotfix/6.6.3
  • Loading branch information
micheleangelofabbri authored Aug 12, 2022
2 parents 603c5a2 + fa8097a commit e95c91e
Show file tree
Hide file tree
Showing 17 changed files with 233 additions and 94 deletions.
48 changes: 42 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# iblrig

iblrig is using gitflow and semantic versioning conventions. Click on the following links for more information on [gitflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) or [semantic versioning](https://semver.org/).
This repository is using [semantic versioning](https://semver.org/) and [gitflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) conventions:
![](README_semver.png)
![](README_gitflow_workflow.png)

Please review these conventions to more easily contribute to the project.

---
## How to work with this repository
Expand All @@ -17,7 +21,7 @@ iblrig is using gitflow and semantic versioning conventions. Click on the follow

### Hotfix branches:
- a `hotfix` or `maintenance` branch is forked from `master`
- once the fix has been thoroughly tested, it will get merged back into `master`, `develop`, `rc`
- once the fix has been thoroughly tested, it will get merged back into `master` and `develop`
- the `hotfix` branch will eventually be deleted

---
Expand All @@ -27,7 +31,7 @@ In order to install iblrig on a Windows machine please ensure that the following
- [Git](https://git-scm.com)
- [Anaconda](https://anaconda.com)

### Installation Instructions:
### Instructions for automated installation from scratch:
- Ensure Git, Anaconda, and your favorite text editor are already installed
- Please also ensure a stable internet connection is present as the installer pulls from various servers throughout the installation process
- Clone the latest version of this repository to the root of the `C:\` drive
Expand All @@ -36,11 +40,43 @@ In order to install iblrig on a Windows machine please ensure that the following
- The installer will take over for a while and ensure the rest of the requisite software is present
- The installer will prompt you to install ONE (yes/no)
- If you decide to install ONE, various prompts will assist you in the default configuration
- _TODO: Add and document better error catching/handling for when these settings are incorrect_
- The installer will prompt you to install Bonsai (yes/no)
- Installation complete

### Running pybpod
- Navigate your Anaconda Prompt to `C:\iblrig`
- Navigate your Anaconda Prompt to the iblrig folder: `cd C:\iblrig`
- Ensure the `iblrig` anaconda environment is activated: `conda activate iblrig`
- At the prompt, run: `.\pybpod.bat`
- _TODO: More instruction on how to work with the software? Other options?_

### Instructions for manual installation from scratch:
The following commands to be run from the Windows command prompt (not tested in powershell). Please ensure that your git and
anaconda environment are up-to-date.
```commandline
cd C:\
git clone https://github.com/int-brain-lab/iblrig
cd C:\iblrig
conda create --name iblrig python=3.7.13 --yes
conda activate iblrig
pip install --editable .
mkdir C:\iblrig_params
python setup_pybpod.py C:\iblrig_params
cd C:\iblrig\Bonsai
setup.bat
cd C:\iblrig
conda create --name ibllib python=3.8.13 --yes
conda activate ibllib
pip install ibllib
python -c "from one.api import ONE; ONE()" # several prompts will require interaction to configure ONE
conda activate iblrig
pybpod.bat
```

### Instructions for manual update from 6.6.2 to 6.6.3:
The following commands to be run from the Windows command prompt (not tested in powershell). Please ensure that your git and
anaconda environment are up-to-date. **Backup any custom tasks or modifications before performing the following**
```commandline
cd C:\iblrig
git reset —-hard
git fetch
git pull
```
Binary file added README_gitflow_workflow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added README_semver.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 1 addition & 6 deletions iblrig/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
__version__ = "6.6.2"
# !/usr/bin/env python
# @Author: Niccolò Bonacchi
# @Creation_Date: Friday, January 11th 2019, 2:04:42 pm
# @Editor: Michele Fabbri
# @Edit_Date: 2022-02-01
__version__ = "6.6.3"
import logging

import colorlog
Expand Down
14 changes: 9 additions & 5 deletions iblrig/frame2TTL.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ def Frame2TTL(serial_port: str, version: int = 2) -> object:
object: Instance of the v1/v2 class
"""
f2ttl = None
log.debug(f"serial_port from Frame2TTL: {serial_port}")
if version == 2:
try:
f2ttl = Frame2TTLv2(serial_port)
assert f2ttl.hw_version == 2, "Not a v2 device, continuing with v1"
if iblrig.params.load_params_file().get("F2TTL_HW_VERSION", None) != 2:
iblrig.params.update_params_file(data={"F2TTL_HW_VERSION": 2})
return f2ttl
except BaseException as e:
except (serial.SerialException, AssertionError) as e:
log.warning(f"Cannot connect assuming F2TTLv2 device, continuing with v1: {e}")
elif version == 1:
try:
Expand All @@ -41,13 +42,16 @@ def Frame2TTL(serial_port: str, version: int = 2) -> object:
if iblrig.params.load_params_file().get("F2TTL_HW_VERSION", None) != 1:
iblrig.params.update_params_file(data={"F2TTL_HW_VERSION": 1})
return f2ttl
except BaseException as e:
except AssertionError as e:
log.error(
f"Couldn't connect to F2TTLv1: {str(e)}\nDisconnecting and then "
f"reconnecting the Frame2TTL cable may resolve this issue."
)
elif version == 0:
return None
raise e
except FileNotFoundError as e:
raise e
else:
raise ValueError("Unsupported version " + str(version))

return Frame2TTL(serial_port, version=version - 1)

Expand Down Expand Up @@ -296,7 +300,7 @@ def connect(self) -> serial.Serial:
# 1 byte response expected (unsigned)
self.hw_version = int.from_bytes(ser.read(1), byteorder="little", signed=False)
if self.hw_version != 2:
self.ser.close()
ser.close()
raise serial.SerialException("Error: Frame2TTLv2 requires hardware version 2.")
return ser

Expand Down
3 changes: 2 additions & 1 deletion iblrig/iotasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import iblrig.raw_data_loaders as raw

log = logging.getLogger("iblrig")
N_PREGENERATED_SESSIONS = 12


class ComplexEncoder(json.JSONEncoder):
Expand Down Expand Up @@ -145,7 +146,7 @@ def load_session_order_idx(last_settings_data: dict) -> tuple:
session_idx = 0
elif "SESSION_ORDER" in last_settings_data.keys():
session_order = last_settings_data["SESSION_ORDER"]
session_idx = last_settings_data["SESSION_IDX"] + 1
session_idx = (last_settings_data["SESSION_IDX"] + 1) % N_PREGENERATED_SESSIONS

return session_order, session_idx

Expand Down
1 change: 1 addition & 0 deletions iblrig/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ def load_params_file(silent=True) -> dict:
"""
iblrig_params = Path(ph.get_iblrig_params_folder())
fpath = iblrig_params / ".iblrig_params.json"
log.debug(f"fpath from load_params_file: {fpath}")
if fpath.exists():
with open(fpath, "r") as f:
out = json.load(f)
Expand Down
6 changes: 4 additions & 2 deletions iblrig/session_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@


# EPHYS CHOICE WORLD
def make_ephysCW_pc():
def make_ephysCW_pc(prob_type='biased'):
"""make_ephysCW_pc Makes positions, contrasts and block lengths for ephysCW
Generates ~2000 trias
:prob_type: (str) 'biased': 0 contrast half has likely to be drawn, 'uniform': 0 contrast as
likely as other contrasts
:return: pc
:rtype: [type]
"""
Expand All @@ -38,7 +40,7 @@ def make_ephysCW_pc():
len_block.append(blocks.get_block_len(60, min_=20, max_=100))
for x in range(len_block[-1]):
p = blocks.draw_position([-35, 35], prob_left)
c = misc.draw_contrast(contrasts, prob_type="uniform")
c = misc.draw_contrast(contrasts, prob_type=prob_type)
pc = np.append(pc, np.array([[p, c, prob_left]]), axis=0)
# do this in PC space
prob_left = np.round(np.abs(1 - prob_left), 1)
Expand Down
7 changes: 7 additions & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# **Release notes**

## **Release Notes 6.6.3**

- additional f2ttl error catching and messaging
- purge_rig_data.py script now functional
- corrected datetime import for `tasks/_iblrig_misc_bpod_ttl_test/trial_params.py`
- manual installation and update instructions added to README.md to simplify troubleshooting efforts

## **Release Notes 6.6.2**

- Update procedure will now ignore the reinstall flag if version increments only micro/patch version
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ Cython==0.29.6
globus-sdk==1.7.1
numpy<1.21,>=1.17
opencv-python==3.4.5.20
ONE-api
packaging==20.8
pandas==0.25.3
pandas
pybpod==1.8.2
pyOpenSSL==19.0.0
PySocks==1.6.8
Expand Down
Empty file added scripts/__init__.py
Empty file.
Empty file added scripts/ibllib/__init__.py
Empty file.
117 changes: 56 additions & 61 deletions scripts/ibllib/purge_rig_data.py
Original file line number Diff line number Diff line change
@@ -1,87 +1,82 @@
#!/usr/bin/env python
# @Author: Niccolò Bonacchi
# @Creation_Date: Thursday, March 28th 2019, 7:53:44 pm
# @Editor: Michele Fabbri
# @Edit_Date: 2022-02-01
"""
Purge data from RIG
- Find all files by rglob
- Find all sessions of the found files
- Check Alyx if corresponding datasetTypes have been registered as existing
sessions and files on Flatiron
- Delete local raw file if found on Flatiron
- looks for datasets matching filename pattern
- datasets that exist in ONE cache are removed
"""
import argparse
import logging
from fnmatch import fnmatch
from pathlib import Path

import one
from one.alf.files import get_session_path
from one.api import ONE

log = logging.getLogger("iblrig")
log = logging.getLogger('iblrig')

try: # Verify ONE-api is at v1.13.0 or greater
assert(tuple(map(int, one.__version__.split('.'))) >= (1, 13, 0))
from one.alf.cache import iter_datasets, iter_sessions
except (AssertionError, ImportError) as e:
if e is AssertionError:
log.error("The found version of ONE needs to be updated to run this script, please run a 'pip install -U ONE-api' from "
"the appropriate anaconda environment")
raise

def session_name(path) -> str:
"""Returns the session name (subject/date/number) string for any filepath
using session_path"""
return "/".join(get_session_path(path).parts[-3:])

def session_name(path, lab=None) -> str:
"""
Returns the session name (subject/date/number) string for a given session path. If lab is given
returns lab/Subjects/subject/date/number.
"""
lab = f'{lab}/Subjects/' if lab else ''
return lab + '/'.join(get_session_path(path).parts[-3:])

def purge_local_data(local_folder, file_name, lab=None, dry=False):
# Figure out datasetType from file_name or file path
file_name = Path(file_name).name
alf_parts = file_name.split(".")
dstype = ".".join(alf_parts[:2])
print(f"Looking for file <{file_name}> in folder <{local_folder}>")
# Get all paths for file_name in local folder

def local_alf_paths(root_dir, filename):
"""Yield session path and relative paths of ALFs that match filename pattern"""
for session_path in iter_sessions(root_dir):
for dataset in iter_datasets(session_path):
if fnmatch(dataset, filename):
yield session_path, dataset


def purge_local_data(local_folder, filename='*', lab=None, dry=False, one=None):
# Figure out datasetType from filename or file path
local_folder = Path(local_folder)
files = list(local_folder.rglob(f"*{file_name}"))
print(f"Found {len(files)} files")
print(f"Checking on Flatiron for datsetType: {dstype}...")
# Get all sessions and details from Alyx that have the dstype
one = ONE(cache_rest=None)
if lab is None:
eid, det = one.search(dataset_types=[dstype], details=True)
else:
eid, det = one.search(dataset_types=[dstype], lab=lab, details=True)
urls = []
for d in det:
urls.extend(
[
x["data_url"]
for x in d["data_dataset_session_related"]
if x["dataset_type"] == dstype
]
)
# Remove None answers when session is registered but dstype not htere yet
urls = [u for u in urls if u is not None]
print(f"Found files on Flatiron: {len(urls)}")

# Get matching files that exist in ONE cache
to_remove = []
for f in files:
sess_name = session_name(f)
for u in urls:
if sess_name in u:
to_remove.append(f)
print(f"Local files to remove: {len(to_remove)}")
for f in to_remove:
print(f)
if dry:
one = one or ONE()
for session_path, dataset in local_alf_paths(local_folder, filename):
session = session_name(session_path, lab=lab)
eid = one.to_eid(session)
if not eid:
continue
matching = one.list_datasets(eid, dataset.as_posix())
if not matching:
continue
else:
assert len(matching) == 1
to_remove.append(local_folder.joinpath(session_path, dataset))

log.info(f'Local files to remove: {len(to_remove)}')
for f in to_remove:
log.info(f'DELETE: {f}')
if not dry:
f.unlink()
return
return to_remove


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Delete files from rig")
parser.add_argument("folder", help="Local iblrig_data folder")
parser.add_argument("file", help="File name to search and destroy for every session")
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Delete files from rig')
parser.add_argument('folder', help='Local iblrig_data folder')
parser.add_argument('file', help='File name to search and destroy for every session')
parser.add_argument(
"-lab", required=False, default=None, help="Lab name, search on Alyx faster. default: None",
'-lab', required=False, default=None, help='Lab name, in case sessions conflict between labs. default: None',
)
parser.add_argument(
"--dry", required=False, default=False, action="store_true", help="Dry run? default: False",
'--dry', required=False, default=False, action='store_true', help='Dry run? default: False',
)
args = parser.parse_args()
purge_local_data(args.folder, args.file, lab=args.lab, dry=args.dry)
print("Done\n")
print('Done\n')
6 changes: 1 addition & 5 deletions tasks/_iblrig_misc_bpod_ttl_test/trial_params.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author: Niccolò Bonacchi
# @Date: 2018-02-02 14:06:34
import datetime
import json
import logging

Expand Down Expand Up @@ -92,7 +89,6 @@ def next_trial(self):
import time
import task_settings as _task_settings
import scratch._user_settings as _user_settings
import datetime # noqa

dt = datetime.datetime.now()
dt = [
Expand Down
6 changes: 6 additions & 0 deletions test_iblrig/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
OPENALYX_PARAMETERS = {
"base_url": "https://openalyx.internationalbrainlab.org",
"username": "intbrainlab",
"password": "international",
"silent": True
}
Loading

0 comments on commit e95c91e

Please sign in to comment.