Skip to content

Commit

Permalink
Version bumped to 4.3.0 + minor tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelzwiers committed Dec 19, 2023
1 parent f4396ef commit 495346b
Show file tree
Hide file tree
Showing 14 changed files with 27 additions and 20 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ The preferred way to contribute to the BIDScoin code base or documentation is to

2. Set up a virtual environment and install BIDScoin's dependencies::

python -m venv venv # Or use any other tool (such as conda). NB: Use Python < 3.11.2 (due to pypet4bids)
python -m venv venv # Or use any other tool (such as conda)
source venv/bin/activate # On Linux, see the documentation for other operating systems
pip install bidscoin[dev] # Unfortunately pyproject.toml has no way to install BIDScoin's dependencies only
pip uninstall bidscoin # Hence we need to retrospectively remove BIDScoin from site-packages
Expand Down
6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ BIDScoin: Coin your imaging data to BIDS

<img name="bidscoin-logo" src="https://github.com/Donders-Institute/bidscoin/blob/master/bidscoin/bidscoin_logo.png" height="340px" align="right" alt=" ">

|PyPI version| |Neurodesk| |BIDS| |PyPI - Python Version| |GPLv3| |RTD| |Tests| |DOI|
|PyPI version| |Neurodesk| |BIDS| |Python Version| |GPLv3| |RTD| |Tests| |DOI|

BIDScoin is a user-friendly Python application suite that converts ("coins") source-level (raw) neuroimaging data sets to standardized data sets that are organized according to the Brain Imaging Data Structure (`BIDS <https://bids-specification.readthedocs.io>`__) specification. Rather than depending on complex programmatic logic for source data-type identification, BIDScoin uses a mapping approach to discover the different source data types in your repository and convert them into BIDS data types. Different runs of source data are uniquely identified by their file system properties (e.g. file name or size) and by their attributes (e.g. ``ProtocolName`` from the DICOM header). Mapping information can be pre-specified (e.g. per site), allowing BIDScoin to make intelligent first suggestions on how to classify and convert the data. While this command-line procedure exploits all information available on disk, BIDScoin presents a `Graphical User Interface (GUI) <./screenshots.html>`__ for researchers to check and edit these mappings -- bringing in the missing knowledge that often exists only in their heads.

Expand Down Expand Up @@ -56,9 +56,9 @@ Are you a Python programmer with an interest in BIDS who knows about GE and/or P
.. |PyPI version| image:: https://img.shields.io/pypi/v/bidscoin?color=success
:target: https://pypi.org/project/bidscoin
:alt: BIDScoin
.. |PyPI - Python Version| image:: https://img.shields.io/pypi/pyversions/bidscoin.svg
.. |Python Version| image:: https://img.shields.io/pypi/pyversions/bidscoin.svg
:alt: Python 3
.. |Neurodesk| image:: https://img.shields.io/badge/Neurodesk-v4.2.1-green
.. |Neurodesk| image:: https://img.shields.io/badge/Neurodesk-green
:target: https://www.neurodesk.org/docs/overview/applications/
:alt: Neurodesk
.. |GPLv3| image:: https://img.shields.io/badge/License-GPLv3+-blue.svg
Expand Down
7 changes: 4 additions & 3 deletions bidscoin/bcoin.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,11 +606,12 @@ def reportcredits(args: list) -> None:
parser = argparse.ArgumentParser()
cmd_summary.setup_parser(parser)
dueargs = parser.parse_args([arg if n%2 else '--'+arg for n,arg in enumerate(args[1:])]) # Assumes key-value pairs, e.g. args[1] = 'style', args[2] = 'apa'
for report in (Path(args[0])/'code'/'bidscoin').glob('.duecredit_*'): # args[0] = bidsfolder
LOGGER.info(f"DueCredit report for {report.stem.replace('.duecredit_', '')}:")
for report in sorted((Path(args[0])/'code'/'bidscoin').glob('.duecredit_*')): # args[0] = bidsfolder
print(f"\n{'-'*42}")
LOGGER.info(f"DueCredit summary for {report.stem.replace('.duecredit_', '').upper()}():")
print(f"{'-'*42}")
dueargs.filename = report
cmd_summary.run(dueargs)
print(f"{'-'*40}\n")


def settracking(value: str) -> None:
Expand Down
2 changes: 1 addition & 1 deletion bidscoin/bidsapps/deface.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sys
if __name__ == "__main__" and os.getenv('DUECREDIT_ENABLE','').lower() not in ('1', 'yes', 'true'): # Ideally the due state (`self.__active=True`) should also be checked (but that's impossible)
os.environ['DUECREDIT_ENABLE'] = 'yes'
os.environ['DUECREDIT_FILE'] = os.path.join(sys.argv[1], 'code', 'bidscoin', '.duecredit_deface.p') # NB: argv[2] = bidsfolder
os.environ['DUECREDIT_FILE'] = os.path.join(sys.argv[1], 'code', 'bidscoin', '.duecredit_deface.p') # NB: argv[1] = bidsfolder

import shutil
import json
Expand Down
2 changes: 1 addition & 1 deletion bidscoin/bidsapps/echocombine.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sys
if __name__ == "__main__" and os.getenv('DUECREDIT_ENABLE','').lower() not in ('1', 'yes', 'true'): # Ideally the due state (`self.__active=True`) should also be checked (but that's impossible)
os.environ['DUECREDIT_ENABLE'] = 'yes'
os.environ['DUECREDIT_FILE'] = os.path.join(sys.argv[1], 'code', 'bidscoin', '.duecredit_echocombine.p') # NB: argv[2] = bidsfolder
os.environ['DUECREDIT_FILE'] = os.path.join(sys.argv[1], 'code', 'bidscoin', '.duecredit_echocombine.p') # NB: argv[1] = bidsfolder

import json
import logging
Expand Down
2 changes: 1 addition & 1 deletion bidscoin/bidsapps/medeface.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sys
if __name__ == "__main__" and os.getenv('DUECREDIT_ENABLE','').lower() not in ('1', 'yes', 'true'): # Ideally the due state (`self.__active=True`) should also be checked (but that's impossible)
os.environ['DUECREDIT_ENABLE'] = 'yes'
os.environ['DUECREDIT_FILE'] = os.path.join(sys.argv[1], 'code', 'bidscoin', '.duecredit_medeface.p') # NB: argv[2] = bidsfolder
os.environ['DUECREDIT_FILE'] = os.path.join(sys.argv[1], 'code', 'bidscoin', '.duecredit_medeface.p') # NB: argv[1] = bidsfolder

import shutil
import json
Expand Down
2 changes: 1 addition & 1 deletion bidscoin/bidsapps/skullstrip.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sys
if __name__ == "__main__" and os.getenv('DUECREDIT_ENABLE','').lower() not in ('1', 'yes', 'true'): # Ideally the due state (`self.__active=True`) should also be checked (but that's impossible)
os.environ['DUECREDIT_ENABLE'] = 'yes'
os.environ['DUECREDIT_FILE'] = os.path.join(sys.argv[1], 'code', 'bidscoin', '.duecredit_skullstrip.p') # NB: argv[2] = bidsfolder
os.environ['DUECREDIT_FILE'] = os.path.join(sys.argv[1], 'code', 'bidscoin', '.duecredit_skullstrip.p') # NB: argv[1] = bidsfolder

import shutil
import json
Expand Down
2 changes: 1 addition & 1 deletion bidscoin/bidsapps/slicereport.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sys
if __name__ == "__main__" and os.getenv('DUECREDIT_ENABLE','').lower() not in ('1', 'yes', 'true'): # Ideally the due state (`self.__active=True`) should also be checked (but that's impossible)
os.environ['DUECREDIT_ENABLE'] = 'yes'
os.environ['DUECREDIT_FILE'] = os.path.join(sys.argv[1], 'code', 'bidscoin', '.duecredit_slicereport.p') # NB: argv[2] = bidsfolder
os.environ['DUECREDIT_FILE'] = os.path.join(sys.argv[1], 'code', 'bidscoin', '.duecredit_slicereport.p') # NB: argv[1] = bidsfolder

import logging
import subprocess
Expand Down
4 changes: 2 additions & 2 deletions bidscoin/bidscoiner.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ def bidscoiner(rawfolder: str, bidsfolder: str, subjects: list=(), force: bool=F
if next(bidssession.rglob('*.json'), None):
bids.addparticipant(bidsfolder/'participants.tsv', subid, sesid, personals)

# Add the special fieldmap metadata (IntendedFor, B0FieldIdentifier, TE, etc)
# Add the special fieldmap metadata (IntendedFor, TE, etc)
addmetadata(bidssession, subid, sesid)

# Clean-up the temporary unpacked data
Expand All @@ -304,7 +304,7 @@ def bidscoiner(rawfolder: str, bidsfolder: str, subjects: list=(), force: bool=F

def addmetadata(bidsses: Path, subid: str, sesid: str) -> None:
"""
Adds the special fieldmap metadata (IntendedFor, B0FieldIdentifier, TE, etc.)
Adds the special fieldmap metadata (IntendedFor, TE, etc.)
:param bidsses: The session folder with the BIDS session data
:param subid: The subject 'sub-label' identifier
Expand Down
2 changes: 1 addition & 1 deletion bidscoin/cli/_bcoin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
them to your needs with any plain text editor)
Set the environment variable BIDSCOIN_DEBUG=TRUE in your console to run BIDScoin in its more verbose DEBUG logging mode.
A custom-made citation report can be generated with the help of duecredit (https://github.com/duecredit/duecredit)
Citation reports can be generated with the help of duecredit (https://github.com/duecredit/duecredit)
For more documentation see: https://bidscoin.readthedocs.io
"""
Expand Down
9 changes: 8 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@

## [dev]

## [4.3.0] - 2023-12-20

### Added
- fslmaths preprocessing on slicereport input images
- A `--cluster` option for running bidscoiner in parallel on a (DRMAA enabled) HPC
- Option to generate BIDScoin specific duecredit reports
- Support for BIDS 1.9.0
- A new special `<<session>>` dynamic meta-data value (most notably useful for creating session specific B0FieldIdentifier/Source tags)

### Changed
- `bidscoiner_plugin()` API: you can just return a personals dict instead of writing it to `participants.tsv`
- Using DRMAA library for skullstrip (instead of qsub/sbatch)
- Removed the pet2bids and phys2bids plugins (code is no longer actively developed)

## [4.2.1] - 2023-10-30

Expand Down Expand Up @@ -413,7 +419,8 @@ A first stable release of BIDScoin :-)
### To do
- Add support for non-imaging data

[dev]: https://github.com/Donders-Institute/bidscoin/compare/4.2.1...HEAD
[dev]: https://github.com/Donders-Institute/bidscoin/compare/4.3.0...HEAD
[4.3.0]: https://github.com/Donders-Institute/bidscoin/compare/4.2.1...4.3.0
[4.2.1]: https://github.com/Donders-Institute/bidscoin/compare/4.2.0...4.2.1
[4.2.0]: https://github.com/Donders-Institute/bidscoin/compare/4.1.1...4.2.0
[4.1.1]: https://github.com/Donders-Institute/bidscoin/compare/4.1.0...4.1.1
Expand Down
2 changes: 1 addition & 1 deletion docs/bidsmap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ If the run index is encoded in the header or filename, then the index number can

Fieldmaps: IntendedFor
^^^^^^^^^^^^^^^^^^^^^^
According to the BIDS specification, the IntendedFor value of fieldmaps must be a list of relative pathnames of associated output files. However, these output files may vary from session to session, i.e. the 'IntendedFor' value is dependent on the presence of other files in the output folder. To handle that, the dynamic `IntendedFor` value of the meta dictionary can be specified using Unix shell-style wildcard search strings. In that way, during bidscoiner runtime, the exact paths of these images on disk will be looked up using the Python ``glob(*value*)`` expression (see `here <https://docs.python.org/3.11/library/pathlib.html#pathlib.Path.glob>`__ for the exact syntax). For instance, using a simple ``{IntendedFor: <<task>>}`` value will use ``glob(*task*)`` to lookup all functional runs in the BIDS subject[/session] folder (since in BIDS these runs always have 'task' in their filename), whereas a more advanced ``{IntendedFor: <<func/*Stop*Go_bold><func/*Reward*_bold>>}`` value will select all 'Stop1Go'-, 'Stop2Go'- and 'Reward' bold-runs in the func sub-folder.
According to the BIDS specification, the IntendedFor value of fieldmaps must be a list of relative pathnames of associated output files. However, these output files may vary from session to session, i.e. the 'IntendedFor' value is dependent on the presence of other files in the output folder. To handle that, the dynamic `IntendedFor` value of the meta dictionary can be specified using Unix shell-style wildcard search strings. In that way, during bidscoiner runtime, the exact paths of these images on disk will be looked up using the Python ``glob(*value*)`` expression (see `here <https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob>`__ for the exact syntax). For instance, using a simple ``{IntendedFor: <<task>>}`` value will use ``glob(*task*)`` to lookup all functional runs in the BIDS subject[/session] folder (since in BIDS these runs always have 'task' in their filename), whereas a more advanced ``{IntendedFor: <<func/*Stop*Go_bold><func/*Reward*_bold>>}`` value will select all 'Stop1Go'-, 'Stop2Go'- and 'Reward' bold-runs in the func sub-folder.

In case duplicated field maps are acquired (e.g. when a scan failed or a session was interrupted) you can limit the search scope by appending a colon-separated "bounding" term to the search pattern. E.g. ``{IntendedFor: <<task:[]>>}`` will bound the wildcard search to files that are 'uninterruptedly connected' to the current field map, i.e. without there being another run of the field map in between. The bounded search can be further constrained by limiting the maximum number of matches, indicated with lower and upper limits. For instance ``{IntendedFor: <<task:[-3:0]>>}`` will limit the bounded search to maximally three runs preceding the field map. Similarly, ``{IntendedFor: <<task:[-2:2]>>}`` will limit the bounded search to maximally two preceding and two subsequent runs, and ``{IntendedFor: <<task:[0:]>>}`` will limit the bounded search to all matches acquired after the field map. In this latter case, for the first field map, only ``task-Stop_run-1`` and ``task-Stop_run-2`` will match the bounded search if the 5 collected runs were named: 1) ``fieldmap_run-1``, 2) ``task-Stop_run-1``, 3) ``task-Stop_run-2``, 4) ``fieldmap_run-2``, 5) ``task-Stop_run-3``. The second run of the field map will match with ``task-Stop_run-3`` only (note that the second field map would have matched all task runs if the bounding term would have been ``[]``, ``[:]`` or ``[-2:2]``).

Expand Down
4 changes: 2 additions & 2 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ If you do not have git (or any other version control system) installed you can `
$ pip install ./bidscoin[dcm2niix2bids]
If you are installing BIDScoin on an older system and you are getting Qt6 errors, you can try to install a ``+qt5`` build, e.g. for version 4.2.1:
If you are installing BIDScoin on an older system and you are getting Qt6 errors, you can try to install a ``+qt5`` build, e.g. for version 4.3.0:

.. code-block:: console
$ pip install bidscoin[dcm2niix2bids]@git+https://github.com/Donders-Institute/bidscoin@v4.2.0+qt5
$ pip install bidscoin[dcm2niix2bids]@git+https://github.com/Donders-Institute/bidscoin@v4.3.0+qt5
Updating BIDScoin
^^^^^^^^^^^^^^^^^
Expand Down
1 change: 0 additions & 1 deletion tests/test_bidscoiner.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ def test_bidscoiner(raw_dicomdir, bids_dicomdir, bidsmap_dicomdir):
assert (bids_dicomdir/'sub-Peter'/'ses-01').is_dir()
assert (bids_dicomdir/'sub-Peter'/'ses-04BrainMRA').is_dir()
assert len(list(bids_dicomdir.rglob('*.nii*'))) > 3 # Exact number (10) is a bit arbitrary (depends on what dcm2niix can convert)
assert sidecars[0].is_file()
with sidecars[0].open('r') as json_fid:
metadict = json.load(json_fid)
assert metadict.get('ProtocolName') == 'T/S/C RF FAST PILOT'
Expand Down

0 comments on commit 495346b

Please sign in to comment.