qmenta-sdk-lib 2.0.1.dev3468__tar.gz → 2.0.1.dev3469__tar.gz

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.
Files changed (37) hide show
  1. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/PKG-INFO +1 -1
  2. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/pyproject.toml +1 -1
  3. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/context.py +163 -104
  4. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/local/context.py +125 -48
  5. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/__init__.py +0 -0
  6. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/__init__.py +0 -0
  7. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/bids/__init__.py +0 -0
  8. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/bids/make_entrypoint.py +0 -0
  9. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/bids/wrapper.py +0 -0
  10. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/client.py +0 -0
  11. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/communication.py +0 -0
  12. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/directory_utils.py +0 -0
  13. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/executor.py +0 -0
  14. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/init.py +0 -0
  15. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/local/__init__.py +0 -0
  16. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/local/client.py +0 -0
  17. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/local/executor.py +0 -0
  18. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/local/parse_settings.py +0 -0
  19. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/log_capture.py +0 -0
  20. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/make_entrypoint.py +0 -0
  21. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/__init__.py +0 -0
  22. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/context.py +0 -0
  23. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/file_filter.py +0 -0
  24. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/inputs.py +0 -0
  25. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/integration_ui.py +0 -0
  26. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/integration_workflow_ui.py +0 -0
  27. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/launch_gui.py +0 -0
  28. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/make_files.py +0 -0
  29. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/modalities.py +0 -0
  30. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/outputs.py +0 -0
  31. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/run_test_docker.py +0 -0
  32. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/templates_tool_maker/Dockerfile_schema +0 -0
  33. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/templates_tool_maker/description_schema +0 -0
  34. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/templates_tool_maker/qmenta.png +0 -0
  35. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/templates_tool_maker/test_tool_schema +0 -0
  36. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/templates_tool_maker/tool_schema +0 -0
  37. {qmenta_sdk_lib-2.0.1.dev3468 → qmenta_sdk_lib-2.0.1.dev3469}/python/qmenta/sdk/tool_maker/tool_maker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: qmenta_sdk_lib
3
- Version: 2.0.1.dev3468
3
+ Version: 2.0.1.dev3469
4
4
  Summary: QMENTA SDK for tool development.
5
5
  Author: QMENTA
6
6
  Maintainer: Marc Ramos
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "qmenta_sdk_lib"
3
- version = "2.0.1.dev3468"
3
+ version = "2.0.1.dev3469"
4
4
  description = "QMENTA SDK for tool development."
5
5
  authors = ["QMENTA"]
6
6
  packages = [
@@ -51,7 +51,8 @@ class AnalysisContext:
51
51
 
52
52
  def __init__(self, analysis_id, comm):
53
53
  """
54
- Context object that interfaces with the QMENTA platform. Should not be created directly.
54
+ Context object that interfaces with the QMENTA platform. Should not be
55
+ created directly.
55
56
  """
56
57
  self.analysis_id = analysis_id
57
58
  self.__comm = comm
@@ -73,15 +74,19 @@ class AnalysisContext:
73
74
  Returns
74
75
  -------
75
76
  dict
76
- Dictionary that contains important information about the current analysis, such as:
77
+ Dictionary that contains important information about the
78
+ current analysis, such as:
77
79
  * state (str) : State of the analysis
78
80
  * name (str) : Name of the analysis
79
81
  * script_name (str) : Name of the script
80
- * analysis_id (str) : A unique identifier for the current analysis
81
- * user_id (str) : Username of the user that started the analysis
82
+ * analysis_id (str) : A unique identifier for the
83
+ current analysis
84
+ * user_id (str) : Username of the user that started
85
+ the analysis
82
86
  * patient_secret_name (str) : Name of the subject
83
87
  * ssid (str) : Timepoint identifier
84
- * settings (dict) : Settings dictionary. See documentation in method `get_settings`
88
+ * settings (dict) : Settings dictionary. See documentation in
89
+ method `get_settings`
85
90
  * tags (list) : List of tags
86
91
  * version (str) : Tool version
87
92
  """
@@ -105,14 +110,17 @@ class AnalysisContext:
105
110
  Returns
106
111
  -------
107
112
  dict
108
- Dictionary that contains important information about the current analysis, such as:
113
+ Dictionary that contains important information about the current
114
+ analysis, such as:
109
115
  * state (str) : State of the analysis
110
116
  * name (str) : Name of the analysis
111
117
  * script_name (str) : Name of the script
112
- * user_id (str) : Username of the user that started the analysis
118
+ * user_id (str) : Username of the user that started the
119
+ analysis
113
120
  * patient_secret_name (str) : Name of the subject
114
121
  * ssid (str) : Timepoint identifier
115
- * settings (dict) : Settings dictionary. See documentation in method `get_settings`
122
+ * settings (dict) : Settings dictionary. See documentation in
123
+ method `get_settings`
116
124
  * tags (list) : List of tags
117
125
  * version (str) : Tool version
118
126
  """
@@ -154,7 +162,8 @@ class AnalysisContext:
154
162
 
155
163
  def get_communication_object(self):
156
164
  """
157
- Return the communication object used for communication with the platform
165
+ Return the communication object used for communication
166
+ with the platform
158
167
 
159
168
  Returns
160
169
  -------
@@ -178,11 +187,13 @@ class AnalysisContext:
178
187
  status : QCStatus
179
188
  QCStatus.PASS or QCStatus.FAIL
180
189
  comments : str, optional
181
- Additional comments explaining why the QC status has been set to pass or fail.
190
+ Additional comments explaining why the QC status has been
191
+ set to pass or fail.
182
192
  entity : QCEntity, optional
183
193
  QCEntity.ANALYSIS or QCEntity.SESSION
184
194
  input_id : str, optional
185
- The id of the session in the settings definition when entity is QCEntity.SESSION
195
+ The id of the session in the settings definition when
196
+ entity is QCEntity.SESSION
186
197
  """
187
198
  if entity == self.QCEntity.ANALYSIS:
188
199
  item_id = self.analysis_id
@@ -232,7 +243,8 @@ class AnalysisContext:
232
243
  "patient_manager/get_patient_list",
233
244
  req_headers={"X-Range": "items=0-9999"},
234
245
  req_data={
235
- "pars_patient_secret_name": f"string;{self.analysis_data['patient_secret_name']}"
246
+ "pars_patient_secret_name": "string;"
247
+ f"{self.analysis_data['patient_secret_name']}"
236
248
  },
237
249
  )
238
250
  self.logger.info(data_sessions[0])
@@ -349,7 +361,8 @@ class AnalysisContext:
349
361
  Parameters
350
362
  ----------
351
363
  value : int, optional
352
- Number between 0 and 100 indicating the current status of the execution
364
+ Number between 0 and 100 indicating the current status of the
365
+ execution
353
366
  message : str, optional
354
367
  Progress message.
355
368
  """
@@ -366,12 +379,14 @@ class AnalysisContext:
366
379
  value = int(value) # Cast to int to avoid float/string problems
367
380
  if value > 100:
368
381
  self.logger.warning(
369
- "The progress value should be between 0 and 100 (using 100 instead)"
382
+ "The progress value should be between 0 and 100 "
383
+ "(using 100 instead)"
370
384
  )
371
385
  value = 100
372
386
  elif value < 0:
373
387
  self.logger.warning(
374
- "The progress value should be between 0 and 100 (using 0 instead)"
388
+ "The progress value should be between 0 and 100 "
389
+ "(using 0 instead)"
375
390
  )
376
391
  value = 0
377
392
  data_to_send["value"] = value
@@ -391,8 +406,10 @@ class AnalysisContext:
391
406
  Returns
392
407
  -------
393
408
  dict
394
- Settings for the current analysis. This includes all the parameters defined in the tool specification
395
- (checkboxes, input fields, etc.) and can be accessed using their identifier.
409
+ Settings for the current analysis. This includes all the
410
+ parameters defined in the tool specification
411
+ (checkboxes, input fields, etc.) and can be accessed using their
412
+ identifier.
396
413
  """
397
414
  return self.analysis_data["settings"]
398
415
 
@@ -404,10 +421,13 @@ class AnalysisContext:
404
421
  reg_expression=None,
405
422
  file_filter_condition_name=None,
406
423
  subject_container_id=None,
424
+ unpack=True,
407
425
  ):
408
426
  """
409
- Returns the files in the input container specified by `input_id` that match the modality, tags
410
- and regular expression, if any. (X)OR that are selected by the file filter condition
427
+ Returns the files in the input container specified by
428
+ `input_id` that match the modality, tags
429
+ and regular expression, if any. (X)OR that are selected by
430
+ the file filter condition
411
431
  defined in the tool settings.
412
432
 
413
433
  Parameters
@@ -418,38 +438,47 @@ class AnalysisContext:
418
438
  Optional modality, will allow any modalities if None given.
419
439
  Default is None.
420
440
  tags : set, optional
421
- Optional set of tags, will allow any tags if None given, and will match any set of tags (including
441
+ Optional set of tags, will allow any tags if None given,
442
+ and will match any set of tags (including
422
443
  the empty set) otherwise.
423
444
  Default is None.
424
445
  reg_expression : str, optional
425
- Regular expression to match with the file names in the specified container, will allow any file names
446
+ Regular expression to match with the file names in the specified
447
+ container, will allow any file names
426
448
  if None given.
427
449
  Default is None.
428
450
  file_filter_condition_name : str, optional
429
451
  File filter specified in the settings of the tool.
430
452
  Default is None.
431
453
  subject_container_id : str, optional
454
+ unpack: bool, optional
432
455
 
433
456
  Examples
434
457
  --------
435
458
 
436
- Get all files that passed c_TEST (including any selection the user may have done using the GUI)
459
+ Get all files that passed c_TEST (including any selection
460
+ the user may have done using the GUI)
437
461
 
438
462
  >>> context.get_files('input', file_filter_condition_name='c_TEST')
439
463
 
440
464
  Get all files that passed c_TEST and have no tags
441
465
 
442
- >>> context.get_files('input', file_fitler_condition_name='c_TEST', tags=set()
466
+ >>> context.get_files('input', file_fitler_condition_name='c_TEST',
467
+ tags=set()
443
468
 
444
- Get all files that passed c_TEST and have the DTI tag in their list of tags
469
+ Get all files that passed c_TEST and have the DTI tag in their
470
+ list of tags
445
471
 
446
- >>> context.get_files('input', file_fitler_condition_name='c_TEST', tags={'DTI'})
472
+ >>> context.get_files('input', file_fitler_condition_name='c_TEST',
473
+ tags={'DTI'})
447
474
 
448
475
  Get all files that passed c_TEST whose name match the given regexp
449
476
 
450
- >>> context.get_files('input', file_filter_condition_name='c_TEST', reg_expression='^some_expression[0-9]')
477
+ >>> context.get_files('input', file_filter_condition_name='c_TEST',
478
+ reg_expression='^some_expression[0-9]')
451
479
 
452
- Alternatively, you can use no file_filter_condition_name to browse through the whole input container
480
+ Alternatively, you can use no file_filter_condition_name to browse
481
+ through the whole input container
453
482
 
454
483
  >>> context.get_files('input')
455
484
 
@@ -475,7 +504,10 @@ class AnalysisContext:
475
504
  )
476
505
 
477
506
  if is_subject_setting and subject_container_id is None:
478
- msg = "must define subject_container_id for subjects with multiple sessions"
507
+ msg = (
508
+ "must define subject_container_id for subjects with"
509
+ " multiple sessions"
510
+ )
479
511
  self.logger.error(msg)
480
512
  raise Exception(msg)
481
513
 
@@ -525,7 +557,8 @@ class AnalysisContext:
525
557
  ]
526
558
 
527
559
  if file_filter_condition_name:
528
- # Get the files that passed the file_filter and take those that have the required conditions
560
+ # Get the files that passed the file_filter and take those that
561
+ # have the required conditions
529
562
 
530
563
  selected_files = []
531
564
 
@@ -535,9 +568,8 @@ class AnalysisContext:
535
568
  file_filter_condition_name
536
569
  ]
537
570
  except KeyError:
538
- msg = "File filter condition {} not specified in the container {} settings !".format(
539
- file_filter_condition_name, input_id
540
- )
571
+ msg = f"File filter condition {file_filter_condition_name}"
572
+ f"not specified in the container {input_id} settings !"
541
573
  self.logger.error(msg)
542
574
  raise Exception(msg)
543
575
 
@@ -575,7 +607,8 @@ class AnalysisContext:
575
607
 
576
608
  return selected_files
577
609
  else:
578
- # in case the file filter condition name is not specified then we need to take the other
610
+ # in case the file filter condition name is not specified then we
611
+ # need to take the other
579
612
  # concrete search conditions i.e. modality, tags, reg_expression
580
613
 
581
614
  # filter the files according to the filter defined
@@ -607,7 +640,8 @@ class AnalysisContext:
607
640
  selected_files = []
608
641
  for file_data in container_data["meta"]:
609
642
 
610
- # TODO: Here we are assuming that file metadata has a name and a metadata attribute. Need of jsonschema
643
+ # TODO: Here we are assuming that file metadata has a name and a
644
+ # metadata attribute. Need of jsonschema
611
645
 
612
646
  fname, fmetadata, ftags = (
613
647
  file_data["name"],
@@ -651,13 +685,15 @@ class AnalysisContext:
651
685
  patient_metadata_id=None,
652
686
  ):
653
687
  """
654
- Add a new scan session. Only functional for analysis tools of type `data`.
688
+ Add a new scan session. Only functional for analysis tools of type
689
+ `data`.
655
690
 
656
691
  Parameters
657
692
  ----------
658
693
  ssid : int, optional
659
694
  date_at_scan : str, optional
660
- Possible formats: '%d.%m.%Y' / '%Y-%m-%d'. Examples: '01.01.2000' / '2000-01-01'
695
+ Possible formats: '%d.%m.%Y' / '%Y-%m-%d'.
696
+ Examples: '01.01.2000' / '2000-01-01'
661
697
  age_at_scan : int, optional
662
698
  backup_age : int, optional
663
699
  patient_metadata_id : int, optional
@@ -671,13 +707,15 @@ class AnalysisContext:
671
707
  patient_secret_name = self.analysis_data["patient_secret_name"]
672
708
  except KeyError:
673
709
  raise RuntimeError(
674
- "Patient secret name not available in analysis information. Check platform failures."
710
+ "Patient secret name not available in analysis information. "
711
+ "Check platform failures."
675
712
  )
676
713
  try:
677
714
  md5_original_file = self.get_settings()["md5_original_file"]
678
715
  except KeyError:
679
716
  raise RuntimeError(
680
- "MD5 hash not available via analysis settings. Check platform failures."
717
+ "MD5 hash not available via analysis settings. "
718
+ "Check platform failures."
681
719
  )
682
720
 
683
721
  data = {
@@ -732,9 +770,8 @@ class AnalysisContext:
732
770
  return True
733
771
  else:
734
772
  raise RuntimeError(
735
- "Intermediate chunk upload failed: {!r} != {!r}".format(
736
- response.text, expected_response
737
- )
773
+ "Intermediate chunk upload "
774
+ f"failed: {response.text} != {expected_response}"
738
775
  )
739
776
  except Exception as e:
740
777
  logger.error(
@@ -813,7 +850,8 @@ class AnalysisContext:
813
850
  file_info : dict, optional
814
851
  File information metadata.
815
852
  file_format : str, optional
816
- Use "dicom" when .zip file with .dcm slices (uploading a "nifti" file is automatically recognized as such).
853
+ Use "dicom" when .zip file with .dcm slices (uploading a "nifti"
854
+ file is automatically recognized as such).
817
855
  protocol : str, optional
818
856
  container_id : str, optional
819
857
  replace : str, optional
@@ -862,7 +900,7 @@ class AnalysisContext:
862
900
  ) and chunk_retry_count <= self.__max_chunk_retries:
863
901
  start_position = chunk_num * self.__comm.chunk_size
864
902
  self.logger.debug(
865
- "Chunk size is {}".format(self.__comm.chunk_size)
903
+ "Chunk size " f"is {self.__comm.chunk_size}"
866
904
  )
867
905
  end_position = start_position + self.__comm.chunk_size - 1
868
906
  bytes_to_send = self.__comm.chunk_size
@@ -883,9 +921,8 @@ class AnalysisContext:
883
921
  "Content-Range": bytes_range,
884
922
  "Session-ID": str(session_id),
885
923
  "Content-Length": str(bytes_to_send),
886
- "Content-Disposition": "attachment; filename={}".format(
887
- file_name
888
- ),
924
+ "Content-Disposition": "attachment; "
925
+ f"filename={file_name}",
889
926
  }
890
927
 
891
928
  # If it is the last chunk, define more header fields
@@ -893,35 +930,30 @@ class AnalysisContext:
893
930
  chunk_num * self.__comm.chunk_size + bytes_to_send
894
931
  )
895
932
  if accumulated_bytes == total_bytes:
933
+ last_data = {
934
+ "X-Mint-Analysis-Output": str(self.analysis_id),
935
+ "X-Mint-File-Destination-Path": destination_path,
936
+ "X-Mint-File-Tags": ",".join(tags),
937
+ }
896
938
  self.logger.debug("Sending last data chunk")
897
- req_headers.update(
898
- {
899
- "X-Mint-Analysis-Output": str(
900
- self.analysis_id
901
- ),
902
- "X-Mint-File-Destination-Path": destination_path,
903
- "X-Mint-File-Tags": ",".join(tags),
904
- }
905
- )
939
+ req_headers.update(last_data)
906
940
 
907
941
  # Conditional headers
908
- if file_info:
909
- req_headers["X-Mint-File-Info"] = json.dumps(
910
- file_info
911
- )
912
- if file_format:
913
- req_headers["X-Mint-File-Format"] = str(
914
- file_format
915
- )
916
- if protocol:
917
- req_headers["X-Mint-Protocol"] = str(protocol)
918
- if container_id:
919
- req_headers["X-Mint-Container"] = str(container_id)
920
- if replace:
921
- assert isinstance(
922
- replace, AnalysisContext.ReplaceFlag
923
- )
924
- req_headers["X-Mint-Replace-Flag"] = replace.value
942
+ for key, value, attr in [
943
+ ("X-Mint-File-Info", file_info, None),
944
+ ("X-Mint-File-Format", file_format, str),
945
+ ("X-Mint-Protocol", protocol, str),
946
+ ("X-Mint-Container", container_id, str),
947
+ (
948
+ "X-Mint-Replace-Flag",
949
+ replace,
950
+ lambda x: x.value,
951
+ ),
952
+ ]:
953
+ if value:
954
+ req_headers[key] = (
955
+ str(value) if attr is None else attr(value)
956
+ )
925
957
  req_headers["X-Requested-With"] = "XMLHttpRequest"
926
958
 
927
959
  # Send data chunk
@@ -943,9 +975,8 @@ class AnalysisContext:
943
975
  wait_time = 2 * wait_time
944
976
  time.sleep(wait_time)
945
977
 
946
- if (
947
- data or chunk_retry_count != 0
948
- ): # Restarted when successful upload
978
+ if data or chunk_retry_count != 0:
979
+ # Restarted when successful upload
949
980
  msg = (
950
981
  "File {} could not be uploaded to the platform.\n"
951
982
  "data - {}\n"
@@ -977,6 +1008,9 @@ class AnalysisContext:
977
1008
  ),
978
1009
  "container_id": container_id,
979
1010
  }
1011
+
1012
+ self.logger.info("REQUEST DATA" + str(request_data))
1013
+
980
1014
  url_request_response = self.__comm.send_request(
981
1015
  "file_manager/get_upload_url", req_data=request_data
982
1016
  )
@@ -1038,13 +1072,16 @@ class AnalysisContext:
1038
1072
  self.logger.error(msg)
1039
1073
  raise RuntimeError(msg)
1040
1074
 
1075
+ req_data = {
1076
+ "container_id": container_id,
1077
+ "path": destination_path,
1078
+ "md5": md5_hash,
1079
+ }
1080
+ self.logger.info("VERIFY RESPONSE " + str(req_data))
1081
+
1041
1082
  verification_response = self.__comm.send_request(
1042
1083
  "file_manager/verify_upload_via_url",
1043
- req_data={
1044
- "container_id": container_id,
1045
- "path": destination_path,
1046
- "md5": md5_hash,
1047
- },
1084
+ req_data=req_data,
1048
1085
  )
1049
1086
  if not verification_response.get("success", False):
1050
1087
  msg = "The upload could not be verified: {}".format(
@@ -1057,7 +1094,8 @@ class AnalysisContext:
1057
1094
 
1058
1095
  def download_resource(self, resource_path, destination_path):
1059
1096
  """
1060
- Downloads a resource file from the bucket (user/group based resources, or legacy).
1097
+ Downloads a resource file from the bucket (user/group based resources,
1098
+ or legacy).
1061
1099
 
1062
1100
  Parameters
1063
1101
  ----------
@@ -1108,7 +1146,8 @@ class AnalysisContext:
1108
1146
 
1109
1147
  def _get_manual_analysis_data(self):
1110
1148
  """
1111
- Returns a dictionary with the manual analysis data generated during the manual step (user interaction)
1149
+ Returns a dictionary with the manual analysis data generated during
1150
+ the manual step (user interaction)
1112
1151
 
1113
1152
  Returns
1114
1153
  -------
@@ -1156,11 +1195,13 @@ class AnalysisContext:
1156
1195
  title : str, optional
1157
1196
  How the metadata field should be presented
1158
1197
  readonly : bool, optional
1159
- Whether the user should be able to edit the value on the platform or not (only by analyses)
1198
+ Whether the user should be able to edit the value on the platform
1199
+ or not (only by analyses)
1160
1200
  patient_secret_name : str, optional
1161
1201
  Manually select the patient_secret_name
1162
1202
  ssid : str, optional
1163
- Manually select the ssid. This is useful to choose a specific session in a longitudinal analysis.
1203
+ Manually select the ssid. This is useful to choose a specific
1204
+ session in a longitudinal analysis.
1164
1205
  """
1165
1206
 
1166
1207
  if not isinstance(key, str):
@@ -1172,7 +1213,8 @@ class AnalysisContext:
1172
1213
  )
1173
1214
  if not isinstance(value, (int, list, str, float)):
1174
1215
  raise Exception(
1175
- "Parameter 'value' must be of type 'int', 'list', 'str', or 'float'."
1216
+ "Parameter 'value' must be of type 'int', 'list', 'str', "
1217
+ "or 'float'."
1176
1218
  )
1177
1219
 
1178
1220
  if not self.get_metadata_value(key):
@@ -1241,11 +1283,13 @@ class AnalysisContext:
1241
1283
  Parameters
1242
1284
  ----------
1243
1285
  key : str, optional
1244
- The ID of the metadata parameter. If not set, it will return all the metadata for the session.
1286
+ The ID of the metadata parameter. If not set, it will return all
1287
+ the metadata for the session.
1245
1288
  patient_secret_name: str, optional
1246
1289
  Manually select the patient_secret_name
1247
1290
  ssid : str, optional
1248
- Manually select the ssid. This is useful to choose a specific session in a longitudinal analysis.
1291
+ Manually select the ssid. This is useful to choose a specific
1292
+ session in a longitudinal analysis.
1249
1293
 
1250
1294
  Returns
1251
1295
  -------
@@ -1294,7 +1338,8 @@ class AnalysisContext:
1294
1338
  Parameters
1295
1339
  ----------
1296
1340
  params : list
1297
- The definition of the parameter. It may contain "title", "type", "visible", "readonly" or "order".
1341
+ The definition of the parameter. It may contain "title", "type",
1342
+ "visible", "readonly" or "order".
1298
1343
  """
1299
1344
 
1300
1345
  if not isinstance(params, list):
@@ -1302,7 +1347,10 @@ class AnalysisContext:
1302
1347
 
1303
1348
  for data_to_send in params:
1304
1349
  if not isinstance(data_to_send, dict):
1305
- msg = "Format error. Metadata param configuration should be stored in a dictionary"
1350
+ msg = (
1351
+ "Format error. Metadata param configuration should be "
1352
+ "stored in a dictionary"
1353
+ )
1306
1354
  self.logger.error(msg)
1307
1355
  raise TypeError(msg)
1308
1356
 
@@ -1380,9 +1428,8 @@ class AnalysisContext:
1380
1428
  """
1381
1429
  if not isinstance(variables_dict, dict):
1382
1430
  self.logger.error(
1383
- "The output variables are not of type dictionary ! (actual type {})".format(
1384
- str(type(variables_dict))
1385
- )
1431
+ "The output variables are not of type dictionary ! "
1432
+ "(actual type {})".format(str(type(variables_dict)))
1386
1433
  )
1387
1434
  raise RuntimeError(
1388
1435
  "The output variables are not of type dictionary !"
@@ -1470,7 +1517,8 @@ class Container:
1470
1517
  class File:
1471
1518
  def __init__(self, container_id, name, metadata, tags, comm):
1472
1519
  """
1473
- Object that represents a file and all its metadata in the platform. Should not be created directly.
1520
+ Object that represents a file and all its metadata in the platform.
1521
+ Should not be created directly.
1474
1522
  """
1475
1523
  self.__comm = comm
1476
1524
  self.__container_id = container_id
@@ -1504,13 +1552,16 @@ class File:
1504
1552
 
1505
1553
  def get_file_info(self):
1506
1554
  """
1507
- Get the file information. The type of information depends on the type of file
1508
- (e.g. nifti files include information such as 'Data strides', 'Data type', 'Dimensions' or 'Voxel size').
1555
+ Get the file information. The type of information depends on the
1556
+ type of file
1557
+ (e.g. nifti files include information such as 'Data strides',
1558
+ 'Data type', 'Dimensions' or 'Voxel size').
1509
1559
 
1510
1560
  Returns
1511
1561
  -------
1512
1562
  dict
1513
- Dictionary of key-value pairs that depend on the format of the file.
1563
+ Dictionary of key-value pairs that depend on the format of the
1564
+ file.
1514
1565
  """
1515
1566
  return self.__metadata.get("info", {})
1516
1567
 
@@ -1540,8 +1591,9 @@ class File:
1540
1591
  If `get_file_path` is called before the file has been downloaded
1541
1592
  """
1542
1593
  if self.__download_path is None:
1543
- err_msg = "File {!r} has no path because it has not been downloaded".format(
1544
- self.name
1594
+ err_msg = (
1595
+ "File {!r} has no path because it has not been"
1596
+ " downloaded".format(self.name)
1545
1597
  )
1546
1598
  self.logger.error(err_msg)
1547
1599
  raise RuntimeError(err_msg)
@@ -1599,7 +1651,8 @@ class File:
1599
1651
 
1600
1652
  os.makedirs(os.path.dirname(destination_file_path), exist_ok=True)
1601
1653
  with open(destination_file_path, "wb") as fp:
1602
- # For Google Cloud Bucket, performance is at its best at 1 MB or larger chunks.
1654
+ # For Google Cloud Bucket, performance is at its best at 1 MB or
1655
+ # larger chunks.
1603
1656
  for chunk in download_response.iter_content(
1604
1657
  chunk_size=self.__comm.chunk_size
1605
1658
  ): # Use 10 MB chunk
@@ -1614,14 +1667,16 @@ class File:
1614
1667
 
1615
1668
  def download(self, dest_path, unpack=True):
1616
1669
  """
1617
- Downloads a file or the contents of a packed file to to the specified `path`.
1670
+ Downloads a file or the contents of a packed file to to the
1671
+ specified `path`.
1618
1672
 
1619
1673
  Parameters
1620
1674
  ----------
1621
1675
  dest_path:
1622
1676
  Path where the file should be downloaded to.
1623
1677
  unpack:
1624
- Tells the function whether the file should be unpacked to the given folder.
1678
+ Tells the function whether the file should be unpacked to the
1679
+ given folder.
1625
1680
 
1626
1681
  Returns
1627
1682
  -------
@@ -1636,13 +1691,16 @@ class File:
1636
1691
  self.logger.debug("Using path {!r}".format(dest_path))
1637
1692
 
1638
1693
  if source_file.endswith(".zip") and unpack:
1639
- # Download the zip to a temporary directory and unpack its contents to the user dir path
1694
+ # Download the zip to a temporary directory and unpack its
1695
+ # contents to the user dir path
1640
1696
  with TemporaryDirectory() as temp_dir:
1641
1697
  temp_path = os.path.join(temp_dir, source_file)
1642
1698
  self.__download_file(temp_path)
1699
+
1643
1700
  with zipfile.ZipFile(temp_path, "r") as zip_ref:
1644
1701
  # file_list = zip_ref.namelist()
1645
- # self.logger.info("Decompressing {!r} to {!r}".format(file_list, dest_path))
1702
+ # self.logger.info("Decompressing {!r} to
1703
+ # {!r}".format(file_list, dest_path))
1646
1704
  os.makedirs(dest_path, exist_ok=True)
1647
1705
  zip_ref.extractall(path=dest_path)
1648
1706
 
@@ -1669,7 +1727,8 @@ def _should_include_file(
1669
1727
  search_tags,
1670
1728
  search_reg_expression,
1671
1729
  ):
1672
- # Checks if file should be selected or not according to the specified selectors.
1730
+ # Checks if file should be selected or not according to the
1731
+ # specified selectors.
1673
1732
 
1674
1733
  assert fname
1675
1734
 
@@ -1,3 +1,5 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
1
3
  import logging
2
4
  import os
3
5
  import shutil
@@ -10,6 +12,7 @@ from qmenta.sdk.directory_utils import TemporaryDirectory
10
12
 
11
13
 
12
14
  class LocalAnalysisContext:
15
+
13
16
  class QCEntity(Enum):
14
17
  """
15
18
  Enum with the following options:
@@ -28,9 +31,16 @@ class LocalAnalysisContext:
28
31
  PASS = "pass"
29
32
  FAIL = "fail"
30
33
 
31
- def __init__(self, settings, src_folder, out_folder, res_folder):
34
+ def __init__(
35
+ self,
36
+ settings,
37
+ src_folder,
38
+ out_folder,
39
+ res_folder,
40
+ ):
32
41
  """
33
- Context object that interfaces with the QMENTA platform. Should not be created directly.
42
+ Context object that interfaces with the QMENTA platform. Should not
43
+ be created directly.
34
44
  """
35
45
 
36
46
  self.__settings = settings
@@ -45,17 +55,21 @@ class LocalAnalysisContext:
45
55
  Returns
46
56
  -------
47
57
  dict
48
- Dictionary that contains important information about the current analysis, such as:
58
+ Dictionary that contains important information about the current
59
+ analysis, such as:
49
60
  * state (str) : State of the analysis
50
61
  * name (str) : Name of the analysis
51
62
  * script_name (str) : Name of the script
52
- * user_id (str) : Username of the user that started the analysis
63
+ * user_id (str) : Username of the user that started the
64
+ analysis
53
65
  * patient_secret_name (str) : Name of the subject
54
66
  * ssid (str) : Timepoint identifier
55
- * settings (dict) : Settings dictionary. See documentation in method `get_settings`
67
+ * settings (dict) : Settings dictionary. See documentation in
68
+ method `get_settings`
56
69
  * tags (list) : List of tags
57
70
  * version (str) : Tool version
58
71
  """
72
+
59
73
  return {
60
74
  "state": "local_running",
61
75
  "name": "local",
@@ -83,12 +97,15 @@ class LocalAnalysisContext:
83
97
  status : QCStatus
84
98
  QCStatus.PASS or QCStatus.FAIL
85
99
  comments : str, optional
86
- Additional comments explaining why the QC status has been set to pass or fail.
100
+ Additional comments explaining why the QC status has been set to
101
+ pass or fail.
87
102
  entity : QCEntity, optional
88
103
  QCEntity.ANALYSIS or QCEntity.SESSION
89
104
  input_id : str, optional
90
- The id of the session in the settings definition when entity is QCEntity.SESSION
105
+ The id of the session in the settings definition when entity is
106
+ QCEntity.SESSION
91
107
  """
108
+
92
109
  logger = logging.getLogger(__name__)
93
110
 
94
111
  logger.info("Setting QC status to {}: {}".format(status, comments))
@@ -98,6 +115,7 @@ class LocalAnalysisContext:
98
115
  Changes the analysis or session QC status.
99
116
 
100
117
  """
118
+
101
119
  logger = logging.getLogger(__name__)
102
120
 
103
121
  logger.info("Getting QC status")
@@ -111,17 +129,17 @@ class LocalAnalysisContext:
111
129
  path : str
112
130
  The location where to store the requirements.r
113
131
  """
132
+
114
133
  logger = logging.getLogger(__name__)
115
134
 
116
135
  if os.path.exists(path):
117
136
  raise RuntimeError(
118
- "Cannot download session qa because {} already exists!".format(
119
- path
120
- )
137
+ "Cannot download session qa because {}"
138
+ "already exists!".format(path)
121
139
  )
122
140
 
123
141
  logger.info(
124
- "Session QA requirements not supported locally. Skipping..."
142
+ "Session QA requirements not supported locally. " "Skipping..."
125
143
  )
126
144
 
127
145
  def upload_session_qa_report(self, input_id, path):
@@ -135,9 +153,10 @@ class LocalAnalysisContext:
135
153
  path : str
136
154
  The json file with the QA status for all files and rules
137
155
  """
156
+
138
157
  logger = logging.getLogger(__name__)
139
158
  logger.info(
140
- "Session QA requirements not supported locally. Skipping..."
159
+ "Session QA requirements not supported locally. " "Skipping..."
141
160
  )
142
161
 
143
162
  @staticmethod
@@ -148,15 +167,17 @@ class LocalAnalysisContext:
148
167
  Parameters
149
168
  ----------
150
169
  value : int, optional
151
- Number between 0 and 100 indicating the current status of the execution
170
+ Number between 0 and 100 indicating the current status of
171
+ the execution
152
172
  message : str, optional
153
173
  Progress message.
154
174
  """
175
+
155
176
  logger = logging.getLogger(__name__)
156
177
 
157
178
  if not value and not message:
158
179
  logger.warning(
159
- "No information is available to report the progress"
180
+ "No information is available to report the " "progress"
160
181
  )
161
182
  return
162
183
 
@@ -164,12 +185,14 @@ class LocalAnalysisContext:
164
185
  value = int(value) # Cast to int to avoid float/string problems
165
186
  if value > 100:
166
187
  logger.warning(
167
- "The progress value should be between 0 and 100 (using 100 instead)"
188
+ "The progress value should be between 0 and 100 (using "
189
+ "100 instead)"
168
190
  )
169
191
  value = 100
170
192
  elif value < 0:
171
193
  logger.warning(
172
- "The progress value should be between 0 and 100 (using 0 instead)"
194
+ "The progress value should be between 0 and 100 "
195
+ "(using 0 instead)"
173
196
  )
174
197
  value = 0
175
198
 
@@ -184,6 +207,7 @@ class LocalAnalysisContext:
184
207
  dict
185
208
  Settings for the current analysis.
186
209
  """
210
+
187
211
  return self.__settings
188
212
 
189
213
  def get_files(
@@ -194,9 +218,11 @@ class LocalAnalysisContext:
194
218
  reg_expression=None,
195
219
  file_filter_condition_name=None,
196
220
  subject_container_id=None,
221
+ unpack=True,
197
222
  ):
198
223
  """
199
- Returns the files in the input container specified by `input_id` that match the modality, tags
224
+ Returns the files in the input container specified by `input_id`
225
+ that match the modality, tags
200
226
  and regular expression, if any.
201
227
 
202
228
  Parameters
@@ -207,11 +233,13 @@ class LocalAnalysisContext:
207
233
  Optional modality, will allow any modalities if None given.
208
234
  Default is None.
209
235
  tags : set, optional
210
- Optional set of tags, will allow any tags if None given, and will match any set of tags (including
236
+ Optional set of tags, will allow any tags if None given,
237
+ and will match any set of tags (including
211
238
  the empty set) otherwise.
212
239
  Default is None.
213
240
  reg_expression : str, optional
214
- Regular expression to match with the file names in the specified container, will allow any file names
241
+ Regular expression to match with the file names in the
242
+ specified container, will allow any file names
215
243
  if None given.
216
244
  Default is None.
217
245
  file_filter_condition_name : str, optional
@@ -223,6 +251,7 @@ class LocalAnalysisContext:
223
251
  list of File
224
252
  List of selected file handlers from the container.
225
253
  """
254
+
226
255
  logger = logging.getLogger(__name__)
227
256
  logger.info(
228
257
  "Get files: {} [modality={} / tags={} / regex={}]".format(
@@ -233,15 +262,18 @@ class LocalAnalysisContext:
233
262
  file_handlers = []
234
263
 
235
264
  # Create File
265
+
236
266
  for file_description in self.__settings[input_id]:
237
267
  if "path" not in file_description:
238
- msg = 'Cannot find the key "path" for the input container {}'.format(
239
- input_id
268
+ msg = (
269
+ 'Cannot find the key "path" for the input container'
270
+ " {}".format(input_id)
240
271
  )
241
272
  logger.error(msg)
242
273
  raise RuntimeError(msg)
243
274
 
244
275
  # Use file_filter_condition_name if available
276
+
245
277
  if file_filter_condition_name:
246
278
  if "file_filter_condition_name" not in file_description:
247
279
  continue # Skip
@@ -308,6 +340,7 @@ class LocalAnalysisContext:
308
340
  protocol : None
309
341
  container_id : None
310
342
  """
343
+
311
344
  logger = logging.getLogger(__name__)
312
345
  if tags:
313
346
  logger.warning("Tags ignored (local execution): {}".format(tags))
@@ -343,7 +376,8 @@ class LocalAnalysisContext:
343
376
 
344
377
  def download_resource(self, resource_path, destination_path):
345
378
  """
346
- Downloads a file from the user/group tool resources. The resource path can include subdirectories and must
379
+ Downloads a file from the user/group tool resources. The resource
380
+ path can include subdirectories and must
347
381
  be relative:
348
382
  Downloads a resource file to the container.
349
383
 
@@ -354,6 +388,7 @@ class LocalAnalysisContext:
354
388
  destination_path : str
355
389
  Path where the file will be downloaded to
356
390
  """
391
+
357
392
  logger = logging.getLogger(__name__)
358
393
  logger.info(self.__res_folder)
359
394
  if not self.__res_folder: # Default keyword for external users
@@ -361,14 +396,12 @@ class LocalAnalysisContext:
361
396
  return
362
397
  else:
363
398
  logger.info(
364
- "Mocking resources from directory: {}".format(
365
- self.__res_folder
366
- )
399
+ "Mocking resources from directory:"
400
+ " {}".format(self.__res_folder)
367
401
  )
368
402
  logger.info(
369
- "The selected mode ({}) is ignored in local execution.".format(
370
- type
371
- )
403
+ "The selected mode ({}) is ignored in local "
404
+ "execution.".format(type)
372
405
  )
373
406
 
374
407
  logger.info(
@@ -389,17 +422,20 @@ class LocalAnalysisContext:
389
422
 
390
423
  def _get_manual_analysis_data(self):
391
424
  """
392
- Returns a dictionary with the manual analysis data generated during the manual step (user interaction)
425
+ Returns a dictionary with the manual analysis data generated during
426
+ the manual step (user interaction)
393
427
 
394
428
  Returns
395
429
  -------
396
430
  dict
397
431
  The values generated during the manual step.
398
432
  """
433
+
399
434
  logger = logging.getLogger(__name__)
400
435
  logger.info("Getting manual analysis data")
401
436
  logger.warning(
402
- "Getting manual analysis data is not supported locally. Skipping..."
437
+ "Getting manual analysis data is not supported locally. "
438
+ "Skipping..."
403
439
  )
404
440
  return {}
405
441
 
@@ -424,8 +460,10 @@ class LocalAnalysisContext:
424
460
  title : str, optional
425
461
  How the metadata field should be presented
426
462
  readonly : bool, optional
427
- Whether the user should be able to edit the value on the platform or not (only by analyses)
463
+ Whether the user should be able to edit the value on the platform
464
+ or not (only by analyses)
428
465
  """
466
+
429
467
  logger = logging.getLogger(__name__)
430
468
 
431
469
  assert isinstance(key, str)
@@ -444,13 +482,20 @@ class LocalAnalysisContext:
444
482
  "Invalid type for the metadata value: {!r}".format(type(value))
445
483
  )
446
484
 
447
- logger.info("Setting metadata parameter: {} = {!r}".format(key, value))
485
+ logger.info(
486
+ "Setting metadata parameter: {} =" "{!r}".format(key, value)
487
+ )
448
488
  logger.warning(
449
- "Setting metadata values is not supported locally. Skipping..."
489
+ "Setting metadata values is not supported locally. " "Skipping..."
450
490
  )
451
491
  return None
452
492
 
453
- def get_metadata_value(self, key, patient_secret_name=None, ssid=None):
493
+ def get_metadata_value(
494
+ self,
495
+ key,
496
+ patient_secret_name=None,
497
+ ssid=None,
498
+ ):
454
499
  """
455
500
  Gets the value of a metadata parameter for the current session
456
501
 
@@ -467,26 +512,30 @@ class LocalAnalysisContext:
467
512
  -------
468
513
  The value of the parameter if it exists, None otherwise
469
514
  """
515
+
470
516
  logger = logging.getLogger(__name__)
471
517
  logger.info("Getting metadata value: {}".format(key))
472
518
  logger.warning(
473
- "Getting metadata values is not supported locally. Skipping..."
519
+ "Getting metadata values is not supported locally. " "Skipping..."
474
520
  )
475
521
  return None
476
522
 
477
523
  def _assure_metadata_parameters(self, params):
478
524
  """
479
- Check whether a metadata parameter exists, creating it if not. Not supported for local testing.
525
+ Check whether a metadata parameter exists, creating it if not.
526
+ Not supported for local testing.
480
527
 
481
528
  Parameters
482
529
  ----------
483
530
  params : dict
484
- The definition of the parameter. It may contain "title", "type", "visible", "readonly" or "order".
531
+ The definition of the parameter. It may contain "title", "type",
532
+ "visible", "readonly" or "order".
485
533
  """
534
+
486
535
  logger = logging.getLogger(__name__)
487
536
  logger.info("Setting metadata param (ckeck): {}".format(params))
488
537
  logger.warning(
489
- "Assuring metadata params is not supported locally. Skipping..."
538
+ "Assuring metadata params is not supported locally. " "Skipping..."
490
539
  )
491
540
 
492
541
  def set_analysis_output_variables(
@@ -502,15 +551,26 @@ class LocalAnalysisContext:
502
551
  clear_previous : bool, optional
503
552
  Whether the previous output variables should be cleared or not
504
553
  """
554
+
505
555
  logger = logging.getLogger(__name__)
506
556
  logger.warning("Output variables not supported locally. Skipping...")
507
557
 
508
558
 
509
559
  class LocalFile:
510
- def __init__(self, name, input_container_path, modality, tags, file_info):
560
+
561
+ def __init__(
562
+ self,
563
+ name,
564
+ input_container_path,
565
+ modality,
566
+ tags,
567
+ file_info,
568
+ ):
511
569
  """
512
- Object that represents a file and all its metadata in the platform. Should not be created directly.
570
+ Object that represents a file and all its metadata in the platform.
571
+ Should not be created directly.
513
572
  """
573
+
514
574
  self.name = name
515
575
  self.__input_container_path = input_container_path
516
576
  self.__download_path = None
@@ -527,6 +587,7 @@ class LocalFile:
527
587
  str or None
528
588
  Modality of the file or None if not known.
529
589
  """
590
+
530
591
  return self.__modality
531
592
 
532
593
  @staticmethod
@@ -539,18 +600,23 @@ class LocalFile:
539
600
  str or None
540
601
  File format or None if not known.
541
602
  """
603
+
542
604
  return None
543
605
 
544
606
  def get_file_info(self):
545
607
  """
546
- Get the file information. The type of information depends on the type of file
547
- (e.g. nifti files include information such as 'Data strides', 'Data type', 'Dimensions' or 'Voxel size').
608
+ Get the file information. The type of information depends on the type
609
+ of file
610
+ (e.g. nifti files include information such as 'Data strides',
611
+ 'Data type', 'Dimensions' or 'Voxel size').
548
612
 
549
613
  Returns
550
614
  -------
551
615
  dict
552
- Dictionary of key-value pairs that depend on the format of the file.
616
+ Dictionary of key-value pairs that depend on the format of the
617
+ file.
553
618
  """
619
+
554
620
  return self.__file_info
555
621
 
556
622
  def get_file_tags(self):
@@ -562,6 +628,7 @@ class LocalFile:
562
628
  set
563
629
  Set of tags associated to this file
564
630
  """
631
+
565
632
  return self.__tags
566
633
 
567
634
  def get_file_path(self):
@@ -578,9 +645,11 @@ class LocalFile:
578
645
  RuntimeError
579
646
  If `get_file_path` is called before the file has been downloaded
580
647
  """
648
+
581
649
  if self.__download_path is None:
582
- err_msg = "File {!r} has no path because it has not been downloaded".format(
583
- self.name
650
+ err_msg = (
651
+ "File {!r} has no path because it has not been "
652
+ "downloaded".format(self.name)
584
653
  )
585
654
  logging.getLogger(__name__).error(err_msg)
586
655
  raise RuntimeError(err_msg)
@@ -596,6 +665,7 @@ class LocalFile:
596
665
  destination_file_path : str
597
666
  Path where the file should be downloaded.
598
667
  """
668
+
599
669
  logger = logging.getLogger(__name__)
600
670
  logger.info(
601
671
  "Downloading {!r} to {!r}".format(self.name, destination_file_path)
@@ -607,30 +677,37 @@ class LocalFile:
607
677
 
608
678
  def download(self, dest_path, unpack=True):
609
679
  """
610
- Downloads a file or the contents of a packed file to to the specified `path`.
680
+ Downloads a file or the contents of a packed file to to the specified
681
+ `path`.
611
682
 
612
683
  Parameters
613
684
  ----------
614
685
  dest_path:
615
686
  Path where the file should be downloaded to.
616
687
  unpack:
617
- Tells the function whether the file should be unpacked to the given folder.
688
+ Tells the function whether the file should be unpacked to the
689
+ given folder.
618
690
 
619
691
  Returns
620
692
  -------
621
693
  str:
622
694
  The full path of the file or the folder with the unpacked files.
623
695
  """
696
+
624
697
  logger = logging.getLogger(__name__)
625
698
  source_file = self.name
626
699
 
627
700
  # Normalize path
701
+
628
702
  dest_path = os.path.abspath(dest_path)
629
703
 
630
704
  logger.debug("Using path {!r}".format(dest_path))
631
705
 
632
706
  if source_file.endswith(".zip") and unpack:
633
- # Download the zip to a temporary directory and unpack its contents to the user dir path
707
+
708
+ # Download the zip to a temporary directory and unpack its
709
+ # contents to the user dir path
710
+
634
711
  with TemporaryDirectory() as temp_dir:
635
712
  temp_path = os.path.join(temp_dir, source_file)
636
713
  self.__download_file(temp_path)