qmenta-client 1.1.dev1295__py3-none-any.whl → 1.1.dev1324__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.
- qmenta/client/Project.py +380 -94
- {qmenta_client-1.1.dev1295.dist-info → qmenta_client-1.1.dev1324.dist-info}/METADATA +1 -1
- {qmenta_client-1.1.dev1295.dist-info → qmenta_client-1.1.dev1324.dist-info}/RECORD +4 -4
- {qmenta_client-1.1.dev1295.dist-info → qmenta_client-1.1.dev1324.dist-info}/WHEEL +0 -0
qmenta/client/Project.py
CHANGED
|
@@ -17,6 +17,7 @@ if sys.version_info[0] == 3:
|
|
|
17
17
|
unicode = str
|
|
18
18
|
|
|
19
19
|
logger_name = "qmenta.client"
|
|
20
|
+
OPERATOR_LIST = ["eq", "ne", "gt", "gte", "lt", "lte"]
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def show_progress(done, total, finish=False):
|
|
@@ -67,6 +68,138 @@ def check_upload_file(file_path):
|
|
|
67
68
|
return True
|
|
68
69
|
|
|
69
70
|
|
|
71
|
+
def operation(reference_value, operator, input_value):
|
|
72
|
+
"""
|
|
73
|
+
The method performs an operation by comparing the two input values.
|
|
74
|
+
The Operation is applied to the Input Value in comparison to the Reference
|
|
75
|
+
Value.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
reference_value : str, list, or int
|
|
80
|
+
Reference value.
|
|
81
|
+
operator : str
|
|
82
|
+
Operation.
|
|
83
|
+
input_value : str, list, or int
|
|
84
|
+
Input value.
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
bool
|
|
89
|
+
True if the operation is satisfied, False otherwise.
|
|
90
|
+
"""
|
|
91
|
+
if input_value is None or input_value == "":
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
if operator == "in":
|
|
95
|
+
return reference_value in input_value
|
|
96
|
+
|
|
97
|
+
elif operator == "in-list":
|
|
98
|
+
return all([el in input_value for el in reference_value])
|
|
99
|
+
|
|
100
|
+
elif operator == "eq":
|
|
101
|
+
return input_value == reference_value
|
|
102
|
+
|
|
103
|
+
elif operator == "gt":
|
|
104
|
+
return input_value > reference_value
|
|
105
|
+
|
|
106
|
+
elif operator == "gte":
|
|
107
|
+
return input_value >= reference_value
|
|
108
|
+
|
|
109
|
+
elif operator == "lt":
|
|
110
|
+
return input_value < reference_value
|
|
111
|
+
|
|
112
|
+
elif operator == "lte":
|
|
113
|
+
return input_value <= reference_value
|
|
114
|
+
else:
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def wrap_search_criteria(search_criteria={}):
|
|
119
|
+
"""
|
|
120
|
+
Wraps the conditions specified within the Search Criteria in order for
|
|
121
|
+
other methods to handle it easily. The conditions are grouped only into
|
|
122
|
+
three groups: Modality, Tags and the File Metadata (if DICOM it corresponds
|
|
123
|
+
to the DICOM information), and each of them is output in a different
|
|
124
|
+
variable.
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
search_criteria : dict
|
|
129
|
+
Each element is a string and is built using the formatting
|
|
130
|
+
"KEYTYPE;VALUE", or "KEYTYPE;OPERATOR|VALUE".
|
|
131
|
+
|
|
132
|
+
Full list of keys avaiable for the dictionary:
|
|
133
|
+
|
|
134
|
+
search_criteria = {
|
|
135
|
+
"pars_patient_secret_name": "string;SUBJECTID",
|
|
136
|
+
"pars_ssid": "integer;OPERATOR|SSID",
|
|
137
|
+
"pars_modalities": "string;MODALITY",
|
|
138
|
+
"pars_tags": "tags;TAGS",
|
|
139
|
+
"pars_age_at_scan": "integer;OPERATOR|AGE_AT_SCAN",
|
|
140
|
+
"pars_[dicom]_KEY": "KEYTYPE;KEYVALUE",
|
|
141
|
+
"pars_PROJECTMETADATA": "METADATATYPE;METADATAVALUE",
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
See documentation for a complete definition.
|
|
145
|
+
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
modality : str
|
|
149
|
+
String containing the modality of the search criteria extracted from
|
|
150
|
+
'pars_modalities'
|
|
151
|
+
|
|
152
|
+
tags : list of str
|
|
153
|
+
List of strings containing the tags of the search criteria extracted
|
|
154
|
+
'from pars_tags'
|
|
155
|
+
|
|
156
|
+
file_metadata : Dict
|
|
157
|
+
Dictionary containing the file metadata of the search criteria
|
|
158
|
+
extracted from 'pars_[dicom]_KEY'
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
# The keys not included bellow apply to the whole session.
|
|
162
|
+
modality, tags, file_metadata = "", list(), dict()
|
|
163
|
+
for key, value in search_criteria.items():
|
|
164
|
+
if key == "pars_modalities":
|
|
165
|
+
modalities = value.split(";")[1].split(",")
|
|
166
|
+
if len(modalities) != 1:
|
|
167
|
+
raise ValueError(f"A file can only have one modality. "
|
|
168
|
+
f"Provided Modalities: "
|
|
169
|
+
f"{', '.join(modalities)}.")
|
|
170
|
+
modality = modalities[0]
|
|
171
|
+
elif key == "pars_tags":
|
|
172
|
+
tags = value.split(";")[1].split(",")
|
|
173
|
+
elif "pars_[dicom]_" in key:
|
|
174
|
+
d_tag = key.split("pars_[dicom]_")[1]
|
|
175
|
+
d_type = value.split(";")[0]
|
|
176
|
+
if d_type == "string":
|
|
177
|
+
file_metadata[d_tag] = {
|
|
178
|
+
"operation": "in",
|
|
179
|
+
"value": value.replace(d_type + ";", "")
|
|
180
|
+
}
|
|
181
|
+
elif d_type == "integer":
|
|
182
|
+
d_operator = value.split(";")[1].split("|")[0]
|
|
183
|
+
d_value = value.split(";")[1].split("|")[1]
|
|
184
|
+
file_metadata[d_tag] = {
|
|
185
|
+
"operation": d_operator,
|
|
186
|
+
"value": int(d_value)}
|
|
187
|
+
elif d_type == "decimal":
|
|
188
|
+
d_operator = value.split(";")[1].split("|")[0]
|
|
189
|
+
d_value = value.split(";")[1].split("|")[1]
|
|
190
|
+
file_metadata[d_tag] = {
|
|
191
|
+
"operation": d_operator,
|
|
192
|
+
"value": float(d_value)
|
|
193
|
+
}
|
|
194
|
+
elif d_type == "list":
|
|
195
|
+
value.replace(d_type + ";", "")
|
|
196
|
+
file_metadata[d_tag] = {
|
|
197
|
+
"operation": "in-list",
|
|
198
|
+
"value": value.replace(d_type + ";", "").split(";")
|
|
199
|
+
}
|
|
200
|
+
return modality, tags, file_metadata
|
|
201
|
+
|
|
202
|
+
|
|
70
203
|
class QCStatus(Enum):
|
|
71
204
|
"""
|
|
72
205
|
Enum with the following options:
|
|
@@ -162,8 +295,8 @@ class Project:
|
|
|
162
295
|
|
|
163
296
|
def get_subjects_metadata(self, search_criteria={}, items=(0, 9999)):
|
|
164
297
|
"""
|
|
165
|
-
List all
|
|
166
|
-
search criteria.
|
|
298
|
+
List all Subject ID/Session ID from the selected project that meet the
|
|
299
|
+
defined search criteria at a session level.
|
|
167
300
|
|
|
168
301
|
Parameters
|
|
169
302
|
----------
|
|
@@ -198,10 +331,12 @@ class Project:
|
|
|
198
331
|
|
|
199
332
|
"pars_modalities": Applies the search to the file 'Modalities'
|
|
200
333
|
available within each Subject ID.
|
|
201
|
-
MODALITY is a comma separated list of string.
|
|
334
|
+
MODALITY is a comma separated list of string. A session is provided as
|
|
335
|
+
long as one MODALITY is available.
|
|
202
336
|
"pars_tags": Applies the search to the file 'Tags' available within
|
|
203
337
|
each Subject ID and to the subject-level 'Tags'.
|
|
204
|
-
TAGS is a comma separated list of strings.
|
|
338
|
+
TAGS is a comma separated list of strings. A session is provided as
|
|
339
|
+
long as one tag is available.
|
|
205
340
|
"pars_age_at_scan": Applies the search to the 'age_at_scan' metadata
|
|
206
341
|
field.
|
|
207
342
|
AGE_AT_SCAN is an integer.
|
|
@@ -211,11 +346,15 @@ class Project:
|
|
|
211
346
|
'file_m["metadata"]["info"].keys()'.
|
|
212
347
|
KEYTYPE is the type of the KEY. One of:
|
|
213
348
|
- integer
|
|
349
|
+
- decimal
|
|
214
350
|
- string
|
|
215
351
|
- list
|
|
216
352
|
|
|
217
|
-
if 'integer' you must also include an OPERATOR
|
|
353
|
+
if 'integer' or 'decimal' you must also include an OPERATOR
|
|
218
354
|
(i.e., "integer;OPERATOR|KEYVALUE").
|
|
355
|
+
if 'list' the KEYVALUE should be a semicolon separated list of
|
|
356
|
+
values. (i.e., "list;KEYVALUE1;KEYVALUE2;KEYVALUE3)
|
|
357
|
+
KEYVALUEs must be strings.
|
|
219
358
|
KEYVALUE is the expected value of the KEY.
|
|
220
359
|
"pars_[dicom]_PROJECTMETADATA": Applies to the metadata defined
|
|
221
360
|
within the 'Metadata Manager' of the project.
|
|
@@ -244,6 +383,7 @@ class Project:
|
|
|
244
383
|
"pars_tags": "tags;flair",
|
|
245
384
|
"pars_[dicom]_Manufacturer": "string;ge",
|
|
246
385
|
"pars_[dicom]_FlipAngle": "integer;gt|5",
|
|
386
|
+
"pars_[dicom]_ImageType": "list;PRIMARY;SECONDARY",
|
|
247
387
|
}
|
|
248
388
|
|
|
249
389
|
Note the search criteria applies to all the files included within a
|
|
@@ -251,8 +391,9 @@ class Project:
|
|
|
251
391
|
are applied to the same files. In example 2) above, it means that
|
|
252
392
|
any Subject ID/Session ID that has a file classified with a 'T1'
|
|
253
393
|
modality, a file with a 'flair' tag, a file whose Manufacturer
|
|
254
|
-
contains 'ge',
|
|
255
|
-
|
|
394
|
+
contains 'ge', a file whose FlipAngle is greater than '5º', and a
|
|
395
|
+
file with ImageType with any of the values: PRIMARY or SECONDARY
|
|
396
|
+
will be selected.
|
|
256
397
|
|
|
257
398
|
Returns
|
|
258
399
|
-------
|
|
@@ -268,12 +409,11 @@ class Project:
|
|
|
268
409
|
f"All keys of the search_criteria dictionary " f"'{search_criteria.keys()}' must start with 'pars_'."
|
|
269
410
|
)
|
|
270
411
|
|
|
271
|
-
operator_list = ["eq", "ne", "gt", "gte", "lt", "lte"]
|
|
272
412
|
for key, value in search_criteria.items():
|
|
273
413
|
if value.split(";")[0] in ["integer", "decimal"]:
|
|
274
|
-
assert value.split(";")[1].split("|")[0] in
|
|
414
|
+
assert value.split(";")[1].split("|")[0] in OPERATOR_LIST, (
|
|
275
415
|
f"Search criteria of type '{value.split(';')[0]}' must "
|
|
276
|
-
f"include an operator ({', '.join(
|
|
416
|
+
f"include an operator ({', '.join(OPERATOR_LIST)})."
|
|
277
417
|
)
|
|
278
418
|
|
|
279
419
|
content = platform.parse_response(
|
|
@@ -286,6 +426,160 @@ class Project:
|
|
|
286
426
|
)
|
|
287
427
|
return content
|
|
288
428
|
|
|
429
|
+
def get_subjects_files_metadata(self, search_criteria={}, items=(0, 9999)):
|
|
430
|
+
"""
|
|
431
|
+
List all Subject ID/Session ID from the selected project that meet the
|
|
432
|
+
defined search criteria at a file level.
|
|
433
|
+
|
|
434
|
+
Note, albeit the search criteria is similar to the one defined in
|
|
435
|
+
method 'get_subjects_metadata()' (see differences below), the
|
|
436
|
+
output is different as this method provides the sessions which
|
|
437
|
+
have a file that satisfy all the conditions of the search criteria.
|
|
438
|
+
This method is slow.
|
|
439
|
+
|
|
440
|
+
Parameters
|
|
441
|
+
----------
|
|
442
|
+
search_criteria: dict
|
|
443
|
+
Each element is a string and is built using the formatting
|
|
444
|
+
"type;value", or "type;operation|value"
|
|
445
|
+
|
|
446
|
+
Complete search_criteria Dictionary Explanation:
|
|
447
|
+
|
|
448
|
+
search_criteria = {
|
|
449
|
+
"pars_patient_secret_name": "string;SUBJECTID",
|
|
450
|
+
"pars_ssid": "integer;OPERATOR|SSID",
|
|
451
|
+
"pars_modalities": "string;MODALITY",
|
|
452
|
+
"pars_tags": "tags;TAGS",
|
|
453
|
+
"pars_age_at_scan": "integer;OPERATOR|AGE_AT_SCAN",
|
|
454
|
+
"pars_[dicom]_KEY": "KEYTYPE;KEYVALUE",
|
|
455
|
+
"pars_PROJECTMETADATA": "METADATATYPE;METADATAVALUE",
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
Where:
|
|
459
|
+
"pars_patient_secret_name": Applies the search to the 'Subject ID'.
|
|
460
|
+
SUBJECTID is a comma separated list of strings.
|
|
461
|
+
"pars_ssid": Applies the search to the 'Session ID'.
|
|
462
|
+
SSID is an integer.
|
|
463
|
+
OPERATOR is the operator to apply. One of:
|
|
464
|
+
- Equal: eq
|
|
465
|
+
- Different Than: ne
|
|
466
|
+
- Greater Than: gt
|
|
467
|
+
- Greater/Equal To: gte
|
|
468
|
+
- Lower Than: lt
|
|
469
|
+
- Lower/Equal To: lte
|
|
470
|
+
|
|
471
|
+
"pars_modalities": Applies the search to the file 'Modalities'
|
|
472
|
+
available within each Subject ID.
|
|
473
|
+
MODALITY is a string.
|
|
474
|
+
"pars_tags": Applies only the search to the file 'Tags' available
|
|
475
|
+
within each Subject ID.
|
|
476
|
+
TAGS is a comma separated list of strings. All tags must be present in
|
|
477
|
+
the same file.
|
|
478
|
+
"pars_age_at_scan": Applies the search to the 'age_at_scan' metadata
|
|
479
|
+
field.
|
|
480
|
+
AGE_AT_SCAN is an integer.
|
|
481
|
+
"pars_[dicom]_KEY": Applies the search to the metadata fields
|
|
482
|
+
available within each file. KEY must be one of the
|
|
483
|
+
metadata keys of the files. The full list of KEYS is shown above via
|
|
484
|
+
'file_m["metadata"]["info"].keys()'. # TODO: SEE HOW TO WRITE THIS
|
|
485
|
+
KEYTYPE is the type of the KEY. One of:
|
|
486
|
+
- integer
|
|
487
|
+
- decimal
|
|
488
|
+
- string
|
|
489
|
+
- list
|
|
490
|
+
|
|
491
|
+
if 'integer' or 'decimal' you must also include an OPERATOR
|
|
492
|
+
(i.e., "integer;OPERATOR|KEYVALUE").
|
|
493
|
+
if 'list' the KEYVALUE should be a semicolon separated list of
|
|
494
|
+
values. (i.e., "list;KEYVALUE1;KEYVALUE2;KEYVALUE3)
|
|
495
|
+
KEYVALUEs must be strings.
|
|
496
|
+
KEYVALUE is the expected value of the KEY.
|
|
497
|
+
"pars_[dicom]_PROJECTMETADATA": Applies to the metadata defined
|
|
498
|
+
within the 'Metadata Manager' of the project.
|
|
499
|
+
PROJECTMETADATA is the ID of the metadata field.
|
|
500
|
+
METADATATYPE is the type of the metadata field. One of:
|
|
501
|
+
- string
|
|
502
|
+
- integer
|
|
503
|
+
- list
|
|
504
|
+
- decimal
|
|
505
|
+
- single_option
|
|
506
|
+
- multiple_option
|
|
507
|
+
|
|
508
|
+
if 'integer' or 'decimal' you must also include an OPERATOR
|
|
509
|
+
(i.e., "integer;OPERATOR|METADATAVALUE").
|
|
510
|
+
KEYVALUE is the expected value of the metadata.
|
|
511
|
+
|
|
512
|
+
1) Example:
|
|
513
|
+
search_criteria = {
|
|
514
|
+
"pars_patient_secret_name": "string;abide",
|
|
515
|
+
"pars_ssid": "integer;eq|2"
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
2) Example:
|
|
519
|
+
search_criteria = {
|
|
520
|
+
"pars_patient_secret_name": "string;001"
|
|
521
|
+
"pars_modalities": "string;T2",
|
|
522
|
+
"pars_tags": "tags;flair",
|
|
523
|
+
"pars_[dicom]_Manufacturer": "string;ge",
|
|
524
|
+
"pars_[dicom]_FlipAngle": "integer;gt|5",
|
|
525
|
+
"pars_[dicom]_ImageType": "list;PRIMARY;SECONDARY",
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
Note the search criteria might apply to both the files metadata
|
|
529
|
+
information available within a session and the metadata of the
|
|
530
|
+
session. And the method provides a session only if all the file
|
|
531
|
+
related conditions are satisfied within the same file.
|
|
532
|
+
In example 2) above, it means that the output will contain any
|
|
533
|
+
session whose Subject ID contains '001', and there is a file with
|
|
534
|
+
modality 'T2', tag 'flair', FlipAngle greater than 5º, and
|
|
535
|
+
ImageType with both values PRIMARY and SECONDARY.
|
|
536
|
+
Further, the acquisition had to be performed in a Manufacturer
|
|
537
|
+
containing 'ge'.
|
|
538
|
+
|
|
539
|
+
Returns
|
|
540
|
+
-------
|
|
541
|
+
dict
|
|
542
|
+
A list of dictionary of {"metadata_name": "metadata_value"}
|
|
543
|
+
|
|
544
|
+
"""
|
|
545
|
+
|
|
546
|
+
content = self.get_subjects_metadata(search_criteria, items=(0, 9999))
|
|
547
|
+
|
|
548
|
+
# Wrap search criteria.
|
|
549
|
+
modality, tags, dicoms = wrap_search_criteria(search_criteria)
|
|
550
|
+
|
|
551
|
+
# Iterate over the files of each subject selected to include/exclude
|
|
552
|
+
# them from the results.
|
|
553
|
+
subjects = list()
|
|
554
|
+
for subject in content:
|
|
555
|
+
files = platform.parse_response(platform.post(
|
|
556
|
+
self._account.auth, "file_manager/get_container_files",
|
|
557
|
+
data={"container_id": str(int(subject["container_id"]))}
|
|
558
|
+
))
|
|
559
|
+
|
|
560
|
+
for file in files["meta"]:
|
|
561
|
+
if modality and \
|
|
562
|
+
modality != (file.get("metadata") or {}).get("modality"):
|
|
563
|
+
continue
|
|
564
|
+
if tags and not all([tag in file.get("tags") for tag in tags]):
|
|
565
|
+
continue
|
|
566
|
+
if dicoms:
|
|
567
|
+
result_values = list()
|
|
568
|
+
for key, dict_value in dicoms.items():
|
|
569
|
+
f_value = ((file.get("metadata") or {})
|
|
570
|
+
.get("info") or {}).get(key)
|
|
571
|
+
d_operator = dict_value["operation"]
|
|
572
|
+
d_value = dict_value["value"]
|
|
573
|
+
result_values.append(
|
|
574
|
+
operation(d_value, d_operator, f_value)
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
if not all(result_values):
|
|
578
|
+
continue
|
|
579
|
+
subjects.append(subject)
|
|
580
|
+
break
|
|
581
|
+
return subjects
|
|
582
|
+
|
|
289
583
|
@property
|
|
290
584
|
def subjects(self):
|
|
291
585
|
"""
|
|
@@ -321,10 +615,14 @@ class Project:
|
|
|
321
615
|
@property
|
|
322
616
|
def metadata_parameters(self):
|
|
323
617
|
"""
|
|
324
|
-
List all the parameters in the subject metadata.
|
|
618
|
+
List all the parameters in the subject-level metadata.
|
|
325
619
|
|
|
326
|
-
Each project has a set of parameters that define the subjects
|
|
327
|
-
This function returns all these parameters and its
|
|
620
|
+
Each project has a set of parameters that define the subjects-level
|
|
621
|
+
metadata. This function returns all these parameters and its
|
|
622
|
+
properties. New subject-level metadata parameters can be creted in the
|
|
623
|
+
QMENTA Platform via the Metadata Manager. The API only allow
|
|
624
|
+
modification of these subject-level metadata parameters via the
|
|
625
|
+
'change_subject_metadata()' method.
|
|
328
626
|
|
|
329
627
|
Returns
|
|
330
628
|
-------
|
|
@@ -345,49 +643,6 @@ class Project:
|
|
|
345
643
|
return None
|
|
346
644
|
return data["fields"]
|
|
347
645
|
|
|
348
|
-
def add_metadata_parameter(self, title, param_id=None, param_type="string", visible=False):
|
|
349
|
-
"""
|
|
350
|
-
Add a metadata parameter to the project.
|
|
351
|
-
|
|
352
|
-
Parameters
|
|
353
|
-
----------
|
|
354
|
-
title : str
|
|
355
|
-
Identifier of this new parameter
|
|
356
|
-
param_id : str
|
|
357
|
-
Title of this new parameter
|
|
358
|
-
param_type : str
|
|
359
|
-
Type of the parameter. One of:
|
|
360
|
-
"integer", "date", "string", "list", "decimal"
|
|
361
|
-
visible : bool
|
|
362
|
-
whether the parameter will be visible in the table of patients
|
|
363
|
-
|
|
364
|
-
Returns
|
|
365
|
-
-------
|
|
366
|
-
bool
|
|
367
|
-
True if parameter was correctly added, False otherwise.
|
|
368
|
-
"""
|
|
369
|
-
# use param_id equal to title if param_id is not provided
|
|
370
|
-
param_id = param_id or title
|
|
371
|
-
|
|
372
|
-
param_properties = [title, param_id, param_type, str(int(visible))]
|
|
373
|
-
|
|
374
|
-
post_data = {"add": "|".join(param_properties), "edit": "", "delete": ""}
|
|
375
|
-
|
|
376
|
-
logger = logging.getLogger(logger_name)
|
|
377
|
-
try:
|
|
378
|
-
answer = platform.parse_response(
|
|
379
|
-
platform.post(self._account.auth, "patient_manager/save_metadata_changes", data=post_data)
|
|
380
|
-
)
|
|
381
|
-
except errors.PlatformError:
|
|
382
|
-
answer = {}
|
|
383
|
-
|
|
384
|
-
if title not in answer:
|
|
385
|
-
logger.error(f"Could not add new parameter: {title}")
|
|
386
|
-
return False
|
|
387
|
-
|
|
388
|
-
logger.info("New parameter added:", title, param_properties)
|
|
389
|
-
return True
|
|
390
|
-
|
|
391
646
|
def get_analysis(self, analysis_name_or_id):
|
|
392
647
|
if isinstance(analysis_name_or_id, int):
|
|
393
648
|
search_tag = "id"
|
|
@@ -574,10 +829,12 @@ class Project:
|
|
|
574
829
|
analysis = self.list_analysis(limit)
|
|
575
830
|
return [{"name": a["name"], "id": a["out_container_id"]} for a in analysis]
|
|
576
831
|
|
|
577
|
-
def list_container_files(
|
|
832
|
+
def list_container_files(
|
|
833
|
+
self,
|
|
834
|
+
container_id,
|
|
835
|
+
):
|
|
578
836
|
"""
|
|
579
837
|
List the name of the files available inside a given container.
|
|
580
|
-
|
|
581
838
|
Parameters
|
|
582
839
|
----------
|
|
583
840
|
container_id : str or int
|
|
@@ -597,12 +854,73 @@ class Project:
|
|
|
597
854
|
except errors.PlatformError as e:
|
|
598
855
|
logging.getLogger(logger_name).error(e)
|
|
599
856
|
return False
|
|
600
|
-
|
|
601
|
-
try:
|
|
602
|
-
return content["files"]
|
|
603
|
-
except KeyError:
|
|
857
|
+
if "files" not in content.keys():
|
|
604
858
|
logging.getLogger(logger_name).error("Could not get files")
|
|
605
859
|
return False
|
|
860
|
+
return content["files"]
|
|
861
|
+
|
|
862
|
+
def list_container_filter_files(
|
|
863
|
+
self,
|
|
864
|
+
container_id,
|
|
865
|
+
modality="",
|
|
866
|
+
metadata_info={},
|
|
867
|
+
tags=[]
|
|
868
|
+
):
|
|
869
|
+
"""
|
|
870
|
+
List the name of the files available inside a given container.
|
|
871
|
+
search condition example:
|
|
872
|
+
"metadata": {"SliceThickness":1},
|
|
873
|
+
}
|
|
874
|
+
Parameters
|
|
875
|
+
----------
|
|
876
|
+
container_id : str or int
|
|
877
|
+
Container identifier.
|
|
878
|
+
|
|
879
|
+
modality: str
|
|
880
|
+
String containing the modality of the files being filtered
|
|
881
|
+
|
|
882
|
+
metadata_info: dict
|
|
883
|
+
Dictionary containing the file metadata of the files being filtered
|
|
884
|
+
|
|
885
|
+
tags: list[str]
|
|
886
|
+
List of strings containing the tags of the files being filtered
|
|
887
|
+
|
|
888
|
+
Returns
|
|
889
|
+
-------
|
|
890
|
+
selected_files: list[str]
|
|
891
|
+
List of file names (strings)
|
|
892
|
+
"""
|
|
893
|
+
content_files = self.list_container_files(container_id)
|
|
894
|
+
content_meta = self.list_container_files_metadata(container_id)
|
|
895
|
+
selected_files = []
|
|
896
|
+
for index, file in enumerate(content_files):
|
|
897
|
+
metadata_file = content_meta[index]
|
|
898
|
+
tags_file = metadata_file.get("tags")
|
|
899
|
+
tags_bool = [tag in tags_file for tag in tags]
|
|
900
|
+
info_bool = []
|
|
901
|
+
if modality == "":
|
|
902
|
+
modality_bool = True
|
|
903
|
+
else:
|
|
904
|
+
modality_bool = modality == metadata_file["metadata"].get(
|
|
905
|
+
"modality"
|
|
906
|
+
)
|
|
907
|
+
for key in metadata_info.keys():
|
|
908
|
+
meta_key = (
|
|
909
|
+
(
|
|
910
|
+
metadata_file.get("metadata") or {}
|
|
911
|
+
).get("info") or {}).get(
|
|
912
|
+
key
|
|
913
|
+
)
|
|
914
|
+
if meta_key is None:
|
|
915
|
+
logging.getLogger(logger_name).warning(
|
|
916
|
+
f"{key} is not in file_info from file {file}"
|
|
917
|
+
)
|
|
918
|
+
info_bool.append(
|
|
919
|
+
metadata_info[key] == meta_key
|
|
920
|
+
)
|
|
921
|
+
if all(tags_bool) and all(info_bool) and modality_bool:
|
|
922
|
+
selected_files.append(file)
|
|
923
|
+
return selected_files
|
|
606
924
|
|
|
607
925
|
def list_container_files_metadata(self, container_id):
|
|
608
926
|
"""
|
|
@@ -788,38 +1106,6 @@ class Project:
|
|
|
788
1106
|
return int(user["_id"])
|
|
789
1107
|
return False
|
|
790
1108
|
|
|
791
|
-
def add_subject(self, subject):
|
|
792
|
-
"""
|
|
793
|
-
Add a subject to the project.
|
|
794
|
-
|
|
795
|
-
Parameters
|
|
796
|
-
----------
|
|
797
|
-
subject : Subject
|
|
798
|
-
Instance of Subject representing the subject to add.
|
|
799
|
-
|
|
800
|
-
Returns
|
|
801
|
-
-------
|
|
802
|
-
bool
|
|
803
|
-
True if correctly added, False otherwise
|
|
804
|
-
"""
|
|
805
|
-
logger = logging.getLogger(logger_name)
|
|
806
|
-
if self.check_subject_name(subject.name):
|
|
807
|
-
logger.error(f"Subject with name {subject.name} already exists in " f"project!")
|
|
808
|
-
return False
|
|
809
|
-
|
|
810
|
-
try:
|
|
811
|
-
platform.parse_response(
|
|
812
|
-
platform.post(self._account.auth, "patient_manager/upsert_patient", data={"secret_name": subject.name})
|
|
813
|
-
)
|
|
814
|
-
except errors.PlatformError:
|
|
815
|
-
logger.error(f"Subject {subject.name} could not be created.")
|
|
816
|
-
return False
|
|
817
|
-
|
|
818
|
-
subject.subject_id = self.get_subject_id(subject.name)
|
|
819
|
-
subject.project = self
|
|
820
|
-
logger.info("Subject {0} was successfully created".format(subject.name))
|
|
821
|
-
return True
|
|
822
|
-
|
|
823
1109
|
def change_subject_metadata(self, patient_id, subject_name, ssid, tags, age_at_scan, metadata):
|
|
824
1110
|
"""
|
|
825
1111
|
Change the Subject ID, Session ID, Tags, Age at Scan and Metadata of
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
qmenta/__init__.py,sha256=jv2YF__bseklT3OWEzlqJ5qE24c4aWd5F4r0TTjOrWQ,65
|
|
2
2
|
qmenta/client/Account.py,sha256=MEljEy9cmg2uP2FG1d3TZAgfj66EE2_3PQAZ9rvpCXY,9647
|
|
3
3
|
qmenta/client/File.py,sha256=ZgvSqejIosUt4uoX7opUnPnp5XGEaJNMRwFC0mQVB8k,5344
|
|
4
|
-
qmenta/client/Project.py,sha256=
|
|
4
|
+
qmenta/client/Project.py,sha256=9a6v7-H6BxU-2EykaWnFhW61GZijDB-XWsYzPdzWxz4,70398
|
|
5
5
|
qmenta/client/Subject.py,sha256=lhxxVdQ6d-GNoQC8mrJwa4L1f44nJc4PcJtDspmKN7I,8756
|
|
6
6
|
qmenta/client/__init__.py,sha256=AjTojBhZeW5nl0i605KS8S1Gl5tPNc1hdzD47BGNfoI,147
|
|
7
7
|
qmenta/client/utils.py,sha256=5DK2T_HQprrCwLS0Ycm2CjseaYmAUKaJkJvYoW-Rqzc,2479
|
|
8
|
-
qmenta_client-1.1.
|
|
9
|
-
qmenta_client-1.1.
|
|
10
|
-
qmenta_client-1.1.
|
|
8
|
+
qmenta_client-1.1.dev1324.dist-info/METADATA,sha256=yDhr3jCEwhPFFSWqIdJUeaZtj10Y3GXyEbAq7fDj7UQ,672
|
|
9
|
+
qmenta_client-1.1.dev1324.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
10
|
+
qmenta_client-1.1.dev1324.dist-info/RECORD,,
|
|
File without changes
|