From ce73f1ac261641937b17b97d48ef889e0fc62fcf Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Thu, 28 Sep 2017 17:32:58 +0200 Subject: [PATCH 1/9] remove queryset.extra(where=...) calls from quakeml --- src/jane/documents/models.py | 65 +++++++++++++++----------- src/jane/quakeml/tests/test_quakeml.py | 4 +- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/jane/documents/models.py b/src/jane/documents/models.py index 42c2fd7..69a4347 100644 --- a/src/jane/documents/models.py +++ b/src/jane/documents/models.py @@ -429,10 +429,6 @@ def get_filtered_queryset(self, document_type, queryset=None, user=None, "bool": bool, "UTCDateTime": UTCDateTime } - - # Filter based on the attributes in the meta field. - where = [] - for key, value_type in meta.items(): # Handle strings. if value_type == "str": @@ -443,22 +439,27 @@ def get_filtered_queryset(self, document_type, queryset=None, user=None, if name not in kwargs: continue value = kwargs[name] + method = 'exact' # Possible wildcards. if "*" in value or "?" in value: - value = value.replace("?", "_").replace("*", r"%%") - # PostgreSQL specific case insensitive LIKE statement. - if operator == "=": - where.append("json->>'%s' ILIKE '%s'" % (key, - value)) - elif operator == "!=": - where.append("json->>'%s' NOT ILIKE '%s'" % ( - key, value)) - else: - raise NotImplementedError() # pragma: no cover + value = value.replace("*", ".*") + method = 'iregex' + # the regex field lookup on JSON fields suffers from a + # bug on Django 1.9, see django/django#6929. + # The patch is super simple but it's only in Django + # 1.11 and upwards, so we might want to consider to + # just apply it somehow or monkey patch the django + # module?? + raise NotImplementedError() + # PostgreSQL specific case insensitive LIKE statement. + if operator == "=": + queryset = queryset.filter(**{ + 'json__{}__{}'.format(key, method): value}) + elif operator == "!=": + queryset = queryset.exclude(**{ + 'json__{}__{}'.format(key, method): value}) else: - where.append( - self._get_json_query(key, operator, value_type, - value)) + raise NotImplementedError() # pragma: no cover # Handle integers, floats, and UTCDateTimes. elif value_type in ("int", "float", "UTCDateTime"): choices = (("min_%s", ">="), ("max_%s", "<="), ("%s", "="), @@ -467,9 +468,23 @@ def get_filtered_queryset(self, document_type, queryset=None, user=None, name = name % key if name not in kwargs: continue - where.append(self._get_json_query( - key, operator, value_type, - type_map[value_type](kwargs[name]))) + value = type_map[value_type](kwargs[name]) + if value_type == "UTCDateTime": + value = value.datetime.isoformat() + if operator == "=": + queryset = queryset.filter(**{ + 'json__{}__exact'.format(key): value}) + elif operator == "!=": + queryset = queryset.exclude(**{ + 'json__{}__exact'.format(key): value}) + elif operator == ">=": + queryset = queryset.filter(**{ + 'json__{}__gte'.format(key): value}) + elif operator == "<=": + queryset = queryset.filter(**{ + 'json__{}__lte'.format(key): value}) + else: + raise NotImplementedError() # pragma: no cover # Handle bools. elif value_type == "bool": # Booleans can be searched for (in)equality. @@ -480,18 +495,16 @@ def get_filtered_queryset(self, document_type, queryset=None, user=None, continue value = str(kwargs[name]).lower() if value in ["t", "true", "yes", "y"]: - value = "true" + value = True elif value in ["f", "false", "no", "n"]: - value = "false" + value = False else: raise NotImplementedError() # pragma: no cover - where.append(self._get_json_query( - key, operator, value_type, value)) + queryset = queryset.filter(**{ + 'json__{}__exact'.format(key): value}) else: raise NotImplementedError() # pragma: no cover - queryset = queryset.extra(where=where) - if "ordering" in kwargs and kwargs["ordering"] in meta: ord = kwargs["ordering"] queryset = queryset.order_by( diff --git a/src/jane/quakeml/tests/test_quakeml.py b/src/jane/quakeml/tests/test_quakeml.py index 915f934..9b0c437 100644 --- a/src/jane/quakeml/tests/test_quakeml.py +++ b/src/jane/quakeml/tests/test_quakeml.py @@ -393,9 +393,11 @@ def test_quakeml_queries(self): **self.valid_auth_headers).json()["results"]), 0) # All authors are None - so as soon as one searches for an author, # only results with an author will return something. + # Changed: authors that are None will be returned if doing a search + # only excluding a specific author self.assertEqual(len(self.client.get( path + "?!author=random", - **self.valid_auth_headers).json()["results"]), 0) + **self.valid_auth_headers).json()["results"]), 2) # Test the ordering. ev = self.client.get(path + "?ordering=depth_in_m").json()["results"] From de61a45df5e4abc059351e80356dcaa67307d989 Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Thu, 28 Sep 2017 17:37:59 +0200 Subject: [PATCH 2/9] remove most of remaining queryset.extra() usages fdsn stationxml seems to be pretty much untested though, so passing tests don't say much.. --- src/jane/documents/models.py | 11 ---- src/jane/fdsnws/station_query.py | 92 +++++++++++++------------------- 2 files changed, 38 insertions(+), 65 deletions(-) diff --git a/src/jane/documents/models.py b/src/jane/documents/models.py index 69a4347..536792f 100644 --- a/src/jane/documents/models.py +++ b/src/jane/documents/models.py @@ -232,14 +232,6 @@ class DocumentIndexManager(models.GeoManager): """ Custom queryset manager for the document indices. """ - JSON_QUERY_TEMPLATE_MAP = { - "int": "CAST(json->>'%s' AS INTEGER) %s %s", - "float": "CAST(json->>'%s' AS REAL) %s %s", - "str": "LOWER(json->>'%s') %s LOWER('%s')", - "bool": "CAST(json->>'%s' AS BOOL) %s %s", - "UTCDateTime": "CAST(json->>'%s' AS TIMESTAMP) %s TIMESTAMP '%s'" - } - JSON_ORDERING_TEMPLATE = { "int": "CAST(json->>'%s' AS INTEGER)", "float": "CAST(json->>'%s' AS REAL)", @@ -260,9 +252,6 @@ def get_queryset(self): annotate(attachments_count=Count('attachments')) return queryset - def _get_json_query(self, key, operator, type, value): - return self.JSON_QUERY_TEMPLATE_MAP[type] % (key, operator, str(value)) - def apply_retrieve_permission(self, document_type, queryset, user): """ Apply potential additional restrictions based on the permissions. diff --git a/src/jane/fdsnws/station_query.py b/src/jane/fdsnws/station_query.py index 6e04701..5cbd05c 100644 --- a/src/jane/fdsnws/station_query.py +++ b/src/jane/fdsnws/station_query.py @@ -15,22 +15,10 @@ from jane.documents.models import DocumentIndex, DocumentType -def _get_json_query(key, operator, type, value): - return JSON_QUERY_TEMPLATE_MAP[type] % (key, operator, str(value)) - - def _format_time(value): return value.strftime("%Y-%m-%dT%H:%M:%S+00:00") -JSON_QUERY_TEMPLATE_MAP = { - int: "CAST(json->>'%s' AS INTEGER) %s %s", - float: "CAST(json->>'%s' AS REAL) %s %s", - str: "json->>'%s' %s '%s'", - UTCDateTime: "CAST(json->>'%s' AS TIMESTAMP) %s TIMESTAMP '%s'" -} - - # Define some constants for writing StationXML files. SOURCE = settings.JANE_FDSN_STATIONXML_SOURCE SENDER = settings.JANE_FDSN_STATIONXML_SENDER @@ -124,66 +112,62 @@ def query_stations(fh, url, nodata, level, format, user, starttime=None, query = DocumentIndex.objects.filter( document__document_type="stationxml") - where = [] if starttime: # If end_date is null it is assumed to be bigger. - where.append( - "((json->>'end_date') is null) OR (" + - _get_json_query("end_date", ">=", UTCDateTime, starttime) + ")") + query.filter(json__end_date__gte=starttime.isoformat()) if endtime: - where.append( - _get_json_query("start_date", "<=", UTCDateTime, endtime)) + query.filter(json__start_date__lte=endtime.isoformat()) if startbefore: - where.append( - _get_json_query("start_date", "<", UTCDateTime, startbefore)) + query.filter(json__start_date__lte=startbefore.isoformat()) if startafter: - where.append( - _get_json_query("start_date", ">", UTCDateTime, startafter)) + query.filter(json__start_date__gt=startafter.isoformat()) if endbefore: # If end_date is null it is assumed to be bigger. We don't want that # here. - where.append( - "((json->>'end_date') is not null) AND (" + - _get_json_query("end_date", "<", UTCDateTime, endbefore) + ")") + query.filter(json__end_date__lt=endbefore.isoformat()) if endafter: # If end_date is null it is assumed to be bigger. - where.append( - "((json->>'end_date') is null) OR (" + - _get_json_query("end_date", ">", UTCDateTime, endafter) + ")") + query.filter(json__end_date__gt=endafter.isoformat()) if minlatitude is not None: - where.append( - _get_json_query("latitude", ">=", float, minlatitude)) + query.filter(json__latitude__gte=minlatitude) if maxlatitude is not None: - where.append( - _get_json_query("latitude", "<=", float, maxlatitude)) + query.filter(json__latitude__lte=maxlatitude) if minlongitude is not None: - where.append( - _get_json_query("longitude", ">=", float, minlongitude)) + query.filter(json__longitude__gte=minlongitude) if maxlongitude is not None: - where.append( - _get_json_query("longitude", "<=", float, maxlongitude)) + query.filter(json__longitude__lte=maxlongitude) for key in ["network", "station", "location", "channel"]: argument = locals()[key] - if argument is not None and '*' not in argument: - # Two percentage signs are needed (for escaping?) - argument = [_i.replace("?", "_").replace("*", r"%%") - for _i in argument] - # A minus sign negates the query. - n = [] - y = [] - for _i in argument: - if _i.startswith("-"): - n.append("json->>'%s' NOT LIKE '%s'" % (key, _i[1:])) + if argument is not None: + # XXX copy/pasted from jane/documents/models.py should be + # refactored, probably.. + # Strings can be searched on wildcarded (in)equalities + choices = (("%s", "="), ("!%s", "!=")) + for name, operator in choices: + name = name % key + value = argument + method = 'exact' + # Possible wildcards. + if "*" in value or "?" in value: + value = value.replace("*", ".*") + method = 'iregex' + # the regex field lookup on JSON fields suffers from a + # bug on Django 1.9, see django/django#6929. + # The patch is super simple but it's only in Django + # 1.11 and upwards, so we might want to consider to + # just apply it somehow or monkey patch the django + # module?? + raise NotImplementedError() + # PostgreSQL specific case insensitive LIKE statement. + if operator == "=": + query = query.filter(**{ + 'json__{}__{}'.format(key, method): value}) + elif operator == "!=": + query = query.exclude(**{ + 'json__{}__{}'.format(key, method): value}) else: - y.append("json->>'%s' LIKE '%s'" % (key, _i)) - if y: - where.append(" OR ".join(y)) - if n: - where.append(" AND ".join(n)) - - if where: - query = query.extra(where=where) + raise NotImplementedError() # pragma: no cover # Radial queries - also apply the per-user filtering right here! if latitude is not None: From 894824c4154e5045dacdd3cd3564d87afa69ff86 Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Fri, 29 Sep 2017 10:58:52 +0200 Subject: [PATCH 3/9] patch django's buggy json field on django <1.11 --- src/jane/__init__.py | 26 ++++++++++++++++++++++++++ src/jane/documents/models.py | 7 ++----- src/jane/fdsnws/station_query.py | 7 ++----- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/jane/__init__.py b/src/jane/__init__.py index 99c8c6e..39e146e 100644 --- a/src/jane/__init__.py +++ b/src/jane/__init__.py @@ -1,8 +1,34 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import +import warnings + from .exceptions import * # NOQA from .version import get_git_version +# monkey patch django's json field to get around django/django#6929 on +# django <1.11 +import django +try: + django_version_major_minor = list( + map(int, django.__version__.split('.')[:2])) +except ValueError: + msg = ("Failed to determine Django version. Django's json field will not " + "be patched (django/django#6929).") + warnings.warn(msg) +else: + if django_version_major_minor < [1, 11]: + import django.contrib.postgres.fields.jsonb + + django.contrib.postgres.fields.jsonb.KeyTransform._as_sql_original = \ + django.contrib.postgres.fields.jsonb.KeyTransform.as_sql + + def as_sql(self, *args, **kwargs): + _as_sql = self._as_sql_original(*args, **kwargs) + return '({})'.format(_as_sql[0]), _as_sql[1] + + django.contrib.postgres.fields.jsonb.KeyTransform.as_sql = as_sql + __version__ = get_git_version() +__all__ = ['__version__'] diff --git a/src/jane/documents/models.py b/src/jane/documents/models.py index 536792f..da423e2 100644 --- a/src/jane/documents/models.py +++ b/src/jane/documents/models.py @@ -435,11 +435,8 @@ def get_filtered_queryset(self, document_type, queryset=None, user=None, method = 'iregex' # the regex field lookup on JSON fields suffers from a # bug on Django 1.9, see django/django#6929. - # The patch is super simple but it's only in Django - # 1.11 and upwards, so we might want to consider to - # just apply it somehow or monkey patch the django - # module?? - raise NotImplementedError() + # We patch django's json field on django <1.11 in + # jane/__init__.py # PostgreSQL specific case insensitive LIKE statement. if operator == "=": queryset = queryset.filter(**{ diff --git a/src/jane/fdsnws/station_query.py b/src/jane/fdsnws/station_query.py index 5cbd05c..ad5f0e2 100644 --- a/src/jane/fdsnws/station_query.py +++ b/src/jane/fdsnws/station_query.py @@ -154,11 +154,8 @@ def query_stations(fh, url, nodata, level, format, user, starttime=None, method = 'iregex' # the regex field lookup on JSON fields suffers from a # bug on Django 1.9, see django/django#6929. - # The patch is super simple but it's only in Django - # 1.11 and upwards, so we might want to consider to - # just apply it somehow or monkey patch the django - # module?? - raise NotImplementedError() + # We patch django's json field on django <1.11 in + # jane/__init__.py # PostgreSQL specific case insensitive LIKE statement. if operator == "=": query = query.filter(**{ From 5e76d351efd3b628b745e170ef8d6e8b94f02263 Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Fri, 29 Sep 2017 12:11:28 +0200 Subject: [PATCH 4/9] station query using json field lookups somehow still doesn't work, not the slightest idea why, in debugger the simplest queries seem to fail somehow: (Pdb) for i in query: print(i.json['station']) ALTM ALTM ALTM (Pdb) query.filter(json__station__exact='ALTM') [] (Pdb) print(type(query)) --- src/jane/fdsnws/station_query.py | 34 +++++++++++++------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/jane/fdsnws/station_query.py b/src/jane/fdsnws/station_query.py index ad5f0e2..576412c 100644 --- a/src/jane/fdsnws/station_query.py +++ b/src/jane/fdsnws/station_query.py @@ -140,31 +140,25 @@ def query_stations(fh, url, nodata, level, format, user, starttime=None, for key in ["network", "station", "location", "channel"]: argument = locals()[key] if argument is not None: - # XXX copy/pasted from jane/documents/models.py should be - # refactored, probably.. - # Strings can be searched on wildcarded (in)equalities - choices = (("%s", "="), ("!%s", "!=")) - for name, operator in choices: - name = name % key - value = argument + queries = [] + for argument_ in argument: + value = argument_ method = 'exact' # Possible wildcards. if "*" in value or "?" in value: value = value.replace("*", ".*") method = 'iregex' - # the regex field lookup on JSON fields suffers from a - # bug on Django 1.9, see django/django#6929. - # We patch django's json field on django <1.11 in - # jane/__init__.py - # PostgreSQL specific case insensitive LIKE statement. - if operator == "=": - query = query.filter(**{ - 'json__{}__{}'.format(key, method): value}) - elif operator == "!=": - query = query.exclude(**{ - 'json__{}__{}'.format(key, method): value}) - else: - raise NotImplementedError() # pragma: no cover + # the regex field lookup on JSON fields suffers from a + # bug on Django 1.9, see django/django#6929. + # We patch django's json field on django <1.11 in + # jane/__init__.py + queries.append(query.filter(**{ + 'json__{}__{}'.format(key, method): value})) + # combine querysets + query = queries.pop() + for query_ in queries: + query = query | query_ + query = query.distinct() # Radial queries - also apply the per-user filtering right here! if latitude is not None: From 61bc79f6e0468998ece65a82f2699474acd77d4c Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Fri, 27 Oct 2017 15:00:04 +0400 Subject: [PATCH 5/9] add helper method to work around django jsonfield "__isnull" query bugs checking for null values in jsonfields is buggy, so work around it as described here using "__contains" query type: https://stackoverflow.com/q/38528516 --- src/jane/jane/utils.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/jane/jane/utils.py b/src/jane/jane/utils.py index cd9e84c..c85ceff 100644 --- a/src/jane/jane/utils.py +++ b/src/jane/jane/utils.py @@ -17,3 +17,33 @@ class OptionalTrailingSlashSimpleRouter(SimpleRouter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.trailing_slash = "/?" + + +def _queryset_filter_jsonfield_isnull(queryset, path, isnull, field='json'): + """ + Replaces the buggy isnull query on json fields, see + https://stackoverflow.com/q/38528516 + + :type queryset: :class:`django.db.models.query.QuerySet` + :param queryset: Django queryset object to do the query on. + :type field: str + :param field: Name of the field (column) in the databse table that holds + the json data for the query. By default should always be "json" for + Jane. + :type path: list + :param path: List of field names as strings to traverse in the json field. + For example use ``field='json', path=['end_date'], isnull=True``, for a + query of type ``queryset.filter(json__end_date__isnull=True)``. + :type isnull: bool + :param isnull: Whether to return items with the respective field being + `null` (``isnull=True``) or it *not* being `null` (``isnull=False``). + """ + key = '__'.join([field] + list(path[:-1]) + ['contains']) + if not path: + raise ValueError() + kwargs = {key: {path[-1]: None}} + if isnull: + method = queryset.filter + else: + method = queryset.exclude + return method(**kwargs) From 3014be03cd8921c769c1decb7f52bcc46b0ee562 Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Fri, 27 Oct 2017 15:02:20 +0400 Subject: [PATCH 6/9] django jsonfield queries: reimplement exclusion in NSLC with leading "-" --- src/jane/fdsnws/station_query.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/jane/fdsnws/station_query.py b/src/jane/fdsnws/station_query.py index 576412c..2044f79 100644 --- a/src/jane/fdsnws/station_query.py +++ b/src/jane/fdsnws/station_query.py @@ -143,6 +143,11 @@ def query_stations(fh, url, nodata, level, format, user, starttime=None, queries = [] for argument_ in argument: value = argument_ + if value.startswith('-'): + value = value[1:] + query_method = query.exclude + else: + query_method = query.filter method = 'exact' # Possible wildcards. if "*" in value or "?" in value: From 87993f9ef98d251494116dabd4e06c4eb2e9e19b Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Fri, 27 Oct 2017 15:03:06 +0400 Subject: [PATCH 7/9] regex json field queries: fix regex (replace '?' with '.') --- src/jane/fdsnws/station_query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jane/fdsnws/station_query.py b/src/jane/fdsnws/station_query.py index 2044f79..c54f993 100644 --- a/src/jane/fdsnws/station_query.py +++ b/src/jane/fdsnws/station_query.py @@ -151,7 +151,7 @@ def query_stations(fh, url, nodata, level, format, user, starttime=None, method = 'exact' # Possible wildcards. if "*" in value or "?" in value: - value = value.replace("*", ".*") + value = value.replace("*", ".*").replace("?", ".") method = 'iregex' # the regex field lookup on JSON fields suffers from a # bug on Django 1.9, see django/django#6929. From 5a13b2b1847372f9dd3e65fe44db5b3d9686e103 Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Fri, 27 Oct 2017 15:03:44 +0400 Subject: [PATCH 8/9] final fixes for django builtin queries on json field --- src/jane/fdsnws/station_query.py | 36 +++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/jane/fdsnws/station_query.py b/src/jane/fdsnws/station_query.py index c54f993..53a3c8e 100644 --- a/src/jane/fdsnws/station_query.py +++ b/src/jane/fdsnws/station_query.py @@ -13,6 +13,7 @@ import jane from jane.documents.models import DocumentIndex, DocumentType +from jane.jane.utils import _queryset_filter_jsonfield_isnull def _format_time(value): @@ -114,33 +115,38 @@ def query_stations(fh, url, nodata, level, format, user, starttime=None, if starttime: # If end_date is null it is assumed to be bigger. - query.filter(json__end_date__gte=starttime.isoformat()) + query = (query.filter(json__end_date__gte=starttime.isoformat()) | + _queryset_filter_jsonfield_isnull(query, path=['end_date'], + isnull=True)).distinct() if endtime: - query.filter(json__start_date__lte=endtime.isoformat()) + query = query.filter(json__start_date__lte=endtime.isoformat()) if startbefore: - query.filter(json__start_date__lte=startbefore.isoformat()) + query = query.filter(json__start_date__lte=startbefore.isoformat()) if startafter: - query.filter(json__start_date__gt=startafter.isoformat()) + query = query.filter(json__start_date__gt=startafter.isoformat()) if endbefore: # If end_date is null it is assumed to be bigger. We don't want that # here. - query.filter(json__end_date__lt=endbefore.isoformat()) + query = (query.filter(json__end_date__lt=endbefore.isoformat()) & + _queryset_filter_jsonfield_isnull(query, path=['end_date'], + isnull=False)).distinct() if endafter: # If end_date is null it is assumed to be bigger. - query.filter(json__end_date__gt=endafter.isoformat()) + query = (query.filter(json__end_date__gt=endafter.isoformat()) | + _queryset_filter_jsonfield_isnull(query, path=['end_date'], + isnull=True)).distinct() if minlatitude is not None: - query.filter(json__latitude__gte=minlatitude) + query = query.filter(json__latitude__gte=minlatitude) if maxlatitude is not None: - query.filter(json__latitude__lte=maxlatitude) + query = query.filter(json__latitude__lte=maxlatitude) if minlongitude is not None: - query.filter(json__longitude__gte=minlongitude) + query = query.filter(json__longitude__gte=minlongitude) if maxlongitude is not None: - query.filter(json__longitude__lte=maxlongitude) + query = query.filter(json__longitude__lte=maxlongitude) for key in ["network", "station", "location", "channel"]: argument = locals()[key] if argument is not None: - queries = [] for argument_ in argument: value = argument_ if value.startswith('-'): @@ -157,12 +163,8 @@ def query_stations(fh, url, nodata, level, format, user, starttime=None, # bug on Django 1.9, see django/django#6929. # We patch django's json field on django <1.11 in # jane/__init__.py - queries.append(query.filter(**{ - 'json__{}__{}'.format(key, method): value})) - # combine querysets - query = queries.pop() - for query_ in queries: - query = query | query_ + query = query & query_method(**{ + 'json__{}__{}'.format(key, method): value}) query = query.distinct() # Radial queries - also apply the per-user filtering right here! From 888845b07759fcf8bb66ddbdc0bf2d48df4f7dd3 Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Sat, 28 Oct 2017 00:16:38 +0400 Subject: [PATCH 9/9] add tests for so far untested comma-separated-list station query --- src/jane/fdsnws/tests/test_station_1.py | 49 +++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/jane/fdsnws/tests/test_station_1.py b/src/jane/fdsnws/tests/test_station_1.py index 15b479b..f97b95b 100644 --- a/src/jane/fdsnws/tests/test_station_1.py +++ b/src/jane/fdsnws/tests/test_station_1.py @@ -611,6 +611,55 @@ def test_total_and_selected_number_of_sta_and_cha(self): self.assertEqual(inv[0][0].total_number_of_channels, 3) self.assertEqual(inv[0][0].selected_number_of_channels, 3) + def test_comma_separated_list_queries(self): + """ + Test comma separated queries in NSCL parameters + """ + client = FDSNClient(self.live_server_url) + + inv = client.get_stations(level="channel", network="BW", + station="ALTM", location="", + channel="EHZ,EHN") + c = inv.get_contents() + self.assertEqual(c["channels"], + ['BW.ALTM..EHN', 'BW.ALTM..EHZ']) + + inv = client.get_stations(level="channel", network="BW", + station="ALTM", location="", + channel="EHZ,EH?") + c = inv.get_contents() + self.assertEqual(c["channels"], + ['BW.ALTM..EHE', 'BW.ALTM..EHN', 'BW.ALTM..EHZ']) + + # the following queries should be improved, we should have multiple + # different network/station codes in the test db + client = FDSNClient(self.live_server_url) + inv = client.get_stations(level="channel", network="AA,BW", + station="ALTM", location="--", channel="EH*") + c = inv.get_contents() + self.assertEqual(c["channels"], + ['BW.ALTM..EHE', 'BW.ALTM..EHN', 'BW.ALTM..EHZ']) + + # the following queries should be improved, we should have multiple + # different network/station codes in the test db + client = FDSNClient(self.live_server_url) + inv = client.get_stations(level="channel", network="BW", + station="ALTM", location="00,,10", + channel="EH*") + c = inv.get_contents() + self.assertEqual(c["channels"], + ['BW.ALTM..EHE', 'BW.ALTM..EHN', 'BW.ALTM..EHZ']) + + # the following queries should be improved, we should have multiple + # different network/station codes in the test db + client = FDSNClient(self.live_server_url) + inv = client.get_stations(level="channel", network="BW", + station="XXX,YYY,ALTM", location="--", + channel="EHZ") + c = inv.get_contents() + self.assertEqual(c["channels"], + ['BW.ALTM..EHZ']) + def test_seed_code_queries(self): client = FDSNClient(self.live_server_url)