Projet

Général

Profil

0001-add-a-new-page-for-resource-paginated-logs-14671.patch

Anonyme, 13 avril 2018 21:50

Télécharger (12,7 ko)

Voir les différences:

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
passerelle/base/models.py
16 16
from django.utils.text import slugify
17 17
from django.utils import timezone
18 18
from django.utils.translation import ugettext_lazy as _
19
from django.utils.dateparse import parse_datetime
20

  
19 21
from django.core.files.base import ContentFile
22
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
20 23

  
21 24
from django.contrib.contenttypes.models import ContentType
22 25
from django.contrib.contenttypes import fields
......
428 431
        return '%s (on %s <%s>) (for %s)' % (self.codename, self.resource_type, self.resource_pk, self.apiuser)
429 432

  
430 433

  
434
class ResourceLogManager(models.Manager):
435
    def get_paginated_logs(self, appname, slug, page=None, lookup_dict=None, lookup_or=None, order_by=None, page_size=None):
436
        if page is None:
437
            page = 1
438
        if order_by is None:
439
            order_by = '-timestamp'
440
        if page_size is None:
441
            page_size = 10
442
        qs = None
443
        if lookup_dict:
444
            q_list = []
445
            # remove forbidden lookup filter keys, transform timestamp
446
            for key, value in lookup_dict.items():
447
                if key in ['appname', 'slug']:
448
                    del lookup_dict[key]
449
                if key.startswith('timestamp'):
450
                    lookup_dict[key] = parse_datetime(lookup_dict[key])
451
                    print lookup_dict[key]
452
                    if lookup_dict[key] is None:
453
                        del lookup_dict[key]
454
                q_list += [Q(**{key: value})]
455

  
456
            if len(q_list):
457
                lookups = q_list.pop()
458
                for q_item in q_list:
459
                    if lookup_or is None:
460
                        lookups &= q_item
461
                    else:
462
                        lookups |= q_item
463
                qs = self.filter(lookups)
464
            if qs and qs.count() > 0:
465
                qs = qs.filter(appname=appname, slug=slug)
466
            else:
467
                qs = self.none()
468
        else:
469
            qs = self.filter(appname=appname, slug=slug)
470

  
471
        qs = qs.order_by(order_by)
472
        paginator = Paginator(qs, int(page_size))
473
        try:
474
            page_obj = paginator.page(page)
475
        except PageNotAnInteger:
476
            page_obj = paginator.page(1)
477
        except (EmptyPage,):
478
            page_obj = paginator.page(paginator.num_pages)
479

  
480
        return page_obj
481

  
482

  
431 483
class ResourceLog(models.Model):
432 484
    timestamp = models.DateTimeField(auto_now_add=True)
433 485
    appname = models.CharField(max_length=128, verbose_name='appname', null=True)
......
437 489
    message = models.TextField(max_length=2048, verbose_name='message')
438 490
    extra = jsonfield.JSONField(verbose_name='extras', default={})
439 491

  
492
    objects = ResourceLogManager()
493

  
440 494
    class Meta:
441 495
        permissions = (
442 496
            ('view_resourcelog', 'Can view resource logs'),
passerelle/base/templatetags/passerelle.py
3 3
from django import template
4 4
from django.contrib.contenttypes.models import ContentType
5 5
from django.contrib.auth import get_permission_codename
6
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
7 6

  
8 7
from passerelle.utils import get_trusted_services
9 8
from ..models import AccessRight, ResourceLog
......
23 22
    return context
24 23

  
25 24

  
26
@register.inclusion_tag('passerelle/includes/resource-logs-table.html', takes_context=True)
27
def resource_logs_table(context, resource):
28
    request = context.get('request')
29
    page = request.GET.get('page', 1)
30

  
31
    connector = resource.get_connector_slug()
32
    context['connector'] = connector
33
    context['slug'] = resource.slug
34
    qs = ResourceLog.objects.filter(appname=connector, slug=resource.slug).order_by('-timestamp')
35

  
36
    paginator = Paginator(qs, 10)
37
    try:
38
        logrecords = paginator.page(page)
39
    except PageNotAnInteger:
40
        logrecords = paginator.page(1)
41
    except (EmptyPage,):
42
        logrecords = paginator.page(paginator.num_pages)
43

  
44
    context['logrecords'] = logrecords
45
    return context
46

  
47

  
48 25
@register.filter
49 26
def can_edit(obj, user):
50 27
    return user.has_perm(get_permission_codename('change', obj._meta), obj=obj)
passerelle/templates/passerelle/includes/resource-logs-table.html
1
{% load i18n passerelle %}
2
{% load tz %}
3

  
4
{% block content %}
5
{% if logrecords %}
6
<table class="main">
7
    <thead>
8
        <th>{% trans 'Timestamp' %}</th>
9
        <th>{% trans 'Source IP' %}</th>
10
        <th>{% trans 'Message' %}</th>
11
    </thead>
12
    <tbody>
13
    {% for record in logrecords %}
14
    <tr class="level-{{record.level}}">
15
        <td class="timestamp">{{ record.timestamp|localtime }}</td>
16
        <td>{{ record.sourceip|default:"-" }}</td>
17
        <td class="message">{{ record.message}}</td>
18
    </tr>
19
    {% endfor %}
20
    </tbody>
21
</table>
22

  
23
{% with page_obj=logrecords %}
24
  {% include "gadjo/pagination.html" with anchor="#logs" %}
25
{% endwith %}
26

  
27
{% else %}
28
<p>{% trans 'No records found' %}</p>
29
{% endif %}
30
{% endblock %}
passerelle/templates/passerelle/manage/service_log.html
1
{% extends "passerelle/manage/service_view.html" %}
2
{% load i18n passerelle %}
3
{% load tz %}
4

  
5
{% block breadcrumb %}
6
{{ block.super }}
7
<a href="{{object.get_absolute_url}}">{{ object.get_verbose_name }}</a>
8
{% endblock %}
9

  
10
{% block content %}
11
{% if perms.base.view_resourcelog %}
12
<div id="logs" class="section">
13
  <h3>{% trans "Logs" %}</h3>
14
  <div>
15
  {% block logs %}
16
  {% if page_obj.paginator.count %}
17
  <table class="main">
18
      <thead>
19
          <th>{% trans 'Timestamp' %}</th>
20
          <th>{% trans 'Source IP' %}</th>
21
          <th>{% trans 'Message' %}</th>
22
      </thead>
23
      <tbody>
24
      {% for record in page_obj %}
25
      <tr class="level-{{record.level}}">
26
          <td class="timestamp">{{ record.timestamp|localtime }}</td>
27
          <td>{{ record.sourceip|default:"-" }}</td>
28
          <td class="message">{{ record.message}}</td>
29
      </tr>
30
      {% endfor %}
31
      </tbody>
32
  </table>
33
  {% include "gadjo/pagination.html" with anchor="#logs" %}
34
  {% else %}
35
  <p>{% trans 'No records found' %}</p>
36
  {% endif %}
37
  {% endblock %}
38
  </div>
39
</div>
40
{% endif %}
41
{% endblock %}
passerelle/templates/passerelle/manage/service_view.html
87 87

  
88 88
{% if perms.base.view_resourcelog %}
89 89
<div id="logs" class="section">
90
  <h3>{% trans "Logs" %}</h3>
91
  <div>
92
  {% block logs %}
93
    {% resource_logs_table resource=object %}
94
  {% endblock %}
95
  </div>
90
  <h3><a href="{% url 'view-connector-log' object.get_connector_slug object.slug %}">{% trans "Logs" %}</a></h3>
96 91
</div>
97 92
{% endif %}
98 93

  
passerelle/urls.py
9 9
from .views import (HomePageView, ManageView, ManageAddView,
10 10
        GenericCreateConnectorView, GenericDeleteConnectorView,
11 11
        GenericEditConnectorView, GenericEndpointView, GenericConnectorView,
12
        GenericConnectorLogView,
12 13
        login, logout, menu_json)
13 14
from .urls_utils import decorated_includes, required, app_enabled, manager_required
14 15
from .base.urls import access_urlpatterns
......
78 79
urlpatterns += [
79 80
    url(r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/$',
80 81
        GenericConnectorView.as_view(), name='view-connector'),
82
    url(r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/logwatch$',
83
        GenericConnectorLogView.as_view(), name='view-connector-log'),
81 84
    url(r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/(?P<endpoint>[\w,-]+)(?:/(?P<rest>.*))?$',
82 85
        GenericEndpointView.as_view(), name='generic-endpoint')
83 86
]
84 87

  
85
if settings.DEBUG and 'debug_toolbar' in settings.INSTALLED_APPS:
86
    import debug_toolbar
87
    urlpatterns = [
88
        url(r'^__debug__/', include(debug_toolbar.urls)),
89
    ] + urlpatterns
90

  
91 88
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
92 89
urlpatterns += staticfiles_urlpatterns()
passerelle/views.py
6 6
from django.core.exceptions import PermissionDenied
7 7
from django.contrib.auth import logout as auth_logout
8 8
from django.contrib.auth import views as auth_views
9
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, Http404
9
from django.http import HttpResponse, HttpResponseRedirect, Http404
10 10
from django.views.decorators.csrf import csrf_exempt
11 11
from django.views.generic import (RedirectView, View, TemplateView, CreateView,
12 12
        DeleteView, UpdateView, DetailView)
......
18 18
from django.utils.encoding import force_text
19 19
from django.forms.models import modelform_factory
20 20
from django.forms.widgets import ClearableFileInput
21
from .base.models import BaseResource, ResourceLog
22
from .utils import to_json, is_authorized
23
from .forms import GenericConnectorForm
21 24

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

  
27
from passerelle.base.models import BaseResource
28

  
29
from .utils import to_json, response_for_json, is_authorized
30
from .forms import GenericConnectorForm
31

  
32 30

  
33 31
def get_all_apps():
34 32
    return [x for x in apps.get_models() if issubclass(x, BaseResource) and \
......
137 135
        template_names.append('passerelle/manage/service_view.html')
138 136
        return template_names
139 137

  
138
class GenericConnectorLogView(GenericConnectorMixin, DetailView):
139
    template_name = 'passerelle/manage/service_log.html'
140

  
141
    def get_context_data(self, object, **kwargs):
142
        '''All search parameters from GET query dict overridden with POST if present
143
        '''
144
        connector = object.get_connector_slug()
145
        kwargs = {}
146
        # Mutable version of WueryDict instance
147
        get = self.request.GET.copy()
148
        post = self.request.POST.copy()
149
        for param in ['page', 'lookup_or', 'order_by', 'page_size']:
150
            try:
151
                kwargs[param] = get.pop(param)[0]
152
                if param in post:
153
                    kwargs[param] = post.pop(param)[0]
154
            except KeyError:
155
                continue
156

  
157
        lookup_dict = get.dict()
158
        lookup_dict.update(post.dict())
159
        page_obj = ResourceLog.objects.get_paginated_logs(connector, object.slug, lookup_dict=lookup_dict,
160
            **kwargs)
161
        return {
162
            'view': self,
163
            'object': object,
164
            'connector': connector,
165
            'slug': object.slug,
166
            'page_obj': page_obj
167
        }
168

  
140 169

  
141 170
class GenericCreateConnectorView(GenericConnectorMixin, CreateView):
142 171
    template_name = 'passerelle/manage/service_form.html'
143
-