All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Alyx-API-Notice header can be used to attach information for ONE users
- Detail key no longer included in pagination response if user likely on unsupported ONE version
- Breeding pair parent no longer cleared when saving Line admin changeview
- Public databases can offer self-registration, enabled with the
PUBLIC_DATABASElab setting. Adds a sign-up page at/signup, email confirmation of new accounts, and a password reset flow so that self-registered accounts are recoverable without an administrator set_public_permissionsmanagement command, creating a 'Public users' group carrying view permissions only, and never over user accounts or the permission structure itself- ec2_modify_groups.sh script now included in container to remove EC2 firewall before letsencrypt validation
- Public users are no longer shown other people's accounts. Over REST,
/usersreturns redacted users and the requester only, without email addresses; in the admin, the user and group models are hidden and user filter dropdowns no longer enumerate every account set_user_permissionsskips public users, which it previously added to the lab members group and marked active - granting members of the public write access and activating accounts that had never confirmed their email address
- Core-api support for the
/docsendpoint.SpectacularRedocViewCoreAPIDeprecationanddata/coreapi.jsonare gone;/docsnow servesSpectacularRedocViewdirectly regardless of the request'sAcceptheader. #1055
- Subjects in different labs may share a nickname. REST endpoints taking a subject nickname
returned a 500 status when it matched more than one subject; the lab is now used to resolve
duplicates - the
labfield of the request data,?lab=on/subjects/<nickname>and/water-requirement/<nickname>, orlabsand the repository's labs for/register-file. An unresolved duplicate returns a 400 status, or 409 for a lookup, naming the labs./subjects/<id>also accepts the subject UUID. - Open water restrictions are ended when a subject already has a death date, e.g. when a cull is added with the same date, not only when the death date is first set. The subject's protocol number is now updated when its water restrictions are ended.
- Relative paths correctly rendered in cache table using pandas 3.
- The data notice admin change page no longer renders the datasets as a multi-select widget:
every attached dataset had to be fetched and rendered as a selected option, which did not scale past a few hundred, and saving posted one form field per dataset, which exceeded
DATA_UPLOAD_MAX_NUMBER_FIELDSand failed with a 400 status. The datasets are now a read-only, scrollable list of the first 100, with the total count and a link to them in the dataset list. They can still be attached when creating a notice, or through the REST API.
- Sessions and probe insertions
tagREST filters no longer join the datasets table into the main query, which made them 3-174x faster. tagfilter on the fields-of-view REST endpoint.FOVFilterhad afilter_tagmethod but never declared the filter that routes to it, so/fields-of-view?tag=was silently ignored.dataset_qc_lteREST filter returned sessions and insertions once per matching dataset, so the rows repeated across the paginated response and the count was the number of (row, dataset) pairs:/sessions?dataset_qc_lte=WARNINGreported 3,234,065 of 94,594 sessions.atlas_name,atlas_acronymandatlas_idREST filters returned a session once per insertion recording the region, and likewise an imaging stack once per slice.- The
dataset_types,datasets,dataset_qc_lteandatlas_*REST filters now count and match in a subquery instead of joining and grouping over the full row, making them up to 3.5x faster (/sessions?datasets=55 s to 16 s,/chronic-insertions?atlas_acronym=0.7 s to 0.01 s)./insertions?tag=&dataset_qc_lte=previously timed out, as the two joins multiplied. - The
datasetsREST filter counted the matching datasets rather than the distinct names on the insertions and fields-of-view endpoints, so a row holding two datasets of one requested name passed as having two different ones:/insertions?datasets=now agrees with/sessions?datasets=. One name matches several datasets across collections and revisions for 142,624 (session, name) and 50,078 (insertion, name) pairs, so this over-returned by ~4x on affected queries. - A dataset name repeated in a
datasetsREST filter, e.g.?datasets=obj.attr.npy,obj.attr.npy, returned nothing at all rather than the rows having that dataset. - The chronic insertions REST list eagerly loaded its nested probe insertions while serialising
each row, which discarded the prefetched result and cost two queries per chronic insertion:
/chronic-insertionsissued 329 queries for 176 rows, and now issues 4 regardless of the number of rows (1.7x faster overall, 2.3x less time in the database). - The projects of a session, and the probe insertions of a chronic insertion, are now serialised in
a defined order.
Projecthas noMeta.orderingand insertions of one chronic insertion often share a session start time and name, so the database was free to return either in any order. - The sessions, insertions, fields-of-view and chronic insertions REST lists are now totally
ordered, so a page is the same from one request to the next. 916 sessions share a start time with
another and 120 insertions share a session start time and name, and rows tied that way came back
in a different order each request;
/chronic-insertionshad no ordering at all. The keys are now(-start_time, number, subject, pk)for sessions and(-session start_time, name, subject, session number, pk)for insertions and fields of view, the primary key being what makes them total:SessionandChronicInsertiondeclare no uniqueness, and the insertion constraint is on (session, name), which does not separate two sessions sharing a start time. /imaging-stacksreturned a stack once per slice, serving 250 rows covering 162 of the 174 stacks while reporting 174, as the list was ordered byslices__nameand so joined the slices into the query. The stacks are ordered by their own fields now and the slices within each stack by the prefetch.- Admin no longer fails to start with djangorestframework >= 3.18
dataset_qc_ltefilter on the fields-of-view REST endpoint, matching the sessions and probe insertions endpoints. Thedatasetsfilter there now applies the QC bound too, as it does on those endpoints, rather than ignoring it.- Index on
data_dataset.name, which the?datasets=REST filters look up and which had no index (23 MB; deduplicated, as a few hundred names repeat across the table). Built concurrently, as the table is continuously written./insertions?datasets=is 7x faster, but note/sessions?datasets=with two or more names is ~1.3x slower, as the planner switches from a parallel sequential scan to a bitmap scan past roughly 5% of the table. - A single dataset name in the sessions
datasetsREST filter is now matched with one Exists semi-join anchored on the session, rather than gathering every dataset of that name in the table to group them:/sessions?datasets=with one name is 1.8x faster (9.9 s to 5.4 s), and 8x faster than before this release. Several names, or adataset_qc_ltebelow WARNING, keep the grouped count, both being cases where the semi-join measured no better or worse. The insertions and fields-of-view filters are not special-cased, measuring the same either way.
- The custom admin site is now installed through
alyx.apps.AlyxAdminConfiginstead of by reassigningdjango.contrib.admin.site. Deployments must replace'django.contrib.admin'with'alyx.apps.AlyxAdminConfig'in theirINSTALLED_APPS, otherwise the stock Django admin index is served in place of the Alyx one.alyx.base.mysiteis gone; usedjango.contrib.admin.site, which now refers to the site Alyx serves. - Groups are administered through
django.contrib.auth'sGroupAdminrather than a bareModelAdmin, so group permissions get the two-pane selector
subjects.default_responsibleselects the primary key rather than the whole user record, so that migrating from scratch is not broken by columns added toLabMemberin later migrations- No longer required to quote special characters in database connection settings
- Scoped delete_zygosity_rule post delete hook, improving performance on delete
- Removed unused dj_database_url dependency
- Omit WSGI from CI code coverage
- Changed default docker project name
- Use commit message for release description on squash commits to master
- Pin ruff and remove flake8
- DOI badge in README
- Release steps to README
- Correct relative paths from aggregate datasets with revisions, registered via REST
- Added missing migration file
- Workflow tests now run in parallel and asserts no new migrations
DataNoticemodel to attach information/notices to datasets (#1007)- Registered-file validation (#1014)
- CI/CD release pipeline: bumping
__version__onmastercuts a git tag + GitHub release and builds/pushes thealyx_apache[_base]docker images, gated behind an ansible smoke test (#954)
- The production docker image and compose are now built from this repository (
deploy/app/) as the single source of truth;iblalyxis no longer baked into the image (bind-mounted at deploy time), and deploy orchestration (ansible, per-server overrides) lives iniblsre(#1017) - Restructured
deploy/intoapp/(production) andeditable/(postgres-only for editable installs) - Bump Django 5.2.14 → 5.2.15 (#1012) and Pillow 12.2.0 → 12.3.0 (#1019)
- Remove unknown dataset type from the test dump fixture (#1021)
- Flaky task-cleanup test (save within the datetime mock context)
- to be culled filter in cull subjects admin
- support registration of datasets unassociated to a session
- sort insertions by name
- use ONE Globus class for transfers
- user REST request rate limits
- error in register-files endpoint when labs is a list
- added actual severity column to SubjectCullAdmin
- renamed 'Mice' to 'Subject' in views
- all alive filters depend on death date instead of cull
- django 5.1 deprecation: CheckConstraint check -> condition
- various filter typos, e.g. 'To be reduced' filter now works
- improvements to notifications performance
- fix log typo in delete_expired_notifications management command
- moved prune_cortexlab.py to iblalyx repository
- removed test for removed subject death save logic
- fixed command for dumping test database fixtures
- in
alyx.miscthe one_cache command module contains utils to generate cache dataframes from sessions and datasets querysets
- default implant weight is 0 and not none to allow plotting of water curves.
- water restriction admin form makes sure the current subject is selectable even if it is not alive anymore.
- water history plots: the weight thresholds is
(w - iw) / (ew - iw), wherewis the measured weight,iwthe implant weight andewthe expected weight. Expected weight is(ew = (rw * a + zw *b) + iw(weighted sum of reference weight and zscore weight + implant weight). The display was computing thresholds according tow/ew, not taking into account the implant weight and is now fixed.
- The documentation endpoint
/docsis only for the browser and uses openapiv3. The database schemes are accessed through/api/schema. For compatibility, if the headers requirecoreapi, the endpoint returns a frozen set of endpoint to the client. #929 - narrative template is now available on the base action instead of only for surgeries #938
- coreapi dependency is removed