datagovmy-python-sdk 1.0.1__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.
- datagovmy/__init__.py +3 -0
- datagovmy/core/__init__.py +0 -0
- datagovmy/core/api.py +76 -0
- datagovmy/datagovmy.py +22 -0
- datagovmy/service/__init__.py +4 -0
- datagovmy/service/data_catalogue.py +23 -0
- datagovmy/service/environment.py +2 -0
- datagovmy/service/exceptions.py +6 -0
- datagovmy/service/open_dosm.py +23 -0
- datagovmy_python_sdk-1.0.1.dist-info/METADATA +63 -0
- datagovmy_python_sdk-1.0.1.dist-info/RECORD +12 -0
- datagovmy_python_sdk-1.0.1.dist-info/WHEEL +4 -0
datagovmy/__init__.py
ADDED
|
File without changes
|
datagovmy/core/api.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Optional, cast
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
from requests.adapters import HTTPAdapter
|
|
6
|
+
from urllib3.util.retry import Retry
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseAPIClient:
|
|
12
|
+
"""Generic API client with configurable retry support.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
base_url: Base URL for all requests (e.g. "https://api.example.com").
|
|
16
|
+
headers: Default headers sent with every request.
|
|
17
|
+
retry_strategy: A ``urllib3.util.retry.Retry`` instance that controls
|
|
18
|
+
which status codes trigger retries, how many times, and the backoff.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
base_url: str,
|
|
24
|
+
headers: Optional[dict[str, str]] = None,
|
|
25
|
+
retry_strategy: Optional[Retry] = None,
|
|
26
|
+
):
|
|
27
|
+
self.base_url = base_url
|
|
28
|
+
self.session = requests.Session()
|
|
29
|
+
if headers:
|
|
30
|
+
self.session.headers.update(headers)
|
|
31
|
+
if retry_strategy:
|
|
32
|
+
adapter = HTTPAdapter(max_retries=cast(Any, retry_strategy))
|
|
33
|
+
self.session.mount("http://", adapter)
|
|
34
|
+
self.session.mount("https://", adapter)
|
|
35
|
+
|
|
36
|
+
def _build_url(self, path: str) -> str:
|
|
37
|
+
return f"{self.base_url}{path}"
|
|
38
|
+
|
|
39
|
+
def _request(self, method: str, path: str, **kwargs: Any) -> requests.Response:
|
|
40
|
+
url = self._build_url(path)
|
|
41
|
+
logger.debug("Requesting %s %s", method, url)
|
|
42
|
+
response = self.session.request(method, url, **kwargs)
|
|
43
|
+
response.raise_for_status()
|
|
44
|
+
return response
|
|
45
|
+
|
|
46
|
+
def get(
|
|
47
|
+
self,
|
|
48
|
+
path: str,
|
|
49
|
+
params: Optional[dict[str, Any]] = None,
|
|
50
|
+
headers: Optional[dict[str, str]] = None,
|
|
51
|
+
) -> requests.Response:
|
|
52
|
+
return self._request("GET", path, params=params, headers=headers)
|
|
53
|
+
|
|
54
|
+
def post(
|
|
55
|
+
self,
|
|
56
|
+
path: str,
|
|
57
|
+
json: Optional[Any] = None,
|
|
58
|
+
headers: Optional[dict[str, str]] = None,
|
|
59
|
+
) -> requests.Response:
|
|
60
|
+
return self._request("POST", path, json=json, headers=headers)
|
|
61
|
+
|
|
62
|
+
def put(
|
|
63
|
+
self,
|
|
64
|
+
path: str,
|
|
65
|
+
json: Optional[Any] = None,
|
|
66
|
+
headers: Optional[dict[str, str]] = None,
|
|
67
|
+
) -> requests.Response:
|
|
68
|
+
return self._request("PUT", path, json=json, headers=headers)
|
|
69
|
+
|
|
70
|
+
def delete(
|
|
71
|
+
self,
|
|
72
|
+
path: str,
|
|
73
|
+
params: Optional[dict[str, Any]] = None,
|
|
74
|
+
headers: Optional[dict[str, str]] = None,
|
|
75
|
+
) -> requests.Response:
|
|
76
|
+
return self._request("DELETE", path, params=params, headers=headers)
|
datagovmy/datagovmy.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from datagovmy.service import DataCatalogueClient, OpenDOSMClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DataGovMyClient:
|
|
7
|
+
def __init__(self, api_key: Optional[str] = None):
|
|
8
|
+
self.api_key = api_key
|
|
9
|
+
self._data_catalogue: Optional[DataCatalogueClient] = None
|
|
10
|
+
self._opendosm: Optional[OpenDOSMClient] = None
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def data_catalogue(self) -> DataCatalogueClient:
|
|
14
|
+
if self._data_catalogue is None:
|
|
15
|
+
self._data_catalogue = DataCatalogueClient(api_key=self.api_key)
|
|
16
|
+
return self._data_catalogue
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def opendosm(self) -> OpenDOSMClient:
|
|
20
|
+
if self._opendosm is None:
|
|
21
|
+
self._opendosm = OpenDOSMClient(api_key=self.api_key)
|
|
22
|
+
return self._opendosm
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from datagovmy.core.api import BaseAPIClient
|
|
4
|
+
from datagovmy.service.environment import get_base_url
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DataCatalogueClient(BaseAPIClient):
|
|
8
|
+
ENDPOINT = "/data-catalogue"
|
|
9
|
+
|
|
10
|
+
def __init__(self, api_key: Optional[str] = None):
|
|
11
|
+
super().__init__(base_url=get_base_url())
|
|
12
|
+
self.api_key = api_key
|
|
13
|
+
|
|
14
|
+
def get_dataset_as_json(self, id: str, **kwargs):
|
|
15
|
+
# kwargs can be used to add additional query parameters for filtering
|
|
16
|
+
# reference: https://developer.data.gov.my/request-query
|
|
17
|
+
endpoint = f"{self.ENDPOINT}?id={id}"
|
|
18
|
+
filters = "&".join(f"{k}={v}" for k, v in kwargs.items())
|
|
19
|
+
|
|
20
|
+
if filters:
|
|
21
|
+
endpoint += f"&{filters}"
|
|
22
|
+
|
|
23
|
+
return self.get(endpoint).json()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from datagovmy.core.api import BaseAPIClient
|
|
4
|
+
from datagovmy.service.environment import get_base_url
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class OpenDOSMClient(BaseAPIClient):
|
|
8
|
+
ENDPOINT = "/opendosm"
|
|
9
|
+
|
|
10
|
+
def __init__(self, api_key: Optional[str] = None):
|
|
11
|
+
super().__init__(base_url=get_base_url())
|
|
12
|
+
self.api_key = api_key
|
|
13
|
+
|
|
14
|
+
def get_dataset_as_json(self, id: str, **kwargs):
|
|
15
|
+
# kwargs can be used to add additional query parameters for filtering
|
|
16
|
+
# reference: https://developer.data.gov.my/request-query
|
|
17
|
+
endpoint = f"{self.ENDPOINT}?id={id}"
|
|
18
|
+
filters = "&".join(f"{k}={v}" for k, v in kwargs.items())
|
|
19
|
+
|
|
20
|
+
if filters:
|
|
21
|
+
endpoint += f"&{filters}"
|
|
22
|
+
|
|
23
|
+
return self.get(endpoint).json()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: datagovmy-python-sdk
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Requires-Python: >=3.13
|
|
6
|
+
Requires-Dist: requests
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# Data.gov.my Python SDK
|
|
10
|
+
|
|
11
|
+
Unofficial SDK for accessing open source data on https://data.gov.my.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install mydata
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from datagovmy import DataGovMyClient
|
|
23
|
+
|
|
24
|
+
client = DataGovMyClient()
|
|
25
|
+
|
|
26
|
+
# Fetch a dataset from the national data catalogue
|
|
27
|
+
data = client.data_catalogue.get_dataset_as_json(id="population_malaysia")
|
|
28
|
+
|
|
29
|
+
# Fetch with filters
|
|
30
|
+
data = client.data_catalogue.get_dataset_as_json(
|
|
31
|
+
id="population_malaysia",
|
|
32
|
+
filter="Selangor@location",
|
|
33
|
+
sort="-year",
|
|
34
|
+
limit=10
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Fetch DOSM data
|
|
38
|
+
data = client.opendosm.get_dataset_as_json(id="cpi_2d_category")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Documentation
|
|
42
|
+
|
|
43
|
+
For detailed usage examples including all supported query filters (filtering, sorting, pagination, date ranges, column selection, and more), see the [Usage Guide](docs/usage.md).
|
|
44
|
+
|
|
45
|
+
## API Reference
|
|
46
|
+
|
|
47
|
+
This SDK wraps the [data.gov.my Open API](https://developer.data.gov.my/). Dataset IDs can be discovered at:
|
|
48
|
+
|
|
49
|
+
- [data.gov.my Data Catalogue](https://data.gov.my/data-catalogue)
|
|
50
|
+
- [OpenDOSM Data Catalogue](https://open.dosm.gov.my/data-catalogue)
|
|
51
|
+
|
|
52
|
+
## Rate Limits
|
|
53
|
+
|
|
54
|
+
- Without API key: **4 requests/minute**
|
|
55
|
+
- With API key: **10 requests/minute**
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
client = DataGovMyClient(api_key="your-api-key")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
datagovmy/__init__.py,sha256=oJ0M_JrIkHoUDTlBbn76AtpyzjafcacVjQFazi7XKic,79
|
|
2
|
+
datagovmy/datagovmy.py,sha256=xWtAliQhHqbY6MvgTq8160acN2_zdxXqZ3OVWPukLQo,739
|
|
3
|
+
datagovmy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
datagovmy/core/api.py,sha256=oHuyGXOl9w0Ahs9eLpZXt48LWmuVAdE0r8LGqHV3McI,2441
|
|
5
|
+
datagovmy/service/__init__.py,sha256=WZwmHeMjr1_uf_O_37facmhL7Rdp89ZPMmEfozHRLGE,173
|
|
6
|
+
datagovmy/service/data_catalogue.py,sha256=hb_cdjPvuQH_J-4YdB5WWawGJVS82mjyyKq41w94T14,754
|
|
7
|
+
datagovmy/service/environment.py,sha256=U5rTbYxoFBx8WdTVygdzO-bVseQCJL3OB0CrMmCp6wE,57
|
|
8
|
+
datagovmy/service/exceptions.py,sha256=CNN8WXJk678minAo2lhYZrzKGz7A6Wk9vePbMUr9l9A,100
|
|
9
|
+
datagovmy/service/open_dosm.py,sha256=pSLU3YgSjVF0ZnmZPJLh3ezzvt8xkB5S6LVKtBPO9Hc,743
|
|
10
|
+
datagovmy_python_sdk-1.0.1.dist-info/METADATA,sha256=gwW9BcS5KOPHv9Z7K5343xRjCT7sIXwhHQTX6kmtnFc,1449
|
|
11
|
+
datagovmy_python_sdk-1.0.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
12
|
+
datagovmy_python_sdk-1.0.1.dist-info/RECORD,,
|