fusion-platform-python-sdk 1.16.6__tar.gz → 1.17.5__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 (57) hide show
  1. {fusion_platform_python_sdk-1.16.6/fusion_platform_python_sdk.egg-info → fusion_platform_python_sdk-1.17.5}/PKG-INFO +3 -3
  2. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/README.md +1 -1
  3. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/__init__.py +2 -2
  4. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/command.py +79 -34
  5. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/fusion_platform_sdk.pdf +0 -0
  6. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/model.py +3 -1
  7. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/process.py +14 -3
  8. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/process_service_execution.md +3 -1
  9. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/process_service_execution.py +2 -0
  10. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/translations.py +15 -1
  11. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5/fusion_platform_python_sdk.egg-info}/PKG-INFO +3 -3
  12. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/setup.py +2 -2
  13. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/LICENSE.txt +0 -0
  14. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/MANIFEST.in +0 -0
  15. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/__main__.py +0 -0
  16. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/base.py +0 -0
  17. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/common/__init__.py +0 -0
  18. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/common/raise_thread.py +0 -0
  19. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/common/utilities.py +0 -0
  20. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/detailed_example.py +0 -0
  21. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/documentation.py +0 -0
  22. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/glasgow.geojson +0 -0
  23. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/lake_district.geojson +0 -0
  24. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/localisations.py +0 -0
  25. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/localise.py +0 -0
  26. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/__init__.py +0 -0
  27. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/credit.md +0 -0
  28. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/credit.py +0 -0
  29. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/data.md +0 -0
  30. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/data.py +0 -0
  31. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/data_file.md +0 -0
  32. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/data_file.py +0 -0
  33. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/fields.py +0 -0
  34. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/organisation.md +0 -0
  35. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/organisation.py +0 -0
  36. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/process.md +0 -0
  37. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/process_execution.md +0 -0
  38. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/process_execution.py +0 -0
  39. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/process_service_execution_log.md +0 -0
  40. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/process_service_execution_log.py +0 -0
  41. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/service.md +0 -0
  42. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/service.py +0 -0
  43. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/user.md +0 -0
  44. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/models/user.py +0 -0
  45. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/quick_example.py +0 -0
  46. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform/session.py +0 -0
  47. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform_python_sdk.egg-info/SOURCES.txt +0 -0
  48. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform_python_sdk.egg-info/dependency_links.txt +0 -0
  49. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform_python_sdk.egg-info/entry_points.txt +0 -0
  50. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform_python_sdk.egg-info/requires.txt +0 -0
  51. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/fusion_platform_python_sdk.egg-info/top_level.txt +0 -0
  52. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/setup.cfg +0 -0
  53. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/tests/test_base.py +0 -0
  54. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/tests/test_command.py +0 -0
  55. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/tests/test_init.py +0 -0
  56. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/tests/test_localise.py +0 -0
  57. {fusion_platform_python_sdk-1.16.6 → fusion_platform_python_sdk-1.17.5}/tests/test_session.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fusion-platform-python-sdk
3
- Version: 1.16.6
3
+ Version: 1.17.5
4
4
  Summary: Python SDK used to interact with the Fusion Platform(r)
5
5
  Home-page: https://github.com/d-cat-support/fusion-platform-python-sdk
6
6
  Author: Digital Content Analysis Technology Ltd
@@ -8,11 +8,11 @@ Author-email: support@d-cat.co.uk
8
8
  License: MIT
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.8
12
11
  Classifier: Programming Language :: Python :: 3.9
13
12
  Classifier: Programming Language :: Python :: 3.10
14
13
  Classifier: Programming Language :: Python :: 3.11
15
14
  Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE.txt
18
18
  Requires-Dist: marshmallow
@@ -48,7 +48,7 @@ API, and this is defined within the corresponding OpenAPI 3.0 specification, whi
48
48
 
49
49
  ## Installation
50
50
 
51
- The SDK has been built for Python 3, and is tested against Python 3.8, 3.9, 3.10, 3.11 and 3.12. To install the SDK into a suitable Python environment containing
51
+ The SDK has been built for Python 3, and is tested against Python 3.9, 3.10, 3.11, 3.12 and 3.13. To install the SDK into a suitable Python environment containing
52
52
  `pip`, execute the following:
53
53
 
54
54
  ```shell
@@ -12,7 +12,7 @@ API, and this is defined within the corresponding OpenAPI 3.0 specification, whi
12
12
 
13
13
  ## Installation
14
14
 
15
- The SDK has been built for Python 3, and is tested against Python 3.8, 3.9, 3.10, 3.11 and 3.12. To install the SDK into a suitable Python environment containing
15
+ The SDK has been built for Python 3, and is tested against Python 3.9, 3.10, 3.11, 3.12 and 3.13. To install the SDK into a suitable Python environment containing
16
16
  `pip`, execute the following:
17
17
 
18
18
  ```shell
@@ -11,8 +11,8 @@ API, and this is defined within the corresponding OpenAPI 3.0 specification, whi
11
11
  """
12
12
 
13
13
  # Do not modify the following two lines as they are maintained by the version.sh script.
14
- __version__ = '1.16.6'
15
- __version_date__ = '2025-02-11T12:17:15Z'
14
+ __version__ = '1.17.5'
15
+ __version_date__ = '2025-03-18T06:52:14Z'
16
16
 
17
17
  # Exclude certain sub-modules from documentation.
18
18
  # @formatter:off
@@ -39,7 +39,7 @@ Localise.setup()
39
39
 
40
40
  import fusion_platform
41
41
  from fusion_platform.base import Base
42
- from fusion_platform.common.utilities import json_default
42
+ from fusion_platform.common.utilities import json_default, string_blank
43
43
  from fusion_platform.models.data import Data
44
44
  from fusion_platform.models.model import Model
45
45
  from fusion_platform.session import RequestError
@@ -103,7 +103,9 @@ class Command(Base):
103
103
  _METRIC_S3_INPUT_SIZE = 'input_size'
104
104
  _METRIC_S3_OUTPUT_SIZE = 'output_size'
105
105
  _METRIC_S3_TRANSFER_BYTES = 's3_transfer_bytes'
106
+ _METRIC_GCS_TRANSFER_BYTES = 'gcs_transfer_bytes'
106
107
  _METRIC_EXTERNAL_TRANSFER_BYTES = 'external_transfer_bytes'
108
+ _METRIC_INTERNAL_TRANSFER_BYTES = 'internal_transfer_bytes'
107
109
 
108
110
  # Process other field constants.
109
111
  _MODEL_FIELDS = ['output_storage_period', 'run_type', 'repeat_count', 'repeat_start', 'repeat_end', 'repeat_gap', 'repeat_offset']
@@ -193,6 +195,7 @@ class Command(Base):
193
195
  while not finished:
194
196
  # Either find the dispatchers from what has been loaded from the YAML file or prompt the user.
195
197
  add = False
198
+ dispatch_intermediate = False
196
199
  dispatcher_options = {}
197
200
 
198
201
  if dispatchers is not None:
@@ -204,6 +207,8 @@ class Command(Base):
204
207
  selected_dispatcher = filtered_dispatchers[0]
205
208
  selected_dispatcher[Model._FIELD_USED] = True
206
209
  add = True
210
+ dispatch_intermediate = selected_dispatcher.get(Model._FIELD_DISPATCH_INTERMEDIATE) if selected_dispatcher.get(
211
+ Model._FIELD_DISPATCH_INTERMEDIATE) is not None else False
207
212
  dispatcher_options = selected_dispatcher.get(Model._FIELD_OPTIONS) if selected_dispatcher.get(Model._FIELD_OPTIONS) is not None else {}
208
213
 
209
214
  else:
@@ -213,8 +218,29 @@ class Command(Base):
213
218
  if (response is not None) and response.lower().startswith(i18n.t('command.prompt_yes').lower()[0]):
214
219
  add = True
215
220
 
221
+ if hasattr(dispatcher, Model._FIELD_DISPATCH_INTERMEDIATE):
222
+ response = self.__get_input(i18n.t('command.dispatch_intermediate'),
223
+ constrained_values=[i18n.t('command.prompt_no'), i18n.t('command.prompt_yes')])
224
+
225
+ if (response is not None) and response.lower().startswith(i18n.t('command.prompt_yes').lower()[0]):
226
+ dispatch_intermediate = True
227
+
216
228
  for option in dispatcher.options:
217
- option_value = self.__get_input(option.name)
229
+ finished_options = False
230
+ option_value = None
231
+
232
+ while not finished_options:
233
+ constrained_values = option.constrained_values if option.data_type == fusion_platform.DATA_TYPE_CONSTRAINED else None
234
+
235
+ prompt_constrained_values = i18n.t('models.process.option.representation_constrained_values',
236
+ constrained_values=constrained_values) if constrained_values is not None else ''
237
+ prompt_required = i18n.t('models.process.option.representation_required') if option.required else ''
238
+ prompt = i18n.t('command.prompt_option', title=option.title, name=option.name, data_type=option.data_type,
239
+ constrained_values=prompt_constrained_values, required=prompt_required)
240
+
241
+ option_value = self.__get_input(prompt, default=option.value, constrained_values=constrained_values)
242
+ finished_options = (not option.required) or (
243
+ option.required and (option_value is not None) and (not string_blank(str(option_value))))
218
244
 
219
245
  if option_value is not None:
220
246
  dispatcher_options[option.name] = option_value
@@ -224,7 +250,8 @@ class Command(Base):
224
250
  dispatchers_added += 1
225
251
 
226
252
  for option_name, option_value in dispatcher_options.items():
227
- process.update(dispatcher_number=dispatchers_added, option_name=option_name, value=option_value)
253
+ process.update(dispatcher_number=dispatchers_added, option_name=option_name, value=option_value, coerce_value=True,
254
+ dispatch_intermediate=dispatch_intermediate)
228
255
  else:
229
256
  finished = True
230
257
 
@@ -254,12 +281,19 @@ class Command(Base):
254
281
  # Get the underlying service name.
255
282
  service, _ = organisation.find_services(ssd_id=process.ssd_id)
256
283
 
257
- # Get the options and dispatchers.
284
+ # Get the options and dispatchers, optionally add in whether intermediate services are being dispatched.
258
285
  options = {option.name: self.__format_value(option.value, data_type=option.data_type) for option in process.options}
259
- dispatchers = [
260
- {Model._FIELD_NAME: dispatcher.name,
261
- Model._FIELD_OPTIONS: {option.name: self.__format_value(option.value, data_type=option.data_type) for option in dispatcher.options}} for dispatcher in
262
- process.dispatchers]
286
+ dispatchers = []
287
+
288
+ for dispatcher in process.dispatchers:
289
+ found_dispatcher = {Model._FIELD_NAME: dispatcher.name}
290
+
291
+ if hasattr(dispatcher, Model._FIELD_DISPATCH_INTERMEDIATE):
292
+ found_dispatcher[Model._FIELD_DISPATCH_INTERMEDIATE] = dispatcher.dispatch_intermediate
293
+
294
+ found_dispatcher[Model._FIELD_OPTIONS] = {option.name: self.__format_value(option.value, data_type=option.data_type) for option in dispatcher.options}
295
+
296
+ dispatchers.append(found_dispatcher)
263
297
 
264
298
  # Add in the standard options.
265
299
  for field in Command._MODEL_FIELDS:
@@ -418,7 +452,8 @@ class Command(Base):
418
452
 
419
453
  return found
420
454
 
421
- def __download_execution(self, process, download_inputs, download_outputs, download_storage, download_intermediate, download_components, output_dir, execution):
455
+ def __download_execution(self, process, download_inputs, download_outputs, download_storage, download_intermediate, download_components, output_dir, wait,
456
+ execution):
422
457
  """
423
458
  Downloads an execution.
424
459
 
@@ -430,6 +465,7 @@ class Command(Base):
430
465
  download_intermediate: Should intermediate components be downloaded?
431
466
  download_components: The names of the components that should be downloaded? If None or [], then all components are downloaded.
432
467
  output_dir: The output directory.
468
+ wait: Wait for the execution to complete?
433
469
  execution: The execution to be downloaded.
434
470
 
435
471
  Returns:
@@ -441,10 +477,10 @@ class Command(Base):
441
477
  metrics = []
442
478
  downloads = []
443
479
 
444
- # Wait for the execution to complete. We explicitly wait in this method to keep the SDK free.
480
+ # Optionally wait for the execution to complete. We explicitly wait in this method to keep the SDK free.
445
481
  complete = False
446
482
 
447
- while not complete:
483
+ while wait and (not complete):
448
484
  try:
449
485
  complete = execution.check_complete()
450
486
  except:
@@ -480,15 +516,20 @@ class Command(Base):
480
516
  if (started_at is None) or (execution.started_at < started_at):
481
517
  started_at = execution.started_at
482
518
 
483
- if (ended_at is None) or (execution.ended_at > ended_at):
484
- ended_at = execution.ended_at
519
+ execution_ended_at = execution.ended_at if hasattr(execution, Model._FIELD_ENDED_AT) and (execution.ended_at is not None) else datetime.now(timezone.utc)
520
+
521
+ if (ended_at is None) or (execution_ended_at > ended_at):
522
+ ended_at = execution_ended_at
485
523
 
486
524
  for j, component in enumerate(components):
487
525
  # Download the component.
488
526
  component_name = '_'.join([item for item in re.sub(r'[\W]', '_', component.name).split('_') if len(item) > 0])
489
527
  component_dir = f"{str(j + 1)}_{component_name[:25]}"
490
528
  component_options = {item.get(Model._FIELD_NAME): item.get(Model._FIELD_VALUE) for item in component.options} if hasattr(component,
491
- Model._FIELD_OPTIONS) else {}
529
+ Model._FIELD_OPTIONS) and (
530
+ component.options is not None) else {}
531
+ component_success = component.success if hasattr(component, Model._FIELD_SUCCESS) and (component.success is not None) else False
532
+ component_runtime = component.runtime if hasattr(component, Model._FIELD_RUNTIME) and (component.runtime is not None) else 0
492
533
 
493
534
  # Save the metrics for each component in the execution.
494
535
  # @formatter:off
@@ -497,25 +538,22 @@ class Command(Base):
497
538
  Command._METRICS_PROCESS: process.name,
498
539
  Command._METRICS_DURATION: ended_at - started_at if (started_at is not None) and (ended_at is not None) else 0,
499
540
  Command._METRICS_COMPONENT: component.name,
500
- Command._METRICS_SUCCESS: component.success,
541
+ Command._METRICS_SUCCESS: component_success,
501
542
  Command._METRICS_CPU: component.cpu,
502
543
  Command._METRICS_MEMORY: component.memory,
503
- Command._METRICS_RUNTIME: component.runtime
544
+ Command._METRICS_RUNTIME: component_runtime
504
545
  }
505
546
  # @formatter:on
506
547
 
507
548
  # Only D-CAT services have metrics.
508
- s3_transfer_bytes = 0
509
- external_transfer_bytes = 0
510
-
511
549
  if hasattr(component, Command._METRICS_METRICS) and (component.metrics is not None):
512
- s3_transfer_bytes = sum(
513
- [metric[Command._METRIC_S3_TRANSFER_BYTES] for metric in component.metrics if metric[Command._METRIC_S3_TRANSFER_BYTES] is not None])
514
- external_transfer_bytes = sum([metric[Command._METRIC_EXTERNAL_TRANSFER_BYTES] for metric in component.metrics if
515
- metric[Command._METRIC_EXTERNAL_TRANSFER_BYTES] is not None])
516
-
517
- metric[Command._METRIC_S3_TRANSFER_BYTES] = s3_transfer_bytes
518
- metric[Command._METRIC_EXTERNAL_TRANSFER_BYTES] = external_transfer_bytes
550
+ for key in [
551
+ Command._METRIC_S3_TRANSFER_BYTES,
552
+ Command._METRIC_GCS_TRANSFER_BYTES,
553
+ Command._METRIC_EXTERNAL_TRANSFER_BYTES,
554
+ Command._METRIC_INTERNAL_TRANSFER_BYTES
555
+ ]:
556
+ metric[key] = sum([metric[key] for metric in component.metrics if metric.get(key) is not None])
519
557
 
520
558
  # Find each input, output and storage to download. While traversing each, save the input and output size. The size might not be immediately available
521
559
  # for every file, so wait for all items to be analysed.
@@ -574,8 +612,8 @@ class Command(Base):
574
612
  with open(os.path.join(download_dir, stac_file_name), 'w') as stac_file:
575
613
  stac_file.write(json.dumps(stac_definition, default=json_default))
576
614
 
577
- metric[Command._METRIC_S3_INPUT_SIZE] = sum(input_sizes)
578
- metric[Command._METRIC_S3_OUTPUT_SIZE] = sum(output_sizes)
615
+ metric[Command._METRIC_S3_INPUT_SIZE] = sum(input_sizes) if len(input_sizes) > 0 else ''
616
+ metric[Command._METRIC_S3_OUTPUT_SIZE] = sum(output_sizes) if len(output_sizes) > 0 else ''
579
617
  metrics.append(metric)
580
618
 
581
619
  return downloads, metrics
@@ -649,7 +687,7 @@ class Command(Base):
649
687
  progress_bar.close()
650
688
 
651
689
  def download_process_execution(self, organisation, process_name, download_inputs, download_outputs, download_storage, download_intermediate,
652
- download_components, save_metrics):
690
+ download_components, save_metrics, wait_for_execution_to_complete):
653
691
  """
654
692
  Downloads the inputs and/or outputs from a process or execution(s).
655
693
 
@@ -662,6 +700,7 @@ class Command(Base):
662
700
  download_intermediate: Should intermediate components be downloaded?
663
701
  download_components: The names of the components that should be downloaded? If None or [], then all components are downloaded.
664
702
  save_metrics: Save process metrics to file?
703
+ wait_for_execution_to_complete: Optionally wait for completion of the execution?
665
704
 
666
705
  Returns:
667
706
  The process model.
@@ -707,7 +746,8 @@ class Command(Base):
707
746
 
708
747
  # Do all the downloads in batches. We use small batches so that we do not swamp memory.
709
748
  batch_size = 2 * os.cpu_count() # Downloads don't swamp CPU.
710
- self.__print(logging.INFO, i18n.t('command.download_executions', executions=len(executions)))
749
+ wait = i18n.t('command.wait_for_completion') if wait_for_execution_to_complete else ''
750
+ self.__print(logging.INFO, i18n.t('command.download_executions', wait=wait, executions=len(executions)))
711
751
  progress_bar = tqdm(total=len(executions), bar_format=Command.__PROGRESS_BAR_FORMAT, colour=Command.__PROGRESS_BAR_COLOUR_ACTIVE,
712
752
  unit='execution') # Unit matches keyword argument.
713
753
  progress_bar.set_postfix(execution=1)
@@ -715,7 +755,7 @@ class Command(Base):
715
755
  try:
716
756
  # Now download each execution in batches.
717
757
  partial_download_execution = partial(self.__download_execution, process, download_inputs, download_outputs, download_storage, download_intermediate,
718
- download_components, output_dir)
758
+ download_components, output_dir, wait_for_execution_to_complete)
719
759
  results = []
720
760
 
721
761
  with Pool(nodes=batch_size) as pool:
@@ -855,7 +895,7 @@ class Command(Base):
855
895
  The user response.
856
896
  """
857
897
  default = constrained_values[0] if (default is None) and (constrained_values is not None) and (len(constrained_values) > 0) else default
858
- input_prompt = f"{input_prompt}{' [' + default + ']' if default is not None else ''}: "
898
+ input_prompt = f"{input_prompt}{' [' + str(default) + ']' if default is not None else ''}: "
859
899
  constrained_values = [''] + constrained_values if constrained_values is not None else constrained_values
860
900
 
861
901
  # Set up any validation against constrained values.
@@ -1089,7 +1129,8 @@ class Command(Base):
1089
1129
  # Also perform the download, if required.
1090
1130
  if arguments.download:
1091
1131
  process = self.download_process_execution(organisation, process_name, arguments.inputs, arguments.outputs, arguments.storage,
1092
- arguments.intermediate, arguments.component, arguments.metrics)
1132
+ arguments.intermediate, arguments.component, arguments.metrics,
1133
+ not arguments.no_wait_for_completion)
1093
1134
 
1094
1135
  # And optionally remove the process and its inputs.
1095
1136
  if arguments.remove:
@@ -1112,7 +1153,7 @@ class Command(Base):
1112
1153
  for process_name in arguments.process:
1113
1154
  # Download the process.
1114
1155
  process = self.download_process_execution(organisation, process_name, arguments.inputs, arguments.outputs, arguments.storage,
1115
- arguments.intermediate, arguments.component, arguments.metrics)
1156
+ arguments.intermediate, arguments.component, arguments.metrics, not arguments.no_wait_for_completion)
1116
1157
 
1117
1158
  # And optionally remove the process and its inputs.
1118
1159
  if arguments.remove:
@@ -1189,6 +1230,8 @@ class Command(Base):
1189
1230
  default=False, action="store_true")
1190
1231
  parser_start.add_argument(i18n.t('command.start.component_short'), i18n.t('command.start.component_long'), help=i18n.t('command.start.component_help'),
1191
1232
  nargs='*')
1233
+ parser_start.add_argument(i18n.t('command.start.no_wait_for_completion_short'), i18n.t('command.start.no_wait_for_completion_long'),
1234
+ help=i18n.t('command.start.no_wait_for_completion_help'), default=False, action="store_true")
1192
1235
 
1193
1236
  # Define arguments.
1194
1237
  parser_define.add_argument(i18n.t('command.define.process_long'), help=i18n.t('command.define.process_help'), nargs='+')
@@ -1213,6 +1256,8 @@ class Command(Base):
1213
1256
  default=False, action="store_true")
1214
1257
  parser_download.add_argument(i18n.t('command.start.component_short'), i18n.t('command.start.component_long'), help=i18n.t('command.start.component_help'),
1215
1258
  nargs='*')
1259
+ parser_download.add_argument(i18n.t('command.start.no_wait_for_completion_short'), i18n.t('command.start.no_wait_for_completion_long'),
1260
+ help=i18n.t('command.start.no_wait_for_completion_help'), default=False, action="store_true")
1216
1261
 
1217
1262
  return parser.parse_args()
1218
1263
 
@@ -92,6 +92,7 @@ class Model(Base):
92
92
  _FIELD_OUTPUTS = 'outputs'
93
93
  _FIELD_PROCESS_STATUS = 'process_status'
94
94
  _FIELD_RESOLUTION = 'resolution'
95
+ _FIELD_RUNTIME = 'runtime'
95
96
  _FIELD_SD = 'sd'
96
97
  _FIELD_SELECTOR = 'selector'
97
98
  _FIELD_SELECTORS = 'selectors'
@@ -99,6 +100,7 @@ class Model(Base):
99
100
  _FIELD_STAC_ITEM = 'stac_item'
100
101
  _FIELD_STAC_ITEM_FILE = 'stac_item_file'
101
102
  _FIELD_STARTED_AT = 'started_at'
103
+ _FIELD_SUCCESS = 'success'
102
104
  _FIELD_PUBLISHABLE = 'publishable'
103
105
  _FIELD_SIZE = 'size'
104
106
  _FIELD_UNIT = 'unit'
@@ -770,7 +772,7 @@ class Model(Base):
770
772
  Returns:
771
773
  A string representation of the object.
772
774
  """
773
- return f"{self.__class__.__name__}{self.attributes}"
775
+ return i18n.t('models.model.representation', name=self.__class__.__name__, attributes=self.attributes)
774
776
 
775
777
  def to_csv(self, exclude=None):
776
778
  """
@@ -228,7 +228,12 @@ class Option(Model):
228
228
  Returns:
229
229
  A string representation of the object.
230
230
  """
231
- return f"{self.title} ('{self.name}', {self.data_type}{' ' + str(self.constrained_values) if self.data_type == fusion_platform.DATA_TYPE_CONSTRAINED else ''}{', required' if self.required else ''}) = {self.value}: {self.description}"
231
+ constrained_values = i18n.t('models.process.option.representation_constrained_values',
232
+ constrained_values=self.constrained_values) if self.data_type == fusion_platform.DATA_TYPE_CONSTRAINED else ''
233
+ required = i18n.t('models.process.option.representation_required') if self.required else ''
234
+
235
+ return i18n.t('models.process.option.representation', title=self.title, name=self.name, data_type=self.data_type, constrained_values=constrained_values,
236
+ required=required, value=self.value, description=self.description)
232
237
 
233
238
 
234
239
  # Define a model class for an input which overrides the representation to display more relevant information.
@@ -243,7 +248,10 @@ class Input(Model):
243
248
  Returns:
244
249
  A string representation of the object.
245
250
  """
246
- return f"{self.title} ({self.file_type}) = {self.name + ' (' + str(self.id) + ')' if hasattr(self, self.__class__._FIELD_NAME) and hasattr(self, self.__class__._FIELD_ID) else None}: {self.description}"
251
+ name = i18n.t('models.process.input.representation_name', name=self.name) if hasattr(self, self.__class__._FIELD_NAME) else ''
252
+ id = i18n.t('models.process.input.representation_id', id=self.id) if hasattr(self, self.__class__._FIELD_ID) else ''
253
+
254
+ return i18n.t('models.process.input.representation', title=self.title, file_type=self.file_type, name=name, id=id, description=self.description)
247
255
 
248
256
 
249
257
  # Define a model class for a dispatcher which overrides the representation to display more relevant information.
@@ -268,7 +276,10 @@ class Dispatcher(Model):
268
276
  Returns:
269
277
  A string representation of the object.
270
278
  """
271
- return f"{self.name} ({self.ssd_id}, intermediate {self.dispatch_intermediate if hasattr(self, self.__class__._FIELD_DISPATCH_INTERMEDIATE) else False}): {self.documentation_summary}"
279
+ dispatch_intermediate = self.dispatch_intermediate if hasattr(self, self.__class__._FIELD_DISPATCH_INTERMEDIATE) else False
280
+
281
+ return i18n.t('models.process.dispatcher.representation', name=self.name, ssd_id=self.ssd_id, dispatch_intermediate=dispatch_intermediate,
282
+ documentation_summary=self.documentation_summary)
272
283
 
273
284
 
274
285
  # Define the model schema classes. These are maintained from the API definitions.
@@ -51,5 +51,7 @@
51
51
  * **tmp_total_bytes**: The total temporary disk space in bytes.
52
52
  * **tmp_free_bytes**: The free temporary disk space in bytes.
53
53
  * **s3_transfer_bytes**: The number of bytes transferred in from S3.
54
- * **external_transfer_bytes**: The number of bytes transferred in externally, excluding S3.
54
+ * **gcs_transfer_bytes**: The number of bytes transferred in from Google Cloud Storage.
55
+ * **external_transfer_bytes**: The number of bytes transferred in externally.
56
+ * **internal_transfer_bytes**: The number of bytes transferred in internally.
55
57
  * **comment**: Any comment recorded with the metric.
@@ -93,7 +93,9 @@ class ProcessServiceExecutionMetricSchema(Schema):
93
93
  tmp_total_bytes = fields.Integer(allow_none=True)
94
94
  tmp_free_bytes = fields.Integer(allow_none=True)
95
95
  s3_transfer_bytes = fields.Integer(allow_none=True)
96
+ gcs_transfer_bytes = fields.Integer(allow_none=True)
96
97
  external_transfer_bytes = fields.Integer(allow_none=True)
98
+ internal_transfer_bytes = fields.Integer(allow_none=True)
97
99
  comment = fields.String(allow_none=True)
98
100
 
99
101
  class Meta:
@@ -46,6 +46,7 @@ i18n.add_translation('command.log_field', '%{field}: %{value}', 'en')
46
46
  i18n.add_translation('command.log_subdivider', '................................................................................................', 'en')
47
47
  i18n.add_translation('command.log_divider', '------------------------------------------------------------------------------------------------', 'en')
48
48
  i18n.add_translation('command.log_bookend', '************************************************************************************************', 'en')
49
+ i18n.add_translation('command.wait_for_completion', ' and waiting for completion', 'en')
49
50
  i18n.add_translation('command.wait_for_next', 'Waiting for next execution...', 'en')
50
51
  i18n.add_translation('command.using_storage', 'Uploading storage \'%{filename}\' with id %{storage_id} to \'%{storage_name}\'', 'en')
51
52
  i18n.add_translation('command.using_service', 'Using service \'%{service}\'', 'en')
@@ -58,6 +59,7 @@ i18n.add_translation('command.remove_process', 'Removing input \'%{process}\'',
58
59
  i18n.add_translation('command.remove_input', 'Removing input \'%{input}\'', 'en')
59
60
  i18n.add_translation('command.prompt_yes', 'Yes', 'en')
60
61
  i18n.add_translation('command.prompt_no', 'No', 'en')
62
+ i18n.add_translation('command.prompt_option', '%{title} (\'%{name}\', %{data_type}%{constrained_values}%{required})', 'en')
61
63
  i18n.add_translation('command.password', 'Password', 'en')
62
64
  i18n.add_translation('command.organisation', 'Organisation', 'en')
63
65
  i18n.add_translation('command.find_inputs', 'Finding %{inputs} input file(s)...', 'en')
@@ -66,11 +68,12 @@ i18n.add_translation('command.email', 'Email', 'en')
66
68
  i18n.add_translation('command.download_process_execution', 'Downloading \'%{process}\' (inputs %{inputs}, outputs %{outputs}, storage %{storage}, intermediate %{intermediate}, metrics %{metrics}, components %{components})', 'en')
67
69
  i18n.add_translation('command.download_process', 'Downloading process \'%{process}\' to %{output}', 'en')
68
70
  i18n.add_translation('command.download_files', 'Downloading %{files} file(s)...', 'en')
69
- i18n.add_translation('command.download_executions', 'Gathering files for %{executions} execution(s)...', 'en')
71
+ i18n.add_translation('command.download_executions', 'Gathering files%{wait} for %{executions} execution(s)...', 'en')
70
72
  i18n.add_translation('command.download_execution', 'Downloading execution %{execution} to %{output}', 'en')
71
73
  i18n.add_translation('command.define_storage', 'Defining storage file(s) for %{slices} slice(s)...', 'en')
72
74
  i18n.add_translation('command.define_process', 'Defining \'%{process}\' (inputs %{inputs}, storage %{storage})', 'en')
73
75
  i18n.add_translation('command.define_inputs', 'Defining %{files} input file(s)...', 'en')
76
+ i18n.add_translation('command.dispatch_intermediate', 'Dispatch intermediate components?', 'en')
74
77
  i18n.add_translation('command.add_dispatcher', 'Add %{dispatcher} dispatcher?', 'en')
75
78
  i18n.add_translation('command.download.process_help', 'the name of the process, the process id or execution id to download', 'en')
76
79
  i18n.add_translation('command.download.process_long', 'process', 'en')
@@ -80,6 +83,9 @@ i18n.add_translation('command.define.process_help', 'the name of the process, th
80
83
  i18n.add_translation('command.define.process_long', 'process', 'en')
81
84
  i18n.add_translation('command.define.help', 'outputs a YAML file for each existing process', 'en')
82
85
  i18n.add_translation('command.define.command', 'define', 'en')
86
+ i18n.add_translation('command.start.no_wait_for_completion_help', 'do not wait for the execution to complete (default %%(default)s)\'', 'en')
87
+ i18n.add_translation('command.start.no_wait_for_completion_long', '--no_wait_for_completion', 'en')
88
+ i18n.add_translation('command.start.no_wait_for_completion_short', '-n', 'en')
83
89
  i18n.add_translation('command.start.metrics_help', 'save process metrics to file (default %%(default)s)', 'en')
84
90
  i18n.add_translation('command.start.metrics_long', '--metrics', 'en')
85
91
  i18n.add_translation('command.start.metrics_short', '-t', 'en')
@@ -280,6 +286,7 @@ i18n.add_translation('models.model.no_such_keys', 'No such keys %{keys}', 'en')
280
286
  i18n.add_translation('models.model.readonly_property', 'Property %{property} is read-only and cannot be set', 'en')
281
287
  i18n.add_translation('models.model.not_persisted', 'Model is not persisted in the Fusion Platform(r)', 'en')
282
288
  i18n.add_translation('models.model.already_persisted', 'Model is already persisted in the Fusion Platform(r)', 'en')
289
+ i18n.add_translation('models.model.representation', '%{name}%{attributes}', 'en')
283
290
  i18n.add_translation('models.process.failed_copy', 'Failed to get process from copy response', 'en')
284
291
  i18n.add_translation('models.process.execution_should_have_started', 'Process execution should have started by now', 'en')
285
292
  i18n.add_translation('models.process.execution_stopped', 'Process execution has been stopped', 'en')
@@ -298,4 +305,11 @@ i18n.add_translation('models.process.option.constrained_values.description', 'Th
298
305
  i18n.add_translation('models.process.option.constrained_values.title', 'Constrained Values', 'en')
299
306
  i18n.add_translation('models.process.option.constrained_names.description', 'The constrained value names for the option.', 'en')
300
307
  i18n.add_translation('models.process.option.constrained_names.title', 'Constrained Names', 'en')
308
+ i18n.add_translation('models.process.option.representation_required', ', required', 'en')
309
+ i18n.add_translation('models.process.option.representation_constrained_values', ' %{constrained_values}', 'en')
310
+ i18n.add_translation('models.process.option.representation', '%{title} (\'%{name}\', %{data_type}%{constrained_values}%{required}) = %{value}: %{description}', 'en')
311
+ i18n.add_translation('models.process.input.representation_id', ' (%{id})', 'en')
312
+ i18n.add_translation('models.process.input.representation_name', ' %{name}', 'en')
313
+ i18n.add_translation('models.process.input.representation', '%{title} (%{file_type}) =%{name}%{id}: %{description}', 'en')
314
+ i18n.add_translation('models.process.dispatcher.representation', '%{name} (%{ssd_id}, intermediate %{dispatch_intermediate}): %{documentation_summary}', 'en')
301
315
  # @formatter:on
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fusion-platform-python-sdk
3
- Version: 1.16.6
3
+ Version: 1.17.5
4
4
  Summary: Python SDK used to interact with the Fusion Platform(r)
5
5
  Home-page: https://github.com/d-cat-support/fusion-platform-python-sdk
6
6
  Author: Digital Content Analysis Technology Ltd
@@ -8,11 +8,11 @@ Author-email: support@d-cat.co.uk
8
8
  License: MIT
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.8
12
11
  Classifier: Programming Language :: Python :: 3.9
13
12
  Classifier: Programming Language :: Python :: 3.10
14
13
  Classifier: Programming Language :: Python :: 3.11
15
14
  Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE.txt
18
18
  Requires-Dist: marshmallow
@@ -48,7 +48,7 @@ API, and this is defined within the corresponding OpenAPI 3.0 specification, whi
48
48
 
49
49
  ## Installation
50
50
 
51
- The SDK has been built for Python 3, and is tested against Python 3.8, 3.9, 3.10, 3.11 and 3.12. To install the SDK into a suitable Python environment containing
51
+ The SDK has been built for Python 3, and is tested against Python 3.9, 3.10, 3.11, 3.12 and 3.13. To install the SDK into a suitable Python environment containing
52
52
  `pip`, execute the following:
53
53
 
54
54
  ```shell
@@ -19,7 +19,7 @@ PACKAGE_DIR = pathlib.Path(__file__).parent
19
19
  # @formatter:off
20
20
  setup(
21
21
  name='fusion-platform-python-sdk',
22
- version='1.16.6',
22
+ version='1.17.5',
23
23
  description='Python SDK used to interact with the Fusion Platform(r)',
24
24
  long_description=(PACKAGE_DIR / 'README.md').read_text(),
25
25
  long_description_content_type='text/markdown',
@@ -30,11 +30,11 @@ setup(
30
30
  classifiers=[
31
31
  'License :: OSI Approved :: MIT License',
32
32
  'Programming Language :: Python :: 3',
33
- 'Programming Language :: Python :: 3.8',
34
33
  'Programming Language :: Python :: 3.9',
35
34
  'Programming Language :: Python :: 3.10',
36
35
  'Programming Language :: Python :: 3.11',
37
36
  'Programming Language :: Python :: 3.12',
37
+ 'Programming Language :: Python :: 3.13',
38
38
  ],
39
39
  packages=find_packages(exclude='tests'),
40
40
  include_package_data=True,