ee-client 2.0.3__tar.gz → 2.1.0__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.
- {ee_client-2.0.3 → ee_client-2.1.0}/PKG-INFO +10 -3
- {ee_client-2.0.3 → ee_client-2.1.0}/README.rst +9 -2
- {ee_client-2.0.3 → ee_client-2.1.0}/ee_client.egg-info/PKG-INFO +10 -3
- {ee_client-2.0.3 → ee_client-2.1.0}/ee_client.egg-info/SOURCES.txt +1 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/__init__.py +1 -1
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/client.py +15 -28
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/export/image.py +20 -7
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/export/table.py +19 -6
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/helpers.py +11 -5
- ee_client-2.1.0/eeclient/sepal_credential_mixin.py +37 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/tasks.py +16 -5
- {ee_client-2.0.3 → ee_client-2.1.0}/pyproject.toml +2 -2
- {ee_client-2.0.3 → ee_client-2.1.0}/LICENSE +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/ee_client.egg-info/dependency_links.txt +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/ee_client.egg-info/requires.txt +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/ee_client.egg-info/top_level.txt +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/data.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/exceptions.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/export/__init__.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/interfaces/__init__.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/interfaces/export.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/interfaces/operations.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/interfaces/tasks.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/models.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/eeclient/oauth_app.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/setup.cfg +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/tests/test_client.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/tests/test_data.py +0 -0
- {ee_client-2.0.3 → ee_client-2.1.0}/tests/test_models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ee-client
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: extends the capabilities of the earthengine-api by providing custom session management and client interactions
|
|
5
5
|
Author-email: Daniel Guerrero <dfgm2006@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/dfguerrerom/ee-client
|
|
@@ -32,15 +32,22 @@ Dynamic: license-file
|
|
|
32
32
|
Earth Engine Client 🌎
|
|
33
33
|
==============================
|
|
34
34
|
|
|
35
|
-
The **Earth Engine Session Client** is a Python package that extends the Google Earth Engine (GEE) API by introducing multi-user session management through
|
|
35
|
+
The **Earth Engine Session Client** is a Python package that extends the Google Earth Engine (GEE) API by introducing multi-user session management through custom authentication.
|
|
36
|
+
|
|
37
|
+
**Why This Package?**
|
|
38
|
+
|
|
39
|
+
While Google Earth Engine applications can be created using a global service account, this approach has significant limitations: users cannot access their private GEE assets without making them public. This package solves this problem by handling custom authentication, allowing each user to access their own private assets securely.
|
|
40
|
+
|
|
41
|
+
Unlike the standard GEE API—which relies on a global session object and does not support multi-user environments—this client ensures that each session is authenticated and managed independently with user-specific credentials.
|
|
36
42
|
|
|
37
43
|
Each session is instantiated via the ``EESession`` class, currently only accepts SEPAL headers as its only parameter. **A valid ``sepal-session-id`` cookie must be present in these headers**, as it is used to retrieve the corresponding GEE credentials. Once authenticated, the session exposes an ``operations`` property that provides easy access to key API methods.
|
|
38
44
|
|
|
39
45
|
Key Features
|
|
40
46
|
------------
|
|
41
47
|
|
|
48
|
+
- **Custom User Authentication**: Enable users to access their private GEE assets without requiring them to be public, solving the limitation of global service account approaches.
|
|
42
49
|
- **SEPAL-based Initialization**: Create sessions using SEPAL headers. The required ``sepal-session-id`` cookie is automatically used to retrieve GEE credentials.
|
|
43
|
-
- **
|
|
50
|
+
- **Multi-User Session Management**: Encapsulate user-specific credentials and project data in independent ``EESession`` objects.
|
|
44
51
|
- **Enhanced API Operations**: Access GEE functionalities via the ``operations`` property, which includes methods such as:
|
|
45
52
|
- ``get_info``: Retrieve detailed information about an Earth Engine object.
|
|
46
53
|
- ``get_map_id``: Generate a map ID for an Earth Engine image.
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
Earth Engine Client 🌎
|
|
2
2
|
==============================
|
|
3
3
|
|
|
4
|
-
The **Earth Engine Session Client** is a Python package that extends the Google Earth Engine (GEE) API by introducing multi-user session management through
|
|
4
|
+
The **Earth Engine Session Client** is a Python package that extends the Google Earth Engine (GEE) API by introducing multi-user session management through custom authentication.
|
|
5
|
+
|
|
6
|
+
**Why This Package?**
|
|
7
|
+
|
|
8
|
+
While Google Earth Engine applications can be created using a global service account, this approach has significant limitations: users cannot access their private GEE assets without making them public. This package solves this problem by handling custom authentication, allowing each user to access their own private assets securely.
|
|
9
|
+
|
|
10
|
+
Unlike the standard GEE API—which relies on a global session object and does not support multi-user environments—this client ensures that each session is authenticated and managed independently with user-specific credentials.
|
|
5
11
|
|
|
6
12
|
Each session is instantiated via the ``EESession`` class, currently only accepts SEPAL headers as its only parameter. **A valid ``sepal-session-id`` cookie must be present in these headers**, as it is used to retrieve the corresponding GEE credentials. Once authenticated, the session exposes an ``operations`` property that provides easy access to key API methods.
|
|
7
13
|
|
|
8
14
|
Key Features
|
|
9
15
|
------------
|
|
10
16
|
|
|
17
|
+
- **Custom User Authentication**: Enable users to access their private GEE assets without requiring them to be public, solving the limitation of global service account approaches.
|
|
11
18
|
- **SEPAL-based Initialization**: Create sessions using SEPAL headers. The required ``sepal-session-id`` cookie is automatically used to retrieve GEE credentials.
|
|
12
|
-
- **
|
|
19
|
+
- **Multi-User Session Management**: Encapsulate user-specific credentials and project data in independent ``EESession`` objects.
|
|
13
20
|
- **Enhanced API Operations**: Access GEE functionalities via the ``operations`` property, which includes methods such as:
|
|
14
21
|
- ``get_info``: Retrieve detailed information about an Earth Engine object.
|
|
15
22
|
- ``get_map_id``: Generate a map ID for an Earth Engine image.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ee-client
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: extends the capabilities of the earthengine-api by providing custom session management and client interactions
|
|
5
5
|
Author-email: Daniel Guerrero <dfgm2006@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/dfguerrerom/ee-client
|
|
@@ -32,15 +32,22 @@ Dynamic: license-file
|
|
|
32
32
|
Earth Engine Client 🌎
|
|
33
33
|
==============================
|
|
34
34
|
|
|
35
|
-
The **Earth Engine Session Client** is a Python package that extends the Google Earth Engine (GEE) API by introducing multi-user session management through
|
|
35
|
+
The **Earth Engine Session Client** is a Python package that extends the Google Earth Engine (GEE) API by introducing multi-user session management through custom authentication.
|
|
36
|
+
|
|
37
|
+
**Why This Package?**
|
|
38
|
+
|
|
39
|
+
While Google Earth Engine applications can be created using a global service account, this approach has significant limitations: users cannot access their private GEE assets without making them public. This package solves this problem by handling custom authentication, allowing each user to access their own private assets securely.
|
|
40
|
+
|
|
41
|
+
Unlike the standard GEE API—which relies on a global session object and does not support multi-user environments—this client ensures that each session is authenticated and managed independently with user-specific credentials.
|
|
36
42
|
|
|
37
43
|
Each session is instantiated via the ``EESession`` class, currently only accepts SEPAL headers as its only parameter. **A valid ``sepal-session-id`` cookie must be present in these headers**, as it is used to retrieve the corresponding GEE credentials. Once authenticated, the session exposes an ``operations`` property that provides easy access to key API methods.
|
|
38
44
|
|
|
39
45
|
Key Features
|
|
40
46
|
------------
|
|
41
47
|
|
|
48
|
+
- **Custom User Authentication**: Enable users to access their private GEE assets without requiring them to be public, solving the limitation of global service account approaches.
|
|
42
49
|
- **SEPAL-based Initialization**: Create sessions using SEPAL headers. The required ``sepal-session-id`` cookie is automatically used to retrieve GEE credentials.
|
|
43
|
-
- **
|
|
50
|
+
- **Multi-User Session Management**: Encapsulate user-specific credentials and project data in independent ``EESession`` objects.
|
|
44
51
|
- **Enhanced API Operations**: Access GEE functionalities via the ``operations`` property, which includes methods such as:
|
|
45
52
|
- ``get_info``: Retrieve detailed information about an Earth Engine object.
|
|
46
53
|
- ``get_map_id``: Generate a map ID for an Earth Engine image.
|
|
@@ -9,6 +9,7 @@ from contextlib import asynccontextmanager
|
|
|
9
9
|
|
|
10
10
|
from eeclient.exceptions import EEClientError, EERestException
|
|
11
11
|
from eeclient.models import GEEHeaders, GoogleTokens, SepalHeaders
|
|
12
|
+
from eeclient.sepal_credential_mixin import SepalCredentialMixin
|
|
12
13
|
|
|
13
14
|
import eeclient.export as _export_module
|
|
14
15
|
import eeclient.data as _operations_module
|
|
@@ -33,7 +34,7 @@ SEPAL_API_DOWNLOAD_URL = None
|
|
|
33
34
|
VERIFY_SSL = True
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
class EESession:
|
|
37
|
+
class EESession(SepalCredentialMixin):
|
|
37
38
|
def __init__(self, sepal_headers: SepalHeaders, enforce_project_id: bool = True):
|
|
38
39
|
"""Session that handles two scenarios to set the headers for Earth Engine API
|
|
39
40
|
|
|
@@ -48,38 +49,17 @@ class EESession:
|
|
|
48
49
|
Raises:
|
|
49
50
|
ValueError: If SEPAL_HOST environment variable is not set
|
|
50
51
|
"""
|
|
51
|
-
|
|
52
|
-
self.sepal_host = os.getenv("SEPAL_HOST")
|
|
53
|
-
if not self.sepal_host:
|
|
54
|
-
raise ValueError("SEPAL_HOST environment variable not set")
|
|
55
|
-
|
|
56
|
-
self.sepal_api_download_url = (
|
|
57
|
-
f"https://{self.sepal_host}/api/user-files/download/"
|
|
58
|
-
"?path=%2F.config%2Fearthengine%2Fcredentials"
|
|
59
|
-
)
|
|
60
|
-
self.verify_ssl = not (
|
|
61
|
-
self.sepal_host == "host.docker.internal"
|
|
62
|
-
or self.sepal_host == "danielg.sepal.io"
|
|
63
|
-
)
|
|
52
|
+
super().__init__(sepal_headers)
|
|
64
53
|
|
|
65
|
-
self.expiry_date = 0
|
|
66
54
|
self.max_retries = 3
|
|
67
55
|
self._credentials = None
|
|
68
|
-
|
|
69
56
|
self.enforce_project_id = enforce_project_id
|
|
70
|
-
self.sepal_headers = SepalHeaders.model_validate(sepal_headers)
|
|
71
|
-
self.sepal_session_id = self.sepal_headers.cookies["SEPAL-SESSIONID"]
|
|
72
|
-
self.sepal_user_data = self.sepal_headers.sepal_user
|
|
73
|
-
self.user = self.sepal_user_data.username
|
|
74
57
|
|
|
75
58
|
# Create user-specific logger
|
|
76
59
|
self.logger = logging.getLogger(f"eeclient.{self.user}")
|
|
77
60
|
|
|
78
61
|
# Initialize credentials from the initial tokens
|
|
79
|
-
self._google_tokens = self.sepal_user_data.google_tokens
|
|
80
62
|
if self._google_tokens:
|
|
81
|
-
self.expiry_date = self._google_tokens.access_token_expiry_date
|
|
82
|
-
self.project_id = self._google_tokens.project_id
|
|
83
63
|
self._credentials = self._google_tokens
|
|
84
64
|
else:
|
|
85
65
|
self._credentials = None
|
|
@@ -118,7 +98,7 @@ class EESession:
|
|
|
118
98
|
return await session.initialize()
|
|
119
99
|
|
|
120
100
|
async def get_assets_folder(self) -> str:
|
|
121
|
-
if self.
|
|
101
|
+
if self.needs_credentials_refresh():
|
|
122
102
|
await self.set_credentials()
|
|
123
103
|
return f"projects/{self.project_id}/assets/"
|
|
124
104
|
|
|
@@ -126,6 +106,10 @@ class EESession:
|
|
|
126
106
|
"""Returns if a token is about to expire"""
|
|
127
107
|
return (self.expiry_date / 1000) - time.time() < 60
|
|
128
108
|
|
|
109
|
+
def needs_credentials_refresh(self) -> bool:
|
|
110
|
+
"""Returns if credentials need to be refreshed (missing or expired)"""
|
|
111
|
+
return not self._credentials or self.is_expired()
|
|
112
|
+
|
|
129
113
|
def get_current_headers(self) -> GEEHeaders:
|
|
130
114
|
"""Get current headers without refreshing credentials"""
|
|
131
115
|
if not self._credentials:
|
|
@@ -143,7 +127,7 @@ class EESession:
|
|
|
143
127
|
|
|
144
128
|
async def get_headers(self) -> GEEHeaders:
|
|
145
129
|
"""Async method to get headers, refreshing credentials if needed"""
|
|
146
|
-
if self.
|
|
130
|
+
if self.needs_credentials_refresh():
|
|
147
131
|
await self.set_credentials()
|
|
148
132
|
return self.get_current_headers()
|
|
149
133
|
|
|
@@ -246,9 +230,12 @@ class EESession:
|
|
|
246
230
|
try:
|
|
247
231
|
async with self.get_client() as client:
|
|
248
232
|
url_with_project = self.set_url_project(url)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
233
|
+
|
|
234
|
+
if "assets" not in url_with_project:
|
|
235
|
+
# Do not log assets requests
|
|
236
|
+
self.logger.debug(
|
|
237
|
+
f"Making async {method} request to {url_with_project}"
|
|
238
|
+
)
|
|
252
239
|
response = await client.request(
|
|
253
240
|
method, url_with_project, json=data, params=params
|
|
254
241
|
)
|
|
@@ -9,6 +9,7 @@ if TYPE_CHECKING:
|
|
|
9
9
|
from ee import serializer
|
|
10
10
|
import ee
|
|
11
11
|
import logging
|
|
12
|
+
from eeclient.tasks import Task
|
|
12
13
|
|
|
13
14
|
log = logging.getLogger("eeclient.export.image")
|
|
14
15
|
|
|
@@ -127,11 +128,14 @@ async def _export_image(
|
|
|
127
128
|
scale: Optional[float] = None,
|
|
128
129
|
crs: Optional[str] = None,
|
|
129
130
|
crs_transform: Optional[AffineTransform] = None,
|
|
130
|
-
) ->
|
|
131
|
+
) -> Task:
|
|
131
132
|
"""
|
|
132
|
-
Export
|
|
133
|
+
Export an image to either Google Drive or Earth Engine Asset.
|
|
133
134
|
|
|
134
135
|
Exactly one of drive_options or asset_options must be provided.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Task: The export task response from the API
|
|
135
139
|
"""
|
|
136
140
|
if (drive_options is None and asset_options is None) or (
|
|
137
141
|
drive_options is not None and asset_options is not None
|
|
@@ -177,7 +181,8 @@ async def _export_image(
|
|
|
177
181
|
log.debug(f"Exporting image with params: {request_params}")
|
|
178
182
|
|
|
179
183
|
url = "{earth_engine_api_url}/projects/{project}/image:export"
|
|
180
|
-
|
|
184
|
+
response_data = await client.rest_call("POST", url, data=request_params)
|
|
185
|
+
return Task.model_validate(response_data)
|
|
181
186
|
|
|
182
187
|
|
|
183
188
|
async def image_to_drive_async(
|
|
@@ -198,8 +203,12 @@ async def image_to_drive_async(
|
|
|
198
203
|
scale: Optional[float] = None,
|
|
199
204
|
crs: Optional[str] = None,
|
|
200
205
|
crs_transform: Optional[AffineTransform] = None,
|
|
201
|
-
) ->
|
|
202
|
-
"""Abstracts the export of an image to Google Drive.
|
|
206
|
+
) -> Task:
|
|
207
|
+
"""Abstracts the export of an image to Google Drive.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Task: The export task response from the API
|
|
211
|
+
"""
|
|
203
212
|
drive_options = DriveOptions(
|
|
204
213
|
file_format=file_format,
|
|
205
214
|
drive_destination=DriveDestination(
|
|
@@ -240,8 +249,12 @@ async def image_to_asset_async(
|
|
|
240
249
|
scale: Optional[float] = None,
|
|
241
250
|
crs: Optional[str] = None,
|
|
242
251
|
crs_transform: Optional[AffineTransform] = None,
|
|
243
|
-
) ->
|
|
244
|
-
"""Abstracts the export of an image to Earth Engine Asset.
|
|
252
|
+
) -> Task:
|
|
253
|
+
"""Abstracts the export of an image to Earth Engine Asset.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Task: The export task response from the API
|
|
257
|
+
"""
|
|
245
258
|
asset_options = AssetOptions(
|
|
246
259
|
earth_engine_destination=EarthEngineDestination(name=asset_id),
|
|
247
260
|
)
|
|
@@ -7,6 +7,7 @@ if TYPE_CHECKING:
|
|
|
7
7
|
from eeclient.client import EESession
|
|
8
8
|
|
|
9
9
|
from ee import serializer, encodable
|
|
10
|
+
from eeclient.tasks import Task
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class TableFileFormat(str, Enum):
|
|
@@ -75,11 +76,14 @@ async def _export_table(
|
|
|
75
76
|
max_vertices: Optional[int] = None,
|
|
76
77
|
workload_tag: Optional[str] = None,
|
|
77
78
|
priority: Optional[int] = None,
|
|
78
|
-
) ->
|
|
79
|
+
) -> Task:
|
|
79
80
|
"""
|
|
80
81
|
Export a table to either Google Drive or Earth Engine Asset.
|
|
81
82
|
|
|
82
83
|
Exactly one of drive_options or asset_options must be provided.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Task: The export task response from the API
|
|
83
87
|
"""
|
|
84
88
|
if (drive_options is None and asset_options is None) or (
|
|
85
89
|
drive_options is not None and asset_options is not None
|
|
@@ -107,24 +111,30 @@ async def _export_table(
|
|
|
107
111
|
params = export_options.model_dump(by_alias=True, exclude_none=True)
|
|
108
112
|
|
|
109
113
|
url = "{earth_engine_api_url}/projects/{project}/table:export"
|
|
110
|
-
|
|
114
|
+
response_data = await client.rest_call("POST", url, data=params)
|
|
115
|
+
return Task.model_validate(response_data)
|
|
111
116
|
|
|
112
117
|
|
|
113
118
|
async def table_to_drive_async(
|
|
114
119
|
client: "EESession",
|
|
115
120
|
collection,
|
|
116
|
-
filename_prefix: str,
|
|
117
121
|
file_format: TableFileFormat,
|
|
122
|
+
filename_prefix: str = "",
|
|
118
123
|
folder: Optional[str] = None,
|
|
119
124
|
description: str = "myExportTableTask",
|
|
120
125
|
selectors: Optional[list] = None,
|
|
121
126
|
max_vertices: Optional[int] = None,
|
|
122
127
|
priority: Optional[int] = None,
|
|
123
|
-
) ->
|
|
128
|
+
) -> Task:
|
|
124
129
|
"""
|
|
125
130
|
Export a table to Google Drive.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Task: The export task response from the API
|
|
126
134
|
"""
|
|
127
|
-
drive_destination = DriveDestination(
|
|
135
|
+
drive_destination = DriveDestination(
|
|
136
|
+
filename_prefix=filename_prefix or description, folder=folder
|
|
137
|
+
)
|
|
128
138
|
drive_options = DriveOptions(
|
|
129
139
|
file_format=file_format, drive_destination=drive_destination
|
|
130
140
|
)
|
|
@@ -148,9 +158,12 @@ async def table_to_asset_async(
|
|
|
148
158
|
selectors: Optional[list] = None,
|
|
149
159
|
max_vertices: Optional[int] = None,
|
|
150
160
|
priority: Optional[int] = None,
|
|
151
|
-
) ->
|
|
161
|
+
) -> Task:
|
|
152
162
|
"""
|
|
153
163
|
Export a table to Earth Engine Asset.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Task: The export task response from the API
|
|
154
167
|
"""
|
|
155
168
|
asset_options = AssetOptions(
|
|
156
169
|
earth_engine_destination=EarthEngineDestination(name=asset_id)
|
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
import requests
|
|
3
3
|
import logging
|
|
4
4
|
from eeclient.models import SepalHeaders, SepalUser
|
|
5
|
-
from typing import Union
|
|
5
|
+
from typing import Optional, Union
|
|
6
6
|
|
|
7
7
|
from ee.imagecollection import ImageCollection
|
|
8
8
|
from ee.feature import Feature
|
|
@@ -59,11 +59,17 @@ def parse_cookie_string(cookie_string):
|
|
|
59
59
|
return cookies
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def get_sepal_headers_from_auth(
|
|
62
|
+
def get_sepal_headers_from_auth(
|
|
63
|
+
sepal_user: Optional[str] = None,
|
|
64
|
+
sepal_password: Optional[str] = None,
|
|
65
|
+
sepal_host: Optional[str] = None,
|
|
66
|
+
) -> SepalHeaders:
|
|
67
|
+
|
|
63
68
|
log.debug("Getting SEPAL headers from authentication")
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
|
|
70
|
+
sepal_user = sepal_user or os.getenv("LOCAL_SEPAL_USER")
|
|
71
|
+
sepal_password = sepal_password or os.getenv("LOCAL_SEPAL_PASSWORD")
|
|
72
|
+
sepal_host = sepal_host or os.getenv("SEPAL_HOST")
|
|
67
73
|
|
|
68
74
|
if not sepal_user or not sepal_password or not sepal_host:
|
|
69
75
|
raise ValueError(
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from eeclient.models import SepalHeaders
|
|
2
|
+
import os
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
log = logging.getLogger("eeclient")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SepalCredentialMixin:
|
|
9
|
+
def __init__(self, sepal_headers: SepalHeaders):
|
|
10
|
+
log.debug("Initializing GDriveInterface with SEPAL headers")
|
|
11
|
+
self.sepal_host = os.getenv("SEPAL_HOST")
|
|
12
|
+
if not self.sepal_host:
|
|
13
|
+
raise ValueError("SEPAL_HOST environment variable not set")
|
|
14
|
+
|
|
15
|
+
self.sepal_headers = SepalHeaders.model_validate(sepal_headers)
|
|
16
|
+
self.sepal_session_id = self.sepal_headers.cookies["SEPAL-SESSIONID"]
|
|
17
|
+
self.sepal_user_data = self.sepal_headers.sepal_user
|
|
18
|
+
self.user = self.sepal_user_data.username
|
|
19
|
+
|
|
20
|
+
self.sepal_api_download_url = (
|
|
21
|
+
f"https://{self.sepal_host}/api/user-files/download/"
|
|
22
|
+
"?path=%2F.config%2Fearthengine%2Fcredentials"
|
|
23
|
+
)
|
|
24
|
+
self.verify_ssl = not (
|
|
25
|
+
self.sepal_host == "host.docker.internal"
|
|
26
|
+
or self.sepal_host == "danielg.sepal.io"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
self._google_tokens = self.sepal_user_data.google_tokens
|
|
30
|
+
if self._google_tokens:
|
|
31
|
+
self.access_token = self._google_tokens.access_token
|
|
32
|
+
self.project_id = self._google_tokens.project_id
|
|
33
|
+
self.expiry_date = self._google_tokens.access_token_expiry_date
|
|
34
|
+
else:
|
|
35
|
+
self.access_token = None
|
|
36
|
+
self.project_id = None
|
|
37
|
+
self.expiry_date = 0
|
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|
|
3
3
|
|
|
4
4
|
if TYPE_CHECKING:
|
|
5
5
|
from eeclient.client import EESession
|
|
6
|
-
from pydantic import BaseModel, Field
|
|
6
|
+
from pydantic import BaseModel, Field, computed_field
|
|
7
7
|
from typing import List, Optional, Any
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
|
|
@@ -31,7 +31,7 @@ class Stage(CamelCaseModel):
|
|
|
31
31
|
|
|
32
32
|
class TaskMetadata(CamelCaseModel):
|
|
33
33
|
type_info: str = Field(alias="@type")
|
|
34
|
-
state: str
|
|
34
|
+
state: Optional[str] = None
|
|
35
35
|
description: str
|
|
36
36
|
priority: int
|
|
37
37
|
create_time: datetime
|
|
@@ -40,7 +40,7 @@ class TaskMetadata(CamelCaseModel):
|
|
|
40
40
|
end_time: Optional[datetime] = None
|
|
41
41
|
type: str
|
|
42
42
|
destination_uris: Optional[List[str]] = None
|
|
43
|
-
attempt: int
|
|
43
|
+
attempt: Optional[int] = None
|
|
44
44
|
progress: Optional[float] = None
|
|
45
45
|
stages: Optional[List[Stage]] = None
|
|
46
46
|
batch_eecu_usage_seconds: Optional[float] = None
|
|
@@ -55,9 +55,20 @@ class Task(CamelCaseModel):
|
|
|
55
55
|
metadata: TaskMetadata
|
|
56
56
|
done: Optional[bool] = None
|
|
57
57
|
response: Optional[Response] = None
|
|
58
|
-
# Add error field which might be present instead of response
|
|
59
58
|
error: Optional[Any] = None
|
|
60
59
|
|
|
60
|
+
@computed_field
|
|
61
|
+
@property
|
|
62
|
+
def id(self) -> str:
|
|
63
|
+
"""Extract the task ID from the name field.
|
|
64
|
+
|
|
65
|
+
The name field has format: 'projects/{project}/operations/{task_id}'
|
|
66
|
+
This property extracts just the {task_id} part.
|
|
67
|
+
"""
|
|
68
|
+
if "/operations/" in self.name:
|
|
69
|
+
return self.name.split("/operations/")[-1]
|
|
70
|
+
return self.name
|
|
71
|
+
|
|
61
72
|
|
|
62
73
|
class TasksResponse(CamelCaseModel):
|
|
63
74
|
operations: List[Task]
|
|
@@ -79,7 +90,7 @@ async def get_tasks_async(client: "EESession") -> TasksResponse:
|
|
|
79
90
|
return operations_response
|
|
80
91
|
|
|
81
92
|
|
|
82
|
-
async def get_task_async(client: "EESession", task_id: str):
|
|
93
|
+
async def get_task_async(client: "EESession", task_id: str) -> Optional[Task]:
|
|
83
94
|
"""Search for the described task in the user Task list return None if
|
|
84
95
|
nothing is found.
|
|
85
96
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ee-client"
|
|
7
|
-
version = "2.0
|
|
7
|
+
version = "2.1.0"
|
|
8
8
|
description = "extends the capabilities of the earthengine-api by providing custom session management and client interactions"
|
|
9
9
|
readme = { file = "README.rst", content-type = "text/x-rst" }
|
|
10
10
|
authors = [
|
|
@@ -66,7 +66,7 @@ branch = true
|
|
|
66
66
|
[tool.commitizen]
|
|
67
67
|
tag_format = "v$major.$minor.$patch$prerelease"
|
|
68
68
|
update_changelog_on_bump = false
|
|
69
|
-
version = "2.0
|
|
69
|
+
version = "2.1.0"
|
|
70
70
|
version_files = [
|
|
71
71
|
"pyproject.toml:version",
|
|
72
72
|
"eeclient/__init__.py:__version__",
|
|
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
|