"""TemplateFlow's Python Client."""
from json import loads
from pathlib import Path
import re
import sys
from .conf import TF_LAYOUT, TF_S3_ROOT, TF_USE_DATALAD
[docs]def get(template, raise_empty=False, **kwargs):
"""
Fetch one file from one particular template.
Parameters
----------
template : str
A template identifier (e.g., ``MNI152NLin2009cAsym``).
raise_empty : bool, optional
Raise exception if no files were matched
Keyword Arguments
-----------------
resolution: int or None
Index to an specific spatial resolution of the template.
suffix : str or None
BIDS suffix
atlas : str or None
Name of a particular atlas
hemi : str or None
Hemisphere
space : str or None
Space template is mapped to
density : str or None
Surface density
desc : str or None
Description field
Examples
--------
>>> str(get('MNI152Lin', resolution=1, suffix='T1w')) # doctest: +ELLIPSIS
'.../tpl-MNI152Lin/tpl-MNI152Lin_res-01_T1w.nii.gz'
>>> str(get('MNI152Lin', resolution=2, suffix='T1w')) # doctest: +ELLIPSIS
'.../tpl-MNI152Lin/tpl-MNI152Lin_res-02_T1w.nii.gz'
>>> [str(p) for p in get(
... 'MNI152Lin', suffix='T1w')] # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
['.../tpl-MNI152Lin/tpl-MNI152Lin_res-01_T1w.nii.gz',
'.../tpl-MNI152Lin/tpl-MNI152Lin_res-02_T1w.nii.gz']
>>> str(get('fsLR', space=None, hemi='L',
... density='32k', suffix='sphere')) # doctest: +ELLIPSIS
'.../tpl-fsLR_hemi-L_den-32k_sphere.surf.gii'
>>> get('fsLR', space='madeup')
[]
>>> get('fsLR', raise_empty=True, space='madeup') # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
Exception:
...
"""
out_file = [
Path(p) for p in TF_LAYOUT.get(template=template, return_type="file", **kwargs)
]
# Try DataLad first
dl_missing = [p for p in out_file if not p.is_file()]
if TF_USE_DATALAD and dl_missing:
for filepath in dl_missing:
_datalad_get(filepath)
dl_missing.remove(filepath)
# Fall-back to S3 if some files are still missing
s3_missing = [p for p in out_file if p.is_file() and p.stat().st_size == 0]
for filepath in s3_missing + dl_missing:
_s3_get(filepath)
not_fetched = [str(p) for p in out_file if not p.is_file() or p.stat().st_size == 0]
if not_fetched:
msg = "Could not fetch template files: %s." % ", ".join(not_fetched)
if dl_missing and not TF_USE_DATALAD:
msg += (
"""\
The $TEMPLATEFLOW_HOME folder %s seems to contain an initiated DataLad \
dataset, but the environment variable $TEMPLATEFLOW_USE_DATALAD is not \
set or set to one of (false, off, 0). Please set $TEMPLATEFLOW_USE_DATALAD \
on (possible values: true, on, 1)."""
% TF_LAYOUT.root
)
if s3_missing and TF_USE_DATALAD:
msg += (
"""\
The $TEMPLATEFLOW_HOME folder %s seems to contain an plain \
dataset, but the environment variable $TEMPLATEFLOW_USE_DATALAD is \
set to one of (true, on, 1). Please set $TEMPLATEFLOW_USE_DATALAD \
off (possible values: false, off, 0)."""
% TF_LAYOUT.root
)
raise RuntimeError(msg)
if not out_file and raise_empty:
raise Exception("No results found")
if len(out_file) == 1:
return out_file[0]
return out_file
[docs]def templates(**kwargs):
"""
Returns a list of available templates.
Keyword Arguments
-----------------
resolution: int or None
Index to an specific spatial resolution of the template.
suffix : str or None
BIDS suffix
atlas : str
Name of a particular atlas
desc : str
Description field
Examples
--------
>>> base = ['MNI152Lin', 'MNI152NLin2009cAsym', 'NKI', 'OASIS30ANTs']
>>> tpls = templates()
>>> all([t in tpls for t in base])
True
>>> templates(suffix='PD')
['MNI152Lin', 'MNI152NLin2009cAsym', 'MNI152NLin2009cSym', 'MNIInfant', 'MNIPediatricAsym']
"""
return sorted(TF_LAYOUT.get_templates(**kwargs))
[docs]def get_citations(template, bibtex=False):
"""
Fetch template citations
Parameters
----------
template : :obj:`str`
A template identifier (e.g., ``MNI152NLin2009cAsym``).
bibtex : :obj:`bool`, optional
Generate citations in BibTeX format.
"""
data = get_metadata(template)
refs = data.get("ReferencesAndLinks", [])
if isinstance(refs, dict):
refs = [x for x in refs.values()]
if not bibtex:
return refs
return [_to_bibtex(ref, template, idx).rstrip() for idx, ref in enumerate(refs, 1)]
def _datalad_get(filepath):
if not filepath:
return
from datalad import api
from datalad.support.exceptions import IncompleteResultsError
try:
api.get(str(filepath))
except IncompleteResultsError as exc:
if exc.failed[0]["message"] == "path not associated with any dataset":
from .conf import TF_GITHUB_SOURCE
api.install(path=TF_LAYOUT.root, source=TF_GITHUB_SOURCE, recursive=True)
api.get(str(filepath))
else:
raise
def _s3_get(filepath):
from sys import stderr
from tqdm import tqdm
import requests
path = str(filepath.relative_to(TF_LAYOUT.root))
url = "%s/%s" % (TF_S3_ROOT, path)
print("Downloading %s" % url, file=stderr)
# Streaming, so we can iterate over the response.
r = requests.get(url, stream=True)
# Total size in bytes.
total_size = int(r.headers.get("content-length", 0))
block_size = 1024
wrote = 0
if not filepath.is_file():
filepath.unlink()
with filepath.open("wb") as f:
with tqdm(total=total_size, unit="B", unit_scale=True) as t:
for data in r.iter_content(block_size):
wrote = wrote + len(data)
f.write(data)
t.update(len(data))
if total_size != 0 and wrote != total_size:
raise RuntimeError("ERROR, something went wrong")
def _to_bibtex(doi, template, idx):
try:
from doi2bib.crossref import get_bib_from_doi
except ImportError:
print(
"Cannot generate BibTeX citation, missing doi2bib dependency",
file=sys.stderr,
)
return doi
if "doi.org" not in doi:
return doi
bib = get_bib_from_doi(doi)[1]
# replace identifier with template name
m = re.search(r"([A-Z])\w+", bib)
return bib.replace(m.group(), "%s%s" % (template.lower(), idx))