From 4e7bd19b04545e85bab4e5f6770df217b0f48a29 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 18 Oct 2020 08:53:29 +0200 Subject: [PATCH 01/28] Add field to prevent multiple notifications notification_send > for notification ticket_raise > for ticket creation --- error_tracker/django/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index 9e3e30a..62a2370 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -30,6 +30,9 @@ class ErrorModel(models.Model, ModelMixin): count = models.IntegerField(default=0) created_on = models.DateTimeField(auto_now=True) last_seen = models.DateTimeField(auto_now=True, db_index=True) + notification_send = models.BooleanField(default=False) + ticket_raise = models.BooleanField(default=False) + @classmethod def get_exceptions_per_page(cls, page_number=1): From c58bf62755cfb5d78223c5d17ab4af9cdfebc210 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 18 Oct 2020 08:59:07 +0200 Subject: [PATCH 02/28] Check previous send Check if a ticket already raised or notification send before sending --- error_tracker/django/middleware.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/error_tracker/django/middleware.py b/error_tracker/django/middleware.py index cf715db..37ce563 100644 --- a/error_tracker/django/middleware.py +++ b/error_tracker/django/middleware.py @@ -65,8 +65,10 @@ def _post_process(request, frame_str, frames, error): else: message = "" message += frame_str - ErrorTracker._send_notification(request, message, frames[-1][:-1], error) - ErrorTracker._raise_ticket(request, error) + if not error.notification_send: + ErrorTracker._send_notification(request, message, frames[-1][:-1], error) + if not error.ticket_raised: + ErrorTracker._raise_ticket(request, error) def capture_exception(self, request=None, exception=None, additional_context=None): """ From 225c4ea4991fb9f982036476b6d9156ffaf51ba8 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 18 Oct 2020 08:59:31 +0200 Subject: [PATCH 03/28] fix type --- error_tracker/django/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index 62a2370..a8a78c8 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -31,7 +31,7 @@ class ErrorModel(models.Model, ModelMixin): created_on = models.DateTimeField(auto_now=True) last_seen = models.DateTimeField(auto_now=True, db_index=True) notification_send = models.BooleanField(default=False) - ticket_raise = models.BooleanField(default=False) + ticket_raised = models.BooleanField(default=False) @classmethod From 14a376af7471941124890bc22704fb2aeebf8b6e Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 18 Oct 2020 09:00:27 +0200 Subject: [PATCH 04/28] update fields --- error_tracker/libs/mixins.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/error_tracker/libs/mixins.py b/error_tracker/libs/mixins.py index 6cebb12..99a6eb2 100755 --- a/error_tracker/libs/mixins.py +++ b/error_tracker/libs/mixins.py @@ -39,6 +39,8 @@ class ModelMixin(object): count = None created_on = None last_seen = None + notification_send = None + ticket_raised = None def __str__(self): return "'%s' '%s' %s" % (self.host, self.path, self.count) From f943cc200393ae6d2f6c9bf2c96f60d573bfab7e Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 18 Oct 2020 09:16:23 +0200 Subject: [PATCH 05/28] save notification send confirm --- error_tracker/django/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 4bc7430..7c90ccc 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -88,6 +88,8 @@ def notify(self, request, exception, recipient_list=None): if recipient_list is not None and from_email is not None: send_mail(email_subject, email_body, from_email, recipient_list, fail_silently=True) + exception.notification_send = True + exception.save() class DefaultDjangoViewPermission(ViewPermissionMixin): From f746ec68081bdd30729ac6a48bab742833724ae7 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 18 Oct 2020 10:41:17 +0200 Subject: [PATCH 06/28] add returnt obj to create_or_update_entity --- error_tracker/django/models.py | 4 ++-- error_tracker/django/utils.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index a8a78c8..0a627bb 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -32,8 +32,6 @@ class ErrorModel(models.Model, ModelMixin): last_seen = models.DateTimeField(auto_now=True, db_index=True) notification_send = models.BooleanField(default=False) ticket_raised = models.BooleanField(default=False) - - @classmethod def get_exceptions_per_page(cls, page_number=1): records = cls.objects.all().order_by('last_seen') @@ -65,6 +63,8 @@ def create_or_update_entity(cls, rhash, host, path, method, request_data, except obj.count += 1 obj.last_seen = now() obj.save(update_fields=['count', 'last_seen']) + + return obj except Exception: print_exc() diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 7c90ccc..a184c4f 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -90,8 +90,6 @@ def notify(self, request, exception, send_mail(email_subject, email_body, from_email, recipient_list, fail_silently=True) exception.notification_send = True exception.save() - - class DefaultDjangoViewPermission(ViewPermissionMixin): def __call__(self, request): From ce1874351edc30d680b72262187e02a4c64ec827 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 18 Oct 2020 11:00:41 +0200 Subject: [PATCH 07/28] add login_required to permission wrapper --- error_tracker/django/utils.py | 1 + error_tracker/django/views.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index a184c4f..3706dbc 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -90,6 +90,7 @@ def notify(self, request, exception, send_mail(email_subject, email_body, from_email, recipient_list, fail_silently=True) exception.notification_send = True exception.save() + class DefaultDjangoViewPermission(ViewPermissionMixin): def __call__(self, request): diff --git a/error_tracker/django/views.py b/error_tracker/django/views.py index 73b8c7a..21fdf62 100644 --- a/error_tracker/django/views.py +++ b/error_tracker/django/views.py @@ -10,6 +10,7 @@ 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 model = get_exception_model() @@ -17,11 +18,11 @@ def has_view_permission(func): + @login_required def wrapper(request, *args, **kwargs): if view_permission(request): return func(request, *args, **kwargs) return HttpResponse(status=401) - return wrapper From 51bf232694c3516de378651389288cd77db7e7d1 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 18 Oct 2020 14:57:27 +0200 Subject: [PATCH 08/28] Pretiffy request_data display --- .../migrations/0002_auto_20201018_1311.py | 28 +++++++++++++++++ .../migrations/0003_auto_20201018_1317.py | 28 +++++++++++++++++ .../templates/error_tracker/detail.html | 2 +- .../django/templatetags/error_tracker.py | 24 ++++++++++++++ error_tracker/django/utils.py | 31 ++++++++++++++++--- 5 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 error_tracker/django/migrations/0002_auto_20201018_1311.py create mode 100644 error_tracker/django/migrations/0003_auto_20201018_1317.py diff --git a/error_tracker/django/migrations/0002_auto_20201018_1311.py b/error_tracker/django/migrations/0002_auto_20201018_1311.py new file mode 100644 index 0000000..3262268 --- /dev/null +++ b/error_tracker/django/migrations/0002_auto_20201018_1311.py @@ -0,0 +1,28 @@ +# Generated by Django 3.1.2 on 2020-10-18 11:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django', '0001_initial'), + ] + + operations = [ + # migrations.AddField( + # model_name='errormodel', + # name='notification_send', + # field=models.BooleanField(default=False), + # ), + # migrations.AddField( + # model_name='errormodel', + # name='ticket_raised', + # field=models.BooleanField(default=False), + # ), + migrations.AlterField( + model_name='errormodel', + name='request_data', + field=models.JSONField(), + ), + ] diff --git a/error_tracker/django/migrations/0003_auto_20201018_1317.py b/error_tracker/django/migrations/0003_auto_20201018_1317.py new file mode 100644 index 0000000..ffd4bce --- /dev/null +++ b/error_tracker/django/migrations/0003_auto_20201018_1317.py @@ -0,0 +1,28 @@ +# Generated by Django 3.1.2 on 2020-10-18 11:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django', '0002_auto_20201018_1311'), + ] + + operations = [ + # migrations.AddField( + # model_name='errormodel', + # name='notification_send', + # field=models.BooleanField(default=False), + # ), + # migrations.AddField( + # model_name='errormodel', + # name='ticket_raised', + # field=models.BooleanField(default=False), + # ), + migrations.AlterField( + model_name='errormodel', + name='request_data', + field=models.TextField(), + ), + ] diff --git a/error_tracker/django/templates/error_tracker/detail.html b/error_tracker/django/templates/error_tracker/detail.html index 2e3811a..4c92ad0 100755 --- a/error_tracker/django/templates/error_tracker/detail.html +++ b/error_tracker/django/templates/error_tracker/detail.html @@ -27,7 +27,7 @@

Errors Seen


Occurrences: {{ obj.count }}


-
Request data: {{ obj.request_data }}
+
Request data: {{obj.request_data|to_pretty}}

Exception detail:
{{ obj.traceback|escape|replace_new_line_with_br|safe }}
diff --git a/error_tracker/django/templatetags/error_tracker.py b/error_tracker/django/templatetags/error_tracker.py index 9f75560..d46e98f 100644 --- a/error_tracker/django/templatetags/error_tracker.py +++ b/error_tracker/django/templatetags/error_tracker.py @@ -7,6 +7,8 @@ # from django import template +import json +from django.utils.safestring import mark_safe register = template.Library() @@ -14,3 +16,25 @@ @register.filter def replace_new_line_with_br(value): return value.replace("\n", "
") + + +@register.filter("to_pretty") +def to_pretty(x): + html = x + try: + x = json.loads(x) + except Exception as e: + try: + x = x.replace("'", '"') + x = json.loads(x) + except Exception as e: + pass + pass + + try: + html = "
{}
".format( + json.dumps(x, indent=4, sort_keys=True)) + except Exception as e: + pass + + return mark_safe(html) diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 3706dbc..edc3760 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -8,8 +8,8 @@ import re import json - from django.http import RawPostDataException +from http.cookies import SimpleCookie from error_tracker.libs.mixins import ContextBuilderMixin, NotificationMixin, ViewPermissionMixin from error_tracker.libs.utils import get_context_dict @@ -54,15 +54,38 @@ def _get_form_data(request): @staticmethod def _get_headers(request): + # lamda fonction for cookie cleaning + clean_value = lambda x : x.value.replace('[["', "").replace('"]]', "").replace('"', "") + if request is not None: try: - headers = request.headers.dict() - except AttributeError: + headers = request.headers + new_headers = {} + for key, value in headers.items(): + print(key) + try: + # Test if value could be json loaded, parse if needed as for cookie. + json.loads('{"%s":"%s"}' % (key, value)) + except Exception as e: + if key in ["Cookie", "cookie"]: + try: + cookie = SimpleCookie() + cookie.load(value) + value = {k: clean_value(v) for k, v in cookie.items()} + except Exception as e: + value = "" + else: + value = "" + + new_headers[key] = value + headers = new_headers + + except AttributeError as e: regex = re.compile('^HTTP_') headers = dict((regex.sub('', header), value) for (header, value) in request.META.items() if header.startswith('HTTP_')) return headers - + @staticmethod def _get_args(request): if request is not None: From ace6530f9219f691d75e9690d63828942aebb6e4 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 18 Oct 2020 21:58:07 +0200 Subject: [PATCH 09/28] - upgrade to bootstrap 4 - add head_script block for extending script and css - add header_block - refactor detail template - refactor list template - ad pre tag to prettify request_data and exception detail view --- .../django/templates/error_tracker/base.html | 38 ++++++---- .../templates/error_tracker/detail.html | 70 +++++++++++-------- .../django/templates/error_tracker/list.html | 6 +- 3 files changed, 69 insertions(+), 45 deletions(-) diff --git a/error_tracker/django/templates/error_tracker/base.html b/error_tracker/django/templates/error_tracker/base.html index 387ec88..2a2617c 100755 --- a/error_tracker/django/templates/error_tracker/base.html +++ b/error_tracker/django/templates/error_tracker/base.html @@ -1,17 +1,29 @@ - - - - - - - -{{ title }} - -
- {% block content_block %} + + + + + + + {% block head_script %} + {% endblock %} -
- + + {{ title }} + +
+ {% block header_block %} +

+ Errors Seen +

+ {% endblock %} + {% block content_block %} + {% endblock %} +
+ \ No newline at end of file diff --git a/error_tracker/django/templates/error_tracker/detail.html b/error_tracker/django/templates/error_tracker/detail.html index 4c92ad0..be8e2e0 100755 --- a/error_tracker/django/templates/error_tracker/detail.html +++ b/error_tracker/django/templates/error_tracker/detail.html @@ -1,36 +1,46 @@ {% extends 'error_tracker/base.html' %} {% load error_tracker %} + {% block content_block %} - - {% if error %} -

{{ error }}

- {% else %} -

Errors Seen

-
-

URL:{{ obj.host }}{{ obj.path }}

-
-

Method: {{ obj.method }}

-
-

First time seen: {{ obj.created_on }}

-
-

Last seen: {{ obj.last_seen }}

-
-

Occurrences: {{ obj.count }}

-
-
Request data: {{obj.request_data|to_pretty}}
-
-
Exception detail:
- {{ obj.traceback|escape|replace_new_line_with_br|safe }}
+
+
Method
+
Referrer
+
+ {{ obj.method }} +
+
+ {{ obj.host }}{{ obj.path }} +
+
+ +
+
+
First time seen
+
{{ obj.created_on }}
+
+
+
Last seen
+
{{ obj.last_seen }}
- {% endif %} +
+
Occurrences
+
{{ obj.count }}
+
+
+
+
Request data
+
{{obj.request_data|to_pretty}}
+
+
+
Exception detail
+
 {{obj.traceback|escape|replace_new_line_with_br|safe}}
+
+
+ + +{% endif %} {% endblock %} \ No newline at end of file diff --git a/error_tracker/django/templates/error_tracker/list.html b/error_tracker/django/templates/error_tracker/list.html index 73be054..f116c1e 100755 --- a/error_tracker/django/templates/error_tracker/list.html +++ b/error_tracker/django/templates/error_tracker/list.html @@ -1,10 +1,11 @@ {% extends 'error_tracker/base.html' %} + {% block content_block %} +
{% if error %}

{{ error }}

{% else %} -

Errors Seen

- +
@@ -55,4 +56,5 @@

Errors Seen

{% endif %} {% endif %} + {% endblock %} \ No newline at end of file From f8d5f23824efec2ccbeb807ec8c823cdb6b9e2c7 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 18 Oct 2020 22:24:14 +0200 Subject: [PATCH 10/28] remove border --- .../django/templates/error_tracker/detail.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/error_tracker/django/templates/error_tracker/detail.html b/error_tracker/django/templates/error_tracker/detail.html index be8e2e0..33345cd 100755 --- a/error_tracker/django/templates/error_tracker/detail.html +++ b/error_tracker/django/templates/error_tracker/detail.html @@ -7,35 +7,35 @@

{{ error }}

{% else %}
-
Method
-
Referrer
-
+
Method
+
Referrer
+
{{ obj.method }}
-
+
{{ obj.host }}{{ obj.path }}
-
+
First time seen
{{ obj.created_on }}
-
+
Last seen
{{ obj.last_seen }}
-
+
Occurrences
{{ obj.count }}
-
+
Request data
{{obj.request_data|to_pretty}}
-
+
Exception detail
 {{obj.traceback|escape|replace_new_line_with_br|safe}}
From eaadf4c23874bac20fde03c71bb42be8189f64ff Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Mon, 19 Oct 2020 07:53:29 +0200 Subject: [PATCH 11/28] add APP_ERROR_NOTIFICATION_ONCE and APP_ERROR_TICKET_ONCE --- error_tracker/django/middleware.py | 17 ++++++++++++++--- error_tracker/django/settings.py | 5 +++++ error_tracker/django/utils.py | 3 +-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/error_tracker/django/middleware.py b/error_tracker/django/middleware.py index 37ce563..b38e5e7 100644 --- a/error_tracker/django/middleware.py +++ b/error_tracker/django/middleware.py @@ -8,7 +8,8 @@ from error_tracker.django import get_masking_module, get_context_builder, get_ticketing_module, \ get_exception_model, get_notification_module, APP_ERROR_SUBJECT_PREFIX, APP_ERROR_EMAIL_SENDER, \ - APP_ERROR_RECIPIENT_EMAIL, TRACK_ALL_EXCEPTIONS + APP_ERROR_RECIPIENT_EMAIL, TRACK_ALL_EXCEPTIONS, APP_ERROR_NOTIFICATION_ONCE, \ + APP_ERROR_TICKET_ONCE from error_tracker.libs.utils import get_exception_name, get_context_detail, get_notification_subject model = get_exception_model() @@ -60,14 +61,24 @@ def _raise_ticket(request, error): @staticmethod def _post_process(request, frame_str, frames, error): + send_notification = True + raise_ticket = True + if request is not None: message = ('URL: %s' % request.path) + '\n\n' else: message = "" message += frame_str - if not error.notification_send: + + if APP_ERROR_NOTIFICATION_ONCE is True and error.notification_send is True: + send_notification = False + + if APP_ERROR_TICKET_ONCE is True and error.ticket_raised is True: + raise_ticket = False + + if send_notification: ErrorTracker._send_notification(request, message, frames[-1][:-1], error) - if not error.ticket_raised: + if raise_ticket: ErrorTracker._raise_ticket(request, error) def capture_exception(self, request=None, exception=None, additional_context=None): diff --git a/error_tracker/django/settings.py b/error_tracker/django/settings.py index 167a952..e9b89e5 100644 --- a/error_tracker/django/settings.py +++ b/error_tracker/django/settings.py @@ -44,3 +44,8 @@ def get(key, default): APP_ERROR_DB_MODEL = get('APP_ERROR_DB_MODEL', None) # Check error views are visible to others or not APP_ERROR_VIEW_PERMISSION = get('APP_ERROR_VIEW_PERMISSION', None) +# Send email notification once +APP_ERROR_NOTIFICATION_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', False) +# Raise ticket once +APP_ERROR_TICKET_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', False) + diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index edc3760..7e489ea 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -62,7 +62,6 @@ def _get_headers(request): headers = request.headers new_headers = {} for key, value in headers.items(): - print(key) try: # Test if value could be json loaded, parse if needed as for cookie. json.loads('{"%s":"%s"}' % (key, value)) @@ -79,7 +78,7 @@ def _get_headers(request): new_headers[key] = value headers = new_headers - + except AttributeError as e: regex = re.compile('^HTTP_') headers = dict((regex.sub('', header), value) for (header, value) From 4321131a5f7ed4b5acf56efb454876d2ff21310f Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Mon, 19 Oct 2020 18:30:57 +0200 Subject: [PATCH 12/28] fix migrations --- .../migrations/0002_auto_20201018_1311.py | 21 ++++++-------- .../migrations/0003_auto_20201018_1317.py | 28 ------------------- 2 files changed, 8 insertions(+), 41 deletions(-) delete mode 100644 error_tracker/django/migrations/0003_auto_20201018_1317.py diff --git a/error_tracker/django/migrations/0002_auto_20201018_1311.py b/error_tracker/django/migrations/0002_auto_20201018_1311.py index 3262268..625abe7 100644 --- a/error_tracker/django/migrations/0002_auto_20201018_1311.py +++ b/error_tracker/django/migrations/0002_auto_20201018_1311.py @@ -10,19 +10,14 @@ class Migration(migrations.Migration): ] operations = [ - # migrations.AddField( - # model_name='errormodel', - # name='notification_send', - # field=models.BooleanField(default=False), - # ), - # migrations.AddField( - # model_name='errormodel', - # name='ticket_raised', - # field=models.BooleanField(default=False), - # ), - migrations.AlterField( + migrations.AddField( model_name='errormodel', - name='request_data', - field=models.JSONField(), + name='notification_send', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='errormodel', + name='ticket_raised', + field=models.BooleanField(default=False), ), ] diff --git a/error_tracker/django/migrations/0003_auto_20201018_1317.py b/error_tracker/django/migrations/0003_auto_20201018_1317.py deleted file mode 100644 index ffd4bce..0000000 --- a/error_tracker/django/migrations/0003_auto_20201018_1317.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.1.2 on 2020-10-18 11:17 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('django', '0002_auto_20201018_1311'), - ] - - operations = [ - # migrations.AddField( - # model_name='errormodel', - # name='notification_send', - # field=models.BooleanField(default=False), - # ), - # migrations.AddField( - # model_name='errormodel', - # name='ticket_raised', - # field=models.BooleanField(default=False), - # ), - migrations.AlterField( - model_name='errormodel', - name='request_data', - field=models.TextField(), - ), - ] From 5d10afd1c92569998c17e90f6c55aec30bbf79b3 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Mon, 19 Oct 2020 18:50:18 +0200 Subject: [PATCH 13/28] Set default to NONE add method to parse cookie --- error_tracker/django/settings.py | 4 +-- error_tracker/django/utils.py | 57 ++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/error_tracker/django/settings.py b/error_tracker/django/settings.py index e9b89e5..a82923d 100644 --- a/error_tracker/django/settings.py +++ b/error_tracker/django/settings.py @@ -45,7 +45,7 @@ def get(key, default): # Check error views are visible to others or not APP_ERROR_VIEW_PERMISSION = get('APP_ERROR_VIEW_PERMISSION', None) # Send email notification once -APP_ERROR_NOTIFICATION_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', False) +APP_ERROR_NOTIFICATION_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', None) # Raise ticket once -APP_ERROR_TICKET_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', False) +APP_ERROR_TICKET_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', None) diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 7e489ea..6376384 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -54,37 +54,15 @@ def _get_form_data(request): @staticmethod def _get_headers(request): - # lamda fonction for cookie cleaning - clean_value = lambda x : x.value.replace('[["', "").replace('"]]', "").replace('"', "") - if request is not None: try: - headers = request.headers - new_headers = {} - for key, value in headers.items(): - try: - # Test if value could be json loaded, parse if needed as for cookie. - json.loads('{"%s":"%s"}' % (key, value)) - except Exception as e: - if key in ["Cookie", "cookie"]: - try: - cookie = SimpleCookie() - cookie.load(value) - value = {k: clean_value(v) for k, v in cookie.items()} - except Exception as e: - value = "" - else: - value = "" - - new_headers[key] = value - headers = new_headers - + headers = cookie_parse(request.headers) except AttributeError as e: regex = re.compile('^HTTP_') headers = dict((regex.sub('', header), value) for (header, value) in request.META.items() if header.startswith('HTTP_')) return headers - + @staticmethod def _get_args(request): if request is not None: @@ -205,3 +183,34 @@ def capture_exception(request=None, exception=None, additional_context=None): from error_tracker.django.middleware import error_tracker error_tracker.capture_exception(request=request, exception=exception, additional_context=additional_context) + + +def clean_value(x): + x = x.value.replace('[["', "").replace('"]]', "").replace('"', "") + return x + + +def cookie_parse(headers): + """ + Parse request headers to extract cookie. + :param headers (request headers]) + :return: [dict]: return parse header with cookie as dict + """ + new_headers = {} + for key, value in headers.items(): + try: + # Test if value could be json loaded, parse if needed as for cookie. + json.loads('{"%s":"%s"}' % (key, value)) + except Exception as e: + if key in ["Cookie", "cookie"]: + try: + cookie = SimpleCookie() + cookie.load(value) + value = {k: clean_value(v) for k, v in cookie.items()} + except Exception as e: + value = "" + else: + value = "" + + new_headers[key] = value + return new_headers From 50a9bb7362f2ffe9e86201e3a659c050afd30354 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Tue, 20 Oct 2020 07:50:13 +0200 Subject: [PATCH 14/28] change notification_send to notification_sent set APP_ERROR_XXX_ONCE to False --- error_tracker/django/middleware.py | 2 +- error_tracker/django/migrations/0002_auto_20201018_1311.py | 2 +- error_tracker/django/models.py | 2 +- error_tracker/django/settings.py | 4 ++-- error_tracker/django/templatetags/error_tracker.py | 2 +- error_tracker/django/utils.py | 2 +- error_tracker/libs/mixins.py | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/error_tracker/django/middleware.py b/error_tracker/django/middleware.py index b38e5e7..63d9e2f 100644 --- a/error_tracker/django/middleware.py +++ b/error_tracker/django/middleware.py @@ -70,7 +70,7 @@ def _post_process(request, frame_str, frames, error): message = "" message += frame_str - if APP_ERROR_NOTIFICATION_ONCE is True and error.notification_send is True: + if APP_ERROR_NOTIFICATION_ONCE is True and error.notification_sent is True: send_notification = False if APP_ERROR_TICKET_ONCE is True and error.ticket_raised is True: diff --git a/error_tracker/django/migrations/0002_auto_20201018_1311.py b/error_tracker/django/migrations/0002_auto_20201018_1311.py index 625abe7..829f2b8 100644 --- a/error_tracker/django/migrations/0002_auto_20201018_1311.py +++ b/error_tracker/django/migrations/0002_auto_20201018_1311.py @@ -12,7 +12,7 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( model_name='errormodel', - name='notification_send', + name='notification_sent', field=models.BooleanField(default=False), ), migrations.AddField( diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index 0a627bb..7cd0c89 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -30,7 +30,7 @@ class ErrorModel(models.Model, ModelMixin): count = models.IntegerField(default=0) created_on = models.DateTimeField(auto_now=True) last_seen = models.DateTimeField(auto_now=True, db_index=True) - notification_send = models.BooleanField(default=False) + notification_sent = models.BooleanField(default=False) ticket_raised = models.BooleanField(default=False) @classmethod def get_exceptions_per_page(cls, page_number=1): diff --git a/error_tracker/django/settings.py b/error_tracker/django/settings.py index a82923d..e9b89e5 100644 --- a/error_tracker/django/settings.py +++ b/error_tracker/django/settings.py @@ -45,7 +45,7 @@ def get(key, default): # Check error views are visible to others or not APP_ERROR_VIEW_PERMISSION = get('APP_ERROR_VIEW_PERMISSION', None) # Send email notification once -APP_ERROR_NOTIFICATION_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', None) +APP_ERROR_NOTIFICATION_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', False) # Raise ticket once -APP_ERROR_TICKET_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', None) +APP_ERROR_TICKET_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', False) diff --git a/error_tracker/django/templatetags/error_tracker.py b/error_tracker/django/templatetags/error_tracker.py index d46e98f..705854c 100644 --- a/error_tracker/django/templatetags/error_tracker.py +++ b/error_tracker/django/templatetags/error_tracker.py @@ -25,7 +25,7 @@ def to_pretty(x): x = json.loads(x) except Exception as e: try: - x = x.replace("'", '"') + x = x.replace("'", '"').replace("\\\\", "\\") x = json.loads(x) except Exception as e: pass diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 6376384..29d70ef 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -88,7 +88,7 @@ def notify(self, request, exception, recipient_list=None): if recipient_list is not None and from_email is not None: send_mail(email_subject, email_body, from_email, recipient_list, fail_silently=True) - exception.notification_send = True + exception.notification_sent = True exception.save() class DefaultDjangoViewPermission(ViewPermissionMixin): diff --git a/error_tracker/libs/mixins.py b/error_tracker/libs/mixins.py index 99a6eb2..d28ba19 100755 --- a/error_tracker/libs/mixins.py +++ b/error_tracker/libs/mixins.py @@ -39,7 +39,7 @@ class ModelMixin(object): count = None created_on = None last_seen = None - notification_send = None + notification_sent = None ticket_raised = None def __str__(self): From 0d12fe8ddb661fb54ece64d3227f780d9418c4c2 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Tue, 20 Oct 2020 16:07:33 +0200 Subject: [PATCH 15/28] linting in model rename cookie_parse to parse_headers --- error_tracker/django/models.py | 1 + error_tracker/django/utils.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index 7cd0c89..23d94d7 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -32,6 +32,7 @@ class ErrorModel(models.Model, ModelMixin): last_seen = models.DateTimeField(auto_now=True, db_index=True) notification_sent = models.BooleanField(default=False) ticket_raised = models.BooleanField(default=False) + @classmethod def get_exceptions_per_page(cls, page_number=1): records = cls.objects.all().order_by('last_seen') diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 29d70ef..21c9f68 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -56,7 +56,7 @@ def _get_form_data(request): def _get_headers(request): if request is not None: try: - headers = cookie_parse(request.headers) + headers = parse_headers(request.headers) except AttributeError as e: regex = re.compile('^HTTP_') headers = dict((regex.sub('', header), value) for (header, value) @@ -190,7 +190,7 @@ def clean_value(x): return x -def cookie_parse(headers): +def parse_headers(headers): """ Parse request headers to extract cookie. :param headers (request headers]) @@ -199,7 +199,7 @@ def cookie_parse(headers): new_headers = {} for key, value in headers.items(): try: - # Test if value could be json loaded, parse if needed as for cookie. + # Pare each key, value from headers items and Test if could be "json loaded". If not, we set the correspondant value to empty except for cookie key. json.loads('{"%s":"%s"}' % (key, value)) except Exception as e: if key in ["Cookie", "cookie"]: From e78538b0a62d7915998268bd38c57aa4ec47c046 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Thu, 22 Oct 2020 13:57:52 +0200 Subject: [PATCH 16/28] add ajax list --- .../migrations/0002_auto_20201018_1311.py | 2 +- error_tracker/django/models.py | 14 +- .../django/templates/error_tracker/list.html | 147 ++++++++++++------ .../error_tracker/partials/navigation.html | 14 ++ .../error_tracker/partials/partial_table.html | 18 +++ error_tracker/django/views.py | 28 +++- 6 files changed, 167 insertions(+), 56 deletions(-) create mode 100644 error_tracker/django/templates/error_tracker/partials/navigation.html create mode 100644 error_tracker/django/templates/error_tracker/partials/partial_table.html diff --git a/error_tracker/django/migrations/0002_auto_20201018_1311.py b/error_tracker/django/migrations/0002_auto_20201018_1311.py index a427d28..829f2b8 100644 --- a/error_tracker/django/migrations/0002_auto_20201018_1311.py +++ b/error_tracker/django/migrations/0002_auto_20201018_1311.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ('error_tracker', '0001_initial'), + ('django', '0001_initial'), ] operations = [ diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index 23d94d7..7e415fc 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -34,8 +34,18 @@ class ErrorModel(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, query): + if 'page' in query: + page_number = query['page'] + del query['page'] + else: + page_number = 1 + + 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/list.html b/error_tracker/django/templates/error_tracker/list.html index f116c1e..c2605df 100755 --- a/error_tracker/django/templates/error_tracker/list.html +++ b/error_tracker/django/templates/error_tracker/list.html @@ -1,59 +1,114 @@ {% extends 'error_tracker/base.html' %} {% block content_block %} +{% block head_script %} +{{block.super}} + + +{%endblock%}
{% if error %}

{{ error }}

{% else %} -
Host
- - - - - - - - - - - - - {% 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' %} + + +
HostMethodPathExceptionLast seen#Action
Reset
+
+
{% if prev_url or next_url %}
-
-
- {% if prev_url %} - - Newer exceptions - - {% endif %} - {% if next_url %} - - Older exceptions - - {% endif %} -
+ + {% endif %} {% endif %}
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..7f6bfc6 --- /dev/null +++ b/error_tracker/django/templates/error_tracker/partials/navigation.html @@ -0,0 +1,14 @@ +
+
+ {% if prev_url %} + + Newer exceptions + + {% endif %} + {% if next_url %} + + Older exceptions + + {% endif %} +
+
\ No newline at end of file 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..aef158a --- /dev/null +++ b/error_tracker/django/templates/error_tracker/partials/partial_table.html @@ -0,0 +1,18 @@ + {% for error in errors.items %} + + {{ error.host }} + {{ error.method }} + + + {{ error.path|truncatechars:30 }} + + + {{ error.exception_name }} + {{ error.last_seen }} + {{ error.count }} + + Delete + + + {% endfor %} diff --git a/error_tracker/django/views.py b/error_tracker/django/views.py index 21fdf62..83d5f5f 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 - error = False - errors = model.get_exceptions_per_page(page_number=page) + query = (request.GET.dict()) + + 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/partial_table.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)) From ec4608dce525c8de24614097a06d3b7507830084 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Thu, 22 Oct 2020 15:19:18 +0200 Subject: [PATCH 17/28] - add ajax to list - add filters - start translation sinto templates --- .../django/templates/error_tracker/list.html | 35 ++++++----- .../error_tracker/partials/navigation.html | 28 ++++----- .../error_tracker/partials/partial_table.html | 3 +- error_tracker/django/views.py | 8 +-- locale/fr_FR/LC_MESSAGES/django.po | 63 +++++++++++++++++++ 5 files changed, 101 insertions(+), 36 deletions(-) create mode 100644 locale/fr_FR/LC_MESSAGES/django.po diff --git a/error_tracker/django/templates/error_tracker/list.html b/error_tracker/django/templates/error_tracker/list.html index c2605df..b2aa9f5 100755 --- a/error_tracker/django/templates/error_tracker/list.html +++ b/error_tracker/django/templates/error_tracker/list.html @@ -1,5 +1,5 @@ {% extends 'error_tracker/base.html' %} - +{% load i18n %} {% block content_block %} {% block head_script %} {{block.super}} @@ -11,10 +11,12 @@ }) function ajax_paginate(){ - $('a.page-link').click(function(e) { + console.log("==== CLICK") + $('a.pagelink').click(function(e) { e.preventDefault(); - $.get($(this).attr("href"), function(data) { - $('#the_table').html(data) + $.get($(this).attr("href"), function(response) { + $('#the_table').html(response.table) + $('#navigations').html(response.navigation) ajax_paginate() }); }); @@ -31,7 +33,9 @@ data = data.replace(/&?[^=&]+=(&|$)/g,''); $.get(url, data, function(response) { + console.log(response.navigation) $('#the_table').html(response.table) + $('#navigations').html(response.navigation) ajax_paginate() }); } @@ -44,7 +48,7 @@ url = $('form[name="filter-form"]').attr("action") $.get(url, function(response) { $('#the_table').html(response.table) - $('#navigation').html(response.navigation) + $('#navigations').html(response.navigation) ajax_paginate() }); $('form[name="filter-form"] .filter-ajax').each(function(e, item){ @@ -79,13 +83,13 @@

{{ error }}

- - - - - + + + + + - + @@ -94,7 +98,7 @@

{{ error }}

- + {% include 'error_tracker/partials/partial_table.html' %} @@ -102,14 +106,13 @@

{{ error }}

HostMethodPathExceptionLast seen{% trans 'Host' %}{% trans 'Method' %}{% trans 'Path' %}{% trans 'Exception' %}{% trans 'Last seen' %} #Action{% trans 'Action' %}
Reset{% trans 'Reset' %}
-
+ {% if prev_url or next_url %} -
- {% 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 index 7f6bfc6..249d9a2 100644 --- a/error_tracker/django/templates/error_tracker/partials/navigation.html +++ b/error_tracker/django/templates/error_tracker/partials/navigation.html @@ -1,14 +1,14 @@ -
-
- {% if prev_url %} - - Newer exceptions - - {% endif %} - {% if next_url %} - - Older exceptions - - {% endif %} -
-
\ No newline at end of file +{% 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 index aef158a..720c29a 100644 --- a/error_tracker/django/templates/error_tracker/partials/partial_table.html +++ b/error_tracker/django/templates/error_tracker/partials/partial_table.html @@ -1,3 +1,4 @@ +{% load i18n %} {% for error in errors.items %} {{ error.host }} @@ -12,7 +13,7 @@ {{ error.count }} Delete + class="delete btn btn-danger btn-sm">{%trans 'Delete' %} {% endfor %} diff --git a/error_tracker/django/views.py b/error_tracker/django/views.py index 83d5f5f..63ee78a 100644 --- a/error_tracker/django/views.py +++ b/error_tracker/django/views.py @@ -37,7 +37,7 @@ def view_list(request): """ title = "App Error" query = (request.GET.dict()) - + error = False errors = model.get_exceptions_per_page(query) next_url = reverse('view_errors') + "?page=" + str(errors.next_num) \ @@ -51,14 +51,12 @@ def view_list(request): 'errors': errors, }) - navigation = render_to_string('error_tracker/partials/partial_table.html', { + navigation = render_to_string('error_tracker/partials/navigation.html', { 'next_url': next_url, 'prev_url': prev_url }) - return JsonResponse({ - 'table': table, - 'navigation': navigation}) + return JsonResponse({'table': table, 'navigation': navigation}) return render(request, template_name='error_tracker/list.html', context=dict(error=error, title=title, errors=errors, 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" From 6cbc85365f5fbff5fd16b4090e0ef3a8b9749803 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Fri, 23 Oct 2020 06:00:38 +0200 Subject: [PATCH 18/28] query refactoring for get_exceptions_per_page --- error_tracker/django/templates/error_tracker/list.html | 3 +-- error_tracker/django/views.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/error_tracker/django/templates/error_tracker/list.html b/error_tracker/django/templates/error_tracker/list.html index b2aa9f5..2b82c51 100755 --- a/error_tracker/django/templates/error_tracker/list.html +++ b/error_tracker/django/templates/error_tracker/list.html @@ -30,8 +30,7 @@ form = $(this).parents('form') //.submit() url = form.attr('action') data = form.serialize() - data = data.replace(/&?[^=&]+=(&|$)/g,''); - + $.get(url, data, function(response) { console.log(response.navigation) $('#the_table').html(response.table) diff --git a/error_tracker/django/views.py b/error_tracker/django/views.py index 63ee78a..b623833 100644 --- a/error_tracker/django/views.py +++ b/error_tracker/django/views.py @@ -36,10 +36,10 @@ def view_list(request): :return: rendered template """ title = "App Error" - query = (request.GET.dict()) + query = {k: v for k, v in request.GET.dict().items() if len(v) > 0} error = False errors = model.get_exceptions_per_page(query) - + next_url = reverse('view_errors') + "?page=" + str(errors.next_num) \ if errors.has_next else None From f8da4ec04724e0b8b69d4d4bf96bd664c980f478 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Fri, 23 Oct 2020 07:31:10 +0200 Subject: [PATCH 19/28] simplify query and change query to **query fix duplicate SimpleCookie line optimize jquery script for ajax --- error_tracker/django/models.py | 2 +- .../django/templates/error_tracker/base.html | 18 ++++++ .../django/templates/error_tracker/list.html | 55 ++++++++----------- error_tracker/django/utils.py | 1 - error_tracker/django/views.py | 6 +- 5 files changed, 45 insertions(+), 37 deletions(-) diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index 7e415fc..f144b78 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -34,7 +34,7 @@ class ErrorModel(models.Model, ModelMixin): ticket_raised = models.BooleanField(default=False) @classmethod - def get_exceptions_per_page(cls, query): + def get_exceptions_per_page(cls, **query): if 'page' in query: page_number = query['page'] del query['page'] diff --git a/error_tracker/django/templates/error_tracker/base.html b/error_tracker/django/templates/error_tracker/base.html index 2a2617c..0f27b51 100755 --- a/error_tracker/django/templates/error_tracker/base.html +++ b/error_tracker/django/templates/error_tracker/base.html @@ -12,6 +12,24 @@ font-size: 14px; } + + {% endblock %} {{ title }} diff --git a/error_tracker/django/templates/error_tracker/list.html b/error_tracker/django/templates/error_tracker/list.html index 2b82c51..c965b81 100755 --- a/error_tracker/django/templates/error_tracker/list.html +++ b/error_tracker/django/templates/error_tracker/list.html @@ -1,6 +1,6 @@ {% extends 'error_tracker/base.html' %} {% load i18n %} -{% block content_block %} + {% block head_script %} {{block.super}} - {%endblock%} + +{% block content_block %}
{% if error %}

{{ error }}

diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index a8e8073..7cfef86 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -197,7 +197,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 b623833..7ebbcaf 100644 --- a/error_tracker/django/views.py +++ b/error_tracker/django/views.py @@ -36,9 +36,11 @@ def view_list(request): :return: rendered template """ title = "App Error" - query = {k: v for k, v in request.GET.dict().items() if len(v) > 0} + + query = request.GET.dict() + error = False - errors = model.get_exceptions_per_page(query) + errors = model.get_exceptions_per_page(**query) next_url = reverse('view_errors') + "?page=" + str(errors.next_num) \ if errors.has_next else None From ea8dd3ec80a49d3c74cffddfc708fe0d3c073398 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Fri, 23 Oct 2020 11:35:38 +0200 Subject: [PATCH 20/28] Move SimpleCookie into code --- error_tracker/django/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 7cfef86..bafe40c 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -9,8 +9,6 @@ import re import json from django.http import RawPostDataException -from http.cookies import SimpleCookie - 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 @@ -198,6 +196,7 @@ def get_value(key, value): if key in ["Cookie", "cookie"]: try: try: + from http.cookies import SimpleCookie cookie = SimpleCookie() cookie.load(value) value = {k: clean_value(v) for k, v in cookie.items()} From 6b946ab4e402100060973739013be5df311d442c Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Fri, 23 Oct 2020 16:57:10 +0200 Subject: [PATCH 21/28] fix mixin with kwarks add page_number to get_exceptions_per_page add partial text filter --- error_tracker/django/models.py | 14 +++++++------- error_tracker/libs/mixins.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index f144b78..184b513 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -34,13 +34,13 @@ class ErrorModel(models.Model, ModelMixin): ticket_raised = models.BooleanField(default=False) @classmethod - def get_exceptions_per_page(cls, **query): - if 'page' in query: - page_number = query['page'] - del query['page'] - else: - page_number = 1 - + def get_exceptions_per_page(cls, page_number=1, **query): + if query: + if 'page' in query: + page_number = query['page'] + del query['page'] + query = {"{}__contains".format(k): v for k, v in query.items()} + if not query: records = cls.objects.all().order_by('last_seen') else: 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 From 61d28a8ca98e6261503b59e2f5e85d56cf946d90 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Sun, 25 Oct 2020 17:03:58 +0100 Subject: [PATCH 22/28] filter input on keypress change contains to icontains on query --- error_tracker/django/models.py | 2 +- error_tracker/django/templates/error_tracker/list.html | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index 184b513..dd0a851 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -39,7 +39,7 @@ def get_exceptions_per_page(cls, page_number=1, **query): if 'page' in query: page_number = query['page'] del query['page'] - query = {"{}__contains".format(k): v for k, v in query.items()} + query = {"{}__icontains".format(k): v for k, v in query.items()} if not query: records = cls.objects.all().order_by('last_seen') diff --git a/error_tracker/django/templates/error_tracker/list.html b/error_tracker/django/templates/error_tracker/list.html index c965b81..f33f1ac 100755 --- a/error_tracker/django/templates/error_tracker/list.html +++ b/error_tracker/django/templates/error_tracker/list.html @@ -18,9 +18,9 @@ } function filter_watch(){ - $(".filter-ajax").on('blur',function(e){ + $(".filter-ajax").on('blur, keyup',function(e){ e.stopImmediatePropagation() - if($(this).val().length>0){ + if(e.which == 9 || e.which == 8 || e.which == 13 || $(this).val().length>=3){ submit_data() } tab_stop(e) @@ -45,10 +45,11 @@ function filter_reset(){ $("#filter-reset").on("click", function(){ - submit_data() + $('form[name="filter-form"] .filter-ajax').each(function(e, item){ $(item).val("") }) + submit_data() }) } From 9ca2c4933d8713d6ccc24b572e38f8ed9c446dd0 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Mon, 26 Oct 2020 07:59:02 +0100 Subject: [PATCH 23/28] remove 002_xx migrations Move Simplecookie to head --- .../migrations/0002_auto_20201018_1311.py | 23 ------------------- error_tracker/django/utils.py | 11 +++++---- 2 files changed, 7 insertions(+), 27 deletions(-) delete mode 100644 error_tracker/django/migrations/0002_auto_20201018_1311.py diff --git a/error_tracker/django/migrations/0002_auto_20201018_1311.py b/error_tracker/django/migrations/0002_auto_20201018_1311.py deleted file mode 100644 index 829f2b8..0000000 --- a/error_tracker/django/migrations/0002_auto_20201018_1311.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.1.2 on 2020-10-18 11:11 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('django', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='errormodel', - name='notification_sent', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='errormodel', - name='ticket_raised', - field=models.BooleanField(default=False), - ), - ] diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index bafe40c..0cfee6f 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -6,12 +6,16 @@ # :license: BSD-3-Clause # -import re import json -from django.http import RawPostDataException -from error_tracker.libs.mixins import ContextBuilderMixin, NotificationMixin, ViewPermissionMixin +import re +from http.cookies import SimpleCookie + +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): @@ -196,7 +200,6 @@ def get_value(key, value): if key in ["Cookie", "cookie"]: try: try: - from http.cookies import SimpleCookie cookie = SimpleCookie() cookie.load(value) value = {k: clean_value(v) for k, v in cookie.items()} From ebe836e2c7e602c4a8edcaa270e4ea83121e905b Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Mon, 26 Oct 2020 11:56:18 +0100 Subject: [PATCH 24/28] restore migration file try/except SimpleCookie --- .../migrations/0002_auto_20201018_1311.py | 23 +++++++++++++++++++ error_tracker/django/utils.py | 5 +++- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 error_tracker/django/migrations/0002_auto_20201018_1311.py diff --git a/error_tracker/django/migrations/0002_auto_20201018_1311.py b/error_tracker/django/migrations/0002_auto_20201018_1311.py new file mode 100644 index 0000000..3634abe --- /dev/null +++ b/error_tracker/django/migrations/0002_auto_20201018_1311.py @@ -0,0 +1,23 @@ +# Generated by Django 3.1.2 on 2020-10-18 11:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('error_tracker', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='errormodel', + name='notification_sent', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='errormodel', + name='ticket_raised', + field=models.BooleanField(default=False), + ), + ] \ No newline at end of file diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 0cfee6f..feef724 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -8,7 +8,10 @@ import json import re -from http.cookies import SimpleCookie +try: + from http.cookies import SimpleCookie +except ImportError: + pass from error_tracker.libs.mixins import (ContextBuilderMixin, NotificationMixin, ViewPermissionMixin) From ad7abcfdd92fecbf0c3333e0bcc9c09497225a9f Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Mon, 26 Oct 2020 17:20:00 +0100 Subject: [PATCH 25/28] fix test --- .gitignore | 1 + .../django/templates/error_tracker/base.html | 2 +- error_tracker/django/views.py | 2 +- tests/DjangoTest/tests/test_end_point.py | 36 +++++++++---------- 4 files changed, 21 insertions(+), 20 deletions(-) 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/templates/error_tracker/base.html b/error_tracker/django/templates/error_tracker/base.html index 0f27b51..b111de8 100755 --- a/error_tracker/django/templates/error_tracker/base.html +++ b/error_tracker/django/templates/error_tracker/base.html @@ -37,7 +37,7 @@
{% block header_block %}

- Errors Seen + Errors Seen

{% endblock %} {% block content_block %} diff --git a/error_tracker/django/views.py b/error_tracker/django/views.py index 7ebbcaf..9b24d39 100644 --- a/error_tracker/django/views.py +++ b/error_tracker/django/views.py @@ -19,7 +19,7 @@ def has_view_permission(func): - @login_required + #@login_required def wrapper(request, *args, **kwargs): if view_permission(request): return func(request, *args, **kwargs) diff --git a/tests/DjangoTest/tests/test_end_point.py b/tests/DjangoTest/tests/test_end_point.py index 6a51682..a7b43e4 100644 --- a/tests/DjangoTest/tests/test_end_point.py +++ b/tests/DjangoTest/tests/test_end_point.py @@ -20,23 +20,22 @@ 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')] - # 2 links for delete operation and 2 links to navigate - self.assertEqual(len(urls), 2 + 2) + urls = [node.attrib['href'] for node in pyquery.PyQuery(html)('a.view-link, a.delete, a.home-link')] + # 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)('.row')[0] - p = pyquery.PyQuery(row)('p') - divs = pyquery.PyQuery(row)('.row div') - self.assertEqual(len(p), 5) - self.assertEqual(len(divs), 2) + row = pyquery.PyQuery(response)('.mb-4') + self.assertEqual(2, len(row)) + divs = pyquery.PyQuery(response)('.row>div') + self.assertEqual(len(divs), 11) def test_delete_view(self): self.get('/value-error') @@ -61,24 +60,25 @@ def test_pagination(self): exception.traceback) response = self.get('/dev', follow=True).content - urls = [node.attrib['href'] for node in pyquery.PyQuery(response)('a')] - self.assertEqual(len(urls), settings.EXCEPTION_APP_DEFAULT_LIST_SIZE * 2 + 1) + + 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')] - self.assertEqual(len(urls), settings.EXCEPTION_APP_DEFAULT_LIST_SIZE * 2 + 2) + 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')] - self.assertEqual(len(urls), 1) + 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 From e2d04a21d818920775a762bbfca575e7a8f6df29 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Mon, 26 Oct 2020 18:07:27 +0100 Subject: [PATCH 26/28] ccheck --- tests/DjangoTest/tests/test_end_point.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/DjangoTest/tests/test_end_point.py b/tests/DjangoTest/tests/test_end_point.py index a7b43e4..cefca43 100644 --- a/tests/DjangoTest/tests/test_end_point.py +++ b/tests/DjangoTest/tests/test_end_point.py @@ -20,7 +20,7 @@ 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.view-link, a.delete, a.home-link')] + 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) From 0b6ae772d1919189d387a2c385184a67ca4b3388 Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Mon, 26 Oct 2020 18:09:13 +0100 Subject: [PATCH 27/28] remove login_required --- error_tracker/django/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/error_tracker/django/views.py b/error_tracker/django/views.py index 9b24d39..420f7c8 100644 --- a/error_tracker/django/views.py +++ b/error_tracker/django/views.py @@ -19,7 +19,6 @@ def has_view_permission(func): - #@login_required def wrapper(request, *args, **kwargs): if view_permission(request): return func(request, *args, **kwargs) From 04dc8facaa5c5fb592f9940caf0b401b1f2c43ec Mon Sep 17 00:00:00 2001 From: Thierry BOULOGNE Date: Mon, 26 Oct 2020 18:19:48 +0100 Subject: [PATCH 28/28] complete translation string --- .../django/templates/error_tracker/base.html | 3 ++- .../django/templates/error_tracker/detail.html | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/error_tracker/django/templates/error_tracker/base.html b/error_tracker/django/templates/error_tracker/base.html index b111de8..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 %} @@ -37,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}}