Changelog¶
0.2.8 (2026-05-07)¶
Features
Add
xnatctl resource refresh URI [--options ...]for targeted catalog refresh against/data/services/refresh/catalog. Options accept any ofchecksum,delete,append,populateStats. Replaces the need to drop toapi postfor single scan/resource catalog refreshes.Add
--project/-Ptoxnatctl resource uploadso label-based session resolution works for scan-level and session-level resource uploads. Default falls back to the active profile’sdefault_project.Allow
xnatctl config show -p NAMEto filter the output to a single profile (and its details). Unknown profile names error with the available list. Unfiltered behavior is unchanged.Add
HierarchyServicefor shared path building, response-envelope parsing (ResultSet.Resultanditems[]), and resolution ofProjectRef/SubjectRef/ExperimentRef/ScanRef/ResourceRef. Services and CLI commands now route through it instead of open-coding paths and parsing.Bundle
pydicomandpynetdicomin the standalone PyInstaller binary soxnatctl dicomutilities work out of the box afterinstall.sh.
Bug fixes
Fix
session showandscan listreturning “Session not found” or “No results” for sessions reachable via subject-scoped paths.HierarchyService.resolve_experimentnow falls back to listing/data/projects/{P}/experiments?columns=ID,label,subject_ID,xsiTypewith client-side label/ID match when the direct project-experiment endpoint fails, and tries/data/experiments/{ID}for accession-ID shaped references that don’t resolve in the active project.Narrow
_inspect_experimentexception swallowing incli/scan.pytoResourceNotFoundErroronly; transient network and auth errors now surface to the user instead of silently producing “No results”.Fix
xnatctl api put -f FILEandapi post -f FILEcorrupting binary files withUnicodeDecodeError. The body is now read in binary, with a UTF-8 + JSON probe before falling back to raw bytes. JSON and plain-text paths are unchanged on the wire.Fix
session upload --mode gradual --prearchivesilently uploading to direct-archive: the CLI wrapper now threadsdirect_archivethrough to the gradual-DICOM service. Consolidate prearchive routing through a singlearchive_destination_params()helper that usesdest=/prearchive/projects/{p}instead ofDirect-Archive=false. Help text now warns that--prearchiveis best-effort against projects with auto-archive enabled.Fix
subject mergesilently destroying experiments. Replace the globalPUT /data/experiments/{id}?xnat:experimentData/subject_ID=...shortcut with the scopedPUT /data/projects/{p}/subjects/{target_id}/experiments/{id}that the XNAT web UI uses, plus per-experiment post-PUT verification before the source-subject delete.SubjectService.delete()now refuses to delete subjects with experiments still attached unlessforce=True.De-flake
tests/test_archive_poller_zero_vs_erroron slower CI runners by waiting on the actual observable (zero_scan_cycles >= 1) rather than the racy proxy.Various resource-listing and transfer edge-case fixes carried into the HierarchyService refactor.
Documentation
Document that
xnatctl resource uploadPUTs files directly to the resource catalog and bypasses XNAT project-level DICOM anonymization scripts and pipelines. Usexnatctl session upload/xnatctl session upload-examwhen anonymization is required.
Refactors
Extract
cli/common.pycontext helpers (get_profile,default_project_from_context,require_project_from_context,resolve_workers_from_context) and adopt them across CLI commands.Migrate services and CLI from open-coded
ResultSet.Result/items[]parsing toHierarchyService.extract_rows/extract_first_item.Session.get()now mergesmeta["xsi:type"]into the model when the field-level value is missing (fixes non-imaging session xsiType). Behavior note: a session label that cannot be resolved in a project now raisesResourceNotFoundError(wasValueError).
0.2.7 (2026-04-20)¶
Features
Add
xnatctl dicom modifyfor batch in-place DICOM tag editing across single files or directories, with repeatable--tag KEYWORD=VALUE, recursive search,--backup,--dry-run, atomic writes, and JSON/table output modes.
Bug fixes
Switch prearchive archiving to XNAT’s archive service API (
/data/services/archive) for bothprearchive archiveand transfer-side prearchive resolution.Surface archive-service failures during transfer instead of letting them degrade into silent archive wait timeouts; blocking fallback paths now record per-experiment archive errors consistently.
Encode archive-service path segments for project, timestamp, session, subject, and experiment values to avoid malformed archive and prearchive-delete requests.
0.2.6 (2026-03-24)¶
CLI simplification
Consolidate
--unzip/--cleanupinto--extract/--no-extracton session and scan download commands. Hidden backward-compat aliases preserved.Consolidate
--gradual/--archive-formatinto--mode {tar|zip|gradual}on session upload. Hidden backward-compat aliases preserved.Collapse
--wait-for-archive/--wait-timeout/--wait-intervalinto single--wait SECONDSflag on session upload-exam (0 = skip, default: 900).Eliminate
--parallel/--no-paralleltoggle; unify to--workersacross all commands that use@parallel_options(scan delete, admin refresh-catalogs, project transfer).Reserve
-Pfor--projecteverywhere (removed fromapi --paramsandpipeline --param).Reserve
-wfor--workerseverywhere (removed frompipeline --waitandpipeline --watch).Normalize
-Eto--experimenton session upload and upload-exam (hidden--sessionalias preserved for backward compatibility).Add profile operational defaults:
workers,overwrite,direct_archive,archive_mode,extract. CLI flags override profile values.Hide advanced flags from
--help(still accepted):--username,--password,--zip-to-tar,--ignore-unparsable,--misc-label,--calling-aet,--name,--session-resources,--dest-url,--dest-user,--dest-pass.Standardize destructive UX: apply
@confirm_destructive(adds--dry-run) toprearchive delete,pipeline cancel,config remove-profile.
Bug fixes
Upload: validate DICOM magic bytes for extensionless files (fixes non-DICOM files like
psbeing uploaded via gradual-DICOM).Upload: add
--direct-archiveflag toupload-exam(was previously missing; gradual-DICOM uploads now passDirect-Archivequery parameter).
0.2.5 (2026)¶
Bug fixes
Auto-refresh XNAT session token on HTTP 401 during large gradual-DICOM uploads, preventing mass failures when the 15-minute session TTL expires
Add manual PyPI publish workflow (
workflow_dispatch) as fallbackFix CI
alls-greencheck treating skipped release jobs as failures
0.2.4 (2026)¶
Bug fixes
Fix
auth loginand top-levelwhoamiso they resolve the authenticated user from dedicated current-user endpoints instead of treating/data/useras a whoami endpoint
0.2.3 (2026)¶
Bug fixes
Ignore non-DICOM sidecar files such as
.txtand.pdfduring gradual DICOM uploads from directories, ZIP archives, and explicit file listsRefactor transfer scan sync to use a two-phase download-then-upload flow
Strip the session
labelattribute from transfer XML overlay uploads to prevent destination-side HTTP 400 errorsAdd transfer debug logging around DICOM import and XML overlay failures
0.2.2 (2026)¶
Bug fixes
Reconcile experiments deleted from destination during incremental transfer
Save experiment ID mappings for future reconciliation
Preserve special characters (colons, brackets) in
api get/put/post/deletequery parameter keysResolve
xsiTypefor non-imaging sessions insession showscan listing
0.2.1 (2026)¶
Features
Pipelined transfer: overlap DICOM uploads with server-side archiving via background poller thread
max_pending_archivesconfig field to throttle concurrent server-side import jobs
Bug fixes
Reconcile previously-synced subjects deleted from destination
Use
folderName(notname) for prearchive archive requests inwait_for_archiveAdd exception guard around
wait_for_archivepoll loop for transient HTTP error resilienceFlatten ZIP hierarchy for non-DICOM resource uploads
Ensure experiment is created when all DICOM uploads fail but DICOM was expected
Resolve
xsiTypecorrectly for non-imaging sessions in scan list
0.2.0 (2026)¶
Features
project transfercommand for cross-instance project synchronisationTransfer orchestrator with per-scan pipeline, retry, and verification
Transfer executor with DICOM-zip import and non-DICOM resource repack
Discovery service for subjects, experiments, and scans
Filter engine for xsiType, scan type, and resource label filtering
XML metadata overlay to preserve session/scan metadata after DICOM import
Prearchive resolution (READY/CONFLICT) during archive wait
Scan resource caching across DICOM and non-DICOM transfer phases
Deferred experiment creation (skip pre-create when DICOM import will create)
Dest-profile CLI helper for dual-instance configuration
Bug fixes
Handle XNAT timestamps with fractional seconds and missing
last_modifiedReject multiple
--resourcevalues inscan download
Docs
Add project transfer command documentation
Update session downloading guide for multi-resource support
0.1.3 (2026)¶
Fix server version endpoint (use
/xapi/siteConfig/buildInfo/version)Build Linux binary on manylinux_2_28 for RHEL 8+/AlmaLinux 9 compatibility
0.1.2 (2026)¶
Bug fixes
Handle 409 Conflict when creating a resource that already exists (
session upload-exam --attach-only)Tolerate missing resource IDs and non-numeric counts in resource responses
Skip experiment lookup when session has no resources
Scope gradual upload clients and reject duplicate files
Validate exam root directory and sort scan classification
Use
filesinput for codecov-action v5
Features
session upload-examcommand for uploading scanner exam-root directories (DICOM + top-level resources)Wait for archived session before attaching resources in
upload-examGradual DICOM upload from explicit file lists
Exam-root classification for mapping directory structure to XNAT resources
CI: harden security (SHA-pinned actions, minimal permissions,
persist-credentials: false)CI: cross-platform test matrix (Ubuntu 3.11/3.12/3.13, macOS 3.12, Windows 3.12)
CI: macOS arm64 binary build
CI: uv caching, mypy caching,
alls-greengate jobBatch upload helper script with YAML-driven folder-to-label contract (
scripts/upload_from_folders.py)
Docs
Add DICOM utilities page documenting
xnatctl[dicom]commandsAdd administration page (catalog refresh, user management, audit log)
Rewrite all user-facing documentation for beginner-friendly onboarding
Add shell completion setup instructions
Document
session upload-examand upload method comparison
Refactoring
Rename
admin user add-to-groupstoadmin user add
0.1.1 (2026)¶
Fix Windows binary build (venv activation in CI workflow)
Improve Windows installation docs with PATH setup instructions
Clarify that install script is Linux/macOS only
0.1.0 (2026)¶
Uniform
-E/-Poptions across all session and scan commands-E/--experimentaccepts ID or label (label requires-Por profiledefault_project)default_projectprofile setting now used as automatic-PfallbackConsistent
metavar=ID_OR_LABELand help text on all-EoptionsPyPI trusted publishing via OIDC (stable releases to PyPI, prereleases to TestPyPI)
Multi-platform standalone binaries: Linux, macOS, Windows via PyInstaller
install.shauto-detects OS and architectureCLI integration test suite: 150 tests covering all commands
Service layer unit test suite: 138 tests covering all services
0.0.2 (2025)¶
Add sequential retry mechanism for failed uploads
Implement thread-local HTTP client for gradual-DICOM uploads
Enhance 400 error logging
0.0.1 (2025)¶
Initial release
Core CLI commands: project, subject, session, scan, resource, prearchive, pipeline, admin, api
Profile-based YAML configuration
httpx-based HTTP client with retry logic
Parallel download and upload support
JSON and table output formats