From 1bab2a9a127a201e050b0a52ff41de2809ca2967 Mon Sep 17 00:00:00 2001 From: Elias Date: Mon, 22 Jan 2018 17:11:31 +0100 Subject: [PATCH] add a new page for resource paginated logs (#14671) --- passerelle/base/models.py | 54 +++++++++++++++++++ passerelle/base/templatetags/passerelle.py | 23 -------- .../includes/resource-logs-table.html | 30 ----------- .../passerelle/manage/service_log.html | 41 ++++++++++++++ .../passerelle/manage/service_view.html | 7 +-- passerelle/urls.py | 9 ++-- passerelle/views.py | 41 +++++++++++--- 7 files changed, 134 insertions(+), 71 deletions(-) delete mode 100644 passerelle/templates/passerelle/includes/resource-logs-table.html create mode 100644 passerelle/templates/passerelle/manage/service_log.html diff --git a/passerelle/base/models.py b/passerelle/base/models.py index 77139a5..8ad4bde 100644 --- a/passerelle/base/models.py +++ b/passerelle/base/models.py @@ -16,7 +16,10 @@ from django.db.models import Q from django.utils.text import slugify from django.utils import timezone from django.utils.translation import ugettext_lazy as _ +from django.utils.dateparse import parse_datetime + from django.core.files.base import ContentFile +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import fields @@ -428,6 +431,55 @@ class AccessRight(models.Model): return '%s (on %s <%s>) (for %s)' % (self.codename, self.resource_type, self.resource_pk, self.apiuser) +class ResourceLogManager(models.Manager): + def get_paginated_logs(self, appname, slug, page=None, lookup_dict=None, lookup_or=None, order_by=None, page_size=None): + if page is None: + page = 1 + if order_by is None: + order_by = '-timestamp' + if page_size is None: + page_size = 10 + qs = None + if lookup_dict: + q_list = [] + # remove forbidden lookup filter keys, transform timestamp + for key, value in lookup_dict.items(): + if key in ['appname', 'slug']: + del lookup_dict[key] + if key.startswith('timestamp'): + lookup_dict[key] = parse_datetime(lookup_dict[key]) + print lookup_dict[key] + if lookup_dict[key] is None: + del lookup_dict[key] + q_list += [Q(**{key: value})] + + if len(q_list): + lookups = q_list.pop() + for q_item in q_list: + if lookup_or is None: + lookups &= q_item + else: + lookups |= q_item + qs = self.filter(lookups) + if qs and qs.count() > 0: + qs = qs.filter(appname=appname, slug=slug) + else: + qs = self.none() + else: + qs = self.filter(appname=appname, slug=slug) + + qs = qs.order_by(order_by) + paginator = Paginator(qs, int(page_size)) + try: + page_obj = paginator.page(page) + except PageNotAnInteger: + page_obj = paginator.page(1) + except (EmptyPage,): + page_obj = paginator.page(paginator.num_pages) + + return page_obj + + class ResourceLog(models.Model): timestamp = models.DateTimeField(auto_now_add=True) appname = models.CharField(max_length=128, verbose_name='appname', null=True) @@ -437,6 +489,8 @@ class ResourceLog(models.Model): message = models.TextField(max_length=2048, verbose_name='message') extra = jsonfield.JSONField(verbose_name='extras', default={}) + objects = ResourceLogManager() + class Meta: permissions = ( ('view_resourcelog', 'Can view resource logs'), diff --git a/passerelle/base/templatetags/passerelle.py b/passerelle/base/templatetags/passerelle.py index 2e51b37..424a72c 100644 --- a/passerelle/base/templatetags/passerelle.py +++ b/passerelle/base/templatetags/passerelle.py @@ -3,7 +3,6 @@ from __future__ import absolute_import from django import template from django.contrib.contenttypes.models import ContentType from django.contrib.auth import get_permission_codename -from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from passerelle.utils import get_trusted_services from ..models import AccessRight, ResourceLog @@ -23,28 +22,6 @@ def access_rights_table(context, resource, permission): return context -@register.inclusion_tag('passerelle/includes/resource-logs-table.html', takes_context=True) -def resource_logs_table(context, resource): - request = context.get('request') - page = request.GET.get('page', 1) - - connector = resource.get_connector_slug() - context['connector'] = connector - context['slug'] = resource.slug - qs = ResourceLog.objects.filter(appname=connector, slug=resource.slug).order_by('-timestamp') - - paginator = Paginator(qs, 10) - try: - logrecords = paginator.page(page) - except PageNotAnInteger: - logrecords = paginator.page(1) - except (EmptyPage,): - logrecords = paginator.page(paginator.num_pages) - - context['logrecords'] = logrecords - return context - - @register.filter def can_edit(obj, user): return user.has_perm(get_permission_codename('change', obj._meta), obj=obj) diff --git a/passerelle/templates/passerelle/includes/resource-logs-table.html b/passerelle/templates/passerelle/includes/resource-logs-table.html deleted file mode 100644 index b618174..0000000 --- a/passerelle/templates/passerelle/includes/resource-logs-table.html +++ /dev/null @@ -1,30 +0,0 @@ -{% load i18n passerelle %} -{% load tz %} - -{% block content %} -{% if logrecords %} - - - - - - - - {% for record in logrecords %} - - - - - - {% endfor %} - -
{% trans 'Timestamp' %}{% trans 'Source IP' %}{% trans 'Message' %}
{{ record.timestamp|localtime }}{{ record.sourceip|default:"-" }}{{ record.message}}
- -{% with page_obj=logrecords %} - {% include "gadjo/pagination.html" with anchor="#logs" %} -{% endwith %} - -{% else %} -

{% trans 'No records found' %}

-{% endif %} -{% endblock %} diff --git a/passerelle/templates/passerelle/manage/service_log.html b/passerelle/templates/passerelle/manage/service_log.html new file mode 100644 index 0000000..634c93f --- /dev/null +++ b/passerelle/templates/passerelle/manage/service_log.html @@ -0,0 +1,41 @@ +{% extends "passerelle/manage/service_view.html" %} +{% load i18n passerelle %} +{% load tz %} + +{% block breadcrumb %} +{{ block.super }} +{{ object.get_verbose_name }} +{% endblock %} + +{% block content %} +{% if perms.base.view_resourcelog %} +
+

{% trans "Logs" %}

+
+ {% block logs %} + {% if page_obj.paginator.count %} + + + + + + + + {% for record in page_obj %} + + + + + + {% endfor %} + +
{% trans 'Timestamp' %}{% trans 'Source IP' %}{% trans 'Message' %}
{{ record.timestamp|localtime }}{{ record.sourceip|default:"-" }}{{ record.message}}
+ {% include "gadjo/pagination.html" with anchor="#logs" %} + {% else %} +

{% trans 'No records found' %}

+ {% endif %} + {% endblock %} +
+
+{% endif %} +{% endblock %} diff --git a/passerelle/templates/passerelle/manage/service_view.html b/passerelle/templates/passerelle/manage/service_view.html index e88c05f..e3b71d4 100644 --- a/passerelle/templates/passerelle/manage/service_view.html +++ b/passerelle/templates/passerelle/manage/service_view.html @@ -87,12 +87,7 @@ {% if perms.base.view_resourcelog %}
-

{% trans "Logs" %}

-
- {% block logs %} - {% resource_logs_table resource=object %} - {% endblock %} -
+

{% trans "Logs" %}

{% endif %} diff --git a/passerelle/urls.py b/passerelle/urls.py index c98af21..e2f49dc 100644 --- a/passerelle/urls.py +++ b/passerelle/urls.py @@ -9,6 +9,7 @@ from django.views.static import serve as static_serve from .views import (HomePageView, ManageView, ManageAddView, GenericCreateConnectorView, GenericDeleteConnectorView, GenericEditConnectorView, GenericEndpointView, GenericConnectorView, + GenericConnectorLogView, login, logout, menu_json) from .urls_utils import decorated_includes, required, app_enabled, manager_required from .base.urls import access_urlpatterns @@ -78,15 +79,11 @@ urlpatterns += [ urlpatterns += [ url(r'^(?P[\w,-]+)/(?P[\w,-]+)/$', GenericConnectorView.as_view(), name='view-connector'), + url(r'^(?P[\w,-]+)/(?P[\w,-]+)/logwatch$', + GenericConnectorLogView.as_view(), name='view-connector-log'), url(r'^(?P[\w,-]+)/(?P[\w,-]+)/(?P[\w,-]+)(?:/(?P.*))?$', GenericEndpointView.as_view(), name='generic-endpoint') ] -if settings.DEBUG and 'debug_toolbar' in settings.INSTALLED_APPS: - import debug_toolbar - urlpatterns = [ - url(r'^__debug__/', include(debug_toolbar.urls)), - ] + urlpatterns - from django.contrib.staticfiles.urls import staticfiles_urlpatterns urlpatterns += staticfiles_urlpatterns() diff --git a/passerelle/views.py b/passerelle/views.py index dcadcce..c424ec3 100644 --- a/passerelle/views.py +++ b/passerelle/views.py @@ -6,7 +6,7 @@ from django.conf.urls import url from django.core.exceptions import PermissionDenied from django.contrib.auth import logout as auth_logout from django.contrib.auth import views as auth_views -from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, Http404 +from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.views.decorators.csrf import csrf_exempt from django.views.generic import (RedirectView, View, TemplateView, CreateView, DeleteView, UpdateView, DetailView) @@ -18,17 +18,15 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import force_text from django.forms.models import modelform_factory from django.forms.widgets import ClearableFileInput +from .base.models import BaseResource, ResourceLog +from .utils import to_json, is_authorized +from .forms import GenericConnectorForm if 'mellon' in settings.INSTALLED_APPS: from mellon.utils import get_idps else: get_idps = lambda: [] -from passerelle.base.models import BaseResource - -from .utils import to_json, response_for_json, is_authorized -from .forms import GenericConnectorForm - def get_all_apps(): return [x for x in apps.get_models() if issubclass(x, BaseResource) and \ @@ -137,6 +135,37 @@ class GenericConnectorView(GenericConnectorMixin, DetailView): template_names.append('passerelle/manage/service_view.html') return template_names +class GenericConnectorLogView(GenericConnectorMixin, DetailView): + template_name = 'passerelle/manage/service_log.html' + + def get_context_data(self, object, **kwargs): + '''All search parameters from GET query dict overridden with POST if present + ''' + connector = object.get_connector_slug() + kwargs = {} + # Mutable version of WueryDict instance + get = self.request.GET.copy() + post = self.request.POST.copy() + for param in ['page', 'lookup_or', 'order_by', 'page_size']: + try: + kwargs[param] = get.pop(param)[0] + if param in post: + kwargs[param] = post.pop(param)[0] + except KeyError: + continue + + lookup_dict = get.dict() + lookup_dict.update(post.dict()) + page_obj = ResourceLog.objects.get_paginated_logs(connector, object.slug, lookup_dict=lookup_dict, + **kwargs) + return { + 'view': self, + 'object': object, + 'connector': connector, + 'slug': object.slug, + 'page_obj': page_obj + } + class GenericCreateConnectorView(GenericConnectorMixin, CreateView): template_name = 'passerelle/manage/service_form.html' -- 2.17.0