updated the ni_mannheim_kjp.py to do the full conversion, update the previous conversion and perform post-conversion to add "indended for" to AP/PA json files. This also uses the dedicated heuristic-kjp.py.
2132 lines
38 KiB
Python
2132 lines
38 KiB
Python
# /// script
|
|
# requires-python = ">=3.12"
|
|
# dependencies = [
|
|
# "datalad",
|
|
# "datalad-next",
|
|
# "datalad-container",
|
|
# "datalad-core",
|
|
# "nibabel",
|
|
# "pydicom",
|
|
# "trr379-rdmtools @ git+https://hub.trr379.de/q02/rdmtools.git@refs/heads/main",
|
|
# ]
|
|
# ///
|
|
|
|
from datalad_core import repo
|
|
from datalad_core.config import ConfigItem
|
|
from datalad_core.runners import (
|
|
CommandError,
|
|
call_git,
|
|
call_git_oneline,
|
|
call_git_success,
|
|
)
|
|
|
|
from pathlib import (
|
|
Path,
|
|
PurePosixPath,
|
|
)
|
|
|
|
import csv
|
|
import json
|
|
import sys
|
|
import tempfile
|
|
import uuid
|
|
|
|
import nibabel as nib
|
|
|
|
import rich_click as click
|
|
|
|
from rich.progress import (
|
|
Progress,
|
|
track,
|
|
)
|
|
|
|
from shutil import copyfile
|
|
|
|
from trr379_rdmtools.dicom_archive import make_dicom_archive
|
|
from trr379_rdmtools.utils import (
|
|
get_remote_dataset_state,
|
|
has_staged_changes,
|
|
post_failure,
|
|
post_success,
|
|
post_noop_result,
|
|
shallow_clone_submodule,
|
|
)
|
|
|
|
|
|
hub_url = "https://hub.trr379.de"
|
|
|
|
dicom_org = "q01-mannheim-mrisessions"
|
|
|
|
all_dicoms_ds_url = (
|
|
"https://hub.trr379.de/q01-mannheim/mri-dicoms"
|
|
)
|
|
|
|
bids_ds_url = (
|
|
"https://hub.trr379.de/q01-mannheim-data/mri-raw"
|
|
)
|
|
|
|
envvar_prefix = "TRR_"
|
|
|
|
|
|
default_dataset_content = {
|
|
".datalad/.gitattributes":
|
|
"config annex.largefiles=nothing\n",
|
|
|
|
".gitattributes":
|
|
"* annex.backend=MD5E\n"
|
|
"**/.git* annex.largefiles=nothing\n",
|
|
}
|
|
|
|
|
|
@click.group(
|
|
"ni-mannheim-kjp",
|
|
epilog=(
|
|
"See https://hub.trr379.de/q02/rdmtools "
|
|
"for a changelog and open issues."
|
|
),
|
|
)
|
|
def cli():
|
|
"""
|
|
Connect Mannheim Q01 KJP MRI data with the TRR379 RDM system.
|
|
|
|
This workflow follows the Mannheim adult MRI workflow but uses the
|
|
dedicated Mannheim KJP HeuDiConv heuristic and performs KJP specific
|
|
post conversion cleanup.
|
|
|
|
The post conversion step:
|
|
|
|
1. Selects the valid 4D task vid BOLD acquisition.
|
|
2. Removes the reduced slice task vid conversion.
|
|
3. Keeps the task vid SBRef.
|
|
4. Adds session relative IntendedFor metadata.
|
|
5. Does not add B0FieldIdentifier or B0FieldSource.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
def get_archive_dest_rpath(
|
|
dicoms: Path,
|
|
sub_id: str,
|
|
ses_id: str,
|
|
):
|
|
return f"{sub_id}-{ses_id}.tar"
|
|
|
|
|
|
def get_session_dataset_url(
|
|
sub_id: str,
|
|
ses_id: str,
|
|
) -> str:
|
|
|
|
return (
|
|
f"{hub_url}/"
|
|
f"{dicom_org}/"
|
|
f"{sub_id}-{ses_id}.git"
|
|
)
|
|
|
|
|
|
def get_session_dataset_rpath(
|
|
sub_id: str,
|
|
ses_id: str,
|
|
) -> str:
|
|
|
|
return (
|
|
f"sessions/"
|
|
f"{sub_id}/"
|
|
f"{ses_id}"
|
|
)
|
|
|
|
|
|
def create_dataset(
|
|
path,
|
|
private_annex=False,
|
|
) -> repo.Worktree:
|
|
|
|
wt = repo.Worktree.init_at(path)
|
|
|
|
if private_annex:
|
|
|
|
wt.config.sources["git-local"].add(
|
|
"annex.private",
|
|
ConfigItem(
|
|
"true",
|
|
coercer=bool,
|
|
),
|
|
)
|
|
|
|
wt.init_annex()
|
|
|
|
wt.config.sources["datalad-branch"].add(
|
|
"datalad.dataset.id",
|
|
ConfigItem(
|
|
str(uuid.uuid4())
|
|
),
|
|
)
|
|
|
|
for rpath, content in default_dataset_content.items():
|
|
|
|
(
|
|
wt.path
|
|
/ PurePosixPath(rpath)
|
|
).write_text(content)
|
|
|
|
call_git(
|
|
["add", "."],
|
|
cwd=wt.path,
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"commit",
|
|
"-q",
|
|
"-m",
|
|
"new datalad dataset",
|
|
],
|
|
cwd=wt.path,
|
|
)
|
|
|
|
return wt
|
|
|
|
|
|
param_cfg = {
|
|
|
|
"dicoms": {
|
|
|
|
"type": click.Path(
|
|
exists=True,
|
|
file_okay=False,
|
|
dir_okay=True,
|
|
readable=True,
|
|
path_type=Path,
|
|
),
|
|
|
|
"help": (
|
|
"Path to a directory with DICOMs "
|
|
"for a single scanning session"
|
|
),
|
|
},
|
|
|
|
"sub-id": {
|
|
|
|
"help": (
|
|
"Pseudonymized TRR379 Q01 subject "
|
|
"identifier corresponding to the scan"
|
|
),
|
|
},
|
|
|
|
"ses-id": {
|
|
|
|
"help": (
|
|
"TRR379 session identifier "
|
|
"for this subject"
|
|
),
|
|
},
|
|
|
|
("-m", "--message"): {
|
|
|
|
"help": (
|
|
"Commit message WHY a registration "
|
|
"was updated"
|
|
),
|
|
},
|
|
|
|
"--container-cache-dir": {
|
|
|
|
"envvar":
|
|
f"{envvar_prefix}CONTAINERCACHE_DIR",
|
|
|
|
"show_envvar":
|
|
True,
|
|
|
|
"type": click.Path(
|
|
exists=True,
|
|
file_okay=False,
|
|
dir_okay=True,
|
|
path_type=Path,
|
|
),
|
|
|
|
"help": (
|
|
"Directory for locally caching "
|
|
"container image files"
|
|
),
|
|
},
|
|
}
|
|
|
|
|
|
@cli.command(
|
|
"create-session-dataset",
|
|
help=(
|
|
"Create and deposit a new DataLad dataset "
|
|
"for a single scanning session"
|
|
),
|
|
)
|
|
@click.argument(
|
|
"dicoms",
|
|
**param_cfg["dicoms"],
|
|
)
|
|
@click.argument(
|
|
"sub-id",
|
|
**param_cfg["sub-id"],
|
|
)
|
|
@click.argument(
|
|
"ses-id",
|
|
**param_cfg["ses-id"],
|
|
)
|
|
def create_session_dataset(
|
|
dicoms,
|
|
sub_id,
|
|
ses_id,
|
|
):
|
|
|
|
try:
|
|
|
|
with tempfile.TemporaryDirectory() as wdir:
|
|
|
|
gitsha, deposit_url, dsid = (
|
|
_do_session_dataset(
|
|
Path(wdir),
|
|
dicoms=dicoms,
|
|
sub_id=sub_id,
|
|
ses_id=ses_id,
|
|
)
|
|
)
|
|
|
|
except ValueError as e:
|
|
|
|
post_failure(
|
|
str(e)
|
|
)
|
|
|
|
sys.exit(1)
|
|
|
|
post_success(
|
|
"DICOM session DataLad dataset "
|
|
f"deposited at {deposit_url}"
|
|
)
|
|
|
|
|
|
def _do_session_dataset(
|
|
wdir: Path,
|
|
dicoms: Path,
|
|
sub_id: str,
|
|
ses_id: str,
|
|
):
|
|
|
|
deposit_url = (
|
|
get_session_dataset_url(
|
|
sub_id,
|
|
ses_id,
|
|
)
|
|
)
|
|
|
|
if call_git_success(
|
|
[
|
|
"ls-remote",
|
|
deposit_url,
|
|
],
|
|
capture_output=True,
|
|
):
|
|
|
|
msg = (
|
|
"There is already a DICOM session "
|
|
f"dataset for {sub_id}-{ses_id} at "
|
|
f"{deposit_url}. "
|
|
"If the subject/session identifier "
|
|
"is correct, this dataset needs to "
|
|
"be updated manually if needed. "
|
|
"DO NOT DELETE this existing repository, "
|
|
"because this would invalidate existing "
|
|
"metadata."
|
|
)
|
|
|
|
raise ValueError(msg)
|
|
|
|
wt = create_dataset(
|
|
wdir,
|
|
private_annex=True,
|
|
)
|
|
|
|
archive_dest_path = (
|
|
wt.path
|
|
/ get_archive_dest_rpath(
|
|
dicoms,
|
|
sub_id,
|
|
ses_id,
|
|
)
|
|
)
|
|
|
|
make_dicom_archive(
|
|
dicoms,
|
|
archive_dest_path,
|
|
track,
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"annex",
|
|
"add",
|
|
archive_dest_path,
|
|
],
|
|
cwd=wt.path,
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"commit",
|
|
"-m",
|
|
"ingest DICOM session archive",
|
|
],
|
|
cwd=wt.path,
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"remote",
|
|
"add",
|
|
"origin",
|
|
deposit_url,
|
|
],
|
|
cwd=wt.path,
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"push",
|
|
"--all",
|
|
],
|
|
cwd=wt.path,
|
|
)
|
|
|
|
gitsha = call_git_oneline(
|
|
[
|
|
"rev-parse",
|
|
"HEAD",
|
|
],
|
|
cwd=wt.path,
|
|
)
|
|
|
|
return (
|
|
gitsha,
|
|
deposit_url,
|
|
wt.config.get(
|
|
"datalad.dataset.id"
|
|
).value,
|
|
)
|
|
|
|
|
|
@cli.command(
|
|
"register-session-dataset",
|
|
help=(
|
|
"Register a single session dataset in "
|
|
"the all scan sessions DataLad dataset"
|
|
),
|
|
)
|
|
@click.argument(
|
|
"sub-id",
|
|
**param_cfg["sub-id"],
|
|
)
|
|
@click.argument(
|
|
"ses-id",
|
|
**param_cfg["ses-id"],
|
|
)
|
|
@click.option(
|
|
"-m",
|
|
"--message",
|
|
**param_cfg[
|
|
("-m", "--message")
|
|
],
|
|
)
|
|
def register_session_dataset(
|
|
sub_id,
|
|
ses_id,
|
|
message: str | None = None,
|
|
):
|
|
|
|
deposit_url = (
|
|
get_session_dataset_url(
|
|
sub_id,
|
|
ses_id,
|
|
)
|
|
)
|
|
|
|
dsid, gitsha = (
|
|
get_remote_dataset_state(
|
|
deposit_url
|
|
)
|
|
)
|
|
|
|
with tempfile.TemporaryDirectory() as wdir:
|
|
|
|
gitsha, updated = (
|
|
_do_dicoms_dataset(
|
|
Path(wdir),
|
|
sub_id=sub_id,
|
|
ses_id=ses_id,
|
|
gitsha=gitsha,
|
|
deposit_url=deposit_url,
|
|
dsid=dsid,
|
|
message=message,
|
|
)
|
|
)
|
|
|
|
if updated:
|
|
|
|
post_success(
|
|
"Pushed updated DICOM sessions "
|
|
"DataLad dataset"
|
|
)
|
|
|
|
else:
|
|
|
|
post_noop_result(
|
|
"Registration found up to date, "
|
|
"no changes made"
|
|
)
|
|
|
|
|
|
def _do_dicoms_dataset(
|
|
wdir: Path,
|
|
sub_id: str,
|
|
ses_id: str,
|
|
gitsha: str,
|
|
deposit_url: str,
|
|
dsid: str,
|
|
message: str | None,
|
|
) -> tuple[str, bool]:
|
|
|
|
call_git(
|
|
[
|
|
"clone",
|
|
"-q",
|
|
"--depth",
|
|
"1",
|
|
all_dicoms_ds_url,
|
|
wdir,
|
|
]
|
|
)
|
|
|
|
subm_rpath = (
|
|
get_session_dataset_rpath(
|
|
sub_id,
|
|
ses_id,
|
|
)
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"update-index",
|
|
"--add",
|
|
"--replace",
|
|
"--cacheinfo",
|
|
"160000",
|
|
gitsha,
|
|
subm_rpath,
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
|
|
for k, v in (
|
|
(
|
|
"path",
|
|
subm_rpath,
|
|
),
|
|
(
|
|
"url",
|
|
deposit_url,
|
|
),
|
|
(
|
|
"datalad-id",
|
|
dsid,
|
|
),
|
|
):
|
|
|
|
call_git(
|
|
[
|
|
"config",
|
|
"--file",
|
|
".gitmodules",
|
|
f"submodule.{subm_rpath}.{k}",
|
|
v,
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"add",
|
|
".gitmodules",
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
|
|
updated = False
|
|
|
|
if has_staged_changes(wdir):
|
|
|
|
call_git(
|
|
[
|
|
"commit",
|
|
"-m",
|
|
message
|
|
or (
|
|
f"add DICOM "
|
|
f"{sub_id}-{ses_id} "
|
|
"session dataset"
|
|
),
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
|
|
call_git(
|
|
["push"],
|
|
cwd=wdir,
|
|
)
|
|
|
|
updated = True
|
|
|
|
gitsha = call_git_oneline(
|
|
[
|
|
"rev-parse",
|
|
"HEAD",
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
|
|
return (
|
|
gitsha,
|
|
updated,
|
|
)
|
|
|
|
|
|
@cli.command(
|
|
"update-bids-sourcedata",
|
|
help=(
|
|
"Update the BIDS dataset to the latest "
|
|
"DICOM source data"
|
|
),
|
|
)
|
|
def update_bids_sourcedata():
|
|
|
|
with tempfile.TemporaryDirectory() as wdir:
|
|
|
|
updated = (
|
|
_do_update_bids_sourcedata(
|
|
Path(wdir)
|
|
)
|
|
)
|
|
|
|
if updated:
|
|
|
|
post_success(
|
|
"Pushed updated BIDS DataLad dataset"
|
|
)
|
|
|
|
else:
|
|
|
|
post_noop_result(
|
|
"DICOM dataset registration found "
|
|
"up to date, no changes made"
|
|
)
|
|
|
|
|
|
def _do_update_bids_sourcedata(
|
|
wdir: Path,
|
|
gitsha: str | None = None,
|
|
) -> bool:
|
|
|
|
if gitsha is None:
|
|
|
|
_, gitsha = (
|
|
get_remote_dataset_state(
|
|
all_dicoms_ds_url
|
|
)
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"clone",
|
|
"-q",
|
|
"--depth",
|
|
"1",
|
|
bids_ds_url,
|
|
wdir,
|
|
]
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"update-index",
|
|
"--add",
|
|
"--replace",
|
|
"--cacheinfo",
|
|
"160000",
|
|
gitsha,
|
|
"sourcedata/dicoms",
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
|
|
if has_staged_changes(wdir):
|
|
|
|
call_git(
|
|
[
|
|
"commit",
|
|
"-m",
|
|
"update DICOM source dataset",
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
|
|
call_git(
|
|
["push"],
|
|
cwd=wdir,
|
|
)
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def _init_container_from_cache(
|
|
wdir: Path,
|
|
container_cache_dir: Path,
|
|
):
|
|
|
|
container_ds_rpath = (
|
|
"code/heudiconv"
|
|
)
|
|
|
|
shallow_clone_submodule(
|
|
wdir,
|
|
container_ds_rpath,
|
|
)
|
|
|
|
container_ds_path = (
|
|
wdir
|
|
/ container_ds_rpath
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"fetch",
|
|
"-q",
|
|
"origin",
|
|
"git-annex:git-annex",
|
|
"--depth",
|
|
"1",
|
|
],
|
|
cwd=container_ds_path,
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"annex",
|
|
"init",
|
|
],
|
|
cwd=container_ds_path,
|
|
)
|
|
|
|
container_img_rpath = (
|
|
call_git_oneline(
|
|
[
|
|
"config",
|
|
"--file",
|
|
".datalad/config",
|
|
"datalad.containers.apptainer.image",
|
|
],
|
|
cwd=container_ds_path,
|
|
)
|
|
)
|
|
|
|
img_key = (
|
|
call_git_oneline(
|
|
[
|
|
"annex",
|
|
"lookupkey",
|
|
container_img_rpath,
|
|
],
|
|
cwd=container_ds_path,
|
|
)
|
|
)
|
|
|
|
cached_img = (
|
|
container_cache_dir
|
|
/ img_key
|
|
)
|
|
|
|
if cached_img.exists():
|
|
|
|
with Progress() as progress:
|
|
|
|
progress.add_task(
|
|
"Injecting cached container image",
|
|
total=None,
|
|
)
|
|
|
|
copyfile(
|
|
cached_img,
|
|
container_ds_path
|
|
/ ".img_key",
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"annex",
|
|
"reinject",
|
|
".img_key",
|
|
container_img_rpath,
|
|
],
|
|
cwd=container_ds_path,
|
|
)
|
|
|
|
|
|
def _cache_container(
|
|
wdir: Path,
|
|
container_cache_dir: Path,
|
|
):
|
|
|
|
container_img_rpath = (
|
|
call_git_oneline(
|
|
[
|
|
"config",
|
|
"--file",
|
|
".datalad/config",
|
|
"datalad.containers.apptainer.image",
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
)
|
|
|
|
img_key = (
|
|
call_git_oneline(
|
|
[
|
|
"annex",
|
|
"lookupkey",
|
|
container_img_rpath,
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
)
|
|
|
|
key_rpath = PurePosixPath(
|
|
call_git_oneline(
|
|
[
|
|
"annex",
|
|
"contentlocation",
|
|
img_key,
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
)
|
|
|
|
if not (
|
|
container_cache_dir
|
|
/ key_rpath.name
|
|
).exists():
|
|
|
|
with Progress() as progress:
|
|
|
|
progress.add_task(
|
|
"Cache container image",
|
|
total=None,
|
|
)
|
|
|
|
copyfile(
|
|
wdir
|
|
/ key_rpath,
|
|
container_cache_dir
|
|
/ key_rpath.name,
|
|
)
|
|
|
|
|
|
def _init_annex(
|
|
repodir: Path,
|
|
*,
|
|
private,
|
|
):
|
|
|
|
if private:
|
|
|
|
call_git(
|
|
[
|
|
"config",
|
|
"annex.private",
|
|
"true",
|
|
],
|
|
cwd=repodir,
|
|
)
|
|
|
|
try:
|
|
|
|
call_git(
|
|
[
|
|
"fetch",
|
|
"-q",
|
|
"origin",
|
|
"git-annex:git-annex",
|
|
"--depth",
|
|
"1",
|
|
],
|
|
cwd=repodir,
|
|
)
|
|
|
|
except CommandError:
|
|
|
|
pass
|
|
|
|
call_git(
|
|
[
|
|
"annex",
|
|
"init",
|
|
],
|
|
cwd=repodir,
|
|
)
|
|
|
|
|
|
|
|
def _dataset_relative_path(
|
|
dataset_root: Path,
|
|
path: Path,
|
|
) -> str:
|
|
return path.relative_to(dataset_root).as_posix()
|
|
|
|
|
|
def _unlock_metadata_for_edit(
|
|
dataset_root: Path,
|
|
session_dir: Path,
|
|
):
|
|
import datalad.api as dl
|
|
|
|
metadata_paths = []
|
|
metadata_paths.extend(sorted(session_dir.glob("*_scans.tsv")))
|
|
|
|
for dirname in ("func", "fmap", "dwi"):
|
|
folder = session_dir / dirname
|
|
if folder.exists():
|
|
metadata_paths.extend(sorted(folder.glob("*.json")))
|
|
|
|
relative_paths = [
|
|
_dataset_relative_path(dataset_root, path)
|
|
for path in metadata_paths
|
|
if path.exists() or path.is_symlink()
|
|
]
|
|
|
|
if not relative_paths:
|
|
return
|
|
|
|
print("\nUnlocking KJP metadata files for post conversion edits...")
|
|
|
|
results = dl.unlock(
|
|
dataset=dl.Dataset(dataset_root),
|
|
path=relative_paths,
|
|
result_renderer="disabled",
|
|
on_failure="ignore",
|
|
)
|
|
|
|
failures = [
|
|
result for result in results
|
|
if result.get("status") in {"error", "impossible"}
|
|
]
|
|
|
|
if failures:
|
|
details = "\n".join(
|
|
f" {result.get('path')}: {result.get('message')}"
|
|
for result in failures
|
|
)
|
|
raise RuntimeError(
|
|
"DataLad could not unlock one or more metadata files:\n"
|
|
f"{details}"
|
|
)
|
|
|
|
|
|
def _remove_tracked_file(
|
|
dataset_root: Path,
|
|
path: Path,
|
|
):
|
|
if not path.exists() and not path.is_symlink():
|
|
return
|
|
|
|
relative_path = _dataset_relative_path(dataset_root, path)
|
|
|
|
if call_git_success(
|
|
["ls-files", "--error-unmatch", "--", relative_path],
|
|
cwd=dataset_root,
|
|
capture_output=True,
|
|
):
|
|
call_git(
|
|
["rm", "-f", "--", relative_path],
|
|
cwd=dataset_root,
|
|
)
|
|
else:
|
|
path.unlink(missing_ok=True)
|
|
|
|
|
|
def _rename_tracked_file(
|
|
dataset_root: Path,
|
|
source: Path,
|
|
destination: Path,
|
|
):
|
|
if source == destination:
|
|
return
|
|
|
|
if not source.exists() and not source.is_symlink():
|
|
raise FileNotFoundError(f"Cannot rename missing file: {source}")
|
|
|
|
if destination.exists() or destination.is_symlink():
|
|
_remove_tracked_file(dataset_root, destination)
|
|
|
|
source_relative = _dataset_relative_path(dataset_root, source)
|
|
destination_relative = _dataset_relative_path(dataset_root, destination)
|
|
|
|
if call_git_success(
|
|
["ls-files", "--error-unmatch", "--", source_relative],
|
|
cwd=dataset_root,
|
|
capture_output=True,
|
|
):
|
|
call_git(
|
|
["mv", "-f", "--", source_relative, destination_relative],
|
|
cwd=dataset_root,
|
|
)
|
|
else:
|
|
source.rename(destination)
|
|
|
|
|
|
def _load_json(
|
|
path: Path,
|
|
) -> dict:
|
|
|
|
with path.open(
|
|
"r",
|
|
encoding="utf-8",
|
|
) as f:
|
|
|
|
return json.load(f)
|
|
|
|
|
|
def _save_json(
|
|
path: Path,
|
|
data: dict,
|
|
):
|
|
|
|
with path.open(
|
|
"w",
|
|
encoding="utf-8",
|
|
) as f:
|
|
|
|
json.dump(
|
|
data,
|
|
f,
|
|
indent=4,
|
|
ensure_ascii=False,
|
|
)
|
|
|
|
f.write("\n")
|
|
|
|
|
|
def _paired_json(
|
|
nii_path: Path,
|
|
) -> Path:
|
|
|
|
name = nii_path.name
|
|
|
|
if not name.endswith(
|
|
".nii.gz"
|
|
):
|
|
|
|
raise ValueError(
|
|
f"Expected .nii.gz file: "
|
|
f"{nii_path}"
|
|
)
|
|
|
|
return nii_path.with_name(
|
|
name[:-7]
|
|
+ ".json"
|
|
)
|
|
|
|
|
|
def _nifti_shape(
|
|
path: Path,
|
|
):
|
|
|
|
return tuple(
|
|
nib.load(
|
|
str(path)
|
|
).shape
|
|
)
|
|
|
|
|
|
def _cleanup_task_vid(
|
|
dataset_root: Path,
|
|
session_dir: Path,
|
|
sub_id: str,
|
|
ses_id: str,
|
|
):
|
|
|
|
func_dir = session_dir / "func"
|
|
|
|
if not func_dir.exists():
|
|
print("WARNING: No func directory found.")
|
|
return {"renamed_from": None, "renamed_to": None, "removed": []}
|
|
|
|
prefix = f"sub-{sub_id}_ses-{ses_id}"
|
|
final_nii = func_dir / f"{prefix}_task-vid_bold.nii.gz"
|
|
candidates = sorted(func_dir.glob(f"{prefix}_task-vid_bold*.nii.gz"))
|
|
|
|
print("\nTask vid BOLD candidates:")
|
|
|
|
if not candidates:
|
|
print(" None found.")
|
|
return {"renamed_from": None, "renamed_to": None, "removed": []}
|
|
|
|
candidate_info = []
|
|
for path in candidates:
|
|
shape = _nifti_shape(path)
|
|
print(f" {path.name}: {shape}")
|
|
if len(shape) == 4 and shape[3] > 1:
|
|
candidate_info.append((shape[3], path.stat().st_size, path, shape))
|
|
|
|
if not candidate_info:
|
|
raise RuntimeError("No valid 4D task vid BOLD candidate was found.")
|
|
|
|
candidate_info.sort(key=lambda x: (x[0], x[1]))
|
|
_, _, keep_nii, keep_shape = candidate_info[-1]
|
|
|
|
print("\nKeeping task vid BOLD:")
|
|
print(f" {keep_nii.name}")
|
|
print(f" shape: {keep_shape}")
|
|
|
|
keep_json = _paired_json(keep_nii)
|
|
old_keep_relative = keep_nii.relative_to(session_dir).as_posix()
|
|
removed_relative = []
|
|
|
|
for path in candidates:
|
|
if path == keep_nii:
|
|
continue
|
|
|
|
removed_relative.append(path.relative_to(session_dir).as_posix())
|
|
json_path = _paired_json(path)
|
|
print(f"Removing: {path.name}")
|
|
_remove_tracked_file(dataset_root, path)
|
|
_remove_tracked_file(dataset_root, json_path)
|
|
|
|
final_json = _paired_json(final_nii)
|
|
|
|
if keep_nii != final_nii:
|
|
print("Renaming task vid BOLD:")
|
|
print(f" {keep_nii.name}")
|
|
print(f" -> {final_nii.name}")
|
|
_rename_tracked_file(dataset_root, keep_nii, final_nii)
|
|
|
|
if keep_json.exists() or keep_json.is_symlink():
|
|
_rename_tracked_file(dataset_root, keep_json, final_json)
|
|
|
|
return {
|
|
"renamed_from": old_keep_relative,
|
|
"renamed_to": final_nii.relative_to(session_dir).as_posix(),
|
|
"removed": removed_relative,
|
|
}
|
|
|
|
def _update_scans_tsv(
|
|
session_dir: Path,
|
|
sub_id: str,
|
|
ses_id: str,
|
|
cleanup_result: dict,
|
|
):
|
|
|
|
scans_tsv = (
|
|
session_dir
|
|
/ (
|
|
f"sub-{sub_id}_"
|
|
f"ses-{ses_id}_"
|
|
"scans.tsv"
|
|
)
|
|
)
|
|
|
|
if not scans_tsv.exists():
|
|
|
|
print(
|
|
"WARNING: scans.tsv not found:"
|
|
)
|
|
|
|
print(
|
|
f" {scans_tsv}"
|
|
)
|
|
|
|
return
|
|
|
|
renamed_from = (
|
|
cleanup_result.get(
|
|
"renamed_from"
|
|
)
|
|
)
|
|
|
|
renamed_to = (
|
|
cleanup_result.get(
|
|
"renamed_to"
|
|
)
|
|
)
|
|
|
|
removed = set(
|
|
cleanup_result.get(
|
|
"removed",
|
|
[],
|
|
)
|
|
)
|
|
|
|
with scans_tsv.open(
|
|
"r",
|
|
encoding="utf-8",
|
|
newline="",
|
|
) as f:
|
|
|
|
reader = csv.DictReader(
|
|
f,
|
|
delimiter="\t",
|
|
)
|
|
|
|
fieldnames = (
|
|
reader.fieldnames
|
|
)
|
|
|
|
rows = list(
|
|
reader
|
|
)
|
|
|
|
if (
|
|
fieldnames is None
|
|
or "filename"
|
|
not in fieldnames
|
|
):
|
|
|
|
print(
|
|
"WARNING: scans.tsv has no "
|
|
"filename column."
|
|
)
|
|
|
|
return
|
|
|
|
new_rows = []
|
|
|
|
for row in rows:
|
|
|
|
filename = (
|
|
row["filename"]
|
|
)
|
|
|
|
if filename in removed:
|
|
|
|
print(
|
|
"Removing scans.tsv entry:"
|
|
)
|
|
|
|
print(
|
|
f" {filename}"
|
|
)
|
|
|
|
continue
|
|
|
|
if (
|
|
renamed_from
|
|
and renamed_to
|
|
and filename == renamed_from
|
|
):
|
|
|
|
print(
|
|
"Updating scans.tsv entry:"
|
|
)
|
|
|
|
print(
|
|
f" {renamed_from}"
|
|
)
|
|
|
|
print(
|
|
f" -> {renamed_to}"
|
|
)
|
|
|
|
row["filename"] = (
|
|
renamed_to
|
|
)
|
|
|
|
new_rows.append(
|
|
row
|
|
)
|
|
|
|
with scans_tsv.open(
|
|
"w",
|
|
encoding="utf-8",
|
|
newline="",
|
|
) as f:
|
|
|
|
writer = csv.DictWriter(
|
|
f,
|
|
fieldnames=fieldnames,
|
|
delimiter="\t",
|
|
lineterminator="\n",
|
|
)
|
|
|
|
writer.writeheader()
|
|
|
|
writer.writerows(
|
|
new_rows
|
|
)
|
|
|
|
|
|
def _clear_b0_metadata(
|
|
json_path: Path,
|
|
):
|
|
|
|
if not json_path.exists():
|
|
|
|
return False
|
|
|
|
data = (
|
|
_load_json(
|
|
json_path
|
|
)
|
|
)
|
|
|
|
changed = False
|
|
|
|
for key in (
|
|
"B0FieldIdentifier",
|
|
"B0FieldSource",
|
|
):
|
|
|
|
if key in data:
|
|
|
|
del data[key]
|
|
|
|
changed = True
|
|
|
|
if changed:
|
|
|
|
_save_json(
|
|
json_path,
|
|
data,
|
|
)
|
|
|
|
return changed
|
|
|
|
|
|
def _set_intended_for(
|
|
fmap_json: Path,
|
|
target_nii: Path,
|
|
session_dir: Path,
|
|
ses_id: str,
|
|
):
|
|
|
|
if not fmap_json.exists():
|
|
|
|
return
|
|
|
|
data = (
|
|
_load_json(
|
|
fmap_json
|
|
)
|
|
)
|
|
|
|
data.pop(
|
|
"B0FieldIdentifier",
|
|
None,
|
|
)
|
|
|
|
data.pop(
|
|
"B0FieldSource",
|
|
None,
|
|
)
|
|
|
|
if target_nii.exists():
|
|
|
|
relative_target = (
|
|
target_nii.relative_to(
|
|
session_dir
|
|
).as_posix()
|
|
)
|
|
|
|
intended_for = (
|
|
f"ses-{ses_id}/"
|
|
f"{relative_target}"
|
|
)
|
|
|
|
data["IntendedFor"] = [
|
|
intended_for
|
|
]
|
|
|
|
else:
|
|
|
|
data.pop(
|
|
"IntendedFor",
|
|
None,
|
|
)
|
|
|
|
print(
|
|
"WARNING: IntendedFor target "
|
|
"does not exist:"
|
|
)
|
|
|
|
print(
|
|
f" {target_nii}"
|
|
)
|
|
|
|
_save_json(
|
|
fmap_json,
|
|
data,
|
|
)
|
|
|
|
|
|
def _finalize_kjp_bids_session(
|
|
dataset_root: Path,
|
|
sub_id: str,
|
|
ses_id: str,
|
|
):
|
|
|
|
session_dir = (
|
|
dataset_root
|
|
/ f"sub-{sub_id}"
|
|
/ f"ses-{ses_id}"
|
|
)
|
|
|
|
if not session_dir.exists():
|
|
|
|
raise RuntimeError(
|
|
"Converted BIDS session "
|
|
"directory does not exist: "
|
|
f"{session_dir}"
|
|
)
|
|
|
|
print(
|
|
"\nFinalizing Mannheim KJP "
|
|
"BIDS session:"
|
|
)
|
|
|
|
print(
|
|
f" {session_dir}"
|
|
)
|
|
|
|
prefix = (
|
|
f"sub-{sub_id}_"
|
|
f"ses-{ses_id}"
|
|
)
|
|
|
|
_unlock_metadata_for_edit(
|
|
dataset_root,
|
|
session_dir,
|
|
)
|
|
|
|
cleanup_result = (
|
|
_cleanup_task_vid(
|
|
dataset_root,
|
|
session_dir,
|
|
sub_id,
|
|
ses_id,
|
|
)
|
|
)
|
|
|
|
_update_scans_tsv(
|
|
session_dir,
|
|
sub_id,
|
|
ses_id,
|
|
cleanup_result,
|
|
)
|
|
|
|
func_dir = (
|
|
session_dir
|
|
/ "func"
|
|
)
|
|
|
|
fmap_dir = (
|
|
session_dir
|
|
/ "fmap"
|
|
)
|
|
|
|
dwi_dir = (
|
|
session_dir
|
|
/ "dwi"
|
|
)
|
|
|
|
rest_bold = (
|
|
func_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"task-rest_bold.nii.gz"
|
|
)
|
|
)
|
|
|
|
vid_bold = (
|
|
func_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"task-vid_bold.nii.gz"
|
|
)
|
|
)
|
|
|
|
vid_sbref_json = (
|
|
func_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"task-vid_sbref.json"
|
|
)
|
|
)
|
|
|
|
b1200_dwi = (
|
|
dwi_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"acq-b1200_"
|
|
"dir-PA_dwi.nii.gz"
|
|
)
|
|
)
|
|
|
|
mshell_dwi = (
|
|
dwi_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"acq-mshell_"
|
|
"dir-PA_dwi.nii.gz"
|
|
)
|
|
)
|
|
|
|
rest_fmaps = [
|
|
|
|
fmap_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"acq-rest_"
|
|
"dir-ap_epi.json"
|
|
),
|
|
|
|
fmap_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"acq-rest_"
|
|
"dir-pa_epi.json"
|
|
),
|
|
]
|
|
|
|
vid_fmaps = [
|
|
|
|
fmap_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"acq-vid_"
|
|
"dir-ap_epi.json"
|
|
),
|
|
|
|
fmap_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"acq-vid_"
|
|
"dir-pa_epi.json"
|
|
),
|
|
]
|
|
|
|
b1200_fmap = (
|
|
fmap_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"acq-b1200_"
|
|
"dir-AP_epi.json"
|
|
)
|
|
)
|
|
|
|
mshell_fmap = (
|
|
fmap_dir
|
|
/ (
|
|
f"{prefix}_"
|
|
"acq-mshell_"
|
|
"dir-AP_epi.json"
|
|
)
|
|
)
|
|
|
|
for fmap_json in rest_fmaps:
|
|
|
|
_set_intended_for(
|
|
fmap_json,
|
|
rest_bold,
|
|
session_dir,
|
|
ses_id,
|
|
)
|
|
|
|
for fmap_json in vid_fmaps:
|
|
|
|
_set_intended_for(
|
|
fmap_json,
|
|
vid_bold,
|
|
session_dir,
|
|
ses_id,
|
|
)
|
|
|
|
_set_intended_for(
|
|
b1200_fmap,
|
|
b1200_dwi,
|
|
session_dir,
|
|
ses_id,
|
|
)
|
|
|
|
_set_intended_for(
|
|
mshell_fmap,
|
|
mshell_dwi,
|
|
session_dir,
|
|
ses_id,
|
|
)
|
|
|
|
if vid_sbref_json.exists():
|
|
|
|
sbref_data = (
|
|
_load_json(
|
|
vid_sbref_json
|
|
)
|
|
)
|
|
|
|
sbref_data.pop(
|
|
"IntendedFor",
|
|
None,
|
|
)
|
|
|
|
sbref_data.pop(
|
|
"B0FieldIdentifier",
|
|
None,
|
|
)
|
|
|
|
sbref_data.pop(
|
|
"B0FieldSource",
|
|
None,
|
|
)
|
|
|
|
_save_json(
|
|
vid_sbref_json,
|
|
sbref_data,
|
|
)
|
|
|
|
for json_path in (
|
|
func_dir.glob(
|
|
"*.json"
|
|
)
|
|
):
|
|
|
|
_clear_b0_metadata(
|
|
json_path
|
|
)
|
|
|
|
for json_path in (
|
|
dwi_dir.glob(
|
|
"*.json"
|
|
)
|
|
):
|
|
|
|
_clear_b0_metadata(
|
|
json_path
|
|
)
|
|
|
|
print(
|
|
"\nMannheim KJP finalization "
|
|
"completed."
|
|
)
|
|
|
|
|
|
@cli.command(
|
|
"bids-conversion",
|
|
help=(
|
|
"Convert Mannheim KJP MRI data from "
|
|
"a single DICOM session to BIDS format "
|
|
"and incorporate it into the Q01 BIDS "
|
|
"DataLad dataset."
|
|
),
|
|
)
|
|
@click.argument(
|
|
"dicoms",
|
|
**param_cfg["dicoms"],
|
|
)
|
|
@click.argument(
|
|
"sub-id",
|
|
**param_cfg["sub-id"],
|
|
)
|
|
@click.argument(
|
|
"ses-id",
|
|
**param_cfg["ses-id"],
|
|
)
|
|
@click.option(
|
|
"-m",
|
|
"--message",
|
|
**param_cfg[
|
|
("-m", "--message")
|
|
],
|
|
)
|
|
@click.option(
|
|
"--container-cache-dir",
|
|
**param_cfg[
|
|
"--container-cache-dir"
|
|
],
|
|
)
|
|
def bids_conversion(
|
|
dicoms: Path,
|
|
sub_id: str,
|
|
ses_id: str,
|
|
message: str | None = None,
|
|
container_cache_dir:
|
|
Path | None = None,
|
|
):
|
|
|
|
with tempfile.TemporaryDirectory() as wdir:
|
|
|
|
_do_bids_dataset(
|
|
Path(wdir),
|
|
dicoms,
|
|
sub_id,
|
|
ses_id,
|
|
gitsha=None,
|
|
container_cache_dir=(
|
|
container_cache_dir
|
|
),
|
|
)
|
|
|
|
|
|
def _do_bids_dataset(
|
|
wdir: Path,
|
|
dicoms: Path,
|
|
sub_id: str,
|
|
ses_id: str,
|
|
gitsha: str | None = None,
|
|
container_cache_dir:
|
|
Path | None = None,
|
|
):
|
|
|
|
_do_update_bids_sourcedata(
|
|
Path(wdir),
|
|
gitsha=gitsha,
|
|
)
|
|
|
|
shallow_clone_submodule(
|
|
wdir,
|
|
".heudiconv",
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"switch",
|
|
"main",
|
|
],
|
|
cwd=(
|
|
wdir
|
|
/ ".heudiconv"
|
|
),
|
|
)
|
|
|
|
for repodir in (
|
|
wdir,
|
|
wdir
|
|
/ ".heudiconv",
|
|
):
|
|
|
|
_init_annex(
|
|
repodir,
|
|
private=True,
|
|
)
|
|
|
|
try:
|
|
|
|
call_git_oneline(
|
|
[
|
|
"status",
|
|
"--porcelain",
|
|
".heudiconv",
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"commit",
|
|
"-m",
|
|
(
|
|
"Sync external updates "
|
|
"to heudiconv log submodule"
|
|
),
|
|
"--",
|
|
".heudiconv",
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
|
|
except AssertionError:
|
|
|
|
pass
|
|
|
|
|
|
dicom_src_rpath = (
|
|
"sourcedata/dicoms"
|
|
)
|
|
|
|
shallow_clone_submodule(
|
|
wdir,
|
|
dicom_src_rpath,
|
|
)
|
|
|
|
dicom_session_rpath = (
|
|
get_session_dataset_rpath(
|
|
sub_id,
|
|
ses_id,
|
|
)
|
|
)
|
|
|
|
shallow_clone_submodule(
|
|
wdir
|
|
/ dicom_src_rpath,
|
|
dicom_session_rpath,
|
|
)
|
|
|
|
session_dicom_ds_path = (
|
|
wdir
|
|
/ dicom_src_rpath
|
|
/ dicom_session_rpath
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"annex",
|
|
"init",
|
|
],
|
|
cwd=session_dicom_ds_path,
|
|
)
|
|
|
|
with Progress() as progress:
|
|
|
|
progress.add_task(
|
|
"Injecting DICOM session archive",
|
|
total=None,
|
|
)
|
|
|
|
make_dicom_archive(
|
|
dicoms,
|
|
session_dicom_ds_path
|
|
/ ".archive_key",
|
|
track,
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"annex",
|
|
"reinject",
|
|
".archive_key",
|
|
get_archive_dest_rpath(
|
|
dicoms,
|
|
sub_id,
|
|
ses_id,
|
|
),
|
|
],
|
|
cwd=session_dicom_ds_path,
|
|
)
|
|
|
|
|
|
if container_cache_dir:
|
|
|
|
_init_container_from_cache(
|
|
wdir,
|
|
container_cache_dir,
|
|
)
|
|
|
|
else:
|
|
|
|
shallow_clone_submodule(
|
|
wdir,
|
|
"code/heudiconv",
|
|
)
|
|
|
|
subm_path = (
|
|
wdir
|
|
/ "code"
|
|
/ "heudiconv"
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"fetch",
|
|
"-q",
|
|
"origin",
|
|
"git-annex:git-annex",
|
|
"--depth",
|
|
"1",
|
|
],
|
|
cwd=subm_path,
|
|
)
|
|
|
|
call_git(
|
|
[
|
|
"annex",
|
|
"init",
|
|
],
|
|
cwd=subm_path,
|
|
)
|
|
|
|
|
|
try:
|
|
|
|
call_git_oneline(
|
|
[
|
|
"status",
|
|
"--porcelain",
|
|
"code/heudiconv",
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
|
|
post_failure(
|
|
"Unincorporated changes to the "
|
|
"heudiconv pipeline detected. "
|
|
"Refusing to continue. "
|
|
"Please check and advance the "
|
|
"code/heudiconv submodule."
|
|
)
|
|
|
|
sys.exit(1)
|
|
|
|
except AssertionError:
|
|
|
|
pass
|
|
|
|
|
|
pre_gitsha = (
|
|
call_git_oneline(
|
|
[
|
|
"rev-parse",
|
|
"HEAD",
|
|
],
|
|
cwd=wdir,
|
|
)
|
|
)
|
|
|
|
|
|
import datalad.api as dl
|
|
|
|
|
|
with Progress() as progress:
|
|
|
|
progress.add_task(
|
|
"KJP BIDS conversion",
|
|
total=None,
|
|
)
|
|
|
|
try:
|
|
|
|
dl.containers_run(
|
|
|
|
dataset=dl.Dataset(
|
|
wdir
|
|
),
|
|
|
|
message=(
|
|
f"Convert KJP "
|
|
f"{sub_id}-{ses_id} "
|
|
"DICOM data"
|
|
),
|
|
|
|
container_name=(
|
|
"code/heudiconv/"
|
|
"apptainer"
|
|
),
|
|
|
|
outputs=[
|
|
|
|
(
|
|
f"sub-{sub_id}/"
|
|
f"ses-{ses_id}/"
|
|
f"sub-{sub_id}_"
|
|
f"ses-{ses_id}_"
|
|
"scans.tsv"
|
|
),
|
|
|
|
(
|
|
f".heudiconv/"
|
|
f"sub-{sub_id}/"
|
|
f"ses-{ses_id}"
|
|
),
|
|
],
|
|
|
|
inputs=[
|
|
|
|
(
|
|
"sourcedata/dicoms/"
|
|
"code/"
|
|
"heuristic-q01-kjp.py"
|
|
),
|
|
|
|
(
|
|
f"{dicom_src_rpath}/"
|
|
f"{dicom_session_rpath}"
|
|
),
|
|
],
|
|
|
|
cmd=(
|
|
"--bids notop "
|
|
"--overwrite "
|
|
"--minmeta "
|
|
"-o . "
|
|
"-f '{inputs[0]}' "
|
|
f'-s "{sub_id}" '
|
|
f'-ss "{ses_id}" '
|
|
"--files "
|
|
f"{dicom_src_rpath}/"
|
|
f"{dicom_session_rpath}"
|
|
),
|
|
)
|
|
|
|
except Exception:
|
|
|
|
call_git(
|
|
["status"],
|
|
cwd=wdir,
|
|
)
|
|
|
|
raise
|
|
|
|
|
|
print(
|
|
"\nRunning Mannheim KJP "
|
|
"post conversion finalization..."
|
|
)
|
|
|
|
_finalize_kjp_bids_session(
|
|
wdir,
|
|
sub_id,
|
|
ses_id,
|
|
)
|
|
|
|
|
|
print(
|
|
"\nSaving Mannheim KJP post conversion changes with DataLad..."
|
|
)
|
|
|
|
dl.save(
|
|
dataset=dl.Dataset(
|
|
wdir
|
|
),
|
|
|
|
path=[
|
|
(
|
|
f"sub-{sub_id}/"
|
|
f"ses-{ses_id}"
|
|
),
|
|
],
|
|
|
|
message=(
|
|
"Finalize Mannheim KJP "
|
|
f"BIDS session "
|
|
f"{sub_id}-{ses_id}"
|
|
),
|
|
)
|
|
|
|
|
|
for repodir in (
|
|
wdir,
|
|
wdir
|
|
/ ".heudiconv",
|
|
):
|
|
|
|
call_git(
|
|
[
|
|
"annex",
|
|
"copy",
|
|
"-t",
|
|
"origin",
|
|
".",
|
|
],
|
|
cwd=repodir,
|
|
)
|
|
|
|
call_git(
|
|
["push"],
|
|
cwd=repodir,
|
|
)
|
|
|
|
|
|
if container_cache_dir:
|
|
|
|
_cache_container(
|
|
wdir
|
|
/ "code"
|
|
/ "heudiconv",
|
|
container_cache_dir,
|
|
)
|
|
|
|
|
|
if pre_gitsha == call_git_oneline(
|
|
[
|
|
"rev-parse",
|
|
"HEAD",
|
|
],
|
|
cwd=wdir,
|
|
):
|
|
|
|
post_noop_result(
|
|
"No changes to tracked content."
|
|
)
|
|
|
|
else:
|
|
|
|
post_success(
|
|
"KJP conversion successful."
|
|
)
|
|
|
|
|
|
@cli.command(
|
|
"ingest-new-session",
|
|
help=(
|
|
"Perform all processing steps through "
|
|
"BIDS conversion for a new Mannheim "
|
|
"KJP scan."
|
|
),
|
|
)
|
|
@click.argument(
|
|
"dicoms",
|
|
**param_cfg["dicoms"],
|
|
)
|
|
@click.argument(
|
|
"sub-id",
|
|
**param_cfg["sub-id"],
|
|
)
|
|
@click.argument(
|
|
"ses-id",
|
|
**param_cfg["ses-id"],
|
|
)
|
|
@click.option(
|
|
"--container-cache-dir",
|
|
**param_cfg[
|
|
"--container-cache-dir"
|
|
],
|
|
)
|
|
@click.pass_context
|
|
def ingest_new_session(
|
|
ctx,
|
|
dicoms: Path,
|
|
sub_id: str,
|
|
ses_id: str,
|
|
container_cache_dir:
|
|
Path | None = None,
|
|
) -> None:
|
|
|
|
ctx.invoke(
|
|
create_session_dataset,
|
|
dicoms=dicoms,
|
|
sub_id=sub_id,
|
|
ses_id=ses_id,
|
|
)
|
|
|
|
ctx.invoke(
|
|
register_session_dataset,
|
|
sub_id=sub_id,
|
|
ses_id=ses_id,
|
|
)
|
|
|
|
ctx.invoke(
|
|
bids_conversion,
|
|
dicoms=dicoms,
|
|
sub_id=sub_id,
|
|
ses_id=ses_id,
|
|
container_cache_dir=(
|
|
container_cache_dir
|
|
),
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
cli() |