fusion-platform-python-sdk 1.11.1__tar.gz → 1.13.1__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.11.1/fusion_platform_python_sdk.egg-info → fusion-platform-python-sdk-1.13.1}/PKG-INFO +1 -1
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/__init__.py +5 -2
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/common/utilities.py +44 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/fusion_platform_sdk.pdf +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/data.py +80 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/data_file.py +177 -1
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/model.py +17 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/process.py +5 -3
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/service.py +2 -1
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/translations.py +2 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1/fusion_platform_python_sdk.egg-info}/PKG-INFO +1 -1
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/setup.py +1 -1
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/LICENSE.txt +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/MANIFEST.in +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/README.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/__main__.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/base.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/command.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/common/__init__.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/common/raise_thread.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/detailed_example.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/documentation.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/glasgow.geojson +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/lake_district.geojson +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/localisations.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/localise.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/__init__.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/credit.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/credit.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/data.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/data_file.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/fields.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/organisation.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/organisation.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/process.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/process_execution.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/process_execution.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/process_service_execution.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/process_service_execution.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/process_service_execution_log.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/process_service_execution_log.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/service.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/user.md +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/models/user.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/quick_example.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/session.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform_python_sdk.egg-info/SOURCES.txt +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform_python_sdk.egg-info/dependency_links.txt +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform_python_sdk.egg-info/entry_points.txt +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform_python_sdk.egg-info/requires.txt +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform_python_sdk.egg-info/top_level.txt +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/setup.cfg +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/tests/test_base.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/tests/test_command.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/tests/test_init.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/tests/test_localise.py +0 -0
- {fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/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.1
|
|
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.11.1 → fusion-platform-python-sdk-1.13.1}/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.1'
|
|
15
|
+
__version_date__ = '2024-07-15T06:23:24Z'
|
|
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:
|
|
@@ -341,3 +343,81 @@ class Data(Model):
|
|
|
341
343
|
ModelError: if a model could not be loaded or validated from the Fusion Platform<sup>®</sup>.
|
|
342
344
|
"""
|
|
343
345
|
return DataFile._models_from_api_path(self._session, self._get_path(self.__class__._PATH_FILES), organisation_id=self.organisation_id)
|
|
346
|
+
|
|
347
|
+
def get_stac_collection(self, items, collection_file_name=DataFile._STAC_COLLECTION_FILE_NAME, owner=None, created_at=None, detail=None):
|
|
348
|
+
"""
|
|
349
|
+
Converts the data representation into a STAC collection with the specified STAC item file names.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
items: The list of items to be put into the collection. This should be a tuple of (STAC item, item_file_name).
|
|
353
|
+
collection_file_name: The optional collection file name.
|
|
354
|
+
owner: The optional owner of the collection. Default None.
|
|
355
|
+
created_at: Optionally when the collection was created. Default None.
|
|
356
|
+
detail: The optional collection detail as a dictionary. Default None.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
A tuple of the STAC collection definition as a dictionary and the collection file name used in the definition.
|
|
360
|
+
"""
|
|
361
|
+
stac_extensions = []
|
|
362
|
+
|
|
363
|
+
# Form the providers.
|
|
364
|
+
provider = {
|
|
365
|
+
'name': i18n.t('fusion_platform.organisation'),
|
|
366
|
+
'roles': ['producer', 'licensor'],
|
|
367
|
+
'url': i18n.t('fusion_platform.url')
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
# Add in the optional processing information, if any.
|
|
371
|
+
if owner is not None:
|
|
372
|
+
provider['processing:lineage'] = owner
|
|
373
|
+
|
|
374
|
+
if created_at is not None:
|
|
375
|
+
provider['processing.datetime'] = created_at
|
|
376
|
+
|
|
377
|
+
if detail is not None:
|
|
378
|
+
provider['processing:software'] = detail
|
|
379
|
+
|
|
380
|
+
if (owner is not None) or (created_at is not None) or (detail is not None):
|
|
381
|
+
stac_extensions.append('https://stac-extensions.github.io/processing/v1.2.0/schema.json')
|
|
382
|
+
|
|
383
|
+
# Form the links.
|
|
384
|
+
links = [
|
|
385
|
+
{'rel': 'self', 'href': collection_file_name, 'type': 'application/json'},
|
|
386
|
+
{'rel': 'root', 'href': collection_file_name, 'type': 'application/json'},
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
# Add in the files calculating the maximal spatial and temporal extents.
|
|
390
|
+
bbox = None
|
|
391
|
+
interval_start = None
|
|
392
|
+
interval_end = None
|
|
393
|
+
|
|
394
|
+
for item, item_file_name in items:
|
|
395
|
+
# Calculate the spatial extent.
|
|
396
|
+
item_bbox = item.get('bbox')
|
|
397
|
+
|
|
398
|
+
if (bbox is None) or (len(bbox) < 4):
|
|
399
|
+
bbox = item_bbox
|
|
400
|
+
|
|
401
|
+
if (bbox is not None) and (len(bbox) >= 4) and (item_bbox is not None) and (len(item_bbox) >= 4):
|
|
402
|
+
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])]
|
|
403
|
+
|
|
404
|
+
# Calculate the temporal extent.
|
|
405
|
+
item_datetime = dict_nested_get(item, ['properties', 'datetime'])
|
|
406
|
+
interval_start = item_datetime if interval_start is None else min(interval_start, item_datetime)
|
|
407
|
+
interval_end = item_datetime if interval_end is None else max(interval_end, item_datetime)
|
|
408
|
+
|
|
409
|
+
# Add in the link.
|
|
410
|
+
links.append({'rel': 'item', 'href': item_file_name, 'type': 'application/geo+json'})
|
|
411
|
+
|
|
412
|
+
# Build the STAC collection.
|
|
413
|
+
return {
|
|
414
|
+
'type': 'Collection',
|
|
415
|
+
'stac_version': '1.0.0',
|
|
416
|
+
'stac_extensions': stac_extensions,
|
|
417
|
+
'id': self.id,
|
|
418
|
+
'description': self.name,
|
|
419
|
+
'license': 'proprietary',
|
|
420
|
+
'providers': [provider],
|
|
421
|
+
'extent': {'spatial': {'bbox': [bbox]}, 'temporal': {'interval': [[interval_start, interval_end]]}},
|
|
422
|
+
'links': links
|
|
423
|
+
}, 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
|
|
|
@@ -473,8 +473,8 @@ 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
|
-
|
|
477
476
|
# Removed creator.
|
|
477
|
+
|
|
478
478
|
# Removed search.
|
|
479
479
|
|
|
480
480
|
available_dispatchers = fields.List(fields.Nested(ProcessDispatcherSchema()), allow_none=True, metadata={'hide': True}) # Added pseudo-field.
|
|
@@ -528,14 +528,16 @@ class Process(Model):
|
|
|
528
528
|
fusion_platform.FILE_TYPE_GEOTIFF: [fusion_platform.FILE_TYPE_GEOTIFF, fusion_platform.FILE_TYPE_DEM, fusion_platform.FILE_TYPE_JPEG2000],
|
|
529
529
|
fusion_platform.FILE_TYPE_JPEG2000: [fusion_platform.FILE_TYPE_JPEG2000],
|
|
530
530
|
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],
|
|
531
|
+
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
532
|
fusion_platform.FILE_TYPE_KML: [fusion_platform.FILE_TYPE_KML],
|
|
534
533
|
fusion_platform.FILE_TYPE_KMZ: [fusion_platform.FILE_TYPE_KMZ],
|
|
535
534
|
fusion_platform.FILE_TYPE_CSV: [fusion_platform.FILE_TYPE_CSV],
|
|
536
535
|
fusion_platform.FILE_TYPE_ESRI_SHAPEFILE: [fusion_platform.FILE_TYPE_ESRI_SHAPEFILE],
|
|
537
536
|
fusion_platform.FILE_TYPE_JPEG: [fusion_platform.FILE_TYPE_JPEG],
|
|
538
537
|
fusion_platform.FILE_TYPE_PNG: [fusion_platform.FILE_TYPE_PNG],
|
|
538
|
+
fusion_platform.FILE_TYPE_PDF: [fusion_platform.FILE_TYPE_PDF],
|
|
539
|
+
fusion_platform.FILE_TYPE_GZIP: [fusion_platform.FILE_TYPE_GZIP],
|
|
540
|
+
fusion_platform.FILE_TYPE_ZIP: [fusion_platform.FILE_TYPE_ZIP],
|
|
539
541
|
fusion_platform.FILE_TYPE_OTHER: [fusion_platform.FILE_TYPE_OTHER],
|
|
540
542
|
}
|
|
541
543
|
# @formatter:on
|
|
@@ -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.1
|
|
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.1',
|
|
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.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/__main__.py
RENAMED
|
File without changes
|
{fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/fusion_platform/base.py
RENAMED
|
File without changes
|
{fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/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.11.1 → fusion-platform-python-sdk-1.13.1}/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
|
|
File without changes
|
|
File without changes
|
{fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/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.11.1 → fusion-platform-python-sdk-1.13.1}/tests/test_command.py
RENAMED
|
File without changes
|
|
File without changes
|
{fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/tests/test_localise.py
RENAMED
|
File without changes
|
{fusion-platform-python-sdk-1.11.1 → fusion-platform-python-sdk-1.13.1}/tests/test_session.py
RENAMED
|
File without changes
|