Skip to content

Commit 156c4ce

Browse files
authored
Merge branch 'main' into patch-1
2 parents 9a99d91 + 149a758 commit 156c4ce

18 files changed

+10968
-267
lines changed

.github/workflows/python.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
run: |
5454
cd pypet2bids
5555
python -m pip install --upgrade pip
56-
pip install poetry
56+
pip install poetry==1.6.1
5757
poetry install --with dev
5858
5959
- name: Collect ECAT and other phantoms

.readthedocs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ build:
2121
post_create_environment:
2222
- cp -r metadata pypet2bids/pypet2bids/
2323
# install poetry
24-
- pip install poetry
24+
- pip install poetry==1.6.1
2525
- poetry config virtualenvs.create false
2626
# just use poetry to export a requirements.txt as that worked much better than the previous attempts
2727
- cd pypet2bids && poetry lock && poetry export --without-hashes --with dev --format=requirements.txt > requirements.txt

Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ add:
2626

2727
# copies metadata to path included in pypet2bids project to enable packaging of those files w/ poetry
2828
buildpackage:
29-
@cp -R metadata/ pypet2bids/pypet2bids/metadata
3029
@cp pypet2bids/pyproject.toml pypet2bids/pypet2bids/pyproject.toml
3130
@rm -rf pypet2bids/dist
3231
@cd pypet2bids && poetry lock && poetry build

contributors.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Granville Matheson 📝 🐛 ✅
1212
Martin Norgaard 💻 💬 🤔 ⚠️ 👀
1313
Cyril Pernet 💻 📖 💬 🎨 💡 📋 🤔 ⚠️
1414
Chris Rorden 💻 🐛 📖
15+
Maximilian Cosmo Sitter 💻
1516
Claus Svarer 💻 💡⚠️
1617
Adam G Thomas 🔍 🤔
1718
Robert Innis 💡⚠️💵

matlab/ecat2nii.m

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,16 @@
211211
Sca = Sca*MinImg/(-32768);
212212
end
213213

214-
% save scaling factor to file located at ecat_save_steps_dir/8.5_sca_matlab.txt
215-
% fid = fopen([ecat_save_steps_dir filesep '8.5_sca_matlab.txt'],'w');
214+
if (ecat_save_steps == '1')
215+
save scaling factor to file located at ecat_save_steps_dir/8.5_sca_matlab.txt
216+
fid = fopen([ecat_save_steps_dir filesep '8.5_sca_matlab.txt'],'w');
217+
fclose(fid);
218+
end
219+
216220
fprintf(fid,'Scaling factor: %10e\n',Sca);
217221
x = mh.ecat_calibration_factor * Sca;
218-
fprintf(fid,'Scaling factor * ECAT Cal Factor: %10.10f\n',x);
219-
% fclose(fid);
222+
fprintf(fid,'Scaling factor * ECAT Cal Factor: %10.10f\n',x);
223+
220224
end
221225

222226
% save debugging step 8 - rescale to 16 bits

pypet2bids/poetry.lock

Lines changed: 118 additions & 131 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pypet2bids/pypet2bids/dcm2niix4pet.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ def __init__(
175175
file_format="%p_%i_%t_%s",
176176
silent=False,
177177
tempdir_location=None,
178+
ezbids=False
178179
):
179180
"""
180181
This class is a simple wrapper for dcm2niix and contains methods to do the following in order:
@@ -215,6 +216,7 @@ def __init__(
215216
self.blood_json = None
216217
self.blood_tsv = None
217218
self.telemetry_data = {}
219+
self.ezbids=ezbids
218220
self.dcm2niix_path = self.check_for_dcm2niix()
219221
if not self.dcm2niix_path:
220222
logger.error(
@@ -588,7 +590,8 @@ def run_dcm2niix(self):
588590
created_path,
589591
check_for_missing,
590592
dicom_header,
591-
dicom2bids_json=metadata_dictionaries["dicom2bids.json"],
593+
dicom2bids_json=metadata_dictionaries["dicom2bids"],
594+
ezbids=self.ezbids,
592595
**self.additional_arguments,
593596
)
594597

@@ -1162,6 +1165,13 @@ def cli():
11621165
"This information helps to improve PET2BIDS and provides an indicator of real world "
11631166
"usage crucial for obtaining funding.",
11641167
)
1168+
parser.add_argument(
1169+
"--ezbids",
1170+
action="store_true",
1171+
default=False,
1172+
help="Add fields to json output that are useful for ezBIDS or other conversion software. This will de-anonymize"
1173+
" pet2bids output and add AcquisitionDate an AcquisitionTime into the output json."
1174+
)
11651175
return parser
11661176

11671177

@@ -1328,6 +1338,7 @@ def main():
13281338
additional_arguments=cli_args.kwargs,
13291339
tempdir_location=cli_args.tempdir,
13301340
silent=cli_args.silent,
1341+
ezbids=cli_args.ezbids,
13311342
)
13321343

13331344
if cli_args.trc:

pypet2bids/pypet2bids/ecat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def make_nifti(self, output_path=None):
244244
self.telemetry_data["NiftiFiles"] = 1
245245
self.telemetry_data["NiftiFilesSize"] = pathlib.Path(output).stat().st_size
246246

247-
if "nii.gz" not in output:
247+
if "nii.gz" not in pathlib.Path(output).name:
248248
output = helper_functions.compress(output)
249249

250250
return output

pypet2bids/pypet2bids/helper_functions.py

Lines changed: 44 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -39,30 +39,26 @@
3939
import argparse
4040
from typing import Union
4141
from platform import system
42+
import importlib
43+
44+
try:
45+
import metadata
46+
except ImportError:
47+
from pypet2bids import metadata
4248

4349
parent_dir = pathlib.Path(__file__).parent.resolve()
4450
project_dir = parent_dir.parent.parent
4551
if "PET2BIDS" not in project_dir.parts:
4652
project_dir = parent_dir
4753

48-
metadata_dir = os.path.join(project_dir, "metadata")
49-
50-
# check to see where the schema is at
51-
pet_metadata_json = os.path.join(metadata_dir, "PET_metadata.json")
52-
permalink_pet_metadata_json = "https://github.com/openneuropet/PET2BIDS/blob/76d95cf65fa8a14f55a4405df3fdec705e2147cf/metadata/PET_metadata.json"
53-
pet_reconstruction_metadata_json = os.path.join(
54-
metadata_dir, "PET_reconstruction_methods.json"
55-
)
5654

5755
# load bids schema
58-
bids_schema_path = os.path.join(metadata_dir, "schema.json")
59-
schema = json.load(open(bids_schema_path, "r"))
60-
56+
schema = metadata.schema
57+
pet_metadata = metadata.PET_metadata
6158
# putting these paths here as they are reused in dcm2niix4pet.py, update_json_pet_file.py, and ecat.py
6259
module_folder = Path(__file__).parent.resolve()
6360
python_folder = module_folder.parent
6461
pet2bids_folder = python_folder.parent
65-
metadata_folder = os.path.join(pet2bids_folder, "metadata")
6662

6763
loggers = {}
6864

@@ -91,19 +87,6 @@ def logger(name):
9187
return logger
9288

9389

94-
def load_pet_bids_requirements_json(
95-
pet_bids_req_json: Union[str, pathlib.Path] = pet_metadata_json
96-
) -> dict:
97-
if type(pet_bids_req_json) is str:
98-
pet_bids_req_json = pathlib.Path(pet_bids_req_json)
99-
if pet_bids_req_json.is_file():
100-
with open(pet_bids_req_json, "r") as infile:
101-
reqs = json.load(infile)
102-
return reqs
103-
else:
104-
raise FileNotFoundError(pet_bids_req_json)
105-
106-
10790
def flatten_series(series):
10891
"""
10992
This function retrieves either a list or a single value from a pandas series object thus converting a complex
@@ -146,12 +129,13 @@ def collect_spreadsheets(folder_path: pathlib.Path):
146129

147130
def single_spreadsheet_reader(
148131
path_to_spreadsheet: Union[str, pathlib.Path],
149-
pet2bids_metadata_json: Union[str, pathlib.Path] = pet_metadata_json,
132+
pet2bids_metadata: dict = metadata.PET_metadata,
150133
dicom_metadata={},
151134
**kwargs,
152135
) -> dict:
153136

154-
metadata = {}
137+
spreadsheet_metadata = {}
138+
metadata_fields = pet2bids_metadata
155139

156140
if type(path_to_spreadsheet) is str:
157141
path_to_spreadsheet = pathlib.Path(path_to_spreadsheet)
@@ -161,24 +145,6 @@ def single_spreadsheet_reader(
161145
else:
162146
raise FileNotFoundError(f"{path_to_spreadsheet} does not exist.")
163147

164-
if pet2bids_metadata_json:
165-
if type(pet_metadata_json) is str:
166-
pet2bids_metadata_json = pathlib.Path(pet2bids_metadata_json)
167-
168-
if pet2bids_metadata_json.is_file():
169-
with open(pet_metadata_json, "r") as infile:
170-
metadata_fields = json.load(infile)
171-
else:
172-
raise FileNotFoundError(
173-
f"Required metadata file not found at {pet_metadata_json}, check to see if this file exists;"
174-
f"\nelse pass path to file formatted to this {permalink_pet_metadata_json} via "
175-
f"pet2bids_metadata_json argument in simplest_spreadsheet_reader call."
176-
)
177-
else:
178-
raise FileNotFoundError(
179-
f"pet2bids_metadata_json input required for function call, you provided {pet2bids_metadata_json}"
180-
)
181-
182148
spreadsheet_dataframe = open_meta_data(path_to_spreadsheet)
183149

184150
log = logging.getLogger("pypet2bids")
@@ -188,7 +154,7 @@ def single_spreadsheet_reader(
188154
for field in metadata_fields[field_level]:
189155
series = spreadsheet_dataframe.get(field, Series(dtype=numpy.float64))
190156
if not series.empty:
191-
metadata[field] = flatten_series(series)
157+
spreadsheet_metadata[field] = flatten_series(series)
192158
elif (
193159
series.empty
194160
and field_level == "mandatory"
@@ -200,10 +166,10 @@ def single_spreadsheet_reader(
200166
)
201167

202168
# lastly apply any kwargs to the metadata
203-
metadata.update(**kwargs)
169+
spreadsheet_metadata.update(**kwargs)
204170

205171
# more lastly, check to see if values are of the correct datatype (e.g. string, number, boolean)
206-
for field, value in metadata.items():
172+
for field, value in spreadsheet_metadata.items():
207173
# check schema for field
208174
field_schema_properties = schema["objects"]["metadata"].get(field, None)
209175
if field_schema_properties:
@@ -216,7 +182,7 @@ def single_spreadsheet_reader(
216182
try:
217183
check_bool = int(value) / 1
218184
if check_bool == 0 or check_bool == 1:
219-
metadata[field] = bool(value)
185+
spreadsheet_metadata[field] = bool(value)
220186
else:
221187
log.warning(
222188
f"{field} is not boolean, it's value is {value}"
@@ -228,7 +194,7 @@ def single_spreadsheet_reader(
228194
log.warning(f"{field} is not string, it's value is {value}")
229195
else:
230196
pass
231-
return metadata
197+
return spreadsheet_metadata
232198

233199

234200
def compress(file_like_object, output_path: str = None):
@@ -316,27 +282,37 @@ def load_vars_from_config(
316282

317283
def get_version():
318284
"""
319-
Gets the version of this software from the toml file
320-
:return: version number from pyproject.toml
285+
Gets the version of this software
286+
:return: version number
321287
"""
322288
# this scripts directory path
323289
scripts_dir = pathlib.Path(os.path.dirname(__file__))
324290

325-
try:
326-
# if this is bundled as a package look next to this file for the pyproject.toml
327-
toml_path = os.path.join(scripts_dir, "pyproject.toml")
328-
with open(toml_path, "r") as infile:
329-
tomlfile = toml.load(infile)
330-
except FileNotFoundError:
331-
# when in development the toml file with the version is 2 directories above (e.g. where it should actually live)
332-
toml_dir = scripts_dir.parent
333-
toml_path = os.path.join(toml_dir, "pyproject.toml")
334-
with open(toml_path, "r") as infile:
335-
tomlfile = toml.load(infile)
336-
337-
attrs = tomlfile.get("tool", {})
338-
poetry = attrs.get("poetry", {})
339-
version = poetry.get("version", "")
291+
# first try using importlib.metadata.version to determine version
292+
293+
version = importlib.metadata.version("pypet2bids")
294+
295+
if not version:
296+
tomlfile = {}
297+
298+
try:
299+
# if this is bundled as a package look next to this file for the pyproject.toml
300+
toml_path = os.path.join(scripts_dir, "pyproject.toml")
301+
with open(toml_path, "r") as infile:
302+
tomlfile = toml.load(infile)
303+
except FileNotFoundError:
304+
# when in development the toml file with the version is 2 directories above (e.g. where it should actually live)
305+
try:
306+
toml_dir = scripts_dir.parent
307+
toml_path = os.path.join(toml_dir, "pyproject.toml")
308+
with open(toml_path, "r") as infile:
309+
tomlfile = toml.load(infile)
310+
except FileNotFoundError:
311+
pass
312+
313+
attrs = tomlfile.get("tool", {})
314+
poetry = attrs.get("poetry", {})
315+
version = poetry.get("version", "")
340316

341317
return version
342318

@@ -820,9 +796,7 @@ def get_recon_method(ReconstructionMethodString: str) -> dict:
820796
dimension = re.search(search_criteria, ReconMethodName)[0]
821797

822798
# doing some more manipulation of the recon method name to expand it from not so helpful acronyms
823-
possible_names = load_pet_bids_requirements_json(pet_reconstruction_metadata_json)[
824-
"reconstruction_names"
825-
]
799+
possible_names = metadata.PET_reconstruction_methods.get("reconstruction_names", [])
826800

827801
# we want to sort the possible names by longest first that we don't break up an acronym prematurely
828802
sorted_df = pandas.DataFrame(possible_names).sort_values(

pypet2bids/pypet2bids/is_pet.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,21 @@
1515
import helper_functions
1616
import ecat
1717
import dcm2niix4pet
18+
import metadata
1819
except ModuleNotFoundError:
1920
import pypet2bids.helper_functions as helper_functions
2021
import pypet2bids.ecat as ecat
2122
import pypet2bids.dcm2niix4pet as dcm2niix4pet
23+
import pypet2bids.metadata
2224

2325

2426
def spread_sheet_check_for_pet(sourcefile: Union[str, Path], **kwargs):
2527
# load data from spreadsheet
2628
data = helper_functions.open_meta_data(sourcefile)
2729

28-
# load BIDS PET requirements
2930
try:
30-
with open(
31-
helper_functions.pet_metadata_json, "r"
32-
) as pet_field_requirements_json:
33-
pet_field_requirements = json.load(pet_field_requirements_json)
34-
except (FileNotFoundError, json.JSONDecodeError) as error:
35-
print(
36-
f"Unable to load list of required, recommended, and optional PET BIDS fields from"
37-
f" {helper_functions.pet_metadata_json}, will not be able to determine if sourcefile contains"
38-
f" PET BIDS specific metadata"
39-
)
31+
pet_field_requirements = metadata.PET_metadata
32+
except:
4033
pet_field_requirements = {}
4134

4235
mandatory_fields = pet_field_requirements.get("mandatory", [])

0 commit comments

Comments
 (0)