fusion-platform-python-sdk 1.17.5__tar.gz → 1.17.9__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.17.5/fusion_platform_python_sdk.egg-info → fusion_platform_python_sdk-1.17.9}/PKG-INFO +1 -1
  2. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/__init__.py +2 -2
  3. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/command.py +127 -46
  4. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/translations.py +18 -5
  5. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9/fusion_platform_python_sdk.egg-info}/PKG-INFO +1 -1
  6. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/setup.py +1 -1
  7. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/tests/test_session.py +1 -1
  8. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/LICENSE.txt +0 -0
  9. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/MANIFEST.in +0 -0
  10. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/README.md +0 -0
  11. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/__main__.py +0 -0
  12. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/base.py +0 -0
  13. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/common/__init__.py +0 -0
  14. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/common/raise_thread.py +0 -0
  15. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/common/utilities.py +0 -0
  16. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/detailed_example.py +0 -0
  17. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/documentation.py +0 -0
  18. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/fusion_platform_sdk.pdf +0 -0
  19. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/glasgow.geojson +0 -0
  20. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/lake_district.geojson +0 -0
  21. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/localisations.py +0 -0
  22. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/localise.py +0 -0
  23. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/__init__.py +0 -0
  24. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/credit.md +0 -0
  25. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/credit.py +0 -0
  26. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/data.md +0 -0
  27. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/data.py +0 -0
  28. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/data_file.md +0 -0
  29. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/data_file.py +0 -0
  30. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/fields.py +0 -0
  31. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/model.py +0 -0
  32. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/organisation.md +0 -0
  33. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/organisation.py +0 -0
  34. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/process.md +0 -0
  35. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/process.py +0 -0
  36. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/process_execution.md +0 -0
  37. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/process_execution.py +0 -0
  38. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/process_service_execution.md +0 -0
  39. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/process_service_execution.py +0 -0
  40. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/process_service_execution_log.md +0 -0
  41. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/process_service_execution_log.py +0 -0
  42. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/service.md +0 -0
  43. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/service.py +0 -0
  44. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/user.md +0 -0
  45. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/models/user.py +0 -0
  46. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/quick_example.py +0 -0
  47. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform/session.py +0 -0
  48. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform_python_sdk.egg-info/SOURCES.txt +0 -0
  49. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform_python_sdk.egg-info/dependency_links.txt +0 -0
  50. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform_python_sdk.egg-info/entry_points.txt +0 -0
  51. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform_python_sdk.egg-info/requires.txt +0 -0
  52. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/fusion_platform_python_sdk.egg-info/top_level.txt +0 -0
  53. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/setup.cfg +0 -0
  54. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/tests/test_base.py +0 -0
  55. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/tests/test_command.py +0 -0
  56. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/tests/test_init.py +0 -0
  57. {fusion_platform_python_sdk-1.17.5 → fusion_platform_python_sdk-1.17.9}/tests/test_localise.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fusion-platform-python-sdk
3
- Version: 1.17.5
3
+ Version: 1.17.9
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
@@ -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.17.5'
15
- __version_date__ = '2025-03-18T06:52:14Z'
14
+ __version__ = '1.17.9'
15
+ __version_date__ = '2025-04-29T13:10:22Z'
16
16
 
17
17
  # Exclude certain sub-modules from documentation.
18
18
  # @formatter:off
@@ -8,28 +8,28 @@ author: Matthew Casey
8
8
 
9
9
  import argparse
10
10
  from argparse import RawTextHelpFormatter
11
+ from collections import defaultdict
11
12
  from copy import deepcopy
12
13
  import csv
13
14
  from datetime import datetime, timedelta, timezone
14
- from dateutil.relativedelta import relativedelta
15
15
  from decimal import Decimal
16
16
  from functools import partial
17
- import i18n
18
17
  import json
19
18
  import logging
20
- from marshmallow.utils import _Missing
21
19
  import os
22
- from pathos.multiprocessing import ProcessPool as Pool
23
- from prompt_toolkit import prompt
24
- from prompt_toolkit import print_formatted_text as print
25
- from prompt_toolkit.completion import WordCompleter
26
- from prompt_toolkit.validation import Validator
27
20
  import re
28
21
  import shutil
29
22
  import tempfile
30
23
  from time import sleep
31
- from tqdm import tqdm
32
24
  import uuid
25
+
26
+ from dateutil.relativedelta import relativedelta
27
+ import i18n
28
+ from pathos.multiprocessing import ProcessPool as Pool
29
+ from prompt_toolkit import print_formatted_text as print, prompt
30
+ from prompt_toolkit.completion import WordCompleter
31
+ from prompt_toolkit.validation import Validator
32
+ from tqdm import tqdm
33
33
  import yaml
34
34
 
35
35
  # Make sure localisation is set up before any SDK-specific imports.
@@ -260,6 +260,18 @@ class Command(Base):
260
260
 
261
261
  return process
262
262
 
263
+ def __data_name_to_file_name(self, data_name):
264
+ """
265
+ Converts a data item name into a sane file name (without extension).
266
+
267
+ Args:
268
+ data_name: The data item name to convert.
269
+
270
+ Returns:
271
+ The corresponding file name.
272
+ """
273
+ return re.sub(r"[^A-Za-z_\-]", '_', str(data_name)) if data_name is not None else None
274
+
263
275
  def define_process(self, organisation, process_name, download_inputs=False, download_storage=False):
264
276
  """
265
277
  Obtains the process definition for a process.
@@ -443,9 +455,35 @@ class Command(Base):
443
455
 
444
456
  self.__print_process(process)
445
457
 
446
- # Display the status of any execution.
458
+ # Group executions, if they are in groups.
459
+ groups = defaultdict(list)
460
+
447
461
  for execution in process.executions:
448
- self.__print_execution(execution)
462
+ if (hasattr(execution, Model._FIELD_GROUP_ID) and (execution.group_id is not None)) and (
463
+ hasattr(execution, Model._FIELD_GROUP_INDEX) and (execution.group_index is not None)) and (
464
+ hasattr(execution, Model._FIELD_GROUP_COUNT) and (execution.group_count is not None)):
465
+ groups[execution.group_id].append(execution)
466
+
467
+ # Display the status of any execution, in groups if used.
468
+ if len(groups) > 0:
469
+ group_number = 1
470
+
471
+ for group_id, group_executions in groups.items():
472
+ self.__print(logging.INFO,
473
+ i18n.t('command.log_execution_group_id', group_number=group_number, group_count=len(groups), group_id=group_id))
474
+
475
+ for execution in sorted(group_executions, key=lambda execution: execution.group_index):
476
+ self.__print_execution(execution, label=i18n.t('command.log_execution_group_index', group_index=execution.group_index,
477
+ group_count=execution.group_count))
478
+
479
+ group_number += 1
480
+ self.__print(logging.INFO, i18n.t('command.log_bookend'))
481
+
482
+ else:
483
+ for execution in process.executions:
484
+ self.__print_execution(execution)
485
+
486
+ self.__print(logging.INFO, i18n.t('command.log_bookend'))
449
487
 
450
488
  except CommandError as e:
451
489
  self.__print(logging.DEBUG, str(e))
@@ -495,8 +533,8 @@ class Command(Base):
495
533
  group_count = None
496
534
  group_index = None
497
535
 
498
- if hasattr(execution, Model._FIELD_GROUP_INDEX) and (execution.group_index is not None) and (not isinstance(execution.group_index, _Missing)) and hasattr(
499
- execution, Model._FIELD_GROUP_COUNT) and (execution.group_count is not None) and (not isinstance(execution.group_count, _Missing)):
536
+ if hasattr(execution, Model._FIELD_GROUP_INDEX) and (execution.group_index is not None) and (isinstance(execution.group_index, int)) and hasattr(
537
+ execution, Model._FIELD_GROUP_COUNT) and (execution.group_count is not None) and (isinstance(execution.group_count, int)):
500
538
  group_count = execution.group_count
501
539
  group_index = execution.group_index
502
540
  number_of_group_digits = len(str(group_count))
@@ -516,7 +554,8 @@ class Command(Base):
516
554
  if (started_at is None) or (execution.started_at < started_at):
517
555
  started_at = execution.started_at
518
556
 
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)
557
+ execution_ended_at = execution.ended_at if hasattr(execution, Model._FIELD_ENDED_AT) and (execution.ended_at is not None) else datetime.now(
558
+ timezone.utc)
520
559
 
521
560
  if (ended_at is None) or (execution_ended_at > ended_at):
522
561
  ended_at = execution_ended_at
@@ -581,7 +620,8 @@ class Command(Base):
581
620
  # Download the data item once it is complete.
582
621
  data_item.check_analysis_complete(wait=True)
583
622
 
584
- download_dir = os.path.join(execution_dir, component_dir, f"{data_type}_{str(k)}")
623
+ name = re.sub('_+', '_', f"{data_type.capitalize()}_{str(k)}_{self.__data_name_to_file_name(data_item.name)}").rstrip('_')
624
+ download_dir = os.path.join(execution_dir, component_dir, name)
585
625
  os.makedirs(download_dir, exist_ok=True)
586
626
 
587
627
  # We also create the corresponding STAC definitions.
@@ -687,7 +727,7 @@ class Command(Base):
687
727
  progress_bar.close()
688
728
 
689
729
  def download_process_execution(self, organisation, process_name, download_inputs, download_outputs, download_storage, download_intermediate,
690
- download_components, save_metrics, wait_for_execution_to_complete):
730
+ download_components, save_metrics, stac_only, wait_for_execution_to_complete):
691
731
  """
692
732
  Downloads the inputs and/or outputs from a process or execution(s).
693
733
 
@@ -700,6 +740,7 @@ class Command(Base):
700
740
  download_intermediate: Should intermediate components be downloaded?
701
741
  download_components: The names of the components that should be downloaded? If None or [], then all components are downloaded.
702
742
  save_metrics: Save process metrics to file?
743
+ stac_only: Save only stac assets?
703
744
  wait_for_execution_to_complete: Optionally wait for completion of the execution?
704
745
 
705
746
  Returns:
@@ -710,9 +751,9 @@ class Command(Base):
710
751
  if (not download_inputs) and (not download_outputs) and (not download_storage):
711
752
  download_outputs = True
712
753
 
713
- self.__print(logging.INFO,
714
- i18n.t('command.download_process_execution', process=process_name, inputs=download_inputs, outputs=download_outputs, storage=download_storage,
715
- intermediate=download_intermediate, metrics=save_metrics, components=download_components))
754
+ self.__print(logging.INFO, i18n.t('command.download_process_execution', process=process_name, inputs=download_inputs, outputs=download_outputs,
755
+ storage=download_storage, intermediate=download_intermediate, stac_only=stac_only, metrics=save_metrics,
756
+ components=download_components))
716
757
 
717
758
  # We are downloading from an existing process.
718
759
  process, execution = self.get_process_or_execution(organisation, process_name)
@@ -777,8 +818,9 @@ class Command(Base):
777
818
  downloads.extend(execution_downloads)
778
819
  metrics.extend(execution_metrics)
779
820
 
780
- # Download the files.
781
- self.__download_files(downloads)
821
+ # Download the files unless we only want the stac information
822
+ if not stac_only:
823
+ self.__download_files(downloads)
782
824
 
783
825
  # Save the metrics to file.
784
826
  if save_metrics and (len(metrics) > 0):
@@ -1029,6 +1071,38 @@ class Command(Base):
1029
1071
  """
1030
1072
  return uuid.uuid5(uuid.NAMESPACE_URL, f"{process_id}_{group_count}_{group_index}_{ssd_id}_{chain_index}")
1031
1073
 
1074
+ def list_processes(self, organisation):
1075
+ """
1076
+ Lists all configured processes.
1077
+
1078
+ Args:
1079
+ organisation: The logged in organisation.
1080
+ """
1081
+ for process in organisation.processes:
1082
+ executions = [execution for execution in process.executions]
1083
+
1084
+ # Get information about any groups.
1085
+ groups = defaultdict(list)
1086
+
1087
+ for execution in executions:
1088
+ if (hasattr(execution, Model._FIELD_GROUP_ID) and (execution.group_id is not None)) and (
1089
+ hasattr(execution, Model._FIELD_GROUP_INDEX) and (execution.group_index is not None)) and (
1090
+ hasattr(execution, Model._FIELD_GROUP_COUNT) and (execution.group_count is not None)):
1091
+ groups[execution.group_id].append(execution)
1092
+
1093
+ # Calculate the overall progress and for each group.
1094
+ progress = sum([execution.progress for execution in executions]) // len(executions)
1095
+ group_progress = ''
1096
+
1097
+ for i, group in enumerate(groups.values()):
1098
+ progress = sum([execution.progress for execution in group]) // len(group)
1099
+ group_progress += i18n.t('command.log_process_group', group_number=(i + 1), executions=len(group), progress=progress)
1100
+
1101
+ # Log the process information.
1102
+ self.__print(logging.INFO,
1103
+ i18n.t('command.log_process_summary', process=process.name, executions=len(executions), progress=progress, groups=len(groups),
1104
+ group_progress=group_progress))
1105
+
1032
1106
  def login(self, deployment, email, organisation_name):
1033
1107
  """
1034
1108
  Logs into the desired Fusion Platform(r) deployment and allows the user to select an organisation.
@@ -1104,12 +1178,16 @@ class Command(Base):
1104
1178
  # Login to the correct deployment and select the organisation.
1105
1179
  organisation, _, _ = self.login(arguments.deployment, arguments.email, arguments.organisation)
1106
1180
 
1107
- # Perform the display, start, define or download.
1108
- if arguments.command == i18n.t('command.display.command'):
1109
- # Display a service or process with the specified name or ids. If a service is displayed, we do not display a process.
1181
+ # Perform the list, display, start, define or download.
1182
+ if arguments.command == i18n.t('command.list.command'):
1183
+ # List all processes.
1184
+ self.list_processes(organisation)
1185
+
1186
+ elif arguments.command == i18n.t('command.display.command'):
1187
+ # Display a service or process with the specified name or ids. If a process is displayed, we do not display a service.
1110
1188
  for process_or_service in arguments.process_or_service:
1111
- if not self.display_service(organisation, process_or_service):
1112
- if not self.display_process(organisation, process_or_service):
1189
+ if not self.display_process(organisation, process_or_service):
1190
+ if not self.display_service(organisation, process_or_service):
1113
1191
  self.__print(logging.WARNING, i18n.t('command.no_process_or_service', process_or_service=process_or_service))
1114
1192
 
1115
1193
  elif arguments.command == i18n.t('command.start.command'):
@@ -1129,7 +1207,7 @@ class Command(Base):
1129
1207
  # Also perform the download, if required.
1130
1208
  if arguments.download:
1131
1209
  process = self.download_process_execution(organisation, process_name, arguments.inputs, arguments.outputs, arguments.storage,
1132
- arguments.intermediate, arguments.component, arguments.metrics,
1210
+ arguments.intermediate, arguments.component, arguments.metrics, arguments.stac,
1133
1211
  not arguments.no_wait_for_completion)
1134
1212
 
1135
1213
  # And optionally remove the process and its inputs.
@@ -1153,7 +1231,8 @@ class Command(Base):
1153
1231
  for process_name in arguments.process:
1154
1232
  # Download the process.
1155
1233
  process = self.download_process_execution(organisation, process_name, arguments.inputs, arguments.outputs, arguments.storage,
1156
- arguments.intermediate, arguments.component, arguments.metrics, not arguments.no_wait_for_completion)
1234
+ arguments.intermediate, arguments.component, arguments.metrics, arguments.stac,
1235
+ not arguments.no_wait_for_completion)
1157
1236
 
1158
1237
  # And optionally remove the process and its inputs.
1159
1238
  if arguments.remove:
@@ -1190,13 +1269,14 @@ class Command(Base):
1190
1269
  version=i18n.t('command.version_content', version=fusion_platform.__version__, version_date=fusion_platform.__version_date__))
1191
1270
 
1192
1271
  subparsers.required = True
1272
+ parser_list = subparsers.add_parser(i18n.t('command.list.command'), help=i18n.t('command.list.help'))
1193
1273
  parser_display = subparsers.add_parser(i18n.t('command.display.command'), help=i18n.t('command.display.help'))
1194
1274
  parser_start = subparsers.add_parser(i18n.t('command.start.command'), help=i18n.t('command.start.help'))
1195
1275
  parser_define = subparsers.add_parser(i18n.t('command.define.command'), help=i18n.t('command.define.help'))
1196
1276
  parser_download = subparsers.add_parser(i18n.t('command.download.command'), help=i18n.t('command.download.help'))
1197
1277
 
1198
1278
  # Add in the common arguments.
1199
- for subparser in [parser_display, parser_start, parser_define, parser_download]:
1279
+ for subparser in [parser_list, parser_display, parser_start, parser_define, parser_download]:
1200
1280
  subparser.add_argument(i18n.t('command.deployment_short'), i18n.t('command.deployment_long'), help=i18n.t('command.deployment_help'),
1201
1281
  default=Command._DEPLOYMENT_PRODUCTION_IRL)
1202
1282
  subparser.add_argument(i18n.t('command.email_short'), i18n.t('command.email_long'), help=i18n.t('command.email_help'))
@@ -1209,9 +1289,10 @@ class Command(Base):
1209
1289
 
1210
1290
  # Start arguments.
1211
1291
  parser_start.add_argument(i18n.t('command.start.definition_long'), help=i18n.t('command.start.definition_help'), nargs='+')
1212
- parser_start.add_argument(i18n.t('command.start.input_list_short'), i18n.t('command.start.input_list_long'), help=i18n.t('command.start.input_list_help'),
1292
+ parser_start.add_argument(i18n.t('command.start.input_list_short'), i18n.t('command.start.input_list_long'),
1293
+ help=i18n.t('command.start.input_list_help'), nargs='*')
1294
+ parser_start.add_argument(i18n.t('command.start.options_short'), i18n.t('command.start.options_long'), help=i18n.t('command.start.options_help'),
1213
1295
  nargs='*')
1214
- parser_start.add_argument(i18n.t('command.start.options_short'), i18n.t('command.start.options_long'), help=i18n.t('command.start.options_help'), nargs='*')
1215
1296
  parser_start.add_argument(i18n.t('command.start.wait_for_start_short'), i18n.t('command.start.wait_for_start_long'),
1216
1297
  help=i18n.t('command.start.wait_for_start_help'), default=False, action="store_true")
1217
1298
  parser_start.add_argument(i18n.t('command.start.download_short'), i18n.t('command.start.download_long'), help=i18n.t('command.start.download_help'),
@@ -1232,6 +1313,8 @@ class Command(Base):
1232
1313
  nargs='*')
1233
1314
  parser_start.add_argument(i18n.t('command.start.no_wait_for_completion_short'), i18n.t('command.start.no_wait_for_completion_long'),
1234
1315
  help=i18n.t('command.start.no_wait_for_completion_help'), default=False, action="store_true")
1316
+ parser_start.add_argument(i18n.t('command.start.stac_only_short'), i18n.t('command.start.stac_only_long'), help=i18n.t('command.start.stac_only_help'),
1317
+ default=False, action="store_true")
1235
1318
 
1236
1319
  # Define arguments.
1237
1320
  parser_define.add_argument(i18n.t('command.define.process_long'), help=i18n.t('command.define.process_help'), nargs='+')
@@ -1254,10 +1337,12 @@ class Command(Base):
1254
1337
  help=i18n.t('command.start.intermediate_help'), default=False, action="store_true")
1255
1338
  parser_download.add_argument(i18n.t('command.start.metrics_short'), i18n.t('command.start.metrics_long'), help=i18n.t('command.start.metrics_help'),
1256
1339
  default=False, action="store_true")
1257
- parser_download.add_argument(i18n.t('command.start.component_short'), i18n.t('command.start.component_long'), help=i18n.t('command.start.component_help'),
1258
- nargs='*')
1340
+ parser_download.add_argument(i18n.t('command.start.component_short'), i18n.t('command.start.component_long'),
1341
+ help=i18n.t('command.start.component_help'), nargs='*')
1259
1342
  parser_download.add_argument(i18n.t('command.start.no_wait_for_completion_short'), i18n.t('command.start.no_wait_for_completion_long'),
1260
1343
  help=i18n.t('command.start.no_wait_for_completion_help'), default=False, action="store_true")
1344
+ parser_download.add_argument(i18n.t('command.start.stac_only_short'), i18n.t('command.start.stac_only_long'), help=i18n.t('command.start.stac_only_help'),
1345
+ default=False, action="store_true")
1261
1346
 
1262
1347
  return parser.parse_args()
1263
1348
 
@@ -1277,18 +1362,20 @@ class Command(Base):
1277
1362
  else:
1278
1363
  print(message)
1279
1364
 
1280
- def __print_execution(self, execution):
1365
+ def __print_execution(self, execution, label=None):
1281
1366
  """
1282
1367
  Prints an execution. This just shows the status in a simple format.
1283
1368
 
1284
1369
  Args:
1285
1370
  execution: The execution to be logged.
1371
+ label: Optional label for the execution. Default None.
1286
1372
  """
1373
+ label = '' if label is None else label
1287
1374
 
1288
1375
  # Form the status of the execution.
1289
1376
  status = ''
1290
- group = ''
1291
- abort_reason = i18n.t('command.log_execution_abort_reason', abort_reason=execution.abort_reason) if hasattr(execution, Model._FIELD_ABORT_REASON) else ''
1377
+ abort_reason = i18n.t('command.log_execution_abort_reason', abort_reason=execution.abort_reason) if hasattr(execution,
1378
+ Model._FIELD_ABORT_REASON) else ''
1292
1379
  exit_type = i18n.t('command.log_execution_exit_type', exit_type=execution.exit_type) if hasattr(execution, Model._FIELD_EXIT_TYPE) else ''
1293
1380
 
1294
1381
  if execution.success:
@@ -1310,16 +1397,10 @@ class Command(Base):
1310
1397
  ended_at = i18n.t('command.log_execution_ended_at', ended_at=execution.ended_at.strftime('%Y-%m-%d %H:%M:%S')) if hasattr(execution,
1311
1398
  Model._FIELD_ENDED_AT) else ''
1312
1399
  period = i18n.t('command.log_execution_period', started_at=started_at, ended_at=ended_at)
1313
- duration = i18n.t('command.log_execution_duration', minutes=round((execution.ended_at - execution.started_at).total_seconds() // 60)) if hasattr(execution,
1314
- Model._FIELD_ENDED_AT) and (
1315
- execution.ended_at is not None) else ''
1316
-
1317
- if (hasattr(execution, Model._FIELD_GROUP_ID) and (execution.group_id is not None)) and (
1318
- hasattr(execution, Model._FIELD_GROUP_INDEX) and (execution.group_index is not None)) and (
1319
- hasattr(execution, Model._FIELD_GROUP_COUNT) and (execution.group_count is not None)):
1320
- group = i18n.t('command.log_execution_group', group_id=execution.group_id, group_index=execution.group_index, group_count=execution.group_count)
1400
+ duration = i18n.t('command.log_execution_duration', minutes=round((execution.ended_at - execution.started_at).total_seconds() // 60)) if hasattr(
1401
+ execution, Model._FIELD_ENDED_AT) and (execution.ended_at is not None) else ''
1321
1402
 
1322
- self.__print(logging.INFO, i18n.t('command.log_execution', id=execution.id, period=period, status=status, duration=duration, group=group))
1403
+ self.__print(logging.INFO, i18n.t('command.log_execution', id=execution.id, label=label, period=period, status=status, duration=duration))
1323
1404
 
1324
1405
  def __print_process(self, process):
1325
1406
  """
@@ -1423,7 +1504,7 @@ class Command(Base):
1423
1504
 
1424
1505
  def __process_name_to_file_name(self, process_name):
1425
1506
  """
1426
- Converts a process name into a same file name (without extension).
1507
+ Converts a process name into a sane file name (without extension).
1427
1508
 
1428
1509
  Args:
1429
1510
  process_name: The process name to convert.
@@ -20,7 +20,10 @@ i18n.add_translation('command.no_such_organisation', 'No such organisation \'%{o
20
20
  i18n.add_translation('command.no_such_input', 'No such input \'%{input_name}\'', 'en')
21
21
  i18n.add_translation('command.no_executions', 'No executions for process \'%{process}\'', 'en')
22
22
  i18n.add_translation('command.invalid_login_response', 'Incorrect username or password', 'en')
23
- i18n.add_translation('command.log_execution_group', ' (group \'%{group_id}\' %{group_index} of %{group_count})', 'en')
23
+ i18n.add_translation('command.log_process_summary', '\'%{process}\': %{executions} execution(s) in %{groups} group(s) (%{progress}%% complete);%{group_progress}', 'en')
24
+ i18n.add_translation('command.log_process_group', ' %{group_number}: %{executions} execution(s) (%{progress}%% complete);', 'en')
25
+ i18n.add_translation('command.log_execution_group_index', ' %{group_index} of %{group_count}', 'en')
26
+ i18n.add_translation('command.log_execution_group_id', 'Group %{group_number} of %{group_count} (%{group_id}):', 'en')
24
27
  i18n.add_translation('command.log_execution_duration', ' - %{minutes} minute(s)', 'en')
25
28
  i18n.add_translation('command.log_execution_period', ' (%{started_at}%{ended_at})', 'en')
26
29
  i18n.add_translation('command.log_execution_ended_at', ' -> %{ended_at}', 'en')
@@ -39,7 +42,7 @@ i18n.add_translation('command.log_dispatchers', 'Dispatchers:', 'en')
39
42
  i18n.add_translation('command.log_options', 'Options:', 'en')
40
43
  i18n.add_translation('command.log_outputs', 'Outputs:', 'en')
41
44
  i18n.add_translation('command.log_inputs', 'Inputs:', 'en')
42
- i18n.add_translation('command.log_execution', 'Execution \'%{id}\'%{period}: %{status}%{duration}%{group}', 'en')
45
+ i18n.add_translation('command.log_execution', 'Execution%{label} \'%{id}\'%{period}: %{status}%{duration}', 'en')
43
46
  i18n.add_translation('command.log_service', 'Service \'%{service}\'', 'en')
44
47
  i18n.add_translation('command.log_process', 'Process \'%{process}\'', 'en')
45
48
  i18n.add_translation('command.log_field', '%{field}: %{value}', 'en')
@@ -65,7 +68,7 @@ i18n.add_translation('command.organisation', 'Organisation', 'en')
65
68
  i18n.add_translation('command.find_inputs', 'Finding %{inputs} input file(s)...', 'en')
66
69
  i18n.add_translation('command.executing', 'Executing...', 'en')
67
70
  i18n.add_translation('command.email', 'Email', 'en')
68
- i18n.add_translation('command.download_process_execution', 'Downloading \'%{process}\' (inputs %{inputs}, outputs %{outputs}, storage %{storage}, intermediate %{intermediate}, metrics %{metrics}, components %{components})', 'en')
71
+ i18n.add_translation('command.download_process_execution', 'Downloading \'%{process}\' (inputs %{inputs}, outputs %{outputs}, storage %{storage}, intermediate %{intermediate}, STAC only %{stac_only}, metrics %{metrics}, components %{components})', 'en')
69
72
  i18n.add_translation('command.download_process', 'Downloading process \'%{process}\' to %{output}', 'en')
70
73
  i18n.add_translation('command.download_files', 'Downloading %{files} file(s)...', 'en')
71
74
  i18n.add_translation('command.download_executions', 'Gathering files%{wait} for %{executions} execution(s)...', 'en')
@@ -83,9 +86,12 @@ i18n.add_translation('command.define.process_help', 'the name of the process, th
83
86
  i18n.add_translation('command.define.process_long', 'process', 'en')
84
87
  i18n.add_translation('command.define.help', 'outputs a YAML file for each existing process', 'en')
85
88
  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')
89
+ i18n.add_translation('command.start.no_wait_for_completion_help', 'do not wait for the execution to complete (default %%(default)s)', 'en')
87
90
  i18n.add_translation('command.start.no_wait_for_completion_long', '--no_wait_for_completion', 'en')
88
91
  i18n.add_translation('command.start.no_wait_for_completion_short', '-n', 'en')
92
+ i18n.add_translation('command.start.stac_only_help', 'only download the STAC metadata for the files (default %%(default)s)', 'en')
93
+ i18n.add_translation('command.start.stac_only_long', '--stac', 'en')
94
+ i18n.add_translation('command.start.stac_only_short', '-a', 'en')
89
95
  i18n.add_translation('command.start.metrics_help', 'save process metrics to file (default %%(default)s)', 'en')
90
96
  i18n.add_translation('command.start.metrics_long', '--metrics', 'en')
91
97
  i18n.add_translation('command.start.metrics_short', '-t', 'en')
@@ -127,6 +133,8 @@ i18n.add_translation('command.display.service_or_process_help', 'the names of th
127
133
  i18n.add_translation('command.display.service_or_process_long', 'process_or_service', 'en')
128
134
  i18n.add_translation('command.display.help', 'displays service information or a configured process', 'en')
129
135
  i18n.add_translation('command.display.command', 'display', 'en')
136
+ i18n.add_translation('command.list.help', 'lists configured processes', 'en')
137
+ i18n.add_translation('command.list.command', 'list', 'en')
130
138
  i18n.add_translation('command.subparser', 'displays a service or process, or starts, defines or downloads a process', 'en')
131
139
  i18n.add_translation('command.version_content', '%%(prog)s %{version} (%{version_date})', 'en')
132
140
  i18n.add_translation('command.version_help', 'show the version information and exit', 'en')
@@ -147,6 +155,7 @@ i18n.add_translation('command.deployment_short', '-y', 'en')
147
155
  i18n.add_translation('command.epilog', '''
148
156
  For more detailed options, use:
149
157
 
158
+ fusion_platform list --help
150
159
  fusion_platform display --help
151
160
  fusion_platform start --help
152
161
  fusion_platform define --help
@@ -158,7 +167,11 @@ Use this command to display service information and processes, and to start, dow
158
167
  defined by a YAML file.
159
168
 
160
169
  Usage:
161
- The following will display information about one or more services or processes from the given \'service_or_process_name\'.
170
+ The following will list all currently configured processes:
171
+
172
+ fusion_platform list
173
+
174
+ The following will display information about one or more services or processes from the given \'service_or_process_name\':
162
175
 
163
176
  fusion_platform display <service_or_process_name> ...
164
177
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fusion-platform-python-sdk
3
- Version: 1.17.5
3
+ Version: 1.17.9
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
@@ -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.17.5',
22
+ version='1.17.9',
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',
@@ -66,7 +66,7 @@ class TestSession(CustomTestCase):
66
66
  session = Session()
67
67
 
68
68
  with pytest.raises(RequestError):
69
- session.download_file('https://www.d-cat.co.uk/.well-known/acme-challenge/test.html', destination)
69
+ session.download_file('https://www.d-cat.co.uk/assets/test.html', destination)
70
70
 
71
71
  self.assertFalse(os.path.exists(destination))
72
72
  session.download_file('https://www.d-cat.co.uk/home', destination, self.download_callback)