Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 9 additions & 8 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
name: Python package

on:
workflow_dispatch:
push:
branches: [ master ]
tags: [ '*' ]
Expand All @@ -23,13 +24,13 @@ jobs:
outputs:
version: ${{ steps.get_version.outputs.version }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: 3
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-cache-v1
Expand All @@ -39,7 +40,7 @@ jobs:
- name: Build sdist and wheel
run: python -m build
- run: twine check dist/*
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
Expand Down Expand Up @@ -73,20 +74,20 @@ jobs:
TEMPLATEFLOW_HOME: /tmp/home
THISVERSION: ${{ needs.build.outputs.version }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
if: matrix.mode == 'repo' || matrix.mode == 'editable'
with:
fetch-depth: 0
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
if: matrix.mode == 'sdist' || matrix.mode == 'wheel'
with:
name: dist
path: /tmp/package/
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-cache-v1
Expand Down
7 changes: 6 additions & 1 deletion templateflow/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,12 @@ def _to_bibtex(doi, template, idx):
)
return doi

return response.text
# doi.org may not honor requested charset, to safeguard force a bytestream with
# response.content, then decode into UTF-8.
bibtex = response.content.decode()

# doi.org / crossref may still point to the no longer preferred proxy service
return bibtex.replace('http://dx.doi.org/', 'https://doi.org/')


def _normalize_ext(value):
Expand Down
179 changes: 133 additions & 46 deletions templateflow/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,77 @@
"""Test citations."""

import pytest

from .. import api


class Bibtex:
def __init__(self, bibtex):
self.text = bibtex
self.url_only = False
self.etype = None
self.citekey = None
self.pairs = {}

# DOI could not be converted
if self.text.startswith("http"):
self.url_only = True
else:
self._parse_bibtex()

def _parse_bibtex(self):
import re

try:
self.etype = re.search(r"@(\w+)", self.text).group(1)
except AttributeError:
raise TypeError(f"Invalid bibtex: {self.text}")
try:
self.citekey = re.search(r"@[^{]*{([^,\s]+)", self.text).group(1)
except AttributeError:
raise TypeError(f"Invalid bibtex: {self.text}")
self.pairs = {
key: val for key, val in re.findall(r"(\w+)=(\{[^{}]+\})", self.text)
}

def get(self, val):
return self.pairs.get(val)

def __str__(self):
return self.text

def __repr__(self):
return f'@{self.etype}{{{self.citekey}, {", ".join([f"{key} = {val}" for key, val in self.pairs.items()])}}}'

def __eq__(self, other):
if isinstance(other, Bibtex):
if self.url_only and self.text == other.text:
return True
if (
self.etype == other.etype
and self.citekey == other.citekey
and self.pairs == other.pairs
):
return True
return False

def assert_same(self, other):
"""Convenience method to find deviations between two Bibtex objects"""
assert isinstance(other, Bibtex)
assert self.etype == other.etype, "Mismatched entry types"
assert self.citekey == other.citekey, "Mismatched citekeys"
for key in self.pairs.keys():
assert key in other.pairs, f"Key ({key}) missing from other"
assert (
self.pairs[key] == other.pairs[key]
), f"Key ({key}) mismatched\n\n{self.pairs[key]}\n\n{other.pairs[key]}"

for key in other.pairs.keys():
assert key in self.pairs, f"Key ({key}) missing from pairs"

assert self.pairs == other.pairs, "Dictionaries do not match"


# test setup to avoid cluttering pytest parameterize
mni2009_urls = [
"https://doi.org/10.1016/j.neuroimage.2010.07.033",
Expand All @@ -14,30 +82,32 @@

mni2009_fbib = """\
@article{Fonov_2011,
\tdoi = {10.1016/j.neuroimage.2010.07.033},
\turl = {https://doi.org/10.1016%2Fj.neuroimage.2010.07.033},
\tyear = 2011,
\tmonth = {jan},
\tpublisher = {Elsevier {BV}},
\tvolume = {54},
\tnumber = {1},
\tpages = {313--327},
\tauthor = {Vladimir Fonov and Alan C. Evans and Kelly Botteron and C. Robert \
Almli and Robert C. McKinstry and D. Louis Collins},
\ttitle = {Unbiased average age-appropriate atlases for pediatric studies},
\tjournal = {{NeuroImage}}
DOI={10.1016/j.neuroimage.2010.07.033},
url={https://doi.org/10.1016/j.neuroimage.2010.07.033},
year={2011},
publisher={Elsevier BV},
ISSN={1053-8119},
volume={54},
number={1},
pages={313327},
author={Fonov, Vladimir and Evans, Alan C. and Botteron, Kelly and Almli, C. Robert \
and McKinstry, Robert C. and Collins, D. Louis},
title={Unbiased average age-appropriate atlases for pediatric studies},
journal={NeuroImage}
}"""

mni2009_lbib = """\
@incollection{Collins_1999,
\tdoi = {10.1007/3-540-48714-x_16},
\turl = {https://doi.org/10.1007%2F3-540-48714-x_16},
\tyear = 1999,
\tpublisher = {Springer Berlin Heidelberg},
\tpages = {210--223},
\tauthor = {D. Louis Collins and Alex P. Zijdenbos and Wim F. C. Baar{\\'{e}} and Alan C. Evans},
\ttitle = {{ANIMAL}$\\mathplus${INSECT}: Improved Cortical Structure Segmentation},
\tbooktitle = {Lecture Notes in Computer Science}
@inbook{Collins_1999,
DOI={10.1007/3-540-48714-x_16},
url={https://doi.org/10.1007/3-540-48714-X_16},
year={1999},
publisher={Springer Berlin Heidelberg},
pages={210–223},
ISBN={9783540487142},
ISSN={0302-9743}
author={Collins, D. Louis and Zijdenbos, Alex P. and Baaré, Wim F. C. and Evans, Alan C.},
title={ANIMAL+INSECT: Improved Cortical Structure Segmentation},
booktitle={Information Processing in Medical Imaging}
}"""

fslr_urls = [
Expand All @@ -47,18 +117,18 @@

fslr_fbib = """\
@article{Van_Essen_2011,
\tdoi = {10.1093/cercor/bhr291},
\turl = {https://doi.org/10.1093%2Fcercor%2Fbhr291},
\tyear = 2011,
\tmonth = {nov},
\tpublisher = {Oxford University Press ({OUP})},
\tvolume = {22},
\tnumber = {10},
\tpages = {2241--2262},
\tauthor = {D. C. Van Essen and M. F. Glasser and D. L. Dierker and J. Harwell and T. Coalson},
\ttitle = {Parcellations and Hemispheric Asymmetries of Human Cerebral Cortex Analyzed on \
DOI={10.1093/cercor/bhr291},
ISSN={1460-2199},
url={https://doi.org/10.1093/cercor/bhr291},
year={2011},
publisher={Oxford University Press (OUP)},
volume={22},
number={10},
pages={22412262},
author={Van Essen, D. C. and Glasser, M. F. and Dierker, D. L. and Harwell, J. and Coalson, T.},
title={Parcellations and Hemispheric Asymmetries of Human Cerebral Cortex Analyzed on \
Surface-Based Atlases},
\tjournal = {Cerebral Cortex}
journal={Cerebral Cortex}
}"""

fslr_lbib = (
Expand All @@ -67,33 +137,50 @@

fsaverage_fbib = """\
@article{Fischl_1999,
\tdoi = {10.1002/(sici)1097-0193(1999)8:4<272::aid-hbm10>3.0.co;2-4},
\turl = {https://doi.org/10.1002%2F%28sici%291097-0193%281999%298%3A4%3C272%3A%3Aaid-hbm10%3E3.0.co%3B2-4},
\tyear = 1999,
\tpublisher = {Wiley},
\tvolume = {8},
\tnumber = {4},
\tpages = {272--284},
\tauthor = {Bruce Fischl and Martin I. Sereno and Roger B.H. Tootell and Anders M. Dale},
\ttitle = {High-resolution intersubject averaging and a coordinate system for the cortical surface},
\tjournal = {Human Brain Mapping}
DOI={10.1002/(sici)1097-0193(1999)8:4<272::aid-hbm10>3.0.co;2-4},
ISSN={1097-0193},
url={https://doi.org/10.1002/(sici)1097-0193(1999)8:4<272::aid-hbm10>3.0.co;2-4},
year={1999},
publisher={Wiley},
volume={8},
number={4},
pages={272–284},
author={Fischl, Bruce and Sereno, Martin I. and Tootell, Roger B.H. and Dale, Anders M.},
title={High-resolution intersubject averaging and a coordinate system for the cortical surface},
journal={Human Brain Mapping}
}"""


@pytest.mark.parametrize(
"template,urls,fbib,lbib",
[
("MNI152NLin2009cAsym", mni2009_urls, mni2009_fbib, mni2009_lbib),
("fsLR", fslr_urls, fslr_fbib, fslr_lbib),
("fsaverage", ["https://doi.org/10.1002/(sici)1097-0193(1999)8:4%3C272::aid-hbm10%3E3.0.co;2-4"], fsaverage_fbib, None),
(
"fsaverage",
[
"https://doi.org/10.1002/(sici)1097-0193(1999)8:4%3C272::aid-hbm10%3E3.0.co;2-4"
],
fsaverage_fbib,
None,
),
],
)
def test_citations(tmp_path, template, urls, fbib, lbib):
"""Check the correct composition of citations."""
assert api.get_citations(template) == urls
bibs = api.get_citations(template, bibtex=True)
if bibs:
assert "".join(bibs[0]) == fbib
assert len(bibs) == 1 if lbib is None else "".join(bibs[-1]) == lbib
bib0 = Bibtex(bibs[0])
exp0 = Bibtex(fbib)
assert bib0 == exp0
if lbib is not None:
bib1 = Bibtex(bibs[-1])
exp1 = Bibtex(lbib)
assert bib1 == exp1
else:
assert len(bibs) == 1

else:
# no citations currently
assert False
Expand All @@ -108,7 +195,7 @@ def test_pybids_magic_get():

with pytest.raises(TypeError):
api.ls_atlases("MNI152NLin6ASym")

# Existing layout.get_* should not be bubbled to the layout
# (that means, raise an AttributeError instead of a BIDSEntityError)
with pytest.raises(AttributeError):
Expand Down