diff --git a/.gitignore b/.gitignore index 250135d..6c67d66 100644 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,4 @@ venv.bak/ # mypy .mypy_cache/ .idea +.vscode/settings.json diff --git a/error_tracker/django/migrations/0002_auto_20201018_1311.py b/error_tracker/django/migrations/0002_auto_20201018_1311.py index a427d28..3634abe 100644 --- a/error_tracker/django/migrations/0002_auto_20201018_1311.py +++ b/error_tracker/django/migrations/0002_auto_20201018_1311.py @@ -20,4 +20,4 @@ class Migration(migrations.Migration): name='ticket_raised', field=models.BooleanField(default=False), ), - ] + ] \ No newline at end of file diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index cfe4db8..0c10b6b 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -34,8 +34,18 @@ class AbstractErrorModel(models.Model, ModelMixin): ticket_raised = models.BooleanField(default=False) @classmethod - def get_exceptions_per_page(cls, page_number=1): - records = cls.objects.all().order_by('last_seen') + def get_exceptions_per_page(cls, page_number=1, **query): + if query: + if 'page' in query: + page_number = query['page'] + del query['page'] + query = {"{}__icontains".format(k): v for k, v in query.items()} + + if not query: + records = cls.objects.all().order_by('last_seen') + else: + records = cls.objects.filter(**query).order_by('last_seen') + paginator = Paginator(records, EXCEPTION_APP_DEFAULT_LIST_SIZE) try: page = paginator.page(page_number) diff --git a/error_tracker/django/templates/error_tracker/base.html b/error_tracker/django/templates/error_tracker/base.html index 2a2617c..b8186e5 100755 --- a/error_tracker/django/templates/error_tracker/base.html +++ b/error_tracker/django/templates/error_tracker/base.html @@ -1,3 +1,4 @@ +{% load i18n %} @@ -12,6 +13,24 @@ font-size: 14px; } + + {% endblock %} {{ title }} @@ -19,7 +38,7 @@
{% block header_block %}

- Errors Seen + {% trans 'Errors Seen' %}

{% endblock %} {% block content_block %} diff --git a/error_tracker/django/templates/error_tracker/detail.html b/error_tracker/django/templates/error_tracker/detail.html index 33345cd..961873a 100755 --- a/error_tracker/django/templates/error_tracker/detail.html +++ b/error_tracker/django/templates/error_tracker/detail.html @@ -1,5 +1,5 @@ {% extends 'error_tracker/base.html' %} -{% load error_tracker %} +{% load error_tracker i18n %} {% block content_block %} {% if error %} @@ -7,8 +7,8 @@

{{ error }}

{% else %}
-
Method
-
Referrer
+
{%trans 'Method' %}
+
{%trans 'Referrer' %}
{{ obj.method }}
@@ -19,24 +19,24 @@

{{ error }}

-
First time seen
+
{%trans 'First time seen' %}
{{ obj.created_on }}
-
Last seen
+
{%trans 'Last seen' %}
{{ obj.last_seen }}
-
Occurrences
+
{%trans 'Occurrences' %}
{{ obj.count }}
-
Request data
+
{%trans 'Request data' %}
{{obj.request_data|to_pretty}}
-
Exception detail
+
{%trans 'Exception detail' %}
 {{obj.traceback|escape|replace_new_line_with_br|safe}}
diff --git a/error_tracker/django/templates/error_tracker/list.html b/error_tracker/django/templates/error_tracker/list.html index f116c1e..f33f1ac 100755 --- a/error_tracker/django/templates/error_tracker/list.html +++ b/error_tracker/django/templates/error_tracker/list.html @@ -1,60 +1,107 @@ {% extends 'error_tracker/base.html' %} +{% load i18n %} + +{% block head_script %} +{{block.super}} + +{%endblock%} {% block content_block %}
{% if error %}

{{ error }}

{% else %} - - - - - - - - - - - - - - {% for error in errors.items %} - - - - - - - - - - {% endfor %} - -
HostMethodPathExceptionLast seenOccurrencesAction
{{ error.host }}{{ error.method }} - - {{ error.path|truncatechars:30 }} - - {{ error.exception_name }}{{ error.last_seen }}{{ error.count }} - Delete -
+
+
+ + + + + + + + + + + + + + + + + + + + + {% include 'error_tracker/partials/partial_table.html' %} + + +
{% trans 'Host' %}{% trans 'Method' %}{% trans 'Path' %}{% trans 'Exception' %}{% trans 'Last seen' %}#{% trans 'Action' %}
{% trans 'Reset' %}
+
+ {% if prev_url or next_url %} -
-
-
- {% if prev_url %} - - Newer exceptions - - {% endif %} - {% if next_url %} - - Older exceptions - - {% endif %} -
+ {% endif %} +
{% endif %}
{% endblock %} \ No newline at end of file diff --git a/error_tracker/django/templates/error_tracker/partials/navigation.html b/error_tracker/django/templates/error_tracker/partials/navigation.html new file mode 100644 index 0000000..249d9a2 --- /dev/null +++ b/error_tracker/django/templates/error_tracker/partials/navigation.html @@ -0,0 +1,14 @@ +{% load i18n %} + +{% if prev_url %} + + {% trans 'Newer exceptions' %} +{% endif %} +{% if next_url %} + + {% trans 'Older exceptions' %} + +{% endif %} + + + diff --git a/error_tracker/django/templates/error_tracker/partials/partial_table.html b/error_tracker/django/templates/error_tracker/partials/partial_table.html new file mode 100644 index 0000000..720c29a --- /dev/null +++ b/error_tracker/django/templates/error_tracker/partials/partial_table.html @@ -0,0 +1,19 @@ +{% load i18n %} + {% for error in errors.items %} + + {{ error.host }} + {{ error.method }} + + + {{ error.path|truncatechars:30 }} + + + {{ error.exception_name }} + {{ error.last_seen }} + {{ error.count }} + + {%trans 'Delete' %} + + + {% endfor %} diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 8fbe6c3..feef724 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -6,13 +6,19 @@ # :license: BSD-3-Clause # -import re import json -from django.http import RawPostDataException +import re +try: + from http.cookies import SimpleCookie +except ImportError: + pass -from error_tracker.libs.mixins import ContextBuilderMixin, NotificationMixin, ViewPermissionMixin +from error_tracker.libs.mixins import (ContextBuilderMixin, NotificationMixin, + ViewPermissionMixin) from error_tracker.libs.utils import get_context_dict + from django.core.mail import send_mail +from django.http import RawPostDataException class DefaultDjangoContextBuilder(ContextBuilderMixin): @@ -90,7 +96,6 @@ def notify(self, request, exception, exception.notification_sent = True exception.save() - class DefaultDjangoViewPermission(ViewPermissionMixin): def __call__(self, request): @@ -197,7 +202,6 @@ def get_value(key, value): except Exception as e: if key in ["Cookie", "cookie"]: try: - from http.cookies import SimpleCookie try: cookie = SimpleCookie() cookie.load(value) diff --git a/error_tracker/django/views.py b/error_tracker/django/views.py index c104a61..e83076a 100644 --- a/error_tracker/django/views.py +++ b/error_tracker/django/views.py @@ -5,12 +5,13 @@ # :copyright: 2020 Sonu Kumar # :license: BSD-3-Clause # -from django.http import Http404, HttpResponse +from django.http import Http404, HttpResponse, JsonResponse from django.shortcuts import redirect, render from django.urls import reverse from django.views.decorators.http import require_GET from error_tracker.django import get_exception_model, get_view_permission from django.contrib.auth.decorators import login_required +from django.template.loader import render_to_string model = get_exception_model() @@ -35,17 +36,30 @@ def view_list(request): :return: rendered template """ title = "App Error" - try: - page = int(request.GET.get('page', 1)) - except: - page = 1 + + query = request.GET.dict() + error = False - errors = model.get_exceptions_per_page(page_number=page) + errors = model.get_exceptions_per_page(**query) + next_url = reverse('view_errors') + "?page=" + str(errors.next_num) \ if errors.has_next else None + prev_url = reverse('view_errors') + "?page=" + str(errors.prev_num) \ if errors.has_prev else None + if request.is_ajax() or request.GET.get('ajax_partial'): + table = render_to_string('error_tracker/partials/partial_table.html', { + 'errors': errors, + }) + + navigation = render_to_string('error_tracker/partials/navigation.html', { + 'next_url': next_url, + 'prev_url': prev_url + }) + + return JsonResponse({'table': table, 'navigation': navigation}) + return render(request, template_name='error_tracker/list.html', context=dict(error=error, title=title, errors=errors, next_url=next_url, prev_url=prev_url)) diff --git a/error_tracker/libs/mixins.py b/error_tracker/libs/mixins.py index d28ba19..18b6f51 100755 --- a/error_tracker/libs/mixins.py +++ b/error_tracker/libs/mixins.py @@ -78,7 +78,7 @@ def create_or_update_entity(cls, rhash, host, path, method, request_data, @classmethod @abc.abstractmethod - def get_exceptions_per_page(cls, page_number=1): + def get_exceptions_per_page(cls, page_number=1, **kwargs): """ An object having these properties, has_next, next_num, has_prev, prev_num and items diff --git a/locale/fr_FR/LC_MESSAGES/django.po b/locale/fr_FR/LC_MESSAGES/django.po new file mode 100644 index 0000000..68a4807 --- /dev/null +++ b/locale/fr_FR/LC_MESSAGES/django.po @@ -0,0 +1,63 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , 2020. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-10-22 15:02+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Thierry BOULOGNE \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: error_tracker/django/apps.py:17 +msgid "Error Monitoring and Exception Tracking" +msgstr "" + +#: error_tracker/django/templates/error_tracker/list.html:86 +msgid "Host" +msgstr "Hôtesss" + +#: error_tracker/django/templates/error_tracker/list.html:87 +msgid "Method" +msgstr "Méthode" + +#: error_tracker/django/templates/error_tracker/list.html:88 +msgid "Path" +msgstr "Chemin" + +#: error_tracker/django/templates/error_tracker/list.html:89 +msgid "Exception" +msgstr "Erreur" + +#: error_tracker/django/templates/error_tracker/list.html:90 +msgid "Last seen" +msgstr "Dernière occurence" + +#: error_tracker/django/templates/error_tracker/list.html:92 +msgid "Action" +msgstr "Action" + +#: error_tracker/django/templates/error_tracker/list.html:101 +msgid "Reset" +msgstr "Effacer" + +#: error_tracker/django/templates/error_tracker/partials/navigation.html:5 +msgid "Newer exceptions" +msgstr "Suivant" + +#: error_tracker/django/templates/error_tracker/partials/navigation.html:9 +msgid "Older exceptions" +msgstr "Précédent" + +#: error_tracker/django/templates/error_tracker/partials/partial_table.html:16 +msgid "Delete" +msgstr "Supprimer" diff --git a/tests/DjangoTest/tests/test_end_point.py b/tests/DjangoTest/tests/test_end_point.py index 5999d54..63017b9 100644 --- a/tests/DjangoTest/tests/test_end_point.py +++ b/tests/DjangoTest/tests/test_end_point.py @@ -20,17 +20,17 @@ def test_list_view(self): self.get('/value-error') self.post('/post-view') html = self.get('/dev', follow=True).content - urls = [node.attrib['href'] for node in pyquery.PyQuery(html)('a')] + urls = [node.attrib['href'] for node in pyquery.PyQuery(html)('a.view-link, a.home-link, a.delete')] # 2 links for delete operation and 2 links to navigate and 1 link to home page self.assertEqual(len(urls), 2 + 3) - urls = [node.attrib['href'] for node in pyquery.PyQuery(html)('.view-link')] + urls = [node.attrib['href'] for node in pyquery.PyQuery(html)('a.view-link')] self.assertEqual(len(urls), 2) def test_detail_view(self): self.get('/value-error') html = self.get('/dev', follow=True).content - url = [node.attrib['href'] for node in pyquery.PyQuery(html)('.view-link')][0] + url = [node.attrib['href'] for node in pyquery.PyQuery(html)('a.view-link')][0] response = self.get(url).content row = pyquery.PyQuery(response)('.mb-4') self.assertEqual(2, len(row)) @@ -60,24 +60,24 @@ def test_pagination(self): exception.traceback) response = self.get('/dev', follow=True).content - urls = [node.attrib['href'] for node in pyquery.PyQuery(response)('a')] + urls = [node.attrib['href'] for node in pyquery.PyQuery(response)('a.view-link, a.delete, a.pagelink, a.home-link')] self.assertEqual(len(urls), settings.EXCEPTION_APP_DEFAULT_LIST_SIZE * 2 + 2) self.assertTrue('/dev/?page=2' in urls) response = self.get('/dev/?page=2', follow=True).content - urls = [node.attrib['href'] for node in pyquery.PyQuery(response)('a')] + urls = [node.attrib['href'] for node in pyquery.PyQuery(response)('a.view-link, a.delete, a.pagelink, a.home-link')] self.assertEqual(len(urls), settings.EXCEPTION_APP_DEFAULT_LIST_SIZE * 2 + 3) self.assertTrue('/dev/?page=1' in urls) self.assertTrue('/dev/?page=3' in urls) response = self.get('/dev/?page=5', follow=True).content - urls = [node.attrib['href'] for node in pyquery.PyQuery(response)('a')] + urls = [node.attrib['href'] for node in pyquery.PyQuery(response)('a.view-link, a.delete, a.pagelink, a.home-link')] self.assertTrue('/dev/?page=4' in urls) response = self.get('/dev/?page=6', follow=True).content - urls = [node.attrib['href'] for node in pyquery.PyQuery(response)('a')] + urls = [node.attrib['href'] for node in pyquery.PyQuery(response)('a.view-link, a.delete, a.pagelink, a.home-link')] self.assertEqual(len(urls), 2) if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file