ee-client 1.2.2__tar.gz → 1.3.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.
Files changed (24) hide show
  1. {ee_client-1.2.2 → ee_client-1.3.0}/PKG-INFO +6 -4
  2. {ee_client-1.2.2 → ee_client-1.3.0}/README.rst +3 -2
  3. {ee_client-1.2.2 → ee_client-1.3.0}/ee_client.egg-info/PKG-INFO +6 -4
  4. {ee_client-1.2.2 → ee_client-1.3.0}/ee_client.egg-info/SOURCES.txt +1 -1
  5. {ee_client-1.2.2 → ee_client-1.3.0}/eeclient/__init__.py +1 -1
  6. {ee_client-1.2.2 → ee_client-1.3.0}/eeclient/client.py +5 -7
  7. {ee_client-1.2.2 → ee_client-1.3.0}/eeclient/data.py +1 -1
  8. {ee_client-1.2.2 → ee_client-1.3.0}/eeclient/helpers.py +49 -1
  9. {ee_client-1.2.2 → ee_client-1.3.0}/eeclient/logger.py +22 -3
  10. ee_client-1.2.2/eeclient/typing.py → ee_client-1.3.0/eeclient/models.py +4 -24
  11. {ee_client-1.2.2 → ee_client-1.3.0}/pyproject.toml +2 -2
  12. {ee_client-1.2.2 → ee_client-1.3.0}/tests/test_client.py +1 -1
  13. {ee_client-1.2.2 → ee_client-1.3.0}/tests/test_models.py +1 -8
  14. {ee_client-1.2.2 → ee_client-1.3.0}/LICENSE +0 -0
  15. {ee_client-1.2.2 → ee_client-1.3.0}/ee_client.egg-info/dependency_links.txt +0 -0
  16. {ee_client-1.2.2 → ee_client-1.3.0}/ee_client.egg-info/requires.txt +0 -0
  17. {ee_client-1.2.2 → ee_client-1.3.0}/ee_client.egg-info/top_level.txt +0 -0
  18. {ee_client-1.2.2 → ee_client-1.3.0}/eeclient/exceptions.py +0 -0
  19. {ee_client-1.2.2 → ee_client-1.3.0}/eeclient/export/image.py +0 -0
  20. {ee_client-1.2.2 → ee_client-1.3.0}/eeclient/export/table.py +0 -0
  21. {ee_client-1.2.2 → ee_client-1.3.0}/eeclient/oauth_app.py +0 -0
  22. {ee_client-1.2.2 → ee_client-1.3.0}/eeclient/tasks.py +0 -0
  23. {ee_client-1.2.2 → ee_client-1.3.0}/setup.cfg +0 -0
  24. {ee_client-1.2.2 → ee_client-1.3.0}/tests/test_data.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: ee-client
3
- Version: 1.2.2
3
+ Version: 1.3.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
@@ -27,6 +27,7 @@ Requires-Dist: tomli; extra == "dev"
27
27
  Requires-Dist: mypy; extra == "dev"
28
28
  Requires-Dist: pytest; extra == "dev"
29
29
  Requires-Dist: flask; extra == "dev"
30
+ Dynamic: license-file
30
31
 
31
32
  Earth Engine Client 🌎
32
33
  ==============================
@@ -103,12 +104,13 @@ After initializing the session, use the ``operations`` property to access the ke
103
104
 
104
105
  import ee
105
106
 
106
- # Initialize the Earth Engine library
107
+ # Initialize the Earth Engine library (this can use any authentication method/account)
108
+ # The purpose of this is to ensure the ee library is available for use
107
109
  ee.Initialize()
108
110
 
109
111
  # Use the operations available in the session
110
112
  result_info = session.operations.get_info(ee.Number(5))
111
- print(result_info)
113
+ print(result_info) # the GEE server call is done using the custom EE client
112
114
 
113
115
  # Example: Generate a map ID for an Earth Engine image
114
116
  image = ee.Image('COPERNICUS/S2/20190726T104031_20190726T104035_T31TGL')
@@ -73,12 +73,13 @@ After initializing the session, use the ``operations`` property to access the ke
73
73
 
74
74
  import ee
75
75
 
76
- # Initialize the Earth Engine library
76
+ # Initialize the Earth Engine library (this can use any authentication method/account)
77
+ # The purpose of this is to ensure the ee library is available for use
77
78
  ee.Initialize()
78
79
 
79
80
  # Use the operations available in the session
80
81
  result_info = session.operations.get_info(ee.Number(5))
81
- print(result_info)
82
+ print(result_info) # the GEE server call is done using the custom EE client
82
83
 
83
84
  # Example: Generate a map ID for an Earth Engine image
84
85
  image = ee.Image('COPERNICUS/S2/20190726T104031_20190726T104035_T31TGL')
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: ee-client
3
- Version: 1.2.2
3
+ Version: 1.3.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
@@ -27,6 +27,7 @@ Requires-Dist: tomli; extra == "dev"
27
27
  Requires-Dist: mypy; extra == "dev"
28
28
  Requires-Dist: pytest; extra == "dev"
29
29
  Requires-Dist: flask; extra == "dev"
30
+ Dynamic: license-file
30
31
 
31
32
  Earth Engine Client 🌎
32
33
  ==============================
@@ -103,12 +104,13 @@ After initializing the session, use the ``operations`` property to access the ke
103
104
 
104
105
  import ee
105
106
 
106
- # Initialize the Earth Engine library
107
+ # Initialize the Earth Engine library (this can use any authentication method/account)
108
+ # The purpose of this is to ensure the ee library is available for use
107
109
  ee.Initialize()
108
110
 
109
111
  # Use the operations available in the session
110
112
  result_info = session.operations.get_info(ee.Number(5))
111
- print(result_info)
113
+ print(result_info) # the GEE server call is done using the custom EE client
112
114
 
113
115
  # Example: Generate a map ID for an Earth Engine image
114
116
  image = ee.Image('COPERNICUS/S2/20190726T104031_20190726T104035_T31TGL')
@@ -12,9 +12,9 @@ eeclient/data.py
12
12
  eeclient/exceptions.py
13
13
  eeclient/helpers.py
14
14
  eeclient/logger.py
15
+ eeclient/models.py
15
16
  eeclient/oauth_app.py
16
17
  eeclient/tasks.py
17
- eeclient/typing.py
18
18
  eeclient/export/image.py
19
19
  eeclient/export/table.py
20
20
  tests/test_client.py
@@ -1,6 +1,6 @@
1
1
  __title__ = "eeclient"
2
2
  __summary__ = "A client for Google Earth Engine"
3
- __version__ = "1.2.2"
3
+ __version__ = "1.3.0"
4
4
 
5
5
  __author__ = "Daniel Guerrero"
6
6
  __email__ = "dfgm2006@gmail.com"
@@ -1,19 +1,16 @@
1
- from pathlib import Path
2
1
  from typing import Any, Dict, Literal, Optional
3
2
 
4
3
  import os
5
4
  import time
6
- import json
7
5
  import asyncio
8
6
  import httpx
9
7
  from contextlib import asynccontextmanager
10
8
 
11
-
12
9
  from eeclient.export.image import image_to_asset, image_to_drive
13
10
  from eeclient.logger import logger
14
11
  from eeclient.exceptions import EEClientError, EERestException
15
12
  from eeclient.tasks import get_task, get_task_by_name, get_tasks
16
- from eeclient.typing import GEEHeaders, GoogleTokens, MapTileOptions, SepalHeaders
13
+ from eeclient.models import GEEHeaders, GoogleTokens, MapTileOptions, SepalHeaders
17
14
  from eeclient.data import (
18
15
  create_folder,
19
16
  delete_asset,
@@ -49,7 +46,7 @@ class EESession:
49
46
  self.max_retries = 3
50
47
 
51
48
  self.enforce_project_id = enforce_project_id
52
-
49
+ logger.debug(str(sepal_headers))
53
50
  self.sepal_headers = SepalHeaders.model_validate(sepal_headers)
54
51
  self.sepal_session_id = self.sepal_headers.cookies["SEPAL-SESSIONID"]
55
52
  self.sepal_user_data = self.sepal_headers.sepal_user
@@ -61,12 +58,13 @@ class EESession:
61
58
  # if not I will get this error:
62
59
  # httpx.HTTPStatusError: Client error '401 Unauthorized' for url 'https://danielg.sepal.io/api/user-files/listFiles/?path=%2F&extensions='
63
60
 
64
- def _initialize_credentials(self):
61
+ def _initialize_credentials(self) -> None:
65
62
  """Initialize credentials from the initial Google tokens"""
66
63
  _google_tokens = self.sepal_user_data.google_tokens
67
64
 
68
65
  if not _google_tokens:
69
- raise EEClientError("No google tokens found in the user data.")
66
+ # Get them with the sepal_session_id
67
+ return asyncio.run(self.set_credentials())
70
68
 
71
69
  self.expiry_date = _google_tokens.access_token_expiry_date
72
70
  self.project_id = _google_tokens.project_id
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, List, Optional, Union
5
5
 
6
6
  from eeclient.exceptions import EERestException
7
7
  from eeclient.logger import logger
8
- from eeclient.typing import MapTileOptions
8
+ from eeclient.models import MapTileOptions
9
9
  from eeclient.helpers import _get_ee_image, convert_asset_id_to_asset_name
10
10
 
11
11
  if TYPE_CHECKING:
@@ -1,10 +1,15 @@
1
+ import os
2
+ import requests
3
+ import logging
4
+ from eeclient.models import SepalHeaders, SepalUser
5
+ from eeclient.logger import logger
1
6
  from typing import Union
2
7
 
3
8
  from ee.imagecollection import ImageCollection
4
9
  from ee.feature import Feature
5
10
  from ee.featurecollection import FeatureCollection
6
11
  from ee.image import Image
7
- from eeclient.typing import MapTileOptions
12
+ from eeclient.models import MapTileOptions
8
13
  from ee.data import convert_asset_id_to_asset_name # type: ignore: it will be imported from another moduel
9
14
 
10
15
 
@@ -51,3 +56,46 @@ def parse_cookie_string(cookie_string):
51
56
  key, value = key_value
52
57
  cookies[key] = value
53
58
  return cookies
59
+
60
+
61
+ def get_sepal_headers_from_auth():
62
+ sepal_user = os.getenv("LOCAL_SEPAL_USER")
63
+ sepal_password = os.getenv("LOCAL_SEPAL_PASSWORD")
64
+ sepal_host = os.getenv("SEPAL_HOST")
65
+
66
+ if not sepal_user or not sepal_password or not sepal_host:
67
+ raise ValueError(
68
+ "LOCAL_SEPAL_USER, LOCAL_SEPAL_PASSWORD, and SEPAL_HOST must be set"
69
+ )
70
+
71
+ session = requests.Session()
72
+ session.verify = False
73
+
74
+ creds_response = session.post(
75
+ f"https://{sepal_host}/api/user/login",
76
+ headers={
77
+ "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
78
+ "No-Auth-Challenge": "true",
79
+ },
80
+ auth=(sepal_user, sepal_password),
81
+ data="",
82
+ )
83
+
84
+ creds_response.raise_for_status()
85
+
86
+ logger.debug(f"Authentication successful. Cookies: {session.cookies}")
87
+ logger.debug(f"Response: {creds_response.json()}")
88
+
89
+ sepal_user_obj = SepalUser.model_validate(creds_response.json())
90
+
91
+ # Replace the project_id with the one from the environment
92
+ sepal_user_obj.google_tokens.project_id = "sepal-ui-421413"
93
+
94
+ cookies_dict = {cookie.name: cookie.value for cookie in session.cookies}
95
+
96
+ sepal_headers = {
97
+ "cookie": cookies_dict,
98
+ "sepal-user": sepal_user_obj,
99
+ }
100
+
101
+ return SepalHeaders.model_validate(sepal_headers)
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import sys
2
3
  import logging
3
4
  from pathlib import Path
@@ -12,14 +13,32 @@ RESET = "\033[0m"
12
13
 
13
14
  class CustomLogger:
14
15
  def __init__(
15
- self, name: str, level: int = logging.DEBUG, module_color: str = BLUE_BG
16
+ self, name: str, level: int = logging.INFO, module_color: str = BLUE_BG
16
17
  ):
18
+ env_log_level_str = os.getenv(
19
+ "EE_CLIENT_LOG_LEVEL", logging.getLevelName(level)
20
+ ).upper()
21
+ try:
22
+ effective_level = logging.getLevelName(env_log_level_str)
23
+ if not isinstance(effective_level, int):
24
+ effective_level = level
25
+ print(
26
+ f"Warning: Invalid EE_CLIENT_LOG_LEVEL '{env_log_level_str}'. Defaulting to {logging.getLevelName(level)}.",
27
+ file=sys.stderr,
28
+ )
29
+ except ValueError:
30
+ effective_level = level
31
+ print(
32
+ f"Warning: Could not parse EE_CLIENT_LOG_LEVEL '{env_log_level_str}'. Defaulting to {logging.getLevelName(level)}.",
33
+ file=sys.stderr,
34
+ )
35
+
17
36
  self.logger = logging.getLogger(name)
18
- self.logger.setLevel(level)
37
+ self.logger.setLevel(effective_level)
19
38
 
20
39
  # Create a colored console handler.
21
40
  console_handler = logging.StreamHandler(sys.stdout)
22
- console_handler.setLevel(level)
41
+ console_handler.setLevel(effective_level) # Also set handler level
23
42
 
24
43
  # Build the format string using the module_color for the logger's name.
25
44
  format_str = f"%(log_color)s%(asctime)s - {module_color}%(name)s{RESET} - %(levelname)s - %(message)s"
@@ -69,38 +69,18 @@ class GoogleTokens(BaseModel):
69
69
 
70
70
 
71
71
  class SepalUser(BaseModel):
72
- id: int
73
72
  username: str
74
- # Make it optional to catch missing tokens
73
+ id: Optional[int] = None
75
74
  google_tokens: Optional[GoogleTokens] = None
76
- status: str
77
- roles: List[str]
78
- system_user: bool
79
- admin: bool
75
+ status: Optional[str] = None
76
+ roles: Optional[List[str]] = None
77
+ system_user: Optional[bool] = None
80
78
 
81
79
  model_config = ConfigDict(
82
80
  alias_generator=to_camel,
83
81
  populate_by_name=True,
84
82
  )
85
83
 
86
- @model_validator(mode="before")
87
- def check_google_tokens_present(cls, values: dict) -> dict:
88
- # Look for the token in both the field name and its alias
89
- token_value = values.get("google_tokens") or values.get("googleTokens")
90
- if not token_value:
91
- raise EEClientError(
92
- "Authentication required: Please authenticate via sepal. See https://docs.sepal.io/en/latest/setup/gee.html."
93
- )
94
- return values
95
-
96
- @model_validator(mode="after")
97
- def check_google_tokens(self) -> "SepalUser":
98
- if not self.google_tokens:
99
- raise EEClientError(
100
- "Authentication required: Please authenticate via sepal. See https://docs.sepal.io/en/latest/setup/gee.html."
101
- )
102
- return self
103
-
104
84
 
105
85
  class SepalHeaders(BaseModel):
106
86
  cookies: SepalCookies = Field(..., alias="cookie")
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ee-client"
7
- version = "1.2.2"
7
+ version = "1.3.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 = "1.2.2"
69
+ version = "1.3.0"
70
70
  version_files = [
71
71
  "pyproject.toml:version",
72
72
  "eeclient/__init__.py:__version__",
@@ -16,7 +16,7 @@ def test_init_client(sepal_headers):
16
16
  def test_is_expired(sepal_headers, dummy_headers):
17
17
 
18
18
  sepal_session = EESession(sepal_headers=sepal_headers)
19
- assert sepal_session.is_expired() == True
19
+ assert sepal_session.is_expired() == False
20
20
 
21
21
  sepal_session = EESession(sepal_headers=dummy_headers)
22
22
  assert sepal_session.is_expired() == True
@@ -4,7 +4,7 @@ import sys
4
4
  import pytest
5
5
 
6
6
  from eeclient.exceptions import EEClientError
7
- from eeclient.typing import SepalHeaders
7
+ from eeclient.models import SepalHeaders
8
8
 
9
9
  sys.path.append("..")
10
10
 
@@ -21,14 +21,7 @@ def test_headers(dummy_headers):
21
21
  assert sepal_headers.sepal_user.google_tokens.project_id == "ee-project"
22
22
 
23
23
 
24
- def test_headers_no_google_tokes(dummy_headers_no_google_tokens):
25
-
26
- with pytest.raises(EEClientError):
27
- SepalHeaders.model_validate(dummy_headers_no_google_tokens)
28
-
29
-
30
24
  def test_without_sepal_user(dummy_headers_no_project_id):
31
25
 
32
26
  with pytest.raises(EEClientError):
33
-
34
27
  SepalHeaders.model_validate(dummy_headers_no_project_id)
File without changes
File without changes
File without changes
File without changes