Projet

Général

Profil

0001-manager-add-full-page-with-logs-and-basic-filtering-.patch

Frédéric Péters, 19 avril 2018 10:46

Télécharger (9,17 ko)

Voir les différences:

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
passerelle/templates/passerelle/includes/resource-logs-table.html
1 1
{% load i18n passerelle %}
2 2
{% load tz %}
3 3

  
4
{% block content %}
5 4
{% if logrecords %}
6 5
<table class="main">
7 6
    <thead>
......
27 26
{% else %}
28 27
<p>{% trans 'No records found' %}</p>
29 28
{% endif %}
30
{% endblock %}
passerelle/templates/passerelle/manage/service_logs.html
1
{% extends "passerelle/manage.html" %}
2
{% load i18n passerelle %}
3

  
4
{% block breadcrumb %}
5
{{ block.super }}
6
<a href="{{object.get_absolute_url}}">{{ object.title }}</a>
7
<a href="#">{% trans "Logs" %}</a>
8
{% endblock %}
9

  
10
{% block appbar %}
11
<h2>{% trans "Logs" %}</h2>
12
{% endblock %}
13

  
14
{% block content %}
15

  
16
<div id="logs">
17

  
18
<form>
19
  <p><input name="q" type="search" value="{{query}}"> <button>{% trans 'Search' %}</button>
20
  <span class="help_text">{% trans "(supports text search in messages, or dates)" %}</span>
21
  </p>
22
</form>
23

  
24
{% include "passerelle/includes/resource-logs-table.html" with logrecords=page_obj %}
25
</div>
26

  
27
{% endblock %}
passerelle/templates/passerelle/manage/service_view.html
90 90

  
91 91
{% if perms.base.view_resourcelog %}
92 92
<div id="logs" class="section">
93
  <h3>{% trans "Logs" %}</h3>
93
  <h3>{% trans "Logs" %}
94
          <a href="{% url 'view-logs-connector' connector=object.get_connector_slug slug=object.slug %}">({% trans "full page & filter" %})</a>
95
  </h3>
94 96
  <div>
95 97
  {% block logs %}
96 98
    {% resource_logs_table resource=object %}
passerelle/urls.py
9 9
from .views import (HomePageView, ManageView, ManageAddView,
10 10
        GenericCreateConnectorView, GenericDeleteConnectorView,
11 11
        GenericEditConnectorView, GenericEndpointView, GenericConnectorView,
12
        login, logout, menu_json)
12
        GenericViewLogsConnectorView, login, logout, menu_json)
13 13
from .urls_utils import decorated_includes, required, app_enabled, manager_required
14 14
from .base.urls import access_urlpatterns
15 15
from .plugins import register_apps_urls
......
72 72
                GenericDeleteConnectorView.as_view(), name='delete-connector'),
73 73
            url(r'^(?P<slug>[\w,-]+)/edit$',
74 74
                GenericEditConnectorView.as_view(), name='edit-connector'),
75
            url(r'^(?P<slug>[\w,-]+)/logs$',
76
                GenericViewLogsConnectorView.as_view(), name='view-logs-connector'),
75 77
        ])))
76 78
]
77 79

  
passerelle/views.py
1
import datetime
1 2
import inspect
2 3
import json
3 4

  
......
9 10
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, Http404
10 11
from django.views.decorators.csrf import csrf_exempt
11 12
from django.views.generic import (RedirectView, View, TemplateView, CreateView,
12
        DeleteView, UpdateView, DetailView)
13
        DeleteView, UpdateView, DetailView, ListView)
13 14
from django.views.generic.detail import SingleObjectMixin
14 15
from django.conf import settings
15 16
from django.shortcuts import resolve_url
16 17
from django.core.urlresolvers import reverse
18
from django.utils.timezone import make_aware
17 19
from django.utils.translation import ugettext_lazy as _
18 20
from django.utils.encoding import force_text
19 21
from django.forms.models import modelform_factory
20 22
from django.forms.widgets import ClearableFileInput
21 23

  
24
from dateutil import parser as date_parser
25

  
22 26
if 'mellon' in settings.INSTALLED_APPS:
23 27
    from mellon.utils import get_idps
24 28
else:
25 29
    get_idps = lambda: []
26 30

  
27
from passerelle.base.models import BaseResource
31
from passerelle.base.models import BaseResource, ResourceLog
28 32

  
29 33
from .utils import to_json, response_for_json, is_authorized
30 34
from .forms import GenericConnectorForm
......
163 167
        return reverse('manage-home')
164 168

  
165 169

  
170
class GenericViewLogsConnectorView(GenericConnectorMixin, ListView):
171
    template_name = 'passerelle/manage/service_logs.html'
172
    paginate_by = 25
173

  
174
    def get_context_data(self, **kwargs):
175
        context = super(GenericViewLogsConnectorView, self).get_context_data(**kwargs)
176
        context['object'] = self.get_object()
177
        context['query'] = self.request.GET.get('q') or ''
178
        return context
179

  
180
    def get_object(self):
181
        return self.model.objects.get(slug=self.kwargs['slug'])
182

  
183
    def get_queryset(self):
184
        qs = ResourceLog.objects.filter(
185
                appname=self.kwargs['connector'],
186
                slug=self.kwargs['slug']).order_by('-timestamp')
187
        query = self.request.GET.get('q')
188
        if query:
189
            try:
190
                date = date_parser.parse(query, dayfirst=True)
191
            except:
192
                qs = qs.filter(message__icontains=query)
193
            else:
194
                date = make_aware(date)
195
                qs = qs.filter(timestamp__gte=date, timestamp__lt=date + datetime.timedelta(days=1))
196
        return qs
197

  
198

  
166 199
class WrongParameter(Exception):
167 200
    http_status = 400
168 201
    log_error = False
tests/test_manager.py
1
import datetime
1 2
import re
3
from StringIO import StringIO
2 4

  
3 5
from django.contrib.auth.models import User
6
from django.contrib.contenttypes.models import ContentType
7
from django.core.files import File
4 8
import pytest
5 9

  
6
from passerelle.base.models import ApiUser
10
from passerelle.base.models import ApiUser, AccessRight
11
from passerelle.apps.csvdatasource.models import CsvDataSource, Query
7 12

  
8 13
pytestmark = pytest.mark.django_db
9 14

  
......
109 114
    resp = app.get('/manage/menu.json?callback=FooBar')
110 115
    assert resp.headers['content-type'] == 'application/javascript'
111 116
    assert resp.content.startswith('FooBar([{"')
117

  
118
def test_logs(app, admin_user):
119
    data = StringIO('1;Foo\n2;Bar\n3;Baz')
120
    csv = CsvDataSource.objects.create(csv_file=File(data, 't.csv'),
121
           columns_keynames='id, text', slug='test', title='a title', description='a description')
122

  
123
    query = Query(slug='fooba', resource=csv, structure='array')
124
    query.projections = '\n'.join(['id:int(id)', 'text:text'])
125
    query.save()
126

  
127
    api = ApiUser.objects.create(username='public',
128
                    fullname='public',
129
                    description='access for all',
130
                    keytype='', key='')
131
    obj_type = ContentType.objects.get_for_model(csv)
132
    AccessRight.objects.create(codename='can_access',
133
                    apiuser=api,
134
                    resource_type=obj_type,
135
                    resource_pk=csv.pk,
136
    )
137

  
138
    app = login(app)
139
    resp = app.get(csv.get_absolute_url())
140
    assert '<p>No records found</p>' in resp.body
141

  
142
    app.get('/csvdatasource/test/query/foobar/')
143
    resp = app.get(csv.get_absolute_url())
144
    assert 'endpoint GET /csvdatasource/test/query/foobar/ ' in resp.text
145

  
146
    app.get('/csvdatasource/test/query/foobar/?q=toto')
147
    resp = app.get(csv.get_absolute_url())
148
    assert 'endpoint GET /csvdatasource/test/query/foobar/?q=toto' in resp.text
149

  
150
    resp = resp.click('full page')
151
    assert resp.text.count('<td class="timestamp">') == 2
152

  
153
    resp.form['q'] = 'toto'
154
    resp = resp.form.submit()
155
    assert resp.text.count('<td class="timestamp">') == 1
156

  
157
    resp.form['q'] = datetime.date.today().strftime('%d/%m/%Y')
158
    resp = resp.form.submit()
159
    assert resp.text.count('<td class="timestamp">') == 2
160

  
161
    resp.form['q'] = datetime.date.today().strftime('%d/%m/2010')
162
    resp = resp.form.submit()
163
    assert resp.text.count('<td class="timestamp">') == 0
112
-