fusion-platform-python-sdk 1.12.2__tar.gz → 1.13.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.
- {fusion-platform-python-sdk-1.12.2/fusion_platform_python_sdk.egg-info → fusion-platform-python-sdk-1.13.5}/PKG-INFO +1 -1
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/__init__.py +5 -2
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/common/utilities.py +44 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/fusion_platform_sdk.pdf +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/data.py +87 -6
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/data_file.py +177 -1
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/model.py +25 -5
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/organisation.py +4 -3
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/process.md +2 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/process.py +12 -5
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/service.py +2 -1
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/translations.py +2 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5/fusion_platform_python_sdk.egg-info}/PKG-INFO +1 -1
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/setup.py +1 -1
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/LICENSE.txt +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/MANIFEST.in +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/README.md +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/__main__.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/base.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/command.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/common/__init__.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/common/raise_thread.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/detailed_example.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/documentation.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/glasgow.geojson +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/lake_district.geojson +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/localisations.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/localise.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/__init__.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/credit.md +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/credit.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/data.md +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/data_file.md +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/fields.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/organisation.md +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/process_execution.md +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/process_execution.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/process_service_execution.md +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/process_service_execution.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/process_service_execution_log.md +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/process_service_execution_log.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/service.md +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/user.md +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/models/user.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/quick_example.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/session.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform_python_sdk.egg-info/SOURCES.txt +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform_python_sdk.egg-info/dependency_links.txt +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform_python_sdk.egg-info/entry_points.txt +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform_python_sdk.egg-info/requires.txt +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform_python_sdk.egg-info/top_level.txt +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/setup.cfg +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/tests/test_base.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/tests/test_command.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/tests/test_init.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/tests/test_localise.py +0 -0
- {fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/tests/test_session.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fusion-platform-python-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.13.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
|
{fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/__init__.py
RENAMED
|
@@ -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.
|
|
15
|
-
__version_date__ = '2024-
|
|
14
|
+
__version__ = '1.13.5'
|
|
15
|
+
__version_date__ = '2024-08-08T11:19:48Z'
|
|
16
16
|
|
|
17
17
|
# Exclude certain sub-modules from documentation.
|
|
18
18
|
# @formatter:off
|
|
@@ -102,6 +102,9 @@ FILE_TYPE_CSV = 'CSV'
|
|
|
102
102
|
FILE_TYPE_ESRI_SHAPEFILE = 'ESRI Shapefile'
|
|
103
103
|
FILE_TYPE_JPEG = 'JPEG'
|
|
104
104
|
FILE_TYPE_PNG = 'PNG'
|
|
105
|
+
FILE_TYPE_PDF = 'PDF'
|
|
106
|
+
FILE_TYPE_GZIP = 'GZIP'
|
|
107
|
+
FILE_TYPE_ZIP = 'ZIP'
|
|
105
108
|
FILE_TYPE_OTHER = 'Other'
|
|
106
109
|
|
|
107
110
|
# Run types.
|
|
@@ -8,9 +8,40 @@ author: Matthew Casey
|
|
|
8
8
|
|
|
9
9
|
from datetime import date, datetime, time, timezone
|
|
10
10
|
from decimal import Decimal
|
|
11
|
+
import re
|
|
11
12
|
from types import MappingProxyType
|
|
12
13
|
|
|
13
14
|
|
|
15
|
+
def datetime_parse(string_or_blank):
|
|
16
|
+
"""
|
|
17
|
+
Attempts to parse an ISO8601 datetime from a string using common formats.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
string_or_blank: The datetime to parse, or blank.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
The parsed datetime or None if not parsable or blank.
|
|
24
|
+
"""
|
|
25
|
+
parsed = None
|
|
26
|
+
|
|
27
|
+
if not string_blank(string_or_blank):
|
|
28
|
+
# Strip out all optional delimiters and then attempt to parse with and without the "Z".
|
|
29
|
+
stripped = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', str(string_or_blank))
|
|
30
|
+
formats = ['%Y%m%dT%H%M%S.%f', '%Y%m%dT%H%M%S.%f%z', '%Y%m%dT%H%M%S', '%Y%m%dT%H%M%S%z']
|
|
31
|
+
|
|
32
|
+
for format in formats:
|
|
33
|
+
try:
|
|
34
|
+
parsed = datetime.strptime(stripped, format)
|
|
35
|
+
parsed = parsed.astimezone(timezone.utc) if parsed.tzinfo is None else parsed # Assume UTC if no timezone is provided.
|
|
36
|
+
except:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
if parsed is not None:
|
|
40
|
+
break
|
|
41
|
+
|
|
42
|
+
return parsed
|
|
43
|
+
|
|
44
|
+
|
|
14
45
|
def dict_nested_get(dictionary_or_value, keys, default=None):
|
|
15
46
|
"""
|
|
16
47
|
Performs a dictionary.get(key, default) using the supplied list of keys assuming that each successive key is nested.
|
|
@@ -60,6 +91,19 @@ def json_default(value):
|
|
|
60
91
|
return str(value)
|
|
61
92
|
|
|
62
93
|
|
|
94
|
+
def string_blank(string_or_blank):
|
|
95
|
+
"""
|
|
96
|
+
Checks if a string is None or blank.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
string_or_blank: The string to check.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
True if the string is None, blank ("") or just made up with spaces.
|
|
103
|
+
"""
|
|
104
|
+
return (string_or_blank is None) or (not bool(string_or_blank.strip()))
|
|
105
|
+
|
|
106
|
+
|
|
63
107
|
def string_camel_to_underscore(string):
|
|
64
108
|
"""
|
|
65
109
|
Converts a camel case string to underscore case.
|
|
Binary file
|
|
@@ -44,10 +44,12 @@ class DataSchema(Schema):
|
|
|
44
44
|
bounds = fields.List(fields.Decimal(required=True), allow_none=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
45
45
|
file_with_preview = fields.UUID(allow_none=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
46
46
|
|
|
47
|
+
# Removed uploaded.
|
|
47
48
|
uploaded_organisation_id = fields.UUID(allow_none=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
48
49
|
deletable = fields.String(allow_none=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
49
50
|
|
|
50
51
|
# Removed creator.
|
|
52
|
+
|
|
51
53
|
# Removed search.
|
|
52
54
|
|
|
53
55
|
class Meta:
|
|
@@ -213,7 +215,7 @@ class Data(Model):
|
|
|
213
215
|
# Return the copy.
|
|
214
216
|
return self.__class__._model_from_api_id(self._session, organisation_id=self.organisation_id, id=copy_data_id)
|
|
215
217
|
|
|
216
|
-
def _create(self, name, file_type, files, wait=False):
|
|
218
|
+
def _create(self, name, file_type, files, wait=False, **kwargs):
|
|
217
219
|
"""
|
|
218
220
|
Attempts to create the model object with the given values. This assumes the model template has been loaded first using the _new method, and that the model
|
|
219
221
|
is then created using a POST RESTful request. This assumes that the post body contains key names which include the name of the model class.
|
|
@@ -221,10 +223,11 @@ class Data(Model):
|
|
|
221
223
|
This method is overridden to also upload the corresponding files and optionally wait for the upload and analysis to complete.
|
|
222
224
|
|
|
223
225
|
Args:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
name: The name of the data object.
|
|
227
|
+
file_type: The type of files that the data object will hold.
|
|
228
|
+
files: The list of file paths to be uploaded.
|
|
229
|
+
wait: Optionally wait for the upload and analysis to complete? Default False.
|
|
230
|
+
kwargs: The model attributes which override those already in the template.
|
|
228
231
|
|
|
229
232
|
Returns:
|
|
230
233
|
The created data object.
|
|
@@ -234,7 +237,7 @@ class Data(Model):
|
|
|
234
237
|
ModelError: if the model could not be created and validated by the Fusion Platform<sup>®</sup>.
|
|
235
238
|
"""
|
|
236
239
|
# Use the super method to create the data item with the correct attributes. This will raise an exception if anything fails.
|
|
237
|
-
super(Data, self)._create(name=name)
|
|
240
|
+
super(Data, self)._create(name=name, **kwargs)
|
|
238
241
|
|
|
239
242
|
# Add each of the files, assuming that each is of the same file type, and start its upload in a thread.
|
|
240
243
|
try:
|
|
@@ -341,3 +344,81 @@ class Data(Model):
|
|
|
341
344
|
ModelError: if a model could not be loaded or validated from the Fusion Platform<sup>®</sup>.
|
|
342
345
|
"""
|
|
343
346
|
return DataFile._models_from_api_path(self._session, self._get_path(self.__class__._PATH_FILES), organisation_id=self.organisation_id)
|
|
347
|
+
|
|
348
|
+
def get_stac_collection(self, items, collection_file_name=DataFile._STAC_COLLECTION_FILE_NAME, owner=None, created_at=None, detail=None):
|
|
349
|
+
"""
|
|
350
|
+
Converts the data representation into a STAC collection with the specified STAC item file names.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
items: The list of items to be put into the collection. This should be a tuple of (STAC item, item_file_name).
|
|
354
|
+
collection_file_name: The optional collection file name.
|
|
355
|
+
owner: The optional owner of the collection. Default None.
|
|
356
|
+
created_at: Optionally when the collection was created. Default None.
|
|
357
|
+
detail: The optional collection detail as a dictionary. Default None.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
A tuple of the STAC collection definition as a dictionary and the collection file name used in the definition.
|
|
361
|
+
"""
|
|
362
|
+
stac_extensions = []
|
|
363
|
+
|
|
364
|
+
# Form the providers.
|
|
365
|
+
provider = {
|
|
366
|
+
'name': i18n.t('fusion_platform.organisation'),
|
|
367
|
+
'roles': ['producer', 'licensor'],
|
|
368
|
+
'url': i18n.t('fusion_platform.url')
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
# Add in the optional processing information, if any.
|
|
372
|
+
if owner is not None:
|
|
373
|
+
provider['processing:lineage'] = owner
|
|
374
|
+
|
|
375
|
+
if created_at is not None:
|
|
376
|
+
provider['processing.datetime'] = created_at
|
|
377
|
+
|
|
378
|
+
if detail is not None:
|
|
379
|
+
provider['processing:software'] = detail
|
|
380
|
+
|
|
381
|
+
if (owner is not None) or (created_at is not None) or (detail is not None):
|
|
382
|
+
stac_extensions.append('https://stac-extensions.github.io/processing/v1.2.0/schema.json')
|
|
383
|
+
|
|
384
|
+
# Form the links.
|
|
385
|
+
links = [
|
|
386
|
+
{'rel': 'self', 'href': collection_file_name, 'type': 'application/json'},
|
|
387
|
+
{'rel': 'root', 'href': collection_file_name, 'type': 'application/json'},
|
|
388
|
+
]
|
|
389
|
+
|
|
390
|
+
# Add in the files calculating the maximal spatial and temporal extents.
|
|
391
|
+
bbox = None
|
|
392
|
+
interval_start = None
|
|
393
|
+
interval_end = None
|
|
394
|
+
|
|
395
|
+
for item, item_file_name in items:
|
|
396
|
+
# Calculate the spatial extent.
|
|
397
|
+
item_bbox = item.get('bbox')
|
|
398
|
+
|
|
399
|
+
if (bbox is None) or (len(bbox) < 4):
|
|
400
|
+
bbox = item_bbox
|
|
401
|
+
|
|
402
|
+
if (bbox is not None) and (len(bbox) >= 4) and (item_bbox is not None) and (len(item_bbox) >= 4):
|
|
403
|
+
bbox = [min(bbox[0], item_bbox[0]), min(bbox[1], item_bbox[1]), max(bbox[2], item_bbox[2]), max(bbox[3], item_bbox[3])]
|
|
404
|
+
|
|
405
|
+
# Calculate the temporal extent.
|
|
406
|
+
item_datetime = dict_nested_get(item, ['properties', 'datetime'])
|
|
407
|
+
interval_start = item_datetime if interval_start is None else min(interval_start, item_datetime)
|
|
408
|
+
interval_end = item_datetime if interval_end is None else max(interval_end, item_datetime)
|
|
409
|
+
|
|
410
|
+
# Add in the link.
|
|
411
|
+
links.append({'rel': 'item', 'href': item_file_name, 'type': 'application/geo+json'})
|
|
412
|
+
|
|
413
|
+
# Build the STAC collection.
|
|
414
|
+
return {
|
|
415
|
+
'type': 'Collection',
|
|
416
|
+
'stac_version': '1.0.0',
|
|
417
|
+
'stac_extensions': stac_extensions,
|
|
418
|
+
'id': self.id,
|
|
419
|
+
'description': self.name,
|
|
420
|
+
'license': 'proprietary',
|
|
421
|
+
'providers': [provider],
|
|
422
|
+
'extent': {'spatial': {'bbox': [bbox]}, 'temporal': {'interval': [[interval_start, interval_end]]}},
|
|
423
|
+
'links': links
|
|
424
|
+
}, collection_file_name
|
|
@@ -8,9 +8,11 @@ author: Matthew Casey
|
|
|
8
8
|
|
|
9
9
|
import i18n
|
|
10
10
|
from marshmallow import Schema, EXCLUDE
|
|
11
|
+
import os
|
|
11
12
|
|
|
13
|
+
import fusion_platform
|
|
12
14
|
from fusion_platform.common.raise_thread import RaiseThread
|
|
13
|
-
from fusion_platform.common.utilities import dict_nested_get
|
|
15
|
+
from fusion_platform.common.utilities import datetime_parse, dict_nested_get
|
|
14
16
|
from fusion_platform.models import fields
|
|
15
17
|
from fusion_platform.models.model import Model, ModelError
|
|
16
18
|
|
|
@@ -65,6 +67,7 @@ class DataFileSchema(Schema):
|
|
|
65
67
|
organisation_id = fields.UUID(allow_none=True, metadata={'read_only': True, 'title': i18n.t('models.data_file.organisation_id.title'), 'description': i18n.t(
|
|
66
68
|
'models.data_file.organisation_id.description')}) # Added parameter so that it can be used.
|
|
67
69
|
data_id = fields.UUID(required=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
70
|
+
# Removed organisation_id_file_type.
|
|
68
71
|
|
|
69
72
|
file_id = fields.UUID(required=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
70
73
|
preview_file_id = fields.UUID(allow_none=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
@@ -120,6 +123,34 @@ class DataFile(Model):
|
|
|
120
123
|
# Plus query parameters.
|
|
121
124
|
_QUERY_PREVIEW = 'preview'
|
|
122
125
|
|
|
126
|
+
# Geospatial file types.
|
|
127
|
+
_GEOSPATIAL_RASTER = 'raster'
|
|
128
|
+
_GEOSPATIAL_VECTOR = 'vector'
|
|
129
|
+
|
|
130
|
+
# File type information - a tuple of typical extension and whether the file type is a geospatial vector, raster or not.
|
|
131
|
+
# @formatter:off
|
|
132
|
+
_FILE_TYPES = {
|
|
133
|
+
fusion_platform.FILE_TYPE_GEOTIFF: ('tif', _GEOSPATIAL_RASTER),
|
|
134
|
+
fusion_platform.FILE_TYPE_JPEG2000: ('jpeg', _GEOSPATIAL_RASTER),
|
|
135
|
+
fusion_platform.FILE_TYPE_DEM: ('dem', _GEOSPATIAL_RASTER),
|
|
136
|
+
fusion_platform.FILE_TYPE_GEOJSON: ('json', _GEOSPATIAL_VECTOR),
|
|
137
|
+
fusion_platform.FILE_TYPE_KML: ('kml', _GEOSPATIAL_VECTOR),
|
|
138
|
+
fusion_platform.FILE_TYPE_KMZ: ('kmz', _GEOSPATIAL_VECTOR),
|
|
139
|
+
fusion_platform.FILE_TYPE_CSV: ('csv', None),
|
|
140
|
+
fusion_platform.FILE_TYPE_ESRI_SHAPEFILE: ('zip', _GEOSPATIAL_VECTOR),
|
|
141
|
+
fusion_platform.FILE_TYPE_JPEG: ('jpg', None),
|
|
142
|
+
fusion_platform.FILE_TYPE_PNG: ('png', None),
|
|
143
|
+
fusion_platform.FILE_TYPE_PDF: ('pdf', None),
|
|
144
|
+
fusion_platform.FILE_TYPE_GZIP: ('gz', None),
|
|
145
|
+
fusion_platform.FILE_TYPE_ZIP: ('zip', None),
|
|
146
|
+
fusion_platform.FILE_TYPE_OTHER: ('*', None),
|
|
147
|
+
}
|
|
148
|
+
# @formatter:on
|
|
149
|
+
|
|
150
|
+
# STAC file name constants. We do not use ".json" because this is used by services and specific file types. Similarly, the name starts with "." to hide it.
|
|
151
|
+
_STAC_COLLECTION_FILE_NAME = '.collection.stac'
|
|
152
|
+
_STAC_ITEM_FILE_NAME_FORMAT = '.{file_name}.stac'
|
|
153
|
+
|
|
123
154
|
def __init__(self, session):
|
|
124
155
|
"""
|
|
125
156
|
Initialises the object.
|
|
@@ -249,3 +280,148 @@ class DataFile(Model):
|
|
|
249
280
|
raise ModelError(i18n.t('models.data_file.failed_download_url'))
|
|
250
281
|
|
|
251
282
|
return url
|
|
283
|
+
|
|
284
|
+
def get_stac_item(self, item_file_name_format=_STAC_ITEM_FILE_NAME_FORMAT, collection_file_name=_STAC_COLLECTION_FILE_NAME):
|
|
285
|
+
"""
|
|
286
|
+
Converts the data file representation into a STAC item.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
item_file_name_format: The optional item file name format string, where the "file_name" placeholder is replaced with the item's file name.
|
|
290
|
+
collection_file_name: The optional owning collection file name.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
A tuple of the STAC item definition as a dictionary and the item file name used in the definition.
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
# Obtain all the relevant information.
|
|
297
|
+
bounds = self.bounds if hasattr(self, Model._FIELD_BOUNDS) else None
|
|
298
|
+
crs = self.crs if hasattr(self, Model._FIELD_CRS) else None
|
|
299
|
+
file_name = self.file_name if hasattr(self, Model._FIELD_FILE_NAME) else None
|
|
300
|
+
size = self.size if hasattr(self, Model._FIELD_SIZE) else None
|
|
301
|
+
|
|
302
|
+
# Attempt to find the file type to get its extension and geospatial type. Default to "Other" if not found.
|
|
303
|
+
found_file_type = DataFile._FILE_TYPES.get(self.file_type)
|
|
304
|
+
|
|
305
|
+
if found_file_type is None:
|
|
306
|
+
found_file_type = DataFile._FILE_TYPES.get(fusion_platform.FILE_TYPE_OTHER)
|
|
307
|
+
|
|
308
|
+
geospatial = found_file_type[1]
|
|
309
|
+
|
|
310
|
+
# Get the file extension.
|
|
311
|
+
extension = os.path.splitext(file_name)[1] if file_name is not None else None
|
|
312
|
+
extension = found_file_type[0] if extension is None else extension
|
|
313
|
+
|
|
314
|
+
if (extension is None) or (extension == '*'):
|
|
315
|
+
extension = ''
|
|
316
|
+
else:
|
|
317
|
+
extension = f".{extension}" if not extension.startswith('.') else extension
|
|
318
|
+
|
|
319
|
+
# Work out a suitable name for the file if one is not available.
|
|
320
|
+
if file_name is None:
|
|
321
|
+
file_name = f"{self.id}{extension}"
|
|
322
|
+
|
|
323
|
+
item_file_name = item_file_name_format.format(file_name=file_name)
|
|
324
|
+
|
|
325
|
+
# Start to build the STAC extensions list.
|
|
326
|
+
stac_extensions = ['https://stac-extensions.github.io/processing/v1.2.0/schema.json'] # For properties#processing.[software,version].
|
|
327
|
+
|
|
328
|
+
# Get the bounding box and geometry, if there is one.
|
|
329
|
+
bbox = []
|
|
330
|
+
geometry = {}
|
|
331
|
+
|
|
332
|
+
if (bounds is not None) and (len(bounds) >= 4):
|
|
333
|
+
bbox = bounds
|
|
334
|
+
geometry = {'type': 'Polygon', 'coordinates': [[[bbox[0], bbox[1]], [bbox[2], bbox[1]], [bbox[2], bbox[3]], [bbox[0], bbox[3]], [bbox[0], bbox[1]]]]}
|
|
335
|
+
|
|
336
|
+
# Get the datetime from the filename, if possible. We try various iterations of sections the file name until we find something.
|
|
337
|
+
for file_part in file_name.split('_'):
|
|
338
|
+
extracted_datetime = datetime_parse(file_part) or datetime_parse(f"{file_part}T00:00:00Z") # Assume UTC.
|
|
339
|
+
|
|
340
|
+
if extracted_datetime is not None:
|
|
341
|
+
break
|
|
342
|
+
|
|
343
|
+
if extracted_datetime is None:
|
|
344
|
+
extracted_datetime = self.created_at
|
|
345
|
+
|
|
346
|
+
# Start to build the properties.
|
|
347
|
+
properties = {'processing.software': i18n.t('fusion_platform.sdk'), 'processing.version': fusion_platform.__version__, 'datetime': extracted_datetime}
|
|
348
|
+
|
|
349
|
+
# Add in the CRS, if there is one.
|
|
350
|
+
if crs is not None:
|
|
351
|
+
properties['proj:epsg'] = crs
|
|
352
|
+
stac_extensions.append('https://stac-extensions.github.io/projection/v1.1.0/schema.json') # For properties#proj:epsg.
|
|
353
|
+
|
|
354
|
+
# Build the links.
|
|
355
|
+
links = [{'rel': 'self', 'href': item_file_name, 'type': 'application/json'}]
|
|
356
|
+
|
|
357
|
+
# Add in the optional owning collection.
|
|
358
|
+
if collection_file_name is not None:
|
|
359
|
+
links.append({'rel': 'root', 'href': collection_file_name, 'type': 'application/json'})
|
|
360
|
+
links.append({'rel': 'parent', 'href': collection_file_name, 'type': 'application/json'})
|
|
361
|
+
|
|
362
|
+
# Start to build the asset from the associated file.
|
|
363
|
+
asset = {'href': file_name, 'roles': []}
|
|
364
|
+
|
|
365
|
+
# Extract out the relevant values depending upon the file type.
|
|
366
|
+
raster_bands = None
|
|
367
|
+
eo_bands = None
|
|
368
|
+
|
|
369
|
+
if (geospatial == DataFile._GEOSPATIAL_RASTER) and hasattr(self, Model._FIELD_SELECTORS) and (self.selectors is not None):
|
|
370
|
+
for selector in self.selectors:
|
|
371
|
+
raster_band = {}
|
|
372
|
+
eo_band = {}
|
|
373
|
+
|
|
374
|
+
if hasattr(self, Model._FIELD_RESOLUTION):
|
|
375
|
+
raster_band['spatial_resolution'] = self.resolution
|
|
376
|
+
|
|
377
|
+
if selector.get(Model._FIELD_UNIT) is not None:
|
|
378
|
+
raster_band['unit'] = selector.get(Model._FIELD_UNIT)
|
|
379
|
+
|
|
380
|
+
if (selector.get(Model._FIELD_MINIMUM) is not None) and (selector.get(Model._FIELD_MAXIMUM) is not None) and (
|
|
381
|
+
selector.get(Model._FIELD_MEAN) is not None) and (selector.get(Model._FIELD_SD) is not None):
|
|
382
|
+
raster_band['statistics'] = {
|
|
383
|
+
'minimum': selector.get(Model._FIELD_MINIMUM),
|
|
384
|
+
'maximum': selector.get(Model._FIELD_MAXIMUM),
|
|
385
|
+
'mean': selector.get(Model._FIELD_MEAN),
|
|
386
|
+
'stddev': selector.get(Model._FIELD_SD)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (selector.get(Model._FIELD_HISTOGRAM_MINIMUM) is not None) and (selector.get(Model._FIELD_HISTOGRAM_MAXIMUM) is not None) and (
|
|
390
|
+
selector.get(Model._FIELD_HISTOGRAM) is not None):
|
|
391
|
+
raster_band['histogram'] = {
|
|
392
|
+
'count': len(selector.get(Model._FIELD_HISTOGRAM)),
|
|
393
|
+
'min': selector.get(Model._FIELD_HISTOGRAM_MINIMUM),
|
|
394
|
+
'max': selector.get(Model._FIELD_HISTOGRAM_MAXIMUM),
|
|
395
|
+
'buckets': selector.get(Model._FIELD_HISTOGRAM),
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if selector.get(Model._FIELD_CATEGORY) is not None:
|
|
399
|
+
eo_band['name'] = selector.get(Model._FIELD_CATEGORY)
|
|
400
|
+
|
|
401
|
+
raster_bands = [raster_band] if raster_bands is None else [raster_band] + raster_bands
|
|
402
|
+
eo_bands = [eo_band] if eo_bands is None else [eo_band] + eo_bands
|
|
403
|
+
|
|
404
|
+
stac_extensions.append('https://stac-extensions.github.io/eo/v1.1.0/schema.json') # For assets#eo:bands.
|
|
405
|
+
stac_extensions.append('https://stac-extensions.github.io/raster/v1.1.0/schema.json') # For assets#raster:bands.
|
|
406
|
+
|
|
407
|
+
if raster_bands is not None:
|
|
408
|
+
asset['raster:bands'] = raster_bands
|
|
409
|
+
asset['eo:bands'] = eo_bands
|
|
410
|
+
|
|
411
|
+
# Add in the file size, if there is one.
|
|
412
|
+
if size is not None:
|
|
413
|
+
asset['file:size'] = size
|
|
414
|
+
stac_extensions.append('https://stac-extensions.github.io/file/v2.1.0/schema.json') # For assets#file:size.
|
|
415
|
+
|
|
416
|
+
# Build the STAC item.
|
|
417
|
+
return {
|
|
418
|
+
'type': 'Feature',
|
|
419
|
+
'stac_version': '1.0.0',
|
|
420
|
+
'stac_extensions': stac_extensions,
|
|
421
|
+
'id': self.id,
|
|
422
|
+
'bbox': bbox,
|
|
423
|
+
'geometry': geometry,
|
|
424
|
+
'properties': properties,
|
|
425
|
+
'links': links,
|
|
426
|
+
'assets': {'asset': asset}
|
|
427
|
+
}, item_file_name
|
|
@@ -41,8 +41,11 @@ class Model(Base):
|
|
|
41
41
|
|
|
42
42
|
# Useful fields and templates.
|
|
43
43
|
_FIELD_AVAILABLE_DISPATCHERS = 'available_dispatchers'
|
|
44
|
+
_FIELD_BOUNDS = 'bounds'
|
|
45
|
+
_FIELD_CATEGORY = 'category'
|
|
44
46
|
_FIELD_CONSTRAINED_NAMES = 'constrained_names'
|
|
45
47
|
_FIELD_CONSTRAINED_VALUES = 'constrained_values'
|
|
48
|
+
_FIELD_CRS = 'crs'
|
|
46
49
|
_FIELD_DATA_TYPE = 'data_type'
|
|
47
50
|
_FIELD_DISPATCH_INTERMEDIATE = 'dispatch_intermediate'
|
|
48
51
|
_FIELD_DISPATCHERS = 'dispatchers'
|
|
@@ -50,23 +53,37 @@ class Model(Base):
|
|
|
50
53
|
_FIELD_DOCUMENTATION_DESCRIPTION = 'documentation_description'
|
|
51
54
|
_FIELD_ERROR = 'error'
|
|
52
55
|
_FIELD_EXECUTIONS = 'executions'
|
|
56
|
+
_FIELD_EXTENSIONS = 'extensions'
|
|
57
|
+
_FIELD_FILE_NAME = 'file_name'
|
|
53
58
|
_FIELD_FILE_TYPE = 'file_type'
|
|
59
|
+
_FIELD_GEOSPATIAL = 'geospatial'
|
|
54
60
|
_FIELD_GROUP_COUNT = 'group_count'
|
|
55
61
|
_FIELD_GROUP_ID = 'group_id'
|
|
56
62
|
_FIELD_GROUP_INDEX = 'group_index'
|
|
57
63
|
_FIELD_HAS_EXECUTIONS = 'has_executions'
|
|
64
|
+
_FIELD_HISTOGRAM = 'histogram'
|
|
65
|
+
_FIELD_HISTOGRAM_MINIMUM = 'histogram_minimum'
|
|
66
|
+
_FIELD_HISTOGRAM_MAXIMUM = 'histogram_maximum'
|
|
58
67
|
_FIELD_ID = 'id'
|
|
59
68
|
_FIELD_INPUT = 'input'
|
|
60
69
|
_FIELD_INPUTS = 'inputs'
|
|
61
70
|
_FIELD_KEYWORDS = 'keywords'
|
|
71
|
+
_FIELD_MAXIMUM = 'maximum'
|
|
72
|
+
_FIELD_MEAN = 'mean'
|
|
73
|
+
_FIELD_MINIMUM = 'minimum'
|
|
62
74
|
_FIELD_NAME = 'name'
|
|
63
75
|
_FIELD_OPTIONS = 'options'
|
|
64
76
|
_FIELD_OUTPUTS = 'outputs'
|
|
65
77
|
_FIELD_PROCESS_STATUS = 'process_status'
|
|
78
|
+
_FIELD_RESOLUTION = 'resolution'
|
|
79
|
+
_FIELD_SD = 'sd'
|
|
80
|
+
_FIELD_SELECTOR = 'selector'
|
|
81
|
+
_FIELD_SELECTORS = 'selectors'
|
|
66
82
|
_FIELD_SSD_ID = 'ssd_id'
|
|
67
83
|
_FIELD_STARTED_AT = 'started_at'
|
|
68
84
|
_FIELD_PUBLISHABLE = 'publishable'
|
|
69
85
|
_FIELD_SIZE = 'size'
|
|
86
|
+
_FIELD_UNIT = 'unit'
|
|
70
87
|
_FIELD_VALIDATION = 'validation'
|
|
71
88
|
_FIELD_VALUE = 'value'
|
|
72
89
|
|
|
@@ -148,11 +165,12 @@ class Model(Base):
|
|
|
148
165
|
|
|
149
166
|
return attributes
|
|
150
167
|
|
|
151
|
-
def __build_body(self, **kwargs):
|
|
168
|
+
def __build_body(self, create=False, **kwargs):
|
|
152
169
|
"""
|
|
153
|
-
Builds a request body suitable for sending to the API. All current model attributes are included, with the keywords arguments
|
|
170
|
+
Builds a request body suitable for sending to the API. All current model attributes are included, with the keywords arguments overriding their value.
|
|
154
171
|
|
|
155
172
|
Args:
|
|
173
|
+
create: Whether the body is being built for a create request. This allows the id to be overridden.
|
|
156
174
|
kwargs: The model attributes which override those already in the template.
|
|
157
175
|
|
|
158
176
|
Returns:
|
|
@@ -167,9 +185,11 @@ class Model(Base):
|
|
|
167
185
|
if (self.__model is not None) and (key in self.__model):
|
|
168
186
|
body[Model._REQUEST_KEY_NAME.format(model=model_name, key=key)] = self.__model.get(key)
|
|
169
187
|
|
|
170
|
-
# Now override any of the passed in attributes, ignoring anything which is read-only or hidden.
|
|
188
|
+
# Now override any of the passed in attributes, ignoring anything which is read-only or hidden. The exception here is the id, which can be overridden
|
|
189
|
+
# during a create operation.
|
|
171
190
|
for key in kwargs:
|
|
172
|
-
if (
|
|
191
|
+
if (create and (key == Model._FIELD_ID)) or (
|
|
192
|
+
(Model._METADATA_READ_ONLY not in schema.fields[key].metadata) and (Model._METADATA_HIDE not in schema.fields[key].metadata)):
|
|
173
193
|
body[Model._REQUEST_KEY_NAME.format(model=model_name, key=key)] = kwargs[key]
|
|
174
194
|
|
|
175
195
|
return body
|
|
@@ -211,7 +231,7 @@ class Model(Base):
|
|
|
211
231
|
raise ModelError(i18n.t('models.model.already_persisted'))
|
|
212
232
|
|
|
213
233
|
# Form the post body dictionary from the current object and the keyword arguments.
|
|
214
|
-
body = self.__build_body(**kwargs)
|
|
234
|
+
body = self.__build_body(create=True, **kwargs)
|
|
215
235
|
|
|
216
236
|
# Make sure there is a create which can be performed. This requires a non-empty body.
|
|
217
237
|
if len(body) <= 0:
|
|
@@ -113,7 +113,7 @@ class Organisation(Model):
|
|
|
113
113
|
_PATH_SERVICES = f"{_PATH_BASE}/services/latest"
|
|
114
114
|
_PATH_DISPATCHERS = f"{_PATH_BASE}/services/dispatchers"
|
|
115
115
|
|
|
116
|
-
def create_data(self, name, file_type, files, wait=False):
|
|
116
|
+
def create_data(self, name, file_type, files, wait=False, **kwargs):
|
|
117
117
|
"""
|
|
118
118
|
Creates a data object for the organisation and uploads the corresponding files. Optionally waits for the upload and analysis to complete.
|
|
119
119
|
|
|
@@ -122,6 +122,7 @@ class Organisation(Model):
|
|
|
122
122
|
file_type: The type of files that the data object will hold.
|
|
123
123
|
files: The list of file paths to be uploaded.
|
|
124
124
|
wait: Optionally wait for the upload and analysis to complete? Default False.
|
|
125
|
+
kwargs: The model attributes which override those already in the template.
|
|
125
126
|
|
|
126
127
|
Returns:
|
|
127
128
|
The created data object.
|
|
@@ -134,8 +135,8 @@ class Organisation(Model):
|
|
|
134
135
|
data = Data(self._session)
|
|
135
136
|
data._new(organisation_id=self.id)
|
|
136
137
|
|
|
137
|
-
# Now attempt to create the data item.
|
|
138
|
-
data._create(name, file_type, files, wait)
|
|
138
|
+
# Now attempt to create the data item with any overriding keyword arguments.
|
|
139
|
+
data._create(name, file_type, files, wait=wait, **kwargs)
|
|
139
140
|
|
|
140
141
|
return data
|
|
141
142
|
|
|
@@ -29,3 +29,5 @@
|
|
|
29
29
|
* **test_run**: Is this a test run of a service?
|
|
30
30
|
* **price**: The price in FPUs to execute this process given its inputs and options.
|
|
31
31
|
* **deletable**: Is this process scheduled for deletion?
|
|
32
|
+
* **non_aggregator_count**: The count of non-aggregator executions within the current or last execution.
|
|
33
|
+
* **aggregator_count**: The count of aggregator executions within the current or last execution.
|
|
@@ -473,8 +473,10 @@ class ProcessSchema(Schema):
|
|
|
473
473
|
has_executions = fields.String(required=True, metadata={'hide': True}) # Changed to hide as an attribute.
|
|
474
474
|
has_executions_organisation_id = fields.String(required=True, metadata={'hide': True}) # Changed to hide as an attribute.
|
|
475
475
|
executions = fields.List(fields.Nested(ProcessExecutionStatusSchema()), allow_none=True, metadata={'hide': True}) # Changed to hide as an attribute.
|
|
476
|
-
|
|
476
|
+
non_aggregator_count = fields.Integer(allow_none=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
477
|
+
aggregator_count = fields.Integer(allow_none=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
477
478
|
# Removed creator.
|
|
479
|
+
|
|
478
480
|
# Removed search.
|
|
479
481
|
|
|
480
482
|
available_dispatchers = fields.List(fields.Nested(ProcessDispatcherSchema()), allow_none=True, metadata={'hide': True}) # Added pseudo-field.
|
|
@@ -528,14 +530,16 @@ class Process(Model):
|
|
|
528
530
|
fusion_platform.FILE_TYPE_GEOTIFF: [fusion_platform.FILE_TYPE_GEOTIFF, fusion_platform.FILE_TYPE_DEM, fusion_platform.FILE_TYPE_JPEG2000],
|
|
529
531
|
fusion_platform.FILE_TYPE_JPEG2000: [fusion_platform.FILE_TYPE_JPEG2000],
|
|
530
532
|
fusion_platform.FILE_TYPE_DEM: [fusion_platform.FILE_TYPE_DEM],
|
|
531
|
-
fusion_platform.FILE_TYPE_GEOJSON: [fusion_platform.FILE_TYPE_GEOJSON, fusion_platform.FILE_TYPE_KML, fusion_platform.FILE_TYPE_KMZ,
|
|
532
|
-
fusion_platform.FILE_TYPE_ESRI_SHAPEFILE],
|
|
533
|
+
fusion_platform.FILE_TYPE_GEOJSON: [fusion_platform.FILE_TYPE_GEOJSON, fusion_platform.FILE_TYPE_KML, fusion_platform.FILE_TYPE_KMZ, fusion_platform.FILE_TYPE_ESRI_SHAPEFILE],
|
|
533
534
|
fusion_platform.FILE_TYPE_KML: [fusion_platform.FILE_TYPE_KML],
|
|
534
535
|
fusion_platform.FILE_TYPE_KMZ: [fusion_platform.FILE_TYPE_KMZ],
|
|
535
536
|
fusion_platform.FILE_TYPE_CSV: [fusion_platform.FILE_TYPE_CSV],
|
|
536
537
|
fusion_platform.FILE_TYPE_ESRI_SHAPEFILE: [fusion_platform.FILE_TYPE_ESRI_SHAPEFILE],
|
|
537
538
|
fusion_platform.FILE_TYPE_JPEG: [fusion_platform.FILE_TYPE_JPEG],
|
|
538
539
|
fusion_platform.FILE_TYPE_PNG: [fusion_platform.FILE_TYPE_PNG],
|
|
540
|
+
fusion_platform.FILE_TYPE_PDF: [fusion_platform.FILE_TYPE_PDF],
|
|
541
|
+
fusion_platform.FILE_TYPE_GZIP: [fusion_platform.FILE_TYPE_GZIP],
|
|
542
|
+
fusion_platform.FILE_TYPE_ZIP: [fusion_platform.FILE_TYPE_ZIP],
|
|
539
543
|
fusion_platform.FILE_TYPE_OTHER: [fusion_platform.FILE_TYPE_OTHER],
|
|
540
544
|
}
|
|
541
545
|
# @formatter:on
|
|
@@ -655,16 +659,19 @@ class Process(Model):
|
|
|
655
659
|
# Return the copy.
|
|
656
660
|
return process
|
|
657
661
|
|
|
658
|
-
def create(self):
|
|
662
|
+
def create(self, **kwargs):
|
|
659
663
|
"""
|
|
660
664
|
Attempts to persist the template process in the Fusion Platform<sup>®</sup> so that it can be executed.
|
|
661
665
|
|
|
666
|
+
Args:
|
|
667
|
+
kwargs: The model attributes which override those already in the template.
|
|
668
|
+
|
|
662
669
|
Raises:
|
|
663
670
|
RequestError: if the create fails.
|
|
664
671
|
ModelError: if the model could not be created and validated by the Fusion Platform<sup>®</sup>.
|
|
665
672
|
"""
|
|
666
673
|
# Attempt to issue the create.
|
|
667
|
-
self._create()
|
|
674
|
+
self._create(**kwargs)
|
|
668
675
|
|
|
669
676
|
def __dispatchers(self, dispatchers):
|
|
670
677
|
"""
|
|
@@ -203,8 +203,8 @@ class ServiceSchema(Schema):
|
|
|
203
203
|
version = fields.Integer(required=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
204
204
|
approval_status = fields.String(required=True, metadata={'read_only': True}) # Changed to prevent this being updated.
|
|
205
205
|
# Removed latest.
|
|
206
|
-
# Removed dispatcher.
|
|
207
206
|
featured = fields.String(allow_none=True)
|
|
207
|
+
# Removed dispatcher.
|
|
208
208
|
show_in_latest = fields.Boolean(allow_none=True) # Changed to optional.
|
|
209
209
|
name = fields.String(required=True)
|
|
210
210
|
|
|
@@ -234,6 +234,7 @@ class ServiceSchema(Schema):
|
|
|
234
234
|
|
|
235
235
|
# Removed creator.
|
|
236
236
|
# Removed approver.
|
|
237
|
+
|
|
237
238
|
# Removed search.
|
|
238
239
|
|
|
239
240
|
documentation_summary = fields.String(required=True)
|
|
@@ -15,10 +15,12 @@ i18n.add_translation('session.request_failed', 'API request failed: %{message}',
|
|
|
15
15
|
i18n.add_translation('session.login_failed', 'Login failed', 'en')
|
|
16
16
|
i18n.add_translation('session.missing_password', 'Password must be specified', 'en')
|
|
17
17
|
i18n.add_translation('session.missing_email_user_id', 'Either an email address or a user id must be specified', 'en')
|
|
18
|
+
i18n.add_translation('fusion_platform.url', 'https://www.d-cat.co.uk', 'en')
|
|
18
19
|
i18n.add_translation('fusion_platform.support', 'Support: support@d-cat.co.uk', 'en')
|
|
19
20
|
i18n.add_translation('fusion_platform.version_date', 'Date: %{version_date}', 'en')
|
|
20
21
|
i18n.add_translation('fusion_platform.version', 'Version: %{version}', 'en')
|
|
21
22
|
i18n.add_translation('fusion_platform.sdk', 'Fusion Platform(r) SDK', 'en')
|
|
23
|
+
i18n.add_translation('fusion_platform.organisation', 'Digital Content Analysis Technology Ltd', 'en')
|
|
22
24
|
i18n.add_translation('models.data_file.failed_download_url', 'Failed to get URL from download file response', 'en')
|
|
23
25
|
i18n.add_translation('models.data_file.no_download', 'No download is in progress', 'en')
|
|
24
26
|
i18n.add_translation('models.data_file.download_already_in_progress', 'Cannot download file as the download is already in progress', 'en')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fusion-platform-python-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.13.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
|
|
@@ -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.
|
|
22
|
+
version='1.13.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',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/__main__.py
RENAMED
|
File without changes
|
{fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/base.py
RENAMED
|
File without changes
|
{fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/command.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/localise.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/fusion_platform/session.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/tests/test_command.py
RENAMED
|
File without changes
|
|
File without changes
|
{fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/tests/test_localise.py
RENAMED
|
File without changes
|
{fusion-platform-python-sdk-1.12.2 → fusion-platform-python-sdk-1.13.5}/tests/test_session.py
RENAMED
|
File without changes
|