diff --git a/src/jane/documents/models.py b/src/jane/documents/models.py index 0cc1635..63d649e 100644 --- a/src/jane/documents/models.py +++ b/src/jane/documents/models.py @@ -320,7 +320,7 @@ def get_filtered_queryset_radial_distance( Distance(km=deg2km(max_radius)))) return queryset - def get_filtered_queryset(self, document_type, queryset=None, + def get_filtered_queryset(self, document_type, queryset=None, negate=False, **kwargs): """ Returns a queryset filtered on the items in the JSON document. @@ -329,6 +329,12 @@ def get_filtered_queryset(self, document_type, queryset=None, queryset is passed. :param queryset: If no queryset is passed, a new one will be created, otherwise an existing one will be used and filtered. + :type negate: bool + :param negate: When ``negate=True`` then the resulting queryset is + inverted, i.e. the resulting queryset returns all events that do + not match all given criteria. For example, ``.., negate=True, + max_magnitude=1, site="unterhaching")`` returns all events except + events at site Unterhaching that are below magnitude 1. :param kwargs: Any additional query parameters. Assuming a key named ``"example"`` in the JSON file you can search @@ -362,6 +368,12 @@ def get_filtered_queryset(self, document_type, queryset=None, ``public=True`` ``kwargs={"!public": True}`` + The resulting query set can be inverted with ``negate=True``:: + + * For example, using ``.., negate=True, max_magnitude=1, + site="unterhaching")`` returns all events except for events that are + both declared at site Unterhaching and below magnitude 1. + Please note that as soon as you search for a value, all values that are null will be discarded from the queryset (even if you search for !=)! This might be changed in the future. @@ -448,6 +460,11 @@ def get_filtered_queryset(self, document_type, queryset=None, else: raise NotImplementedError() + # if "negate" option is selected: instead of filtering to given + # criteria, filter out all items that match given criteria + if where != [] and negate: + where = ["NOT ({})".format(" AND ".join(where))] + queryset = queryset.extra(where=where) if "ordering" in kwargs and kwargs["ordering"] in meta: diff --git a/src/jane/quakeml/plugins.py b/src/jane/quakeml/plugins.py index e4e8ae9..93599b7 100644 --- a/src/jane/quakeml/plugins.py +++ b/src/jane/quakeml/plugins.py @@ -83,6 +83,94 @@ def filter_queryset_user_does_not_have_permission(self, queryset, return queryset +def _site_magnitude_threshold_retrieve_permission( + class_name, magnitude_threshold, site=None): + """ + Class factory that returns a quakeml retrieve permission based on a + magnitude threshold, optionally only working on a specific site. + If multiple of these restrictions are defined, all of them apply separately + and the user must have all of them set, down to the lowest threshold + restriction that is supposed to apply. + """ + class _SiteMagnitudeThresholdRetrievePermissionPlugin( + RetrievePermissionPluginPoint): + """ + If user does not have this permission, any events below given magnitude + threshold are filtered out (optionally only for a specific site). + """ + name = 'quakeml' + title = 'Can See Magnitude <{} Events {}Permission'.format( + magnitude_threshold, site and "At site='{}' ".format(site) or "") + + # Permission codename and name according to Django's nomenclature. + # XXX no idea if dots are allowed in codename, so replace them + permission_codename = 'can_see_mag_lessthan_{}_site_{}_events'.format( + magnitude_threshold, site or "any").replace(".", "_") + permission_name = 'Can See Magnitude <{} Events{}'.format( + magnitude_threshold, site and " At site='{}'".format(site) or "") + + def filter_queryset_user_has_permission(self, queryset, model_type): + # If the user has the permission: don't restrict queryset. + return queryset + + def filter_queryset_user_does_not_have_permission(self, queryset, + model_type): + # model_type can be document or document index. + if model_type == "document": + # XXX: Find a good way to do this. + raise NotImplementedError() + elif model_type == "index": + # Modify the queryset to only contain indices that are above + # given magnitude threshold. + # XXX check what happens with events that have null for + # XXX magnitude.. + kwargs = {} + # if no site is specified, just do a normal filter by magnitude + # threshold + if site is None: + kwargs["min_magnitude"] = magnitude_threshold + negate = False + # if site is specified, we need to search for events matching + # both criteria and then invert the resulting queryset + else: + kwargs['site'] = site + kwargs["max_magnitude"] = magnitude_threshold - 0.01 + negate = True + queryset = queryset.model.objects.get_filtered_queryset( + document_type="quakeml", queryset=queryset, negate=negate, + **kwargs) + else: + raise NotImplementedError() + return queryset + + new_class = _SiteMagnitudeThresholdRetrievePermissionPlugin + # Set the class type name. + setattr(new_class, "__name__", class_name) + return new_class + + +# Retrieve permissions for small events, if users don't have these permissions +# small events are not accessible to them +MagnitudeLessThan1RetrievePermissionPlugin = \ + _site_magnitude_threshold_retrieve_permission( + "MagnitudeLessThan1RetrievePermissionPlugin", magnitude_threshold=1.0) +MagnitudeLessThan2RetrievePermissionPlugin = \ + _site_magnitude_threshold_retrieve_permission( + "MagnitudeLessThan2RetrievePermissionPlugin", magnitude_threshold=2.0) + +# Retrieve permissions for small events attributed to a specific site (e.g. a +# specific deep geothermal project), if users don't have these permissions +# small events that are attributed to that site are not accessible to them +UnterhachingLessThan1RetrievePermissionPlugin = \ + _site_magnitude_threshold_retrieve_permission( + "UnterhachingLessThan1RetrievePermissionPlugin", + magnitude_threshold=1.0, site="geothermie_unterhaching") +UnterhachingLessThan2RetrievePermissionPlugin = \ + _site_magnitude_threshold_retrieve_permission( + "UnterhachingLessThan2RetrievePermissionPlugin", + magnitude_threshold=2.0, site="geothermie_unterhaching") + + class QuakeMLIndexerPlugin(IndexerPluginPoint): """ Each document type can have one indexer. @@ -114,7 +202,8 @@ class QuakeMLIndexerPlugin(IndexerPluginPoint): "author": "str", "public": "bool", "evaluation_mode": "str", - "event_type": "str" + "event_type": "str", + "site": "str", } def index(self, document): @@ -160,6 +249,10 @@ def index(self, document): evaluation_mode = extra["evaluationMode"]["value"] else: evaluation_mode = None + if "site" in extra: + site = extra["site"]["value"] + else: + site = None indices.append({ "quakeml_id": str(event.resource_id), @@ -181,6 +274,7 @@ def index(self, document): # fast queries using PostGIS. "geometry": [Point(org.longitude, org.latitude)] if org else None, + "site": site, }) return indices diff --git a/src/jane/static/web_gis/src/directives/map.js b/src/jane/static/web_gis/src/directives/map.js index 5079e4e..71ce4e7 100644 --- a/src/jane/static/web_gis/src/directives/map.js +++ b/src/jane/static/web_gis/src/directives/map.js @@ -482,9 +482,14 @@ app.directive('openlayers3', function($q, $log, bing_key, $modal) { public = "not specified" } + var site = feature.get("site"); + if (site == null) { + site = "not specified" + } + tooltip_title += "\nAgency: " + feature.get("agency") + " | Author: " + author + " | Evaluation mode: " + evaluation_mode + - " | Public: " + public; + " | Public: " + public + " | Site: " + site; if (feature.get('magnitude')) { @@ -561,6 +566,7 @@ app.directive('openlayers3', function($q, $log, bing_key, $modal) { modal.$scope.magnitude_type = feature.get("magnitude_type"); modal.$scope.origin_time = feature.get("origin_time"); modal.$scope.public = feature.get("public"); + modal.$scope.site = feature.get("site"); modal.$scope.quakeml_id = feature.get("quakeml_id"); diff --git a/src/jane/static/web_gis/templates/event_modal.tpl.html b/src/jane/static/web_gis/templates/event_modal.tpl.html index 0a16037..9641adf 100644 --- a/src/jane/static/web_gis/templates/event_modal.tpl.html +++ b/src/jane/static/web_gis/templates/event_modal.tpl.html @@ -29,6 +29,8 @@
{{ evaluation_mode }}
Public
{{ public }}
+
Site
+
{{ site }}