0004-add-a-new-page-for-resource-paginated-logs-14671.patch
passerelle/base/models.py | ||
---|---|---|
190 | 190 |
return reverse('delete-connector', |
191 | 191 |
kwargs={'connector': self.get_connector_slug(), 'slug': self.slug}) |
192 | 192 | |
193 |
def get_log_url(self): |
|
194 |
return reverse('view-connector-log', |
|
195 |
kwargs={'connector': self.get_connector_slug(), 'slug': self.slug}) |
|
196 | ||
197 | ||
193 | 198 |
def get_description_fields(self): |
194 | 199 |
fields = [] |
195 | 200 |
for field in self._meta.fields: |
... | ... | |
430 | 435 | |
431 | 436 | |
432 | 437 |
class ResourceLogManager(models.Manager): |
433 |
def get_paginated_logs(self, appname, slug, page, order_by=None, page_size=None): |
|
438 |
def get_paginated_logs(self, appname, slug, page, lookup_dict=None, lookup_or=None, order_by=None, page_size=None):
|
|
434 | 439 |
if order_by is None: |
435 | 440 |
order_by = '-timestamp' |
436 | 441 |
if page_size is None: |
437 | 442 |
page_size = 10 |
438 | 443 | |
439 |
qs = self.filter(appname=appname, slug=slug).order_by(order_by) |
|
444 |
if lookup_dict: |
|
445 |
q_list = [Q(key=value) for key, value in lookup_dict.items()] |
|
446 |
lookups = q_list.pop() |
|
447 |
for q_item in q_list: |
|
448 |
if lookup_or is None: |
|
449 |
lookups &= q_item |
|
450 |
else: |
|
451 |
lookups |= q_item |
|
452 |
qs = self.filter(lookups, appname=appname, slug=slug) |
|
453 |
else: |
|
454 |
qs = self.filter(appname=appname, slug=slug) |
|
455 |
qs.order_by(order_by) |
|
440 | 456 |
paginator = Paginator(qs, page_size) |
441 | 457 |
try: |
442 | 458 |
logrecords = paginator.page(page) |
... | ... | |
471 | 487 |
def __unicode__(self): |
472 | 488 |
return '%s %s %s %s' % (self.timestamp, self.levelno, self.appname, self.slug) |
473 | 489 | |
490 | ||
474 | 491 |
STATUS_CHOICES = ( |
475 | 492 |
('unknown', _('Unknown')), |
476 | 493 |
('up', _('Up')), |
passerelle/base/templatetags/passerelle.py | ||
---|---|---|
22 | 22 |
return context |
23 | 23 | |
24 | 24 | |
25 |
@register.inclusion_tag('passerelle/includes/resource-logs-table.html', takes_context=True) |
|
26 |
def resource_logs_table(context, resource): |
|
27 |
request = context.get('request') |
|
28 |
page = request.GET.get('page', 1) |
|
29 | ||
30 |
connector = resource.get_connector_slug() |
|
31 |
context['connector'] = connector |
|
32 |
context['slug'] = resource.slug |
|
33 |
context['logrecords'] = ResourceLog.objects.get_paginated_logs(connector, resource.slug, page) |
|
34 |
return context |
|
35 | ||
36 | ||
37 | 25 |
@register.filter |
38 | 26 |
def can_edit(obj, user): |
39 | 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 | ||
17 |
{% if logrecords %} |
|
18 |
<table class="main"> |
|
19 |
<thead> |
|
20 |
<th>{% trans 'Timestamp' %}</th> |
|
21 |
<th>{% trans 'Source IP' %}</th> |
|
22 |
<th>{% trans 'Message' %}</th> |
|
23 |
</thead> |
|
24 |
<tbody> |
|
25 |
{% for record in logrecords %} |
|
26 |
<tr class="level-{{record.level}}"> |
|
27 |
<td class="timestamp">{{ record.timestamp|localtime }}</td> |
|
28 |
<td>{{ record.sourceip|default:"-" }}</td> |
|
29 |
<td class="message">{{ record.message}}</td> |
|
30 |
</tr> |
|
31 |
{% endfor %} |
|
32 |
</tbody> |
|
33 |
</table> |
|
34 | ||
35 |
{% with page_obj=logrecords %} |
|
36 |
{% include "gadjo/pagination.html" with anchor="#logs" %} |
|
37 |
{% endwith %} |
|
38 | ||
39 |
{% else %} |
|
40 |
<p>{% trans 'No records found' %}</p> |
|
41 |
{% endif %} |
|
42 | ||
43 |
{% endblock %} |
|
44 |
</div> |
|
45 |
</div> |
|
46 |
{% endif %} |
|
47 |
{% 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={{ object.get_log_url }}>{% trans "Logs" %}</a></h3> |
|
96 | 91 |
</div> |
97 | 92 |
{% endif %} |
98 | 93 |
passerelle/urls.py | ||
---|---|---|
79 | 79 |
urlpatterns += [ |
80 | 80 |
url(r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/$', |
81 | 81 |
GenericConnectorView.as_view(), name='view-connector'), |
82 |
url(r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/logs.json$',
|
|
82 |
url(r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/logs$', |
|
83 | 83 |
GenericConnectorLogView.as_view(), name='view-connector-log'), |
84 | 84 |
url(r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/(?P<endpoint>[\w,-]+)(?:/(?P<rest>.*))?$', |
85 | 85 |
GenericEndpointView.as_view(), name='generic-endpoint') |
passerelle/views.py | ||
---|---|---|
8 | 8 |
from django.core.exceptions import PermissionDenied |
9 | 9 |
from django.contrib.auth import logout as auth_logout |
10 | 10 |
from django.contrib.auth import views as auth_views |
11 |
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, Http404
|
|
11 |
from django.http import HttpResponse, HttpResponseRedirect, Http404 |
|
12 | 12 |
from django.views.decorators.csrf import csrf_exempt |
13 | 13 |
from django.views.generic import (RedirectView, View, TemplateView, CreateView, |
14 | 14 |
DeleteView, UpdateView, DetailView) |
... | ... | |
16 | 16 |
from django.conf import settings |
17 | 17 |
from django.shortcuts import resolve_url |
18 | 18 |
from django.core.urlresolvers import reverse |
19 |
from django.core.paginator import EmptyPage, PageNotAnInteger |
|
20 | 19 |
from django.utils.translation import ugettext_lazy as _ |
21 | 20 |
from django.utils.encoding import force_text |
22 |
from django.utils.timezone import make_aware |
|
23 | 21 |
from django.forms.models import modelform_factory |
24 | 22 |
from django.forms.widgets import ClearableFileInput |
23 |
from .base.models import BaseResource, ResourceLog |
|
24 |
from .utils import to_json, is_authorized |
|
25 |
from .forms import GenericConnectorForm |
|
25 | 26 | |
26 | 27 |
if 'mellon' in settings.INSTALLED_APPS: |
27 | 28 |
from mellon.utils import get_idps |
28 | 29 |
else: |
29 | 30 |
get_idps = lambda: [] |
30 | 31 | |
31 |
from passerelle.base.models import BaseResource, ResourceLog |
|
32 | ||
33 |
from .utils import to_json, response_for_json, is_authorized |
|
34 |
from .forms import GenericConnectorForm |
|
35 | ||
36 | 32 | |
37 | 33 |
def get_all_apps(): |
38 | 34 |
return [x for x in apps.get_models() if issubclass(x, BaseResource) and \ |
... | ... | |
141 | 137 |
template_names.append('passerelle/manage/service_view.html') |
142 | 138 |
return template_names |
143 | 139 | |
144 |
class GenericConnectorLogView(GenericConnectorMixin, View): |
|
145 | ||
146 |
content_type = 'application/json' |
|
147 |
epoch = make_aware(datetime(1970,1,1)) |
|
148 | ||
149 |
def totimestamp(self, datetime_obj): |
|
150 |
td = datetime_obj - self.epoch |
|
151 |
# return td.total_seconds() |
|
152 |
return int((td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**3) |
|
153 | ||
154 |
def resource_logs_table(self, request, *args, **kwargs): |
|
155 |
if 'slug' not in kwargs: |
|
156 |
raise HttpResponseBadRequest('URL must specify a slug value') |
|
157 |
if 'connector' not in kwargs: |
|
158 |
raise HttpResponseBadRequest('URL must specify a connector value') |
|
159 |
page = request.GET.get('page', 1) |
|
160 |
return ResourceLog.objects.get_paginated_logs(kwargs['connector'], kwargs['slug'], page) |
|
161 | ||
162 |
def get(self, request, *args, **kwargs): |
|
163 |
page_obj = self.resource_logs_table(request, *args, **kwargs) |
|
164 |
# no previous or next is representezd a None value |
|
165 |
try: |
|
166 |
previous_page_number = page_obj.previous_page_number() |
|
167 |
except (EmptyPage,): |
|
168 |
previous_page_number = None |
|
169 |
try: |
|
170 |
next_page_number = page_obj.next_page_number() |
|
171 |
except (EmptyPage,): |
|
172 |
next_page_number = None |
|
173 |
return response_for_json(request, dict([ |
|
174 |
('count', page_obj.paginator.count), |
|
175 |
('page_range', list(page_obj.paginator.page_range)), |
|
176 |
('number', page_obj.number), |
|
177 |
('next_page_number', next_page_number), |
|
178 |
('previous_page_number', previous_page_number), |
|
179 |
('logs', [dict([('message', log.message), |
|
180 |
('timestamp', self.totimestamp(log.timestamp)), |
|
181 |
('level', log.level), |
|
182 |
('extra', log.extra), |
|
183 |
('sourceip', log.sourceip)]) for log in page_obj]) |
|
184 |
])) |
|
140 |
class GenericConnectorLogView(GenericConnectorMixin, DetailView): |
|
141 | ||
142 |
template_name = 'passerelle/manage/service_log.html' |
|
143 | ||
144 |
def query_resource_log_paginator(self, connector, slug, **kwargs): |
|
145 |
page = kwargs.get('page', 1) |
|
146 |
return ResourceLog.objects.get_paginated_logs(connector, slug, page, |
|
147 |
lookup_dict=kwargs.get('lookups'), lookup_or=kwargs.get('lookup_or'), |
|
148 |
order_by=kwargs.get('order_by'), page_size=kwargs.get('page_size')) |
|
149 | ||
150 |
def get_context_data(self, object, **kwargs): |
|
151 |
# request = context.get('request') |
|
152 |
connector = object.get_connector_slug() |
|
153 |
page_obj = self.query_resource_log_paginator(connector, object.slug, **kwargs) |
|
154 |
return { |
|
155 |
'view': self, |
|
156 |
'object': object, |
|
157 |
'connector': connector, |
|
158 |
'slug': object.slug, |
|
159 |
'logrecords': page_obj |
|
160 |
} |
|
185 | 161 | |
186 | 162 | |
187 | 163 |
class GenericCreateConnectorView(GenericConnectorMixin, CreateView): |
188 |
- |