PyGeoModel 1.0.14__tar.gz → 1.0.16__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 (39) hide show
  1. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/PKG-INFO +4 -1
  2. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/PyGeoModel.egg-info/PKG-INFO +4 -1
  3. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/ogmsServer2/openUtils/http_client.py +11 -11
  4. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/__init__.py +1 -1
  5. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/client.py +3 -3
  6. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/modeler.py +20 -3
  7. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/setup.py +5 -1
  8. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/tests/test_core_api.py +121 -0
  9. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/LICENSE +0 -0
  10. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/MANIFEST.in +0 -0
  11. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/PyGeoModel.egg-info/SOURCES.txt +0 -0
  12. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/PyGeoModel.egg-info/dependency_links.txt +0 -0
  13. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/PyGeoModel.egg-info/requires.txt +0 -0
  14. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/PyGeoModel.egg-info/top_level.txt +0 -0
  15. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/README.md +0 -0
  16. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/ogmsServer2/__init__.py +0 -0
  17. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/ogmsServer2/base.py +0 -0
  18. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/ogmsServer2/constants.py +0 -0
  19. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/ogmsServer2/openModel.py +0 -0
  20. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/ogmsServer2/openUtils/__init__.py +0 -0
  21. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/ogmsServer2/openUtils/exceptions.py +0 -0
  22. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/ogmsServer2/openUtils/mdlUtils.py +0 -0
  23. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/ogmsServer2/openUtils/parameterValidator.py +0 -0
  24. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/ogmsServer2/openUtils/stateManager.py +0 -0
  25. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/config.py +0 -0
  26. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/consensus.py +0 -0
  27. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/context.py +0 -0
  28. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/data/__init__.py +0 -0
  29. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/data/computeModel.json +0 -0
  30. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/data/description_translations_en.json +0 -0
  31. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/data/modelContext.txt +0 -0
  32. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/data/modellist_2070.csv +0 -0
  33. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/models.py +0 -0
  34. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/notebook.py +0 -0
  35. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/qa.py +0 -0
  36. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/recommendation.py +0 -0
  37. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/pygeomodel/results.py +0 -0
  38. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/scripts.py +0 -0
  39. {pygeomodel-1.0.14 → pygeomodel-1.0.16}/setup.cfg +0 -0
@@ -1,10 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyGeoModel
3
- Version: 1.0.14
3
+ Version: 1.0.16
4
4
  Summary: A Python package for integrating OpenGMS geographic model services.
5
5
  Home-page: https://github.com/MpLebron/PyGeoModel
6
6
  Author: Peilong Ma
7
7
  Author-email: mpl_gis@nnu.edu.cn
8
+ Project-URL: Documentation, https://mplebron.github.io/PyGeoModel-docs/
9
+ Project-URL: Source, https://github.com/MpLebron/PyGeoModel
8
10
  Keywords: geographic modeling,GIS,OpenGMS,model services,geospatial analysis,jupyter
9
11
  Classifier: Intended Audience :: Science/Research
10
12
  Classifier: Topic :: Scientific/Engineering :: GIS
@@ -38,6 +40,7 @@ Dynamic: description-content-type
38
40
  Dynamic: home-page
39
41
  Dynamic: keywords
40
42
  Dynamic: license-file
43
+ Dynamic: project-url
41
44
  Dynamic: provides-extra
42
45
  Dynamic: requires-dist
43
46
  Dynamic: requires-python
@@ -1,10 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyGeoModel
3
- Version: 1.0.14
3
+ Version: 1.0.16
4
4
  Summary: A Python package for integrating OpenGMS geographic model services.
5
5
  Home-page: https://github.com/MpLebron/PyGeoModel
6
6
  Author: Peilong Ma
7
7
  Author-email: mpl_gis@nnu.edu.cn
8
+ Project-URL: Documentation, https://mplebron.github.io/PyGeoModel-docs/
9
+ Project-URL: Source, https://github.com/MpLebron/PyGeoModel
8
10
  Keywords: geographic modeling,GIS,OpenGMS,model services,geospatial analysis,jupyter
9
11
  Classifier: Intended Audience :: Science/Research
10
12
  Classifier: Topic :: Scientific/Engineering :: GIS
@@ -38,6 +40,7 @@ Dynamic: description-content-type
38
40
  Dynamic: home-page
39
41
  Dynamic: keywords
40
42
  Dynamic: license-file
43
+ Dynamic: project-url
41
44
  Dynamic: provides-extra
42
45
  Dynamic: requires-dist
43
46
  Dynamic: requires-python
@@ -12,7 +12,7 @@ class HttpClient:
12
12
  def _make_sync_request(
13
13
  method: str,
14
14
  url: str,
15
- timeout: int = 10,
15
+ timeout: int = 60,
16
16
  data: Optional[Union[Dict[str, Any], str, bytes]] = None,
17
17
  json: Optional[Dict[str, Any]] = None,
18
18
  files: Optional[Files] = None,
@@ -76,7 +76,7 @@ class HttpClient:
76
76
  @staticmethod
77
77
  def get_sync(
78
78
  url: str,
79
- timeout: int = 10,
79
+ timeout: int = 60,
80
80
  params: Optional[Dict[str, Any]] = None,
81
81
  headers: Optional[Headers] = None,
82
82
  ) -> Dict[str, Any]:
@@ -87,7 +87,7 @@ class HttpClient:
87
87
  @staticmethod
88
88
  def get_file_sync(
89
89
  url: str,
90
- timeout: int = 10,
90
+ timeout: int = 60,
91
91
  params: Optional[Dict[str, Any]] = None,
92
92
  headers: Optional[Headers] = None,
93
93
  ):
@@ -104,7 +104,7 @@ class HttpClient:
104
104
  @staticmethod
105
105
  def post_sync(
106
106
  url: str,
107
- timeout: int = 10,
107
+ timeout: int = 60,
108
108
  data: Optional[Union[Dict[str, Any], str, bytes]] = None,
109
109
  json: Optional[Dict[str, Any]] = None,
110
110
  files: Optional[Files] = None,
@@ -123,7 +123,7 @@ class HttpClient:
123
123
  @staticmethod
124
124
  def put_sync(
125
125
  url: str,
126
- timeout: int = 10,
126
+ timeout: int = 60,
127
127
  data: Optional[Union[Dict[str, Any], str, bytes]] = None,
128
128
  json: Optional[Dict[str, Any]] = None,
129
129
  headers: Optional[Headers] = None,
@@ -135,7 +135,7 @@ class HttpClient:
135
135
  @staticmethod
136
136
  def delete_sync(
137
137
  url: str,
138
- timeout: int = 10,
138
+ timeout: int = 60,
139
139
  params: Optional[Dict[str, Any]] = None,
140
140
  headers: Optional[Headers] = None,
141
141
  ) -> Dict[str, Any]:
@@ -147,7 +147,7 @@ class HttpClient:
147
147
  async def _make_async_request(
148
148
  method: str,
149
149
  url: str,
150
- timeout: int = 10,
150
+ timeout: int = 60,
151
151
  data: Optional[Union[Dict[str, Any], str, bytes]] = None,
152
152
  json: Optional[Dict[str, Any]] = None,
153
153
  files: Optional[Files] = None,
@@ -196,7 +196,7 @@ class HttpClient:
196
196
  @staticmethod
197
197
  async def get_async(
198
198
  url: str,
199
- timeout: int = 10,
199
+ timeout: int = 60,
200
200
  params: Optional[Dict[str, Any]] = None,
201
201
  headers: Optional[Headers] = None,
202
202
  ) -> Dict[str, Any]:
@@ -207,7 +207,7 @@ class HttpClient:
207
207
  @staticmethod
208
208
  async def post_async(
209
209
  url: str,
210
- timeout: int = 10,
210
+ timeout: int = 60,
211
211
  data: Optional[Union[Dict[str, Any], str, bytes]] = None,
212
212
  json: Optional[Dict[str, Any]] = None,
213
213
  files: Optional[Files] = None,
@@ -226,7 +226,7 @@ class HttpClient:
226
226
  @staticmethod
227
227
  async def put_async(
228
228
  url: str,
229
- timeout: int = 10,
229
+ timeout: int = 60,
230
230
  data: Optional[Union[Dict[str, Any], str, bytes]] = None,
231
231
  json: Optional[Dict[str, Any]] = None,
232
232
  headers: Optional[Headers] = None,
@@ -238,7 +238,7 @@ class HttpClient:
238
238
  @staticmethod
239
239
  async def delete_async(
240
240
  url: str,
241
- timeout: int = 10,
241
+ timeout: int = 60,
242
242
  params: Optional[Dict[str, Any]] = None,
243
243
  headers: Optional[Headers] = None,
244
244
  ) -> Dict[str, Any]:
@@ -5,7 +5,7 @@ from .modeler import GeoModeler
5
5
  from .models import ModelInput, ModelOutput, ModelService, ModelSummary
6
6
  from .results import QAResult, RecommendationResult, TaskResult
7
7
 
8
- __version__ = "1.0.14"
8
+ __version__ = "1.0.15"
9
9
 
10
10
  __all__ = [
11
11
  "GeoModeler",
@@ -33,7 +33,7 @@ class OpenGMSClient:
33
33
  response = requests.get(
34
34
  f"{self.portal_url}/sdk/check_test/",
35
35
  params={"token": self.token},
36
- timeout=10,
36
+ timeout=60,
37
37
  )
38
38
  response.raise_for_status()
39
39
  return response.json().get("data") == 1
@@ -41,7 +41,7 @@ class OpenGMSClient:
41
41
  def check_model(self, model_name: str) -> dict[str, Any]:
42
42
  response = requests.get(
43
43
  f"{self.portal_url}/computableModel/ModelInfo_name/{quote(model_name)}",
44
- timeout=15,
44
+ timeout=60,
45
45
  )
46
46
  response.raise_for_status()
47
47
  return response.json().get("data", {})
@@ -53,7 +53,7 @@ class OpenGMSClient:
53
53
  return False
54
54
  response = requests.get(
55
55
  f"{self.manager_url}/GeoModeling/task/verify/{md5}",
56
- timeout=10,
56
+ timeout=60,
57
57
  )
58
58
  response.raise_for_status()
59
59
  return response.json().get("data") is True
@@ -29,6 +29,7 @@ class GeoModeler:
29
29
  self._uses_custom_data_path = data_path is not None
30
30
  self.data_path = self._resolve_data_path(data_path)
31
31
  self._models_data = self._load_catalog()
32
+ self._model_aliases = self._build_model_aliases()
32
33
  self.model_names = list(self._models_data.keys())
33
34
  self.client = client or OpenGMSClient()
34
35
  self.last_result: TaskResult | None = None
@@ -68,9 +69,25 @@ class GeoModeler:
68
69
  return [summary for _, _, summary in scored[:limit]]
69
70
 
70
71
  def get_model(self, model_name: str) -> ModelService:
71
- if model_name not in self._models_data:
72
+ canonical_name = self._canonical_model_name(model_name)
73
+ return ModelService.from_raw(canonical_name, self._models_data[canonical_name])
74
+
75
+ def _canonical_model_name(self, model_name: str) -> str:
76
+ requested_name = str(model_name or "").strip()
77
+ canonical_name = self._model_aliases.get(requested_name)
78
+ if canonical_name is None:
72
79
  raise KeyError(f"Model '{model_name}' was not found in the local OpenGMS catalog")
73
- return ModelService.from_raw(model_name, self._models_data[model_name])
80
+ return canonical_name
81
+
82
+ def _build_model_aliases(self) -> dict[str, str]:
83
+ aliases: dict[str, str] = {}
84
+ for original_name, raw in self._models_data.items():
85
+ service = ModelService.from_raw(original_name, raw)
86
+ for alias in (original_name, service.display_name):
87
+ clean_alias = str(alias or "").strip()
88
+ if clean_alias:
89
+ aliases.setdefault(clean_alias, original_name)
90
+ return aliases
74
91
 
75
92
  def invoke(
76
93
  self,
@@ -83,7 +100,7 @@ class GeoModeler:
83
100
  model = self.get_model(model_name)
84
101
  normalized_params = model.normalize_params(params)
85
102
  started = time.time()
86
- response = self.client.create_task(model_name, normalized_params, wait=wait)
103
+ response = self.client.create_task(model.name, normalized_params, wait=wait)
87
104
  result = TaskResult(
88
105
  model_name=model.name,
89
106
  model_id=model.model_id,
@@ -11,13 +11,17 @@ def read_readme():
11
11
 
12
12
  setup(
13
13
  name="PyGeoModel",
14
- version="1.0.14",
14
+ version="1.0.16",
15
15
  author="Peilong Ma",
16
16
  author_email="mpl_gis@nnu.edu.cn",
17
17
  description="A Python package for integrating OpenGMS geographic model services.",
18
18
  long_description=read_readme(),
19
19
  long_description_content_type="text/markdown",
20
20
  url="https://github.com/MpLebron/PyGeoModel",
21
+ project_urls={
22
+ "Documentation": "https://mplebron.github.io/PyGeoModel-docs/",
23
+ "Source": "https://github.com/MpLebron/PyGeoModel",
24
+ },
21
25
  py_modules=["scripts"],
22
26
  packages=find_packages(include=["pygeomodel", "pygeomodel.*", "ogmsServer2", "ogmsServer2.*"]),
23
27
  package_data={
@@ -2,6 +2,7 @@ import json
2
2
  import os
3
3
  import tempfile
4
4
  import unittest
5
+ import asyncio
5
6
  from pathlib import Path
6
7
 
7
8
 
@@ -27,6 +28,90 @@ class FakeClient:
27
28
 
28
29
 
29
30
  class CoreApiTests(unittest.TestCase):
31
+ def test_http_client_sync_methods_use_sixty_second_default_timeout(self):
32
+ from ogmsServer2.openUtils.http_client import HttpClient
33
+
34
+ calls = []
35
+
36
+ def fake_make_sync_request(method, url, timeout=60, **kwargs):
37
+ calls.append((method, timeout, kwargs))
38
+ return {"status_code": 200, "headers": {}, "json": {}}
39
+
40
+ original_make_sync_request = HttpClient._make_sync_request
41
+ try:
42
+ HttpClient._make_sync_request = staticmethod(fake_make_sync_request)
43
+ HttpClient.get_sync("https://example.test/get")
44
+ HttpClient.get_file_sync("https://example.test/file")
45
+ HttpClient.post_sync("https://example.test/post")
46
+ HttpClient.put_sync("https://example.test/put")
47
+ HttpClient.delete_sync("https://example.test/delete")
48
+ finally:
49
+ HttpClient._make_sync_request = original_make_sync_request
50
+
51
+ self.assertEqual([call[1] for call in calls], [60, 60, 60, 60, 60])
52
+
53
+ def test_http_client_async_methods_use_sixty_second_default_timeout(self):
54
+ from ogmsServer2.openUtils.http_client import HttpClient
55
+
56
+ calls = []
57
+
58
+ async def fake_make_async_request(method, url, timeout=60, **kwargs):
59
+ calls.append((method, timeout, kwargs))
60
+ return {"status_code": 200, "headers": {}, "json": {}}
61
+
62
+ async def run_requests():
63
+ await HttpClient.get_async("https://example.test/get")
64
+ await HttpClient.post_async("https://example.test/post")
65
+ await HttpClient.put_async("https://example.test/put")
66
+ await HttpClient.delete_async("https://example.test/delete")
67
+
68
+ original_make_async_request = HttpClient._make_async_request
69
+ try:
70
+ HttpClient._make_async_request = staticmethod(fake_make_async_request)
71
+ asyncio.run(run_requests())
72
+ finally:
73
+ HttpClient._make_async_request = original_make_async_request
74
+
75
+ self.assertEqual([call[1] for call in calls], [60, 60, 60, 60])
76
+
77
+ def test_opengms_client_metadata_requests_use_sixty_second_timeout(self):
78
+ import pygeomodel.client as client_module
79
+ from pygeomodel.client import OpenGMSClient
80
+
81
+ calls = []
82
+
83
+ class FakeResponse:
84
+ def __init__(self, payload):
85
+ self._payload = payload
86
+
87
+ def raise_for_status(self):
88
+ return None
89
+
90
+ def json(self):
91
+ return self._payload
92
+
93
+ def fake_get(url, params=None, timeout=None):
94
+ calls.append((url, params, timeout))
95
+ if url.endswith("/sdk/check_test/"):
96
+ return FakeResponse({"data": 1})
97
+ if "/computableModel/ModelInfo_name/" in url:
98
+ return FakeResponse({"data": {"md5": "model-md5"}})
99
+ if url.endswith("/GeoModeling/task/verify/model-md5"):
100
+ return FakeResponse({"data": True})
101
+ raise AssertionError(f"Unexpected URL: {url}")
102
+
103
+ original_get = client_module.requests.get
104
+ try:
105
+ client_module.requests.get = fake_get
106
+ client = OpenGMSClient(token="token")
107
+ self.assertTrue(client.validate_token())
108
+ self.assertEqual(client.check_model("Demo Model"), {"md5": "model-md5"})
109
+ self.assertTrue(client.check_model_service("Demo Model"))
110
+ finally:
111
+ client_module.requests.get = original_get
112
+
113
+ self.assertEqual([call[2] for call in calls], [60, 60, 60, 60])
114
+
30
115
  def test_catalog_loading_uses_executable_registry_when_available(self):
31
116
  from pygeomodel import GeoModeler
32
117
 
@@ -124,6 +209,42 @@ class CoreApiTests(unittest.TestCase):
124
209
  self.assertEqual(model.display_name, "BES Land Surface Temperature Algorithm")
125
210
  self.assertEqual(model.inputs[0].description, "Satellite Brightness of Channel 4")
126
211
 
212
+ def test_english_display_name_resolves_to_original_opengms_name(self):
213
+ from pygeomodel import GeoModeler
214
+
215
+ with tempfile.TemporaryDirectory() as tmpdir:
216
+ tmp_path = Path(tmpdir)
217
+ catalog_path = tmp_path / "computeModel.json"
218
+ registry_path = tmp_path / "modellist_2070.csv"
219
+ catalog_path.write_text(
220
+ json.dumps(
221
+ {
222
+ "中文模型": {
223
+ "md5": "md5-demo",
224
+ "_id": "uid-demo",
225
+ "description": "Demo model",
226
+ "mdlJson": {"mdl": {"states": []}},
227
+ }
228
+ }
229
+ ),
230
+ encoding="utf-8",
231
+ )
232
+ registry_path.write_text(
233
+ "\ufeff序号,MD5,名称,介绍,模型UID,模型参数及说明,是否有示例数据(资源),display_name_en\n"
234
+ "1,md5-demo,中文模型,,uid-demo,,,English Demo Model\n",
235
+ encoding="utf-8",
236
+ )
237
+
238
+ client = FakeClient()
239
+ modeler = GeoModeler(data_path=catalog_path, client=client)
240
+ model = modeler.get_model("English Demo Model")
241
+ result = modeler.invoke("English Demo Model", params={})
242
+
243
+ self.assertEqual(model.name, "中文模型")
244
+ self.assertEqual(model.display_name, "English Demo Model")
245
+ self.assertEqual(result.model_name, "中文模型")
246
+ self.assertEqual(client.calls[0][0], "中文模型")
247
+
127
248
  def test_search_and_get_model_parse_metadata(self):
128
249
  from pygeomodel import GeoModeler, ModelService
129
250
 
File without changes
File without changes
File without changes
File without changes
File without changes