bagel 0.5.1.dev7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bagel/__init__.py +0 -0
- bagel/cli.py +520 -0
- bagel/dictionary_models.py +164 -0
- bagel/logger.py +63 -0
- bagel/mappings.py +102 -0
- bagel/models.py +97 -0
- bagel/pipeline-catalog/.git +1 -0
- bagel/pipeline-catalog/.pre-commit-config.yaml +16 -0
- bagel/pipeline-catalog/LICENSE +21 -0
- bagel/pipeline-catalog/README.md +2 -0
- bagel/pipeline-catalog/conversion/conversion.json +20 -0
- bagel/pipeline-catalog/processing/processing.json +36 -0
- bagel/utilities/__init__.py +0 -0
- bagel/utilities/bids_utils.py +74 -0
- bagel/utilities/derivative_utils.py +138 -0
- bagel/utilities/file_utils.py +96 -0
- bagel/utilities/model_utils.py +119 -0
- bagel/utilities/pheno_utils.py +590 -0
- bagel-0.5.1.dev7.dist-info/METADATA +146 -0
- bagel-0.5.1.dev7.dist-info/RECORD +23 -0
- bagel-0.5.1.dev7.dist-info/WHEEL +4 -0
- bagel-0.5.1.dev7.dist-info/entry_points.txt +2 -0
- bagel-0.5.1.dev7.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Iterable
|
|
4
|
+
|
|
5
|
+
import pydantic
|
|
6
|
+
from pydantic import ValidationError
|
|
7
|
+
|
|
8
|
+
from bagel import models
|
|
9
|
+
from bagel.logger import log_error, logger
|
|
10
|
+
from bagel.mappings import NB, SUPPORTED_NAMESPACES
|
|
11
|
+
from bagel.utilities import file_utils
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def generate_context():
|
|
15
|
+
# Adapted from the dandi-schema context generation function
|
|
16
|
+
# https://github.com/dandi/dandi-schema/blob/c616d87eaae8869770df0cb5405c24afdb9db096/dandischema/metadata.py
|
|
17
|
+
field_preamble = {
|
|
18
|
+
namespace.pf: namespace.url for namespace in SUPPORTED_NAMESPACES
|
|
19
|
+
}
|
|
20
|
+
fields = {}
|
|
21
|
+
for klass_name, klass in inspect.getmembers(models):
|
|
22
|
+
if inspect.isclass(klass) and issubclass(klass, pydantic.BaseModel):
|
|
23
|
+
fields[klass_name] = f"{NB.pf}:{klass_name}"
|
|
24
|
+
for name, field in klass.model_fields.items():
|
|
25
|
+
if name == "schemaKey":
|
|
26
|
+
fields[name] = "@type"
|
|
27
|
+
elif name == "identifier":
|
|
28
|
+
fields[name] = "@id"
|
|
29
|
+
elif name not in fields:
|
|
30
|
+
fields[name] = {"@id": f"{NB.pf}:{name}"}
|
|
31
|
+
|
|
32
|
+
field_preamble.update(**fields)
|
|
33
|
+
|
|
34
|
+
return {"@context": field_preamble}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def add_context_to_graph_dataset(dataset: models.Dataset) -> dict:
|
|
38
|
+
"""Add the Neurobagel context to a graph-ready dataset to form a JSONLD dictionary."""
|
|
39
|
+
context = generate_context()
|
|
40
|
+
# We can't just exclude_unset here because the identifier and schemaKey
|
|
41
|
+
# for each instance are created as default values and so technically are never set
|
|
42
|
+
# TODO: we should revisit this because there may be reasons to have None be meaningful in the future
|
|
43
|
+
return {**context, **dataset.model_dump(exclude_none=True)}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_subs_missing_from_pheno_data(
|
|
47
|
+
subjects: Iterable, pheno_subjects: Iterable
|
|
48
|
+
) -> list:
|
|
49
|
+
"""Check a list of subject IDs and return any not found in the provided phenotypic subject list."""
|
|
50
|
+
return list(set(subjects).difference(pheno_subjects))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def confirm_subs_match_pheno_data(
|
|
54
|
+
subjects: Iterable, subject_source_for_err: str, pheno_subjects: Iterable
|
|
55
|
+
):
|
|
56
|
+
"""
|
|
57
|
+
Return an error if not all subjects in the subject list are found in the provided phenotypic subject list.
|
|
58
|
+
"""
|
|
59
|
+
missing_subs = get_subs_missing_from_pheno_data(
|
|
60
|
+
subjects=subjects,
|
|
61
|
+
pheno_subjects=pheno_subjects,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if len(missing_subs) > 0:
|
|
65
|
+
log_error(
|
|
66
|
+
logger,
|
|
67
|
+
f"The specified {subject_source_for_err} contains subject IDs not found in "
|
|
68
|
+
"the provided json-ld file:\n"
|
|
69
|
+
f"{missing_subs}\n"
|
|
70
|
+
"Subject IDs are case sensitive. "
|
|
71
|
+
f"Please check that the {subject_source_for_err} corresponds to the dataset in the provided .jsonld.",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def extract_and_validate_jsonld_dataset(file_path: Path) -> models.Dataset:
|
|
76
|
+
"""
|
|
77
|
+
Strip the context from a user-provided JSONLD and validate the remaining contents
|
|
78
|
+
against the data model for a Neurobagel dataset.
|
|
79
|
+
"""
|
|
80
|
+
jsonld = file_utils.load_json(file_path)
|
|
81
|
+
jsonld.pop("@context")
|
|
82
|
+
try:
|
|
83
|
+
jsonld_dataset = models.Dataset.model_validate(jsonld)
|
|
84
|
+
except ValidationError as err:
|
|
85
|
+
log_error(
|
|
86
|
+
logger,
|
|
87
|
+
f"Error: {file_path} is not a valid Neurobagel JSONLD dataset. "
|
|
88
|
+
"Please ensure to provide a valid JSONLD file generated by Neurobagel CLI commands.\n"
|
|
89
|
+
f"Validation errors: {str(err)}",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return jsonld_dataset
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_subject_instances(
|
|
96
|
+
dataset: models.Dataset,
|
|
97
|
+
) -> dict[str, models.Subject]:
|
|
98
|
+
"""
|
|
99
|
+
Return a dictionary of subjects for a given Neurobagel dataset from JSONLD data,
|
|
100
|
+
where keys are subject labels and values are the subject objects.
|
|
101
|
+
"""
|
|
102
|
+
return {
|
|
103
|
+
subject.hasLabel: subject for subject in getattr(dataset, "hasSamples")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_imaging_session_instances(
|
|
108
|
+
jsonld_subject: models.Subject,
|
|
109
|
+
) -> dict[str, models.ImagingSession]:
|
|
110
|
+
"""
|
|
111
|
+
Return a dictionary of imaging sessions for a given subject from JSONLD data,
|
|
112
|
+
where the keys are the session labels and values are the session objects.
|
|
113
|
+
"""
|
|
114
|
+
jsonld_sub_sessions_dict = {}
|
|
115
|
+
for jsonld_sub_ses in getattr(jsonld_subject, "hasSession"):
|
|
116
|
+
if jsonld_sub_ses.schemaKey == "ImagingSession":
|
|
117
|
+
jsonld_sub_sessions_dict[jsonld_sub_ses.hasLabel] = jsonld_sub_ses
|
|
118
|
+
|
|
119
|
+
return jsonld_sub_sessions_dict
|