Skip to content
Merged

3.3.0 #945

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
4ef8e4b
update documentation
oliche May 22, 2025
a291e56
documentation WIP
oliche May 22, 2025
183a1ff
docs: motivation and reference parts
oliche May 22, 2025
a627fa5
Merge remote-tracking branch 'origin/master' into deploy
oliche May 26, 2025
4fbf94c
need to commit requirements to build image
oliche May 26, 2025
d352e4c
add whitenoise to serve static files
oliche May 26, 2025
798bd0d
getting started example uses gunicorn instead of django dev
oliche May 26, 2025
d6b4c38
Update docker files for certbot
oliche May 28, 2025
5b216ab
typo
oliche May 29, 2025
4855b08
pin django-filters to 24.2 #929
oliche May 29, 2025
df57c80
remove deprecated settings templates
oliche May 29, 2025
7b7fced
Merge remote-tracking branch 'origin/master' into deploy
oliche May 29, 2025
11e96d0
work on the settings files to provide s3 and file backends
oliche May 29, 2025
9477347
fix settings for media root
oliche May 29, 2025
a612c2b
Skip 443 virtual host if ssl module not enabled
k1o0 Jun 17, 2025
dc631b9
add instructions to run a development server with 3.13
oliche Jun 17, 2025
9e922d5
small documentation updates
oliche Jun 17, 2025
9728dac
APACHE_SERVER_ADMIN env var
k1o0 Jul 1, 2025
084904a
Removed IBL-specific dataset type fixtures to iblalyx repo (#928)
k1o0 Jul 1, 2025
3012fad
Correct postgres url typo; change media -> uploaded
k1o0 Jul 9, 2025
71442a4
Minor change to allowed hosts
k1o0 Jul 9, 2025
b0cd5da
add dependabot minimum versions
oliche Jul 17, 2025
fc78509
modify CI to run with unique settings
oliche Jul 17, 2025
1fe8d61
add settings lab for CI
oliche Jul 17, 2025
dfd1aa4
add log directory that matches the env file in the CI
oliche Jul 17, 2025
a1be637
permissions
oliche Jul 17, 2025
21a8a00
set environment variables
oliche Jul 17, 2025
25b8f9b
Ci (#940)
oliche Jul 17, 2025
f1a37fb
docker compose for local postgres has a specific env file
oliche Jul 29, 2025
9f0aff0
updates w/ Miles
oliche Jul 29, 2025
6dfca60
update instructions for getting started
oliche Jul 29, 2025
fa49568
allow a custom `settings_lab-user.py` file to build the container
oliche Jul 29, 2025
2bd611d
use the main env file in docker containers
oliche Jul 31, 2025
fd3a2de
add the maintenance_trigger redirect in apache configuration file
oliche Aug 1, 2025
5b2ecc2
Change DJANGO_SECRET_KEY quotes from double to single
k1o0 Aug 28, 2025
87b912b
New backend for automatic REST docs #929
oliche Sep 11, 2025
3166250
maintain endpoint /docs with coreapi headers
oliche Sep 17, 2025
a0ae496
move to ruff - update CI versions
oliche Sep 17, 2025
f60f298
Merge pull request #939 from cortex-lab/deploy
oliche Sep 17, 2025
6b8b9f7
Move narrative templates to base action admin (#938)
k1o0 Sep 17, 2025
ac92eae
add contribution checklist to README and CHANGELOG
oliche Sep 17, 2025
1b1d156
Breeding Pair form validation removes parents when culled (#944)
oliche Oct 21, 2025
55a28af
Safer db URL
k1o0 Oct 29, 2025
37705d8
Bump version
k1o0 Oct 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions .flake8

This file was deleted.

48 changes: 13 additions & 35 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
image: postgres:17
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
Expand All @@ -34,7 +34,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.12'
python-version: '3.13'

- name: Checkout branch
uses: actions/checkout@v3
Expand All @@ -46,49 +46,27 @@ jobs:
pip install coverage coveralls pyarrow pandas # for one_cache tests
env:
PIP_USE_MIRRORS: true

- name: Ruff
run: |
ruff check ./alyx
- name: Run tests
run: |
sudo touch /var/log/alyx.log; sudo chmod 666 /var/log/alyx.log
sudo touch /var/log/alyx_json.log; sudo chmod 666 /var/log/alyx_json.log
sudo mkdir /var/log/alyx
sudo touch /var/log/alyx/django.log
sudo chmod 666 /var/log/alyx/django.log
cd alyx
cp alyx/settings_ci.py alyx/settings.py
cp ./alyx/environment_template.env ./alyx/.env
cp ../deploy/docker/settings-deploy.py alyx/settings.py
cp ../deploy/docker/settings_lab-deploy.py alyx/settings_lab.py
python manage.py collectstatic --noinput --link
coverage run manage.py test -n
coveralls --service=github
env:
DJANGO_SETTINGS_MODULE: alyx.settings
PYTHONPATH: $HOME/builds/cortexlab/alyx
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Flake
run: |
cd alyx
flake8 .

- name: Generate new requirements_frozen.txt if needed
# Only runs when branch pushed to directly OR when a PR is merged
if: ${{ github.event_name == 'push' }}
run: |
pip freeze > requirements_frozen_temp.txt
if diff requirements_frozen.txt requirements_frozen_temp.txt > /dev/null; then
echo "requirements_frozen.txt unchanged"
rm requirements_frozen_temp.txt
echo "GIT_PUSH_NEEDED=false" >> "$GITHUB_ENV"
else
echo "requirements_frozen.txt is different, git push needed"
mv requirements_frozen_temp.txt requirements_frozen.txt
echo "GIT_PUSH_NEEDED=true" >> "$GITHUB_ENV"
fi

- name: Setup git/commit/push for requirements_frozen.txt if needed
# Only runs when requirements_frozen.txt is updated
if: env.GIT_PUSH_NEEDED == 'true'
run: |
git config user.name github-actions
git config user.email [email protected]
git add requirements_frozen.txt
git commit -m "GitHub Actions generated requirements_frozen.txt"
git push
APACHE_LOG_DIR: /var/log/alyx
POSTGRES_HOST: localhost

# Docker steps only run when master branch pushed to directly OR when a PR is merged
- name: Set Docker conditional value if needed
Expand Down
28 changes: 20 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# environment files for deployments
.env*
# all of the iblreports sym links
alyx/data/management/commands/*ibl*
alyx/data/management/commands/sync_patcher.py
alyx/ibl_reports
alyx/templates/ibl_reports
# setting files
alyx/alyx/settings_secret.py
alyx/alyx/settings_lab.py
alyx/alyx/settings.py
alyx/static/*/*
scripts/deployment_examples/docker-apache/settings*
# this is a custom lab settings to be provided by the user rebuilding the head docker
deploy/docker/settings_lab-user.py

# package if installed
alyx/alyx.egg-info

# other
*.pyc
.DS_Store
*~
Expand All @@ -24,14 +44,6 @@ build
.cache/
alyx/alyx_full.sql.gz
.vscode/
alyx/data/management/commands/*ibl*
alyx/ibl_reports
alyx/templates/ibl_reports
alyx/alyx/settings_secret.py
alyx/alyx/settings_lab.py
alyx/alyx/settings.py
alyx/static/*/*
scripts/deployment_examples/docker-apache/settings*

alyx/.idea/

Expand Down
8 changes: 2 additions & 6 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@ version: 2

# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
os: ubuntu-24.04
tools:
python: "3.9"
# You can also specify other tool versions:
# nodejs: "16"
# rust: "1.55"
# golang: "1.17"
python: "3.12"

# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added


### Changed

- The documentation endpoint `/docs` is only for the browser and uses openapiv3. The database schemes are accessed through `/api/schema`. For compatibility, if the headers require `coreapi`, the endpoint returns a frozen set of endpoint to the client. [#929](https://github.com/cortex-lab/alyx/pull/929)

- narrative template is now available on the base action instead of only for surgeries [#938](https://github.com/cortex-lab/alyx/pull/938)


### Removed

- coreapi dependency is removed
43 changes: 33 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,53 @@

Database for experimental neuroscience laboratories

Documentation: [Installation and getting started](http://alyx.readthedocs.io), [Alyx usage guide](https://docs.google.com/document/d/1cx3XLZiZRh3lUzhhR_p65BggEqTKpXHUDkUDagvf9Kc/edit?usp=sharing)
[Documentation](https://alyx.readthedocs.io)

[Alyx Experimenter Guide](https://docs.google.com/document/d/1cx3XLZiZRh3lUzhhR_p65BggEqTKpXHUDkUDagvf9Kc/edit?usp=sharing)


## Installation
Alyx has only been tested on Ubuntu (16.04 / 18.04 / 20.04), the latest is recommended. There are no guarantees that
this setup will work on other systems. Assumptions made are that you have sudo permissions under an account named

[The getting started](docs/gettingstarted.md) section of the documentation details the steps for
[The getting started](https://alyx.readthedocs.io/en/latest/gettingstarted.html) section of the documentation details the steps for
- installing the Python/Django environment
- serving a local database
- running the app with a development server
- registering local data
- accessing local data using [ONE](https://one.internationalbrainlab.org)

More complex deployments scenarios using web servers and Cloud applications are in the [how-to guides section of the documtentaiton](docs/how-to-guides)

## Contribution

* Development happens on the **dev** branch
* alyx is sync with the **master** branch
* alyx-dev is sync with the **dev** branch
* Migrations files are provided by the repository
* Continuous integration is setup, to run tests locally:
* Migrations files are always provided by the repository

Contribution checklist:
- [ ] lint using ruff `ruff check .` at the root of the repository
- [ ] tests pass (see below how to run tests)
- [ ] migrations are provided with the commit
- [ ] update version number in `./alyx/alyx/__init__.py`
- [ ] update `CHANGELOG.md`


### Running tests

Continuous integration is setup. But before submitting a PR or commit,the tests can run locally.
- `./manage.py test -n` test without migrations (faster)
- `./manage.py test` test with migrations (recommended if model changes)
- NB: When running tests ensure `DEBUG = True` in the settings.py file (specifically `SECURE_SSL_REDIRECT = True` causes REST tests to fail)

```shell
./manage.py test -n
### Documentation contribution guide

#### Dependencies
```
pip install myst-parser sphinx_rtd_theme sphinx-autobuild
```

#### Build documentation locally

From the root of the repository.
````shell
sphinx-autobuild -b html ./docs ./docs/_build/ --port 8700
````
https://www.scan.co.uk/products/3xs-evolve-studio-pro-intel-core-ultra-9-285k-64gb-ddr5-16gb-nvidia-rtx-5070-ti-super-1tb-ssd-2tb-ss
26 changes: 13 additions & 13 deletions alyx/actions/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import base64
import json
import structlog
import logging

from django import forms
from django.conf import settings
Expand All @@ -27,7 +27,7 @@
from experiments.models import ProbeInsertion, FOV
from jobs.models import Task

logger = structlog.get_logger(__name__)
logger = logging.getLogger(__name__)


# Filters
Expand Down Expand Up @@ -213,6 +213,17 @@ def get_form(self, request, obj=None, **kwargs):
form.last_subject_id = self._get_last_subject(request)
return form

def change_view(self, request, object_id, extra_context=None, **kwargs):
context = extra_context or {}
context = _pass_narrative_templates(context)
return super(BaseActionAdmin, self).change_view(
request, object_id, extra_context=context, **kwargs)

def add_view(self, *args, extra_context=None):
context = extra_context or {}
context = _pass_narrative_templates(context)
return super(BaseActionAdmin, self).add_view(*args, extra_context=context)

def formfield_for_foreignkey(self, db_field, request, **kwargs):
# Logged-in user by default.
if db_field.name == 'user':
Expand Down Expand Up @@ -616,17 +627,6 @@ def get_form(self, request, obj=None, **kwargs):
).distinct()
return form

def change_view(self, request, object_id, extra_context=None, **kwargs):
context = extra_context or {}
context = _pass_narrative_templates(context)
return super(SessionAdmin, self).change_view(
request, object_id, extra_context=context, **kwargs)

def add_view(self, request, extra_context=None):
context = extra_context or {}
context = _pass_narrative_templates(context)
return super(SessionAdmin, self).add_view(request, extra_context=context)

def project_(self, obj):
return [getattr(p, 'name', None) for p in obj.projects.all()]

Expand Down
6 changes: 3 additions & 3 deletions alyx/actions/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import timedelta
from math import inf

import structlog
import logging
from one.alf.spec import QC

from django.conf import settings
Expand All @@ -13,7 +13,7 @@
from misc.models import Lab, LabLocation, LabMember, Note


logger = structlog.get_logger(__name__)
logger = logging.getLogger(__name__)


def _default_water_type():
Expand Down Expand Up @@ -387,7 +387,7 @@ class OtherAction(BaseAction):

def delay_since_last_notification(notification_type, title, subject):
"""Return the delay since the last notification corresponding to the given
type, title, subject, in seconds, wheter it was actually sent or not."""
type, title, subject, in seconds, whether it was actually sent or not."""
last_notif = Notification.objects.filter(
notification_type=notification_type,
title=title,
Expand Down
4 changes: 2 additions & 2 deletions alyx/actions/notifications.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import structlog
import logging
from textwrap import dedent

from django.utils import timezone

from actions.models import create_notification


logger = structlog.get_logger(__name__)
logger = logging.getLogger(__name__)


def responsible_user_changed(subject, old_user, new_user):
Expand Down
4 changes: 2 additions & 2 deletions alyx/actions/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,8 @@ def test_notif_water_1(self):
def test_notif_water_2(self):
# If the last water admin was on June 3 at 12pm, the notification
# should be created after June 4 at 11am.
l = ((9, False), (10, False), (11, True), (12, True))
for (h, r) in l:
teupeul = ((9, False), (10, False), (11, True), (12, True))
for (h, r) in teupeul:
date = timezone.datetime(2018, 6, 4, h, 0, 0)
check_water_administration(self.subject, date=date)
notif = Notification.objects.last()
Expand Down
12 changes: 6 additions & 6 deletions alyx/actions/tests_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,15 +548,15 @@ def test_list_retrieve_water_restrictions(self):
def test_list_retrieve_lab_locations(self):
# test list
url = reverse("location-list")
l = self.ar(self.client.get(url))
self.assertTrue(len(l) > 0)
self.assertEqual(set(l[0].keys()), {"name", "json", "lab"})
reponse = self.ar(self.client.get(url))
self.assertTrue(len(reponse) > 0)
self.assertEqual(set(reponse[0].keys()), {"name", "json", "lab"})
# test detail
url = reverse("location-detail", args=[l[0]["name"]])
url = reverse("location-detail", args=[reponse[0]["name"]])
d = self.ar(self.client.get(url))
self.assertEqual(d, l[0])
self.assertEqual(d, reponse[0])
# test patch
url = reverse("location-detail", args=[l[0]["name"]])
url = reverse("location-detail", args=[reponse[0]["name"]])
json_dict = {
"string": "look at me! I'm a Json field",
"integer": 15,
Expand Down
4 changes: 2 additions & 2 deletions alyx/actions/water_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from dateutil.rrule import HOURLY
import functools
import io
import structlog
import logging
from operator import attrgetter, itemgetter
import os.path as op

Expand All @@ -17,7 +17,7 @@
import numpy as np


logger = structlog.get_logger(__name__)
logger = logging.getLogger(__name__)


PALETTE = {
Expand Down
2 changes: 1 addition & 1 deletion alyx/alyx/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = __version__ = '3.2.2'
VERSION = __version__ = '3.3.0'
Loading