From 9720a5a8fbeda08c20619ca21ddc69eacbb9295c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Tue, 17 Apr 2018 17:38:33 +0200 Subject: [PATCH] manager: add full page with logs and basic filtering (#14671) --- .../includes/resource-logs-table.html | 2 - .../passerelle/manage/service_logs.html | 27 ++++++++++ .../passerelle/manage/service_view.html | 4 +- passerelle/urls.py | 4 +- passerelle/views.py | 37 ++++++++++++- tests/test_manager.py | 54 ++++++++++++++++++- 6 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 passerelle/templates/passerelle/manage/service_logs.html diff --git a/passerelle/templates/passerelle/includes/resource-logs-table.html b/passerelle/templates/passerelle/includes/resource-logs-table.html index b618174..7c57460 100644 --- a/passerelle/templates/passerelle/includes/resource-logs-table.html +++ b/passerelle/templates/passerelle/includes/resource-logs-table.html @@ -1,7 +1,6 @@ {% load i18n passerelle %} {% load tz %} -{% block content %} {% if logrecords %} @@ -27,4 +26,3 @@ {% else %}

{% trans 'No records found' %}

{% endif %} -{% endblock %} diff --git a/passerelle/templates/passerelle/manage/service_logs.html b/passerelle/templates/passerelle/manage/service_logs.html new file mode 100644 index 0000000..60ccf22 --- /dev/null +++ b/passerelle/templates/passerelle/manage/service_logs.html @@ -0,0 +1,27 @@ +{% extends "passerelle/manage.html" %} +{% load i18n passerelle %} + +{% block breadcrumb %} +{{ block.super }} +{{ object.title }} +{% trans "Logs" %} +{% endblock %} + +{% block appbar %} +

{% trans "Logs" %}

+{% endblock %} + +{% block content %} + +
+ +
+

+ {% trans "(supports text search in messages, or dates)" %} +

+ + +{% include "passerelle/includes/resource-logs-table.html" with logrecords=page_obj %} +
+ +{% endblock %} diff --git a/passerelle/templates/passerelle/manage/service_view.html b/passerelle/templates/passerelle/manage/service_view.html index 3670df7..e4a58d4 100644 --- a/passerelle/templates/passerelle/manage/service_view.html +++ b/passerelle/templates/passerelle/manage/service_view.html @@ -90,7 +90,9 @@ {% if perms.base.view_resourcelog %}
-

{% trans "Logs" %}

+

{% trans "Logs" %} + ({% trans "full page & filter" %}) +

{% block logs %} {% resource_logs_table resource=object %} diff --git a/passerelle/urls.py b/passerelle/urls.py index c98af21..121560a 100644 --- a/passerelle/urls.py +++ b/passerelle/urls.py @@ -9,7 +9,7 @@ from django.views.static import serve as static_serve from .views import (HomePageView, ManageView, ManageAddView, GenericCreateConnectorView, GenericDeleteConnectorView, GenericEditConnectorView, GenericEndpointView, GenericConnectorView, - login, logout, menu_json) + GenericViewLogsConnectorView, login, logout, menu_json) from .urls_utils import decorated_includes, required, app_enabled, manager_required from .base.urls import access_urlpatterns from .plugins import register_apps_urls @@ -72,6 +72,8 @@ urlpatterns += [ GenericDeleteConnectorView.as_view(), name='delete-connector'), url(r'^(?P[\w,-]+)/edit$', GenericEditConnectorView.as_view(), name='edit-connector'), + url(r'^(?P[\w,-]+)/logs$', + GenericViewLogsConnectorView.as_view(), name='view-logs-connector'), ]))) ] diff --git a/passerelle/views.py b/passerelle/views.py index dcadcce..890d7e3 100644 --- a/passerelle/views.py +++ b/passerelle/views.py @@ -1,3 +1,4 @@ +import datetime import inspect import json @@ -9,22 +10,25 @@ from django.contrib.auth import views as auth_views from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, Http404 from django.views.decorators.csrf import csrf_exempt from django.views.generic import (RedirectView, View, TemplateView, CreateView, - DeleteView, UpdateView, DetailView) + DeleteView, UpdateView, DetailView, ListView) from django.views.generic.detail import SingleObjectMixin from django.conf import settings from django.shortcuts import resolve_url from django.core.urlresolvers import reverse +from django.utils.timezone import make_aware 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 dateutil import parser as date_parser + if 'mellon' in settings.INSTALLED_APPS: from mellon.utils import get_idps else: get_idps = lambda: [] -from passerelle.base.models import BaseResource +from passerelle.base.models import BaseResource, ResourceLog from .utils import to_json, response_for_json, is_authorized from .forms import GenericConnectorForm @@ -163,6 +167,35 @@ class GenericDeleteConnectorView(GenericConnectorMixin, DeleteView): return reverse('manage-home') +class GenericViewLogsConnectorView(GenericConnectorMixin, ListView): + template_name = 'passerelle/manage/service_logs.html' + paginate_by = 25 + + def get_context_data(self, **kwargs): + context = super(GenericViewLogsConnectorView, self).get_context_data(**kwargs) + context['object'] = self.get_object() + context['query'] = self.request.GET.get('q') or '' + return context + + def get_object(self): + return self.model.objects.get(slug=self.kwargs['slug']) + + def get_queryset(self): + qs = ResourceLog.objects.filter( + appname=self.kwargs['connector'], + slug=self.kwargs['slug']).order_by('-timestamp') + query = self.request.GET.get('q') + if query: + try: + date = date_parser.parse(query, dayfirst=True) + except: + qs = qs.filter(message__icontains=query) + else: + date = make_aware(date) + qs = qs.filter(timestamp__gte=date, timestamp__lt=date + datetime.timedelta(days=1)) + return qs + + class WrongParameter(Exception): http_status = 400 log_error = False diff --git a/tests/test_manager.py b/tests/test_manager.py index f1c5096..947bced 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -1,9 +1,14 @@ +import datetime import re +from StringIO import StringIO from django.contrib.auth.models import User +from django.contrib.contenttypes.models import ContentType +from django.core.files import File import pytest -from passerelle.base.models import ApiUser +from passerelle.base.models import ApiUser, AccessRight +from passerelle.apps.csvdatasource.models import CsvDataSource, Query pytestmark = pytest.mark.django_db @@ -109,3 +114,50 @@ def test_menu_json(app, admin_user): resp = app.get('/manage/menu.json?callback=FooBar') assert resp.headers['content-type'] == 'application/javascript' assert resp.content.startswith('FooBar([{"') + +def test_logs(app, admin_user): + data = StringIO('1;Foo\n2;Bar\n3;Baz') + csv = CsvDataSource.objects.create(csv_file=File(data, 't.csv'), + columns_keynames='id, text', slug='test', title='a title', description='a description') + + query = Query(slug='fooba', resource=csv, structure='array') + query.projections = '\n'.join(['id:int(id)', 'text:text']) + query.save() + + api = ApiUser.objects.create(username='public', + fullname='public', + description='access for all', + keytype='', key='') + obj_type = ContentType.objects.get_for_model(csv) + AccessRight.objects.create(codename='can_access', + apiuser=api, + resource_type=obj_type, + resource_pk=csv.pk, + ) + + app = login(app) + resp = app.get(csv.get_absolute_url()) + assert '

No records found

' in resp.body + + app.get('/csvdatasource/test/query/foobar/') + resp = app.get(csv.get_absolute_url()) + assert 'endpoint GET /csvdatasource/test/query/foobar/ ' in resp.text + + app.get('/csvdatasource/test/query/foobar/?q=toto') + resp = app.get(csv.get_absolute_url()) + assert 'endpoint GET /csvdatasource/test/query/foobar/?q=toto' in resp.text + + resp = resp.click('full page') + assert resp.text.count('
') == 2 + + resp.form['q'] = 'toto' + resp = resp.form.submit() + assert resp.text.count('') == 1 + + resp.form['q'] = datetime.date.today().strftime('%d/%m/%Y') + resp = resp.form.submit() + assert resp.text.count('') == 2 + + resp.form['q'] = datetime.date.today().strftime('%d/%m/2010') + resp = resp.form.submit() + assert resp.text.count('') == 0 -- 2.17.0