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