hdx-python-api 6.6.9__py3-none-any.whl → 6.7.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.
- hdx/api/_version.py +2 -2
- hdx/api/hdx_base_configuration.yaml +4 -0
- hdx/api/locations.py +3 -4
- hdx/data/location.py +237 -0
- hdx/data/resource.py +180 -1
- {hdx_python_api-6.6.9.dist-info → hdx_python_api-6.7.1.dist-info}/METADATA +3 -3
- {hdx_python_api-6.6.9.dist-info → hdx_python_api-6.7.1.dist-info}/RECORD +9 -8
- {hdx_python_api-6.6.9.dist-info → hdx_python_api-6.7.1.dist-info}/WHEEL +1 -1
- {hdx_python_api-6.6.9.dist-info → hdx_python_api-6.7.1.dist-info}/licenses/LICENSE +0 -0
hdx/api/_version.py
CHANGED
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '6.
|
|
22
|
-
__version_tuple__ = version_tuple = (6,
|
|
21
|
+
__version__ = version = '6.7.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (6, 7, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
hdx/api/locations.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from collections.abc import Sequence
|
|
4
4
|
|
|
5
5
|
from hdx.api.configuration import Configuration
|
|
6
|
+
from hdx.data.location import Location
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class Locations:
|
|
@@ -22,10 +23,8 @@ class Locations:
|
|
|
22
23
|
A list of valid locations
|
|
23
24
|
"""
|
|
24
25
|
if cls._validlocations is None:
|
|
25
|
-
|
|
26
|
-
configuration =
|
|
27
|
-
cls._validlocations = configuration.call_remoteckan(
|
|
28
|
-
"group_list", {"all_fields": True}
|
|
26
|
+
cls._validlocations = Location.get_all_location_names(
|
|
27
|
+
configuration=configuration, all_fields=True
|
|
29
28
|
)
|
|
30
29
|
return cls._validlocations
|
|
31
30
|
|
hdx/data/location.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""Location class containing all logic for creating, checking, and updating locations."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from hdx.api.configuration import Configuration
|
|
8
|
+
from hdx.data.hdxobject import HDXObject
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Location(HDXObject):
|
|
14
|
+
"""Location class containing all logic for creating, checking, and updating locations.
|
|
15
|
+
A location is a CKAN group.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
initial_data: Initial location metadata dictionary. Defaults to None.
|
|
19
|
+
configuration: HDX configuration. Defaults to global configuration.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
initial_data: dict | None = None,
|
|
25
|
+
configuration: Configuration | None = None,
|
|
26
|
+
) -> None:
|
|
27
|
+
if not initial_data:
|
|
28
|
+
initial_data = {}
|
|
29
|
+
super().__init__(initial_data, configuration=configuration)
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def actions() -> dict[str, str]:
|
|
33
|
+
"""Dictionary of actions that can be performed on object
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Dictionary of actions that can be performed on object
|
|
37
|
+
"""
|
|
38
|
+
return {
|
|
39
|
+
"show": "group_show",
|
|
40
|
+
"update": "group_update",
|
|
41
|
+
"create": "group_create",
|
|
42
|
+
"delete": "group_delete",
|
|
43
|
+
"list": "group_list",
|
|
44
|
+
"autocomplete": "group_autocomplete",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def read_from_hdx(
|
|
49
|
+
cls, identifier: str, configuration: Configuration | None = None
|
|
50
|
+
) -> Optional["Location"]:
|
|
51
|
+
"""Reads the location given by identifier from HDX and returns Location object
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
identifier: Identifier of location
|
|
55
|
+
configuration: HDX configuration. Defaults to global configuration.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Location object if successful read, None if not
|
|
59
|
+
"""
|
|
60
|
+
return cls._read_from_hdx_class("group", identifier, configuration)
|
|
61
|
+
|
|
62
|
+
def check_required_fields(self, ignore_fields: Sequence[str] = ()) -> None:
|
|
63
|
+
"""Check that metadata for location is complete. The parameter ignore_fields should
|
|
64
|
+
be set if required to any fields that should be ignored for the particular operation.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
ignore_fields: Fields to ignore. Default is ().
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
None
|
|
71
|
+
"""
|
|
72
|
+
self._check_required_fields("group", ignore_fields)
|
|
73
|
+
|
|
74
|
+
def update_in_hdx(self, **kwargs: Any) -> None:
|
|
75
|
+
"""Check if location exists in HDX and if so, update location
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
None
|
|
79
|
+
"""
|
|
80
|
+
self._update_in_hdx("group", "id", **kwargs)
|
|
81
|
+
|
|
82
|
+
def create_in_hdx(self, **kwargs: Any) -> None:
|
|
83
|
+
"""Check if location exists in HDX and if so, update it, otherwise create location
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
None
|
|
87
|
+
"""
|
|
88
|
+
self._create_in_hdx("group", "id", "name", **kwargs)
|
|
89
|
+
|
|
90
|
+
def delete_from_hdx(self) -> None:
|
|
91
|
+
"""Deletes a location from HDX.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
None
|
|
95
|
+
"""
|
|
96
|
+
self._delete_from_hdx("group", "id")
|
|
97
|
+
|
|
98
|
+
def get_datasets(self, query: str = "*:*", **kwargs: Any) -> list["Dataset"]: # noqa: F821
|
|
99
|
+
"""Get list of datasets in location
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
query: Restrict datasets returned to this query (in Solr format). Defaults to '*:*'.
|
|
103
|
+
**kwargs: See below
|
|
104
|
+
sort (string): Sorting of the search results. Defaults to 'relevance asc, metadata_modified desc'.
|
|
105
|
+
rows (int): Number of matching rows to return. Defaults to all datasets (sys.maxsize).
|
|
106
|
+
start (int): Offset in the complete result for where the set of returned datasets should begin
|
|
107
|
+
facet (string): Whether to enable faceted results. Default to True.
|
|
108
|
+
facet.mincount (int): Minimum counts for facet fields should be included in the results
|
|
109
|
+
facet.limit (int): Maximum number of values the facet fields return (- = unlimited). Defaults to 50.
|
|
110
|
+
facet.field (list[str]): Fields to facet upon. Default is empty.
|
|
111
|
+
use_default_schema (bool): Use default package schema instead of custom schema. Defaults to False.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
List of datasets in location
|
|
115
|
+
"""
|
|
116
|
+
import hdx.data.dataset # avoid circular import
|
|
117
|
+
|
|
118
|
+
return hdx.data.dataset.Dataset.search_in_hdx(
|
|
119
|
+
query=query,
|
|
120
|
+
configuration=self.configuration,
|
|
121
|
+
fq=f"groups:{self.data['name']}",
|
|
122
|
+
**kwargs,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def get_all_location_names(
|
|
127
|
+
configuration: Configuration | None = None, **kwargs: Any
|
|
128
|
+
) -> list[str]:
|
|
129
|
+
"""Get all location names in HDX
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
configuration: HDX configuration. Defaults to global configuration.
|
|
133
|
+
**kwargs: See below
|
|
134
|
+
sort (str): Sort the search results according to field name and sort-order. Allowed fields are ‘name’, ‘package_count’ and ‘title’. Defaults to 'name asc'.
|
|
135
|
+
groups (list[str]): List of names of the groups to return.
|
|
136
|
+
all_fields (bool): Return group dictionaries instead of just names. Only core fields are returned - get some more using the include_* options. Defaults to False.
|
|
137
|
+
include_extras (bool): If all_fields, include the group extra fields. Defaults to False.
|
|
138
|
+
include_tags (bool): If all_fields, include the group tags. Defaults to False.
|
|
139
|
+
include_groups (bool): If all_fields, include the groups the groups are in. Defaults to False.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
List of all location names in HDX
|
|
143
|
+
"""
|
|
144
|
+
location = Location(configuration=configuration)
|
|
145
|
+
|
|
146
|
+
# Early return for the standard, non-paginated case
|
|
147
|
+
if not kwargs.get("all_fields"):
|
|
148
|
+
return location._write_to_hdx("list", kwargs)
|
|
149
|
+
|
|
150
|
+
compiled_locations = {}
|
|
151
|
+
# Extract limit and offset to dictate our paging sizes, defaulting to 400 and 0
|
|
152
|
+
page_size = kwargs.pop("limit", 400)
|
|
153
|
+
current_offset = kwargs.pop("offset", 0)
|
|
154
|
+
|
|
155
|
+
# 1. Fetch in pages using the provided/default page size
|
|
156
|
+
while True:
|
|
157
|
+
page_kwargs = kwargs.copy()
|
|
158
|
+
page_kwargs["limit"] = page_size
|
|
159
|
+
page_kwargs["offset"] = current_offset
|
|
160
|
+
|
|
161
|
+
page_data = location._write_to_hdx("list", page_kwargs)
|
|
162
|
+
|
|
163
|
+
if not page_data:
|
|
164
|
+
break
|
|
165
|
+
|
|
166
|
+
# Store by name to automatically deduplicate offset shifts
|
|
167
|
+
for loc in page_data:
|
|
168
|
+
compiled_locations[loc["name"]] = loc
|
|
169
|
+
|
|
170
|
+
if len(page_data) < page_size:
|
|
171
|
+
break
|
|
172
|
+
|
|
173
|
+
current_offset += page_size
|
|
174
|
+
|
|
175
|
+
# 2. Establish Ground Truth LAST (without limit/offset constraints)
|
|
176
|
+
truth_kwargs = kwargs.copy()
|
|
177
|
+
truth_kwargs["all_fields"] = False
|
|
178
|
+
|
|
179
|
+
ground_truth_names = location._write_to_hdx("list", truth_kwargs)
|
|
180
|
+
|
|
181
|
+
# 3 & 4. Check for missing IDs, fetch individually, and implicitly prune deletes
|
|
182
|
+
final_locations = []
|
|
183
|
+
for name in ground_truth_names:
|
|
184
|
+
if name in compiled_locations:
|
|
185
|
+
final_locations.append(compiled_locations[name])
|
|
186
|
+
else:
|
|
187
|
+
try:
|
|
188
|
+
missing_location = location._write_to_hdx("show", {"id": name})
|
|
189
|
+
if missing_location:
|
|
190
|
+
final_locations.append(missing_location)
|
|
191
|
+
except Exception:
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
return final_locations
|
|
195
|
+
|
|
196
|
+
@staticmethod
|
|
197
|
+
def get_data_grid_countries(
|
|
198
|
+
configuration: Configuration | None = None,
|
|
199
|
+
) -> list[str]:
|
|
200
|
+
"""Fetch HDX's active Data Grid countries (3-letter group names).
|
|
201
|
+
|
|
202
|
+
Treated as a live/short-TTL lookup rather than a frozen static list, since Data Grid
|
|
203
|
+
membership changes over time.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
configuration: HDX configuration. Defaults to global configuration.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Sorted list of active Data Grid country (group) names
|
|
210
|
+
"""
|
|
211
|
+
groups = Location.get_all_location_names(
|
|
212
|
+
configuration=configuration, all_fields=True, include_extras=True
|
|
213
|
+
)
|
|
214
|
+
return sorted(
|
|
215
|
+
group["name"]
|
|
216
|
+
for group in groups
|
|
217
|
+
if len(group["name"]) == 3 and group.get("data_completeness") == "active"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
@classmethod
|
|
221
|
+
def autocomplete(
|
|
222
|
+
cls,
|
|
223
|
+
name: str,
|
|
224
|
+
limit: int = 20,
|
|
225
|
+
configuration: Configuration | None = None,
|
|
226
|
+
) -> list:
|
|
227
|
+
"""Autocomplete a location name and return matches
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
name: Name to autocomplete
|
|
231
|
+
limit: Maximum number of matches to return
|
|
232
|
+
configuration: HDX configuration. Defaults to global configuration.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
Autocomplete matches
|
|
236
|
+
"""
|
|
237
|
+
return cls._autocomplete(name, limit, configuration)
|
hdx/data/resource.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
-
from collections.abc import Sequence
|
|
5
|
+
from collections.abc import Iterator, Sequence
|
|
6
6
|
from datetime import datetime
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import Any, Optional
|
|
@@ -34,6 +34,7 @@ class Resource(HDXObject):
|
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
36
|
_formats_dict = None
|
|
37
|
+
_valid_datastore_dump_formats = frozenset({"csv", "tsv", "json", "xml"})
|
|
37
38
|
_valid_data_dictionary_types = frozenset(
|
|
38
39
|
{"text", "numeric", "date", "timestamp without time zone"}
|
|
39
40
|
)
|
|
@@ -70,6 +71,7 @@ class Resource(HDXObject):
|
|
|
70
71
|
"datastore_insert": "datastore_insert",
|
|
71
72
|
"datastore_upsert": "datastore_upsert",
|
|
72
73
|
"datastore_search": "datastore_search",
|
|
74
|
+
"datastore_search_sql": "datastore_search_sql",
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
def update_from_yaml(
|
|
@@ -711,6 +713,62 @@ class Resource(HDXObject):
|
|
|
711
713
|
path = downloader.download_file(url, folder=folder, filename=filename)
|
|
712
714
|
return url, path
|
|
713
715
|
|
|
716
|
+
def get_datastore_dump_url(self, file_format: str = "csv") -> str:
|
|
717
|
+
"""Get the URL from which the resource datastore can be downloaded in
|
|
718
|
+
full.
|
|
719
|
+
|
|
720
|
+
Args:
|
|
721
|
+
file_format: Dump format (csv, tsv, json or xml). Defaults to csv.
|
|
722
|
+
|
|
723
|
+
Returns:
|
|
724
|
+
Datastore dump URL
|
|
725
|
+
"""
|
|
726
|
+
file_format = file_format.lower()
|
|
727
|
+
if file_format not in self._valid_datastore_dump_formats:
|
|
728
|
+
valid_formats = ", ".join(sorted(self._valid_datastore_dump_formats))
|
|
729
|
+
raise HDXError(
|
|
730
|
+
f"Invalid datastore dump format '{file_format}'! Must be one of: {valid_formats}"
|
|
731
|
+
)
|
|
732
|
+
url = (
|
|
733
|
+
f"{self.configuration.get_hdx_site_url()}/datastore/dump/{self.data['id']}"
|
|
734
|
+
)
|
|
735
|
+
if file_format != "csv":
|
|
736
|
+
url = f"{url}?format={file_format}"
|
|
737
|
+
return url
|
|
738
|
+
|
|
739
|
+
def download_datastore(
|
|
740
|
+
self,
|
|
741
|
+
folder: Path | str | None = None,
|
|
742
|
+
filename: str | None = None,
|
|
743
|
+
file_format: str = "csv",
|
|
744
|
+
retriever: Retrieve | None = None,
|
|
745
|
+
) -> tuple[str, Path]:
|
|
746
|
+
"""Download the resource datastore in full to provided folder or
|
|
747
|
+
temporary folder if no folder supplied
|
|
748
|
+
|
|
749
|
+
Args:
|
|
750
|
+
folder: Folder to download datastore to. Defaults to None.
|
|
751
|
+
filename: Filename to use. Defaults to None (resource name with file_format extension).
|
|
752
|
+
file_format: Dump format (csv, tsv, json or xml). Defaults to csv.
|
|
753
|
+
retriever: Retrieve object to use. Defaults to None.
|
|
754
|
+
|
|
755
|
+
Returns:
|
|
756
|
+
(URL downloaded, Path to downloaded file)
|
|
757
|
+
"""
|
|
758
|
+
url = self.get_datastore_dump_url(file_format)
|
|
759
|
+
logger.debug(f"Downloading {url}")
|
|
760
|
+
if filename is None:
|
|
761
|
+
filename = self.data["name"]
|
|
762
|
+
extension = f".{file_format.lower()}"
|
|
763
|
+
if not filename.endswith(extension):
|
|
764
|
+
filename = f"{filename}{extension}"
|
|
765
|
+
session = hdx.api.utilities.url_utils.get_ckan_ready_session(self.configuration)
|
|
766
|
+
with Download(session=session) as downloader:
|
|
767
|
+
if retriever:
|
|
768
|
+
downloader = retriever.clone(downloader)
|
|
769
|
+
path = downloader.download_file(url, folder=folder, filename=filename)
|
|
770
|
+
return url, path
|
|
771
|
+
|
|
714
772
|
@staticmethod
|
|
715
773
|
def get_all_resource_ids_in_datastore(
|
|
716
774
|
configuration: Configuration | None = None,
|
|
@@ -805,6 +863,127 @@ class Resource(HDXObject):
|
|
|
805
863
|
}
|
|
806
864
|
self._write_to_hdx("datastore_upsert", data, "resource_id")
|
|
807
865
|
|
|
866
|
+
def search_datastore(
|
|
867
|
+
self,
|
|
868
|
+
filters: dict | None = None,
|
|
869
|
+
q: str | dict | None = None,
|
|
870
|
+
fields: Sequence[str] | None = None,
|
|
871
|
+
sort: str | None = None,
|
|
872
|
+
distinct: bool = False,
|
|
873
|
+
limit: int = 100,
|
|
874
|
+
offset: int = 0,
|
|
875
|
+
include_total: bool = True,
|
|
876
|
+
**kwargs: Any,
|
|
877
|
+
) -> dict:
|
|
878
|
+
"""Search the resource datastore using CKAN datastore_search.
|
|
879
|
+
|
|
880
|
+
Args:
|
|
881
|
+
filters: Dictionary of field name to value(s) to match. Defaults to None.
|
|
882
|
+
q: Full text query string or dictionary of field name to query. Defaults to None.
|
|
883
|
+
fields: Fields to return. Defaults to None (all fields).
|
|
884
|
+
sort: Comma separated field names with optional asc/desc eg. "code desc". Defaults to None.
|
|
885
|
+
distinct: Whether to return only distinct rows. Defaults to False.
|
|
886
|
+
limit: Maximum number of rows to return. Defaults to 100.
|
|
887
|
+
offset: Number of rows to skip. Defaults to 0.
|
|
888
|
+
include_total: Whether to include total number of matching rows. Defaults to True.
|
|
889
|
+
**kwargs: Other parameters to pass to datastore_search eg. plain, language.
|
|
890
|
+
|
|
891
|
+
Returns:
|
|
892
|
+
datastore_search result dictionary with keys including records, fields and total
|
|
893
|
+
"""
|
|
894
|
+
data: dict = {
|
|
895
|
+
"resource_id": self.data["id"],
|
|
896
|
+
"limit": limit,
|
|
897
|
+
"offset": offset,
|
|
898
|
+
"include_total": include_total,
|
|
899
|
+
}
|
|
900
|
+
if filters is not None:
|
|
901
|
+
data["filters"] = filters
|
|
902
|
+
if q is not None:
|
|
903
|
+
data["q"] = q
|
|
904
|
+
if fields is not None:
|
|
905
|
+
data["fields"] = ",".join(fields)
|
|
906
|
+
if sort is not None:
|
|
907
|
+
data["sort"] = sort
|
|
908
|
+
if distinct:
|
|
909
|
+
data["distinct"] = True
|
|
910
|
+
data.update(kwargs)
|
|
911
|
+
return self._write_to_hdx("datastore_search", data, "resource_id")
|
|
912
|
+
|
|
913
|
+
def get_datastore_records(
|
|
914
|
+
self,
|
|
915
|
+
filters: dict | None = None,
|
|
916
|
+
q: str | dict | None = None,
|
|
917
|
+
fields: Sequence[str] | None = None,
|
|
918
|
+
sort: str | None = None,
|
|
919
|
+
distinct: bool = False,
|
|
920
|
+
page_size: int = 10000,
|
|
921
|
+
**kwargs: Any,
|
|
922
|
+
) -> Iterator[dict]:
|
|
923
|
+
"""Iterate over all matching records in the resource datastore, paging
|
|
924
|
+
through datastore_search results.
|
|
925
|
+
|
|
926
|
+
Args:
|
|
927
|
+
filters: Dictionary of field name to value(s) to match. Defaults to None.
|
|
928
|
+
q: Full text query string or dictionary of field name to query. Defaults to None.
|
|
929
|
+
fields: Fields to return. Defaults to None (all fields).
|
|
930
|
+
sort: Comma separated field names with optional asc/desc eg. "code desc". Defaults to None.
|
|
931
|
+
distinct: Whether to return only distinct rows. Defaults to False.
|
|
932
|
+
page_size: Number of rows to request per call. Defaults to 10000.
|
|
933
|
+
**kwargs: Other parameters to pass to datastore_search eg. plain, language.
|
|
934
|
+
|
|
935
|
+
Returns:
|
|
936
|
+
Iterator of record dictionaries
|
|
937
|
+
"""
|
|
938
|
+
offset = 0
|
|
939
|
+
while True:
|
|
940
|
+
result = self.search_datastore(
|
|
941
|
+
filters=filters,
|
|
942
|
+
q=q,
|
|
943
|
+
fields=fields,
|
|
944
|
+
sort=sort,
|
|
945
|
+
distinct=distinct,
|
|
946
|
+
limit=page_size,
|
|
947
|
+
offset=offset,
|
|
948
|
+
include_total=False,
|
|
949
|
+
**kwargs,
|
|
950
|
+
)
|
|
951
|
+
records = result["records"]
|
|
952
|
+
yield from records
|
|
953
|
+
if len(records) < page_size:
|
|
954
|
+
return
|
|
955
|
+
offset += page_size
|
|
956
|
+
|
|
957
|
+
def get_datastore_fields(self) -> list[dict]:
|
|
958
|
+
"""Get the field definitions of the resource datastore excluding the
|
|
959
|
+
internal _id field.
|
|
960
|
+
|
|
961
|
+
Returns:
|
|
962
|
+
List of field dictionaries each with id and type keys
|
|
963
|
+
"""
|
|
964
|
+
result = self.search_datastore(limit=0, include_total=False)
|
|
965
|
+
return [field for field in result["fields"] if field["id"] != "_id"]
|
|
966
|
+
|
|
967
|
+
@staticmethod
|
|
968
|
+
def search_datastore_sql(
|
|
969
|
+
sql: str,
|
|
970
|
+
configuration: Configuration | None = None,
|
|
971
|
+
) -> list[dict]:
|
|
972
|
+
"""Run a read-only SQL query against the HDX datastore using CKAN
|
|
973
|
+
datastore_search_sql. Tables are named by resource id, which must be
|
|
974
|
+
double quoted eg. SELECT * FROM "RESOURCE_ID".
|
|
975
|
+
|
|
976
|
+
Args:
|
|
977
|
+
sql: SQL SELECT statement
|
|
978
|
+
configuration: HDX configuration. Defaults to global configuration.
|
|
979
|
+
|
|
980
|
+
Returns:
|
|
981
|
+
List of record dictionaries
|
|
982
|
+
"""
|
|
983
|
+
resource = Resource(configuration=configuration)
|
|
984
|
+
result = resource._write_to_hdx("datastore_search_sql", {"sql": sql})
|
|
985
|
+
return result["records"]
|
|
986
|
+
|
|
808
987
|
def delete_datastore(self) -> None:
|
|
809
988
|
"""Delete a resource from the HDX datastore
|
|
810
989
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: hdx-python-api
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.7.1
|
|
4
4
|
Summary: HDX Python API for interacting with the Humanitarian Data Exchange
|
|
5
5
|
Project-URL: Homepage, https://github.com/OCHA-DAP/hdx-python-api
|
|
6
6
|
Author: Michael Rans
|
|
@@ -28,8 +28,8 @@ Requires-Python: >=3.10
|
|
|
28
28
|
Requires-Dist: ckanapi>=4.11
|
|
29
29
|
Requires-Dist: defopt>=7.0.0
|
|
30
30
|
Requires-Dist: email-validator
|
|
31
|
-
Requires-Dist: hdx-python-country>=4.
|
|
32
|
-
Requires-Dist: hdx-python-utilities>=4.1
|
|
31
|
+
Requires-Dist: hdx-python-country>=4.2.3
|
|
32
|
+
Requires-Dist: hdx-python-utilities>=4.2.1
|
|
33
33
|
Requires-Dist: makefun
|
|
34
34
|
Requires-Dist: requests
|
|
35
35
|
Provides-Extra: docs
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
hdx/api/__init__.py,sha256=11ozJKiUsqDCZ3_mcAHhGYUyGK_Unl54djVSBBExFB4,59
|
|
2
|
-
hdx/api/_version.py,sha256=
|
|
2
|
+
hdx/api/_version.py,sha256=7uXJAam46P0GVJ-CiBxd6tVnUUkt-epJdnvQ6m2fpb4,520
|
|
3
3
|
hdx/api/configuration.py,sha256=9WvuVI-3th2Dv70_t_4VoyrlJwB_9qUSN5_zUKbp4sQ,23805
|
|
4
|
-
hdx/api/hdx_base_configuration.yaml,sha256=
|
|
5
|
-
hdx/api/locations.py,sha256=
|
|
4
|
+
hdx/api/hdx_base_configuration.yaml,sha256=jj2t5b7NnI499rHVwOjj3Q5BAjE8i7NDGVKnWZ5P5-4,2222
|
|
5
|
+
hdx/api/locations.py,sha256=bFl_cKz_AEQzkE_xWOkrRlUQ0GT2LMPSk-G_LsbV-Ns,4099
|
|
6
6
|
hdx/api/remotehdx.py,sha256=IjKwP3jutlkrjvWCJPP7YM-xzg3pP_QVmiJfblllBY0,2079
|
|
7
7
|
hdx/api/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
hdx/api/utilities/date_helper.py,sha256=sXxjRtXz-2rpBVZBbJAk80gfjpYKl_AtHplWr-rniIk,6396
|
|
@@ -13,8 +13,9 @@ hdx/api/utilities/url_utils.py,sha256=Pb4r-BAqIHYmeoB_i7QjutDp6nOAUh_OHvCtiNLda3
|
|
|
13
13
|
hdx/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
hdx/data/dataset.py,sha256=0gS7tQSswTgG0g8YeXg0FL8pFVkocBQSRPFpQAurN00,99455
|
|
15
15
|
hdx/data/hdxobject.py,sha256=_jlwYjb_CcjaXsTXObknMfq-OiNOcpJfLJ9b1vKV1Zo,27494
|
|
16
|
+
hdx/data/location.py,sha256=WIQRdY1boy6xPweIWRczWNiJLFrp4w0DVUwppHeNccU,8765
|
|
16
17
|
hdx/data/organization.py,sha256=HGopFsCYOWrR7sUtZzZSXq6wLFw4djus2gzg6yNXOqc,12296
|
|
17
|
-
hdx/data/resource.py,sha256=
|
|
18
|
+
hdx/data/resource.py,sha256=iVARfA3_8gGsGjXgNxomm7umAadvxzFsGGqxTpBB3WQ,45196
|
|
18
19
|
hdx/data/resource_matcher.py,sha256=Yzk9pWa4zk1p09m6zcZMj0XZf5Y16Pa3KToOdUt7rP8,4247
|
|
19
20
|
hdx/data/resource_view.py,sha256=GcYkh0iDAe6_m2ErY_A6i3Z7ZFX949eGt1TLnLZv_8s,7346
|
|
20
21
|
hdx/data/showcase.py,sha256=vnRZ45xd9Ct65eFQkWZtMIJPFifZfKOrH4obO5D5Pvc,14062
|
|
@@ -24,7 +25,7 @@ hdx/facades/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
24
25
|
hdx/facades/infer_arguments.py,sha256=DvyDuozfOPmoaV7HjhQySojGIrMWW9nos31auQBVVl8,4032
|
|
25
26
|
hdx/facades/keyword_arguments.py,sha256=aVapHCfqBO6H7xxBgTdK9ISdgrBFsNypPDWihfQ3edU,1125
|
|
26
27
|
hdx/facades/simple.py,sha256=QWsBkaHG_UM6X3Zu2u3LWVOQzeR24q-EUClrfsL_mkM,1064
|
|
27
|
-
hdx_python_api-6.
|
|
28
|
-
hdx_python_api-6.
|
|
29
|
-
hdx_python_api-6.
|
|
30
|
-
hdx_python_api-6.
|
|
28
|
+
hdx_python_api-6.7.1.dist-info/METADATA,sha256=7SaqX2zZKJ828e94hbPjvjJ_3q5obaL0NU9Ghut_86A,4946
|
|
29
|
+
hdx_python_api-6.7.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
30
|
+
hdx_python_api-6.7.1.dist-info/licenses/LICENSE,sha256=wc-4GpMn-ODs-U_bTe1YCiPVgvcjzrpYOx2wPuyAeII,1079
|
|
31
|
+
hdx_python_api-6.7.1.dist-info/RECORD,,
|
|
File without changes
|