datacosmos 0.0.7__py3-none-any.whl → 0.0.8__py3-none-any.whl
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.
Potentially problematic release.
This version of datacosmos might be problematic. Click here for more details.
- datacosmos/config/config.py +0 -1
- datacosmos/datacosmos_client.py +50 -18
- datacosmos/uploader/dataclasses/upload_path.py +0 -30
- datacosmos/uploader/datacosmos_uploader.py +3 -8
- {datacosmos-0.0.7.dist-info → datacosmos-0.0.8.dist-info}/METADATA +1 -1
- {datacosmos-0.0.7.dist-info → datacosmos-0.0.8.dist-info}/RECORD +9 -11
- datacosmos/utils/constants.py +0 -16
- datacosmos/utils/missions.py +0 -27
- {datacosmos-0.0.7.dist-info → datacosmos-0.0.8.dist-info}/WHEEL +0 -0
- {datacosmos-0.0.7.dist-info → datacosmos-0.0.8.dist-info}/licenses/LICENSE.md +0 -0
- {datacosmos-0.0.7.dist-info → datacosmos-0.0.8.dist-info}/top_level.txt +0 -0
datacosmos/config/config.py
CHANGED
|
@@ -29,7 +29,6 @@ class Config(BaseSettings):
|
|
|
29
29
|
authentication: Optional[AuthenticationConfig] = None
|
|
30
30
|
stac: Optional[URL] = None
|
|
31
31
|
datacosmos_cloud_storage: Optional[URL] = None
|
|
32
|
-
mission_id: int = 0
|
|
33
32
|
|
|
34
33
|
DEFAULT_AUTH_TYPE: ClassVar[str] = "m2m"
|
|
35
34
|
DEFAULT_AUTH_TOKEN_URL: ClassVar[str] = "https://login.open-cosmos.com/oauth/token"
|
datacosmos/datacosmos_client.py
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
Automatically manages token refreshing and provides HTTP convenience
|
|
4
|
-
methods.
|
|
5
|
-
"""
|
|
1
|
+
"""Client to interact with the Datacosmos API with authentication and request handling."""
|
|
6
2
|
|
|
7
3
|
from datetime import datetime, timedelta, timezone
|
|
8
4
|
from typing import Any, Optional
|
|
@@ -19,23 +15,57 @@ from datacosmos.exceptions.datacosmos_exception import DatacosmosException
|
|
|
19
15
|
class DatacosmosClient:
|
|
20
16
|
"""Client to interact with the Datacosmos API with authentication and request handling."""
|
|
21
17
|
|
|
22
|
-
def __init__(
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
config: Optional[Config | Any] = None,
|
|
21
|
+
http_session: Optional[requests.Session | OAuth2Session] = None,
|
|
22
|
+
):
|
|
23
23
|
"""Initialize the DatacosmosClient.
|
|
24
24
|
|
|
25
25
|
Args:
|
|
26
|
-
config (Optional[Config]): Configuration object.
|
|
26
|
+
config (Optional[Config]): Configuration object (only needed when SDK creates its own session).
|
|
27
|
+
http_session (Optional[requests.Session]): Pre-authenticated session.
|
|
27
28
|
"""
|
|
28
|
-
if
|
|
29
|
-
self.
|
|
30
|
-
|
|
29
|
+
if http_session is not None:
|
|
30
|
+
self._http_client = http_session
|
|
31
|
+
self._owns_session = False
|
|
32
|
+
if isinstance(http_session, OAuth2Session):
|
|
33
|
+
token_data = http_session.token
|
|
34
|
+
elif isinstance(http_session, requests.Session):
|
|
35
|
+
auth_header = http_session.headers.get("Authorization", "")
|
|
36
|
+
if not auth_header.startswith("Bearer "):
|
|
37
|
+
raise DatacosmosException(
|
|
38
|
+
"Injected requests.Session must include a 'Bearer' token in its headers"
|
|
39
|
+
)
|
|
40
|
+
token_data = {"access_token": auth_header.split(" ", 1)[1]}
|
|
41
|
+
else:
|
|
42
|
+
raise DatacosmosException(
|
|
43
|
+
f"Unsupported session type: {type(http_session)}"
|
|
44
|
+
)
|
|
31
45
|
try:
|
|
32
|
-
self.
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
self.token = token_data.get("access_token")
|
|
47
|
+
self.token_expiry = token_data.get("expires_at") or token_data.get(
|
|
48
|
+
"expires_in"
|
|
49
|
+
)
|
|
50
|
+
except Exception:
|
|
51
|
+
raise DatacosmosException(
|
|
52
|
+
"Failed to extract token from injected session"
|
|
53
|
+
)
|
|
35
54
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
55
|
+
self.config = config
|
|
56
|
+
else:
|
|
57
|
+
if config:
|
|
58
|
+
self.config = config
|
|
59
|
+
else:
|
|
60
|
+
try:
|
|
61
|
+
self.config = Config.from_yaml()
|
|
62
|
+
except ValueError:
|
|
63
|
+
self.config = Config.from_env()
|
|
64
|
+
|
|
65
|
+
self._owns_session = True
|
|
66
|
+
self.token = None
|
|
67
|
+
self.token_expiry = None
|
|
68
|
+
self._http_client = self._authenticate_and_initialize_client()
|
|
39
69
|
|
|
40
70
|
def _authenticate_and_initialize_client(self) -> requests.Session:
|
|
41
71
|
"""Authenticate and initialize the HTTP client with a valid token."""
|
|
@@ -68,8 +98,10 @@ class DatacosmosClient:
|
|
|
68
98
|
) from e
|
|
69
99
|
|
|
70
100
|
def _refresh_token_if_needed(self):
|
|
71
|
-
"""Refresh the token if it has expired."""
|
|
72
|
-
if
|
|
101
|
+
"""Refresh the token if it has expired (only if SDK created it)."""
|
|
102
|
+
if self._owns_session and (
|
|
103
|
+
not self.token or self.token_expiry <= datetime.now(timezone.utc)
|
|
104
|
+
):
|
|
73
105
|
self._http_client = self._authenticate_and_initialize_client()
|
|
74
106
|
|
|
75
107
|
def request(
|
|
@@ -8,7 +8,6 @@ import structlog
|
|
|
8
8
|
|
|
9
9
|
from datacosmos.stac.enums.processing_level import ProcessingLevel
|
|
10
10
|
from datacosmos.stac.item.models.datacosmos_item import DatacosmosItem
|
|
11
|
-
from datacosmos.utils.missions import get_mission_id
|
|
12
11
|
|
|
13
12
|
logger = structlog.get_logger()
|
|
14
13
|
|
|
@@ -35,11 +34,6 @@ class UploadPath:
|
|
|
35
34
|
cls, item: DatacosmosItem, mission: str, item_path: str
|
|
36
35
|
) -> "Path":
|
|
37
36
|
"""Create a Path instance from a DatacosmosItem and a path."""
|
|
38
|
-
for asset in item.assets.values():
|
|
39
|
-
if mission == "":
|
|
40
|
-
mission = cls._get_mission_name(asset.href)
|
|
41
|
-
else:
|
|
42
|
-
break
|
|
43
37
|
dt = datetime.strptime(item.properties["datetime"], "%Y-%m-%dT%H:%M:%SZ")
|
|
44
38
|
path = UploadPath(
|
|
45
39
|
mission=mission,
|
|
@@ -67,27 +61,3 @@ class UploadPath:
|
|
|
67
61
|
id=parts[5],
|
|
68
62
|
path="/".join(parts[6:]),
|
|
69
63
|
)
|
|
70
|
-
|
|
71
|
-
@classmethod
|
|
72
|
-
def _get_mission_name(cls, href: str) -> str:
|
|
73
|
-
mission = ""
|
|
74
|
-
# bruteforce mission name from asset path
|
|
75
|
-
# traverse the path and check if any part is a mission name (generates a mission id)
|
|
76
|
-
href_parts = href.split("/")
|
|
77
|
-
for idx, part in enumerate(href_parts):
|
|
78
|
-
try:
|
|
79
|
-
# when an id is found, then the mission name is valid
|
|
80
|
-
get_mission_id(
|
|
81
|
-
part, "test"
|
|
82
|
-
) # using test as it is more wide and anything on prod should exists on test
|
|
83
|
-
except KeyError:
|
|
84
|
-
continue
|
|
85
|
-
# validate the mission name by checking if the path is correct
|
|
86
|
-
# using the same logic as the __str__ method
|
|
87
|
-
mission = part.lower()
|
|
88
|
-
h = "/".join(["full", *href_parts[idx:]])
|
|
89
|
-
p = UploadPath.from_path("/".join([mission, *href_parts[idx + 1 :]]))
|
|
90
|
-
if str(p) != h:
|
|
91
|
-
raise ValueError(f"Could not find mission name in asset path {href}")
|
|
92
|
-
break
|
|
93
|
-
return mission
|
|
@@ -9,7 +9,6 @@ from datacosmos.datacosmos_client import DatacosmosClient
|
|
|
9
9
|
from datacosmos.stac.item.item_client import ItemClient
|
|
10
10
|
from datacosmos.stac.item.models.datacosmos_item import DatacosmosItem
|
|
11
11
|
from datacosmos.uploader.dataclasses.upload_path import UploadPath
|
|
12
|
-
from datacosmos.utils.missions import get_mission_name
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class DatacosmosUploader:
|
|
@@ -17,14 +16,10 @@ class DatacosmosUploader:
|
|
|
17
16
|
|
|
18
17
|
def __init__(self, client: DatacosmosClient):
|
|
19
18
|
"""Initialize the uploader with DatacosmosClient."""
|
|
20
|
-
|
|
21
|
-
environment = client.config.environment
|
|
19
|
+
self.environment = client.config.environment
|
|
22
20
|
|
|
23
21
|
self.datacosmos_client = client
|
|
24
22
|
self.item_client = ItemClient(client)
|
|
25
|
-
self.mission_name = (
|
|
26
|
-
get_mission_name(mission_id, environment) if mission_id != 0 else ""
|
|
27
|
-
)
|
|
28
23
|
self.base_url = client.config.datacosmos_cloud_storage.as_domain_url()
|
|
29
24
|
|
|
30
25
|
def upload_and_register_item(self, item_json_file_path: str) -> None:
|
|
@@ -92,9 +87,9 @@ class DatacosmosUploader:
|
|
|
92
87
|
except Exception: # nosec
|
|
93
88
|
pass # Ignore if item doesn't exist
|
|
94
89
|
|
|
95
|
-
def _get_upload_path(self, item: DatacosmosItem) -> str:
|
|
90
|
+
def _get_upload_path(self, item: DatacosmosItem, mission_name: str = "") -> str:
|
|
96
91
|
"""Constructs the storage upload path based on the item and mission name."""
|
|
97
|
-
return UploadPath.from_item_path(item,
|
|
92
|
+
return UploadPath.from_item_path(item, mission_name, "")
|
|
98
93
|
|
|
99
94
|
def _update_item_assets(self, item: DatacosmosItem) -> None:
|
|
100
95
|
"""Updates the item's assets with uploaded file URLs."""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
datacosmos/__init__.py,sha256=dVHKpbz5FVtfoJAWHRdsUENG6H-vs4UrkuwnIvOGJr4,66
|
|
2
|
-
datacosmos/datacosmos_client.py,sha256=
|
|
2
|
+
datacosmos/datacosmos_client.py,sha256=3BurTz1fPk1Dzp8B5xt5gZZrFiqk1AT5oaqKeYmXPec,6517
|
|
3
3
|
datacosmos/config/__init__.py,sha256=KCsaTb9-ZgFui1GM8wZFIPLJy0D0O8l8Z1Sv3NRD9UM,140
|
|
4
|
-
datacosmos/config/config.py,sha256=
|
|
4
|
+
datacosmos/config/config.py,sha256=qj9e68enxX61Iw8lqEXUUCNJ2O7vy-1j9nAoBIB-4Ao,7219
|
|
5
5
|
datacosmos/config/models/__init__.py,sha256=r3lThPkyKjBjUZXRNscFzOrmn_-m_i9DvG3RePfCFYc,41
|
|
6
6
|
datacosmos/config/models/authentication_config.py,sha256=01Q90-yupbJ5orYDtatZIm9EaL7roQ-oUMoZfFMRzIM,499
|
|
7
7
|
datacosmos/config/models/local_user_account_authentication_config.py,sha256=8WApn720MBXMKQa6w7bCd7Z37GRmYR-I7mBUgUI20lQ,701
|
|
@@ -32,20 +32,18 @@ datacosmos/stac/item/models/eo_band.py,sha256=YC3Scn_wFhIo51pIVcJeuJienF7JGWoEv3
|
|
|
32
32
|
datacosmos/stac/item/models/item_update.py,sha256=_CpjQn9SsfedfuxlHSiGeptqY4M-p15t9YX__mBRueI,2088
|
|
33
33
|
datacosmos/stac/item/models/raster_band.py,sha256=CoEVs-YyPE5Fse0He9DdOs4dGZpzfCsCuVzOcdXa_UM,354
|
|
34
34
|
datacosmos/uploader/__init__.py,sha256=ZtfCVJ_pWKKh2F1r_NArnbG3_JtpcEiXcA_tmSwSKmQ,128
|
|
35
|
-
datacosmos/uploader/datacosmos_uploader.py,sha256=
|
|
35
|
+
datacosmos/uploader/datacosmos_uploader.py,sha256=FDBUSZ9mpieXi3dms3RM17aIrnRuAKd0rFkD7Jwl1pY,4195
|
|
36
36
|
datacosmos/uploader/dataclasses/__init__.py,sha256=IjcyA8Vod-z1_Gi1FMZhK58Owman0foL25Hs0YtkYYs,43
|
|
37
|
-
datacosmos/uploader/dataclasses/upload_path.py,sha256=
|
|
37
|
+
datacosmos/uploader/dataclasses/upload_path.py,sha256=5QadynHxkJrnOk1lyPtLyiVAHdzBshEuhjA9hwVF0NI,1903
|
|
38
38
|
datacosmos/utils/__init__.py,sha256=XQbAnoqJrPpnSpEzAbjh84yqYWw8cBM8mNp8ynTG-54,50
|
|
39
|
-
datacosmos/utils/constants.py,sha256=f7pOqCpdXk7WFGoaTyuCpr65jb-TtfhoVGuYTz3_T6Y,272
|
|
40
|
-
datacosmos/utils/missions.py,sha256=7GOnrjxB8V11C_Jr3HHI4vpXifgkOSeirNjIDx17C58,940
|
|
41
39
|
datacosmos/utils/url.py,sha256=iQwZr6mYRoePqUZg-k3KQSV9o2wju5ZuCa5WS_GyJo4,2114
|
|
42
40
|
datacosmos/utils/http_response/__init__.py,sha256=BvOWwC5coYqq_kFn8gIw5m54TLpdfJKlW9vgRkfhXiA,33
|
|
43
41
|
datacosmos/utils/http_response/check_api_response.py,sha256=dKWW01jn2_lWV0xpOBABhEP42CFSsx9dP0iSxykbN54,1186
|
|
44
42
|
datacosmos/utils/http_response/models/__init__.py,sha256=Wj8YT6dqw7rAz_rctllxo5Or_vv8DwopvQvBzwCTvpw,45
|
|
45
43
|
datacosmos/utils/http_response/models/datacosmos_error.py,sha256=Uqi2uM98nJPeCbM7zngV6vHSk97jEAb_nkdDEeUjiQM,740
|
|
46
44
|
datacosmos/utils/http_response/models/datacosmos_response.py,sha256=oV4n-sue7K1wwiIQeHpxdNU8vxeqF3okVPE2rydw5W0,336
|
|
47
|
-
datacosmos-0.0.
|
|
48
|
-
datacosmos-0.0.
|
|
49
|
-
datacosmos-0.0.
|
|
50
|
-
datacosmos-0.0.
|
|
51
|
-
datacosmos-0.0.
|
|
45
|
+
datacosmos-0.0.8.dist-info/licenses/LICENSE.md,sha256=vpbRI-UUbZVQfr3VG_CXt9HpRnL1b5kt8uTVbirxeyI,1486
|
|
46
|
+
datacosmos-0.0.8.dist-info/METADATA,sha256=tK8nQIG9_5zJ-gCQPQB7S2q6z7HabdgYdpPLfkinP9g,896
|
|
47
|
+
datacosmos-0.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
48
|
+
datacosmos-0.0.8.dist-info/top_level.txt,sha256=ueobs5CNeyDbPMgXPcVV0d0yNdm8CvGtDT3CaksRVtA,11
|
|
49
|
+
datacosmos-0.0.8.dist-info/RECORD,,
|
datacosmos/utils/constants.py
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"""Package for storing constants."""
|
|
2
|
-
|
|
3
|
-
TEST_MISSION_NAMES = {
|
|
4
|
-
55: "MENUT",
|
|
5
|
-
56: "PHISAT-2",
|
|
6
|
-
57: "HAMMER",
|
|
7
|
-
63: "MANTIS",
|
|
8
|
-
64: "PLATERO",
|
|
9
|
-
}
|
|
10
|
-
PROD_MISSION_NAMES = {
|
|
11
|
-
23: "MENUT",
|
|
12
|
-
29: "MANTIS",
|
|
13
|
-
35: "PHISAT-2",
|
|
14
|
-
37: "PLATERO",
|
|
15
|
-
48: "HAMMER",
|
|
16
|
-
}
|
datacosmos/utils/missions.py
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"""Package for storing mission specific information."""
|
|
2
|
-
|
|
3
|
-
from datacosmos.utils.constants import PROD_MISSION_NAMES, TEST_MISSION_NAMES
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def get_mission_name(mission: int, env: str) -> str:
|
|
7
|
-
"""Get the mission name from the mission number."""
|
|
8
|
-
if env == "test" or env == "local":
|
|
9
|
-
return TEST_MISSION_NAMES[mission]
|
|
10
|
-
elif env == "prod":
|
|
11
|
-
return PROD_MISSION_NAMES[mission]
|
|
12
|
-
else:
|
|
13
|
-
raise ValueError(f"Unsupported environment: {env}")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def get_mission_id(mission_name: str, env: str) -> int:
|
|
17
|
-
"""Get the mission number from the mission name."""
|
|
18
|
-
if env == "test" or env == "local":
|
|
19
|
-
return {v.upper(): k for k, v in TEST_MISSION_NAMES.items()}[
|
|
20
|
-
mission_name.upper()
|
|
21
|
-
]
|
|
22
|
-
elif env == "prod":
|
|
23
|
-
return {v.upper(): k for k, v in PROD_MISSION_NAMES.items()}[
|
|
24
|
-
mission_name.upper()
|
|
25
|
-
]
|
|
26
|
-
else:
|
|
27
|
-
raise ValueError(f"Unsupported environment: {env}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|