Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new retrieve permissions working on "site" custom tag of events #22

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/jane/documents/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
96 changes: 95 additions & 1 deletion src/jane/quakeml/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Collaborator

@barsch barsch May 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can filter also right here by appending your filter after get_filtered_queryset(), as this should return a QuerySet object itself - as long the queryset is not evaluated by looping over it you can continue to modify it like filtering, ordering etc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I saw that, but I couldn't figure out how to invert the queryset at the end, so the only way I saw was to combine all these queries and invert them inside the SQL extra(where=..) query.

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.
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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),
Expand All @@ -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
8 changes: 7 additions & 1 deletion src/jane/static/web_gis/src/directives/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')) {

Expand Down Expand Up @@ -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");


Expand Down
2 changes: 2 additions & 0 deletions src/jane/static/web_gis/templates/event_modal.tpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ <h4 class="modal-title">Details for event {{title}}</h4>
<dd>{{ evaluation_mode }}</dd>
<dt>Public</dt>
<dd>{{ public }}</dd>
<dt>Site</dt>
<dd>{{ site }}</dd>
</dl>
</div>
<div class="row">
Expand Down