Skip to content

Commit f193d27

Browse files
author
Favio Medrano
committed
advanced Search, results obj
1 parent 4f04648 commit f193d27

File tree

5 files changed

+161
-50
lines changed

5 files changed

+161
-50
lines changed

pyerddap/erddap_dataset.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
from pyerddap import url_operations
33
from pyerddap.remote_requests import urlread
44
from pyerddap.parse_utils import parseDictMetadata, parseConstraintValue
5-
from pyerddap.formatting import dataset_repr
5+
from pyerddap.formatting import dataset_repr, simple_dataset_repr
66
import datetime as dt
77

8+
89
class ERDDAP_Dataset:
910

1011
DEFAULT_FILETYPE = 'csvp'
@@ -16,16 +17,20 @@ def __init__(self, erddapurl, datasetid, protocol='tabledap', auth=None, lazyloa
1617
self.protocol = protocol
1718
self.metadata = None
1819
self.erddapauth = auth
19-
self.requestURL = None
20+
2021
self.resultVariables = []
2122
self.constraints = []
2223
self.serverSideFunctions = []
2324
self.variables = {}
2425
if not lazyload:
2526
self.loadMetadata()
2627

28+
2729
def __repr__(self):
2830
return dataset_repr(self)
31+
32+
def __simple_repr__(self):
33+
return simple_dataset_repr(self)
2934

3035

3136
def setResultVariables(self, variables):
@@ -38,14 +43,17 @@ def setResultVariables(self, variables):
3843

3944
return self
4045

46+
4147
def addResultVariable(self, variable):
4248
self.resultVariables.append(variable)
4349
return self
4450

51+
4552
def setConstraints(self, constraintListOrDict):
4653
self.clearConstraints()
4754
self.addConstraints(constraintListOrDict)
4855

56+
4957
def addConstraints(self, constraintListOrDict):
5058
if isinstance(constraintListOrDict,dict):
5159
for k,v in constraintListOrDict.items():
@@ -57,6 +65,7 @@ def addConstraints(self, constraintListOrDict):
5765
raise Exception("Constraints argument must be either dictionary or list")
5866
return self
5967

68+
6069
def addConstraint(self, constraint):
6170
if isinstance(constraint,dict):
6271
self._addConstraintDict(constraint)
@@ -65,10 +74,12 @@ def addConstraint(self, constraint):
6574
else:
6675
raise Exception("constraint argument must be either string or a dictionary")
6776
return self
68-
77+
78+
6979
def _addConstraintStr(self, constraint):
7080
self.constraints.append(constraint)
7181

82+
7283
def _addConstraintDict(self, constraintDict):
7384
constraintKey = next(iter(constraintDict))
7485
self._addConstraintStr(
@@ -126,12 +137,15 @@ def getMetadataURL(self, request_format='json'):
126137
def clearConstraints(self):
127138
self.constraints = []
128139

140+
129141
def clearServerSideFunctions(self):
130142
self.serverSideFunctions = []
131143

144+
132145
def clearResultVariables(self):
133146
self.resultVariables = []
134147

148+
135149
def clearQuery(self):
136150
self.clearConstraints()
137151
self.clearServerSideFunctions()
@@ -145,6 +159,7 @@ def getDataRequest(self, filetype=DEFAULT_FILETYPE, request_kwargs={}):
145159
else:
146160
return rawRequest.text
147161

162+
148163
# Server side functions wrappers
149164

150165
def addVariablesWhere(self, attributeName, attributeValue):

pyerddap/erddap_server.py

Lines changed: 91 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,107 @@
11
import os
22
from urllib.parse import quote_plus
33
from pyerddap import url_operations
4-
from pyerddap.parse_utils import parseConstraintValue, parseConstraintDateTime
4+
from pyerddap.formatting import erddap_search_results_repr
5+
from pyerddap.parse_utils import parseConstraintValue, parseConstraintDateTime, parseSearchResults
56
from pyerddap.remote_requests import urlread
67
from pyerddap.erddap_dataset import ERDDAP_Dataset
8+
from pyerddap.erddap_tabledap import ERDDAP_Tabledap
9+
from pyerddap.erddap_griddap import ERDDAP_Griddap
10+
11+
12+
class ERDDAP_SearchResults(list):
13+
14+
def __repr__(self):
15+
return erddap_search_results_repr(self)
16+
17+
@property
18+
def results(self):
19+
return list(self)
720

821
class ERDDAP:
922

1023
ALLDATASETS_VARIABLES = ['datasetID','accessible','institution','dataStructure','cdm_data_type','class','title','minLongitude','maxLongitude','longitudeSpacing','minLatitude','maxLatitude','latitudeSpacing','minAltitude','maxAltitude','minTime','maxTime','timeSpacing','griddap','subset','tabledap','MakeAGraph','sos','wcs','wms','files','fgdc','iso19115','metadata','sourceUrl','infoUrl','rss','email','testOutOfDate','outOfDate','summary']
1124

1225
def __init__(self, url, auth=None, lazyload=True):
1326
self.serverURL = url
27+
self.auth = auth
1428
self.tabledapAllDatasets = ERDDAP_Dataset(self.serverURL, 'allDatasets', auth=auth)
1529

1630

17-
def getSearchURL(self, filetype='json', searchFor="",
18-
protocol="",
19-
cdm_data_type="",
20-
institution="",
21-
ioos_category="",
22-
keywords="",
23-
long_name="",
24-
standard_name="",
25-
variableName="",
26-
minLon="",
27-
maxLon="",
28-
minLat="",
29-
maxLat=None,
30-
minTime="",
31-
maxTime="",
32-
itemsPerPage=1000, page=1):
31+
def advancedSearch(self, **filters):
32+
33+
searchURL = self.getSearchURL( **filters)
34+
rawSearchResults = urlread(searchURL, self.auth)
35+
dictSearchResult = rawSearchResults.json()
36+
formatedResults = ERDDAP_SearchResults()
37+
38+
_griddap_dsets , _tabledap_dsets = parseSearchResults(dictSearchResult)
39+
for dst in _tabledap_dsets:
40+
formatedResults.append(ERDDAP_Tabledap(self.serverURL, dst, auth=self.auth))
41+
for dst in _griddap_dsets:
42+
formatedResults.append(ERDDAP_Griddap(self.serverURL, dst, auth=self.auth))
43+
44+
return formatedResults
45+
46+
47+
48+
49+
#def getSearchURL(self, filetype='json', searchFor="",
50+
# protocol="",
51+
# cdm_data_type="",
52+
# institution="",
53+
# ioos_category="",
54+
# keywords="",
55+
# long_name="",
56+
# standard_name="",
57+
# variableName="",
58+
# minLon="",
59+
# maxLon="",
60+
# minLat="",
61+
# maxLat="",
62+
# minTime="",
63+
# maxTime="",
64+
# itemsPerPage=1000, page=1):
65+
def getSearchURL(self, filetype='json', **searchFilters):
66+
"""
67+
Builds the url call for the advanced Search ERDDAP API Rest service.
68+
69+
Arguments
70+
filetype - The result format (htmlTable, csv, json, tsv, etc)
71+
https://coastwatch.pfeg.noaa.gov/erddap/rest.html#responses
72+
73+
Search filters:
74+
searchFor - This is a Google-like search of the datasets metadata, set the words you
75+
want to search for with spaces between the words. ERDDAP will search
76+
for the words separately, not as a phrase.
77+
To search for a phrase, put double quotes around the phrase (for
78+
example, "wind speed").
79+
To exclude datasets with a specific word, use -excludedWord.
80+
To exclude datasets with a specific phrase, use -"excluded phrase"
81+
To search for specific attribute values, use attName=attValue
82+
To find just grid or table datasets, include protocol=griddap
83+
or protocol=tabledap
84+
85+
protocolol - Set either: griddap, tabledap or wms
86+
cdm_data_type -
87+
institution -
88+
ioos_category -
89+
keywords -
90+
long_name -
91+
standard_name -
92+
variableName -
93+
minLon -
94+
maxLon -
95+
minLat -
96+
maxLat -
97+
minTime -
98+
maxTime -
99+
itemsPerPage -
100+
page -
101+
102+
Returns the string url for the search service.
103+
104+
"""
33105

34106
searchAPIEndpoint = "search/advanced.{}".format(filetype)
35107
searchAPIURL = os.path.join( self.serverURL, searchAPIEndpoint )
@@ -55,7 +127,8 @@ def getSearchURL(self, filetype='json', searchFor="",
55127

56128
for queryElement, queryElementDefault in queryElementsDefaults.items():
57129

58-
queryValue = eval(queryElement) if eval(queryElement) else queryElementDefault
130+
#queryValue = eval(queryElement) if eval(queryElement) else queryElementDefault
131+
queryValue = searchFilters.get(queryElement, queryElementDefault)
59132

60133
if queryElement == 'searchFor':
61134
if queryValue:

pyerddap/formatting.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11

22

3+
MAX_SUMMARY_LEN = 75
4+
5+
def simple_dataset_repr(ds):
6+
summary = ["<pyerddap.{}>".format(type(ds).__name__)]
7+
dstTitle = ds.getAttribute('title')
8+
truncatedTitle = (dstTitle[:MAX_SUMMARY_LEN] + '..') if len(dstTitle) > MAX_SUMMARY_LEN else dstTitle
9+
summary.append("\"{}\"".format(truncatedTitle))
10+
return ' '.join(summary)
11+
312
def dataset_repr(ds):
413
summary = ["<pyerddap.{}>".format(type(ds).__name__)]
514
summary.append("Title: {}".format(ds.getAttribute('title')))
@@ -16,3 +25,12 @@ def dataset_repr(ds):
1625

1726
return "\n".join(summary)
1827

28+
29+
def erddap_search_results_repr(srobj):
30+
summary = ["<pyerddap.{}>".format(type(srobj).__name__)]
31+
summary.append ("Results: {}".format(len(list(srobj))))
32+
summary.append('[')
33+
for item in list(srobj):
34+
summary.append( " " + item.__simple_repr__() )
35+
summary.append(']')
36+
return '\n'.join(summary)

pyerddap/parse_utils.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import re
22
import datetime as dt
33

4-
4+
55

66
class ERDDAP_Metadata_Rows:
77
ROW_TYPE = 0
@@ -10,6 +10,26 @@ class ERDDAP_Metadata_Rows:
1010
DATA_TYPE = 3
1111
VALUE = 4
1212

13+
class ERDDAP_Search_Results_Rows:
14+
GRIDDAP = 0
15+
SUBSET = 1
16+
TABLEDAP = 2
17+
MAKEAGRAPH = 3
18+
WMS = 4
19+
FILES = 5
20+
ACCESIBLE = 6
21+
TITLE = 7
22+
SUMMARY = 8
23+
FGDC = 9
24+
ISO19115 = 10
25+
INFO = 11
26+
BACKINFO = 12
27+
RSS = 13
28+
EMAIL = 14
29+
INSTITUTION = 15
30+
DATASETID = 16
31+
32+
1333
def parseDictMetadata(dmetadata):
1434
"""
1535
This function parses the metadata json response from a erddap dataset
@@ -55,6 +75,19 @@ def parseMetadataAttribute(data_type, valuestr):
5575
return tuple(_castedvalue)
5676

5777

78+
def parseSearchResults(dresults):
79+
_griddap_dsets = []
80+
_tabledap_dsets = []
81+
82+
for row in dresults['table']['rows']:
83+
if row[ERDDAP_Search_Results_Rows.GRIDDAP]:
84+
_griddap_dsets.append(row[ERDDAP_Search_Results_Rows.DATASETID])
85+
elif row[ERDDAP_Search_Results_Rows.TABLEDAP]:
86+
_tabledap_dsets.append(row[ERDDAP_Search_Results_Rows.DATASETID])
87+
88+
return _griddap_dsets, _tabledap_dsets
89+
90+
5891
def parseConstraintValue(value):
5992
"""
6093
This functions detect the constraint value type and decide if is a

pyerddap/url_operations.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,7 @@
11
import os
22
from urllib.parse import quote, quote_plus, urlparse, ParseResult
33

4-
5-
#class URLBulder:
6-
#
7-
# def __init__(self, base, query="", auth=(None,None)):
8-
# self.baseurl = base
9-
# self.query = query
10-
# self.auth = auth
11-
#
12-
# def addQueryPair(self, query):
13-
# if self._urlparseresult.query:
14-
# self._urlparseresult.query = '&'.join([self._urlparseresult.query, query])
15-
# else:
16-
# self._urlparseresult.query = query
17-
#
18-
# def build(self, includeAuth=False):
19-
# if includeAuth:
20-
# return self._urlparseresult.geturl()
21-
# else:
22-
# return self._buildWithoutAuth()
23-
# _authbk = (self._urlparseresult.username, self._urlparseresult.password)
24-
# self._urlparseresult.username, self._urlparseresult.password = None, None
25-
#
26-
# def _buildWithoutAuth(self):
27-
# _noauthparseresult = self._urlparseresult.copy()
28-
# _noauthparseresult.username = None
29-
# _noauthparseresult.password = None
30-
# return _noauthparseresult.geturl()
314

32-
335
def parseQueryItems(items, useSafeURL=True, safe='', item_separator='&'):
346
if useSafeURL:
357
return quote(item_separator.join(items), safe=safe)

0 commit comments

Comments
 (0)