Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse Log entries #23

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 85 additions & 3 deletions django_walletpass/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,88 @@
from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html
from django.utils.safestring import mark_safe

from django_walletpass.models import Pass, Registration, Log

admin.site.register(Pass)
admin.site.register(Registration)
admin.site.register(Log)

@admin.register(Log)
class LogAdmin(admin.ModelAdmin):
list_display = (
"created_at",
"status",
"task_type",
# "pass_type_identifier",
# "serial_number",
"pass_",
# "web_service_url",
"device_id",
"msg",
)
list_filter = ("status", "task_type", "pass_type_identifier")
search_fields = ("pass_type_identifier", "serial_number", "device_id", "msg", "message")
readonly_fields = ("created_at", "pass_")
raw_id_fields = ("pazz",)
list_select_related = ("pazz",)

def pass_(self, obj: Log):
if obj.pazz_id:
url = reverse(
"admin:%s_%s_change" % (obj.pazz._meta.app_label, obj.pazz._meta.model_name),
args=[obj.pazz_id],
)
return format_html(
"<a href='{url}'>{title}</a>",
url=url,
title=obj.serial_number,
)
return obj.serial_number

pass_.short_description = "Pass"


@admin.register(Pass)
class PassAdmin(admin.ModelAdmin):
list_display = ("serial_number", "updated_at", "pass_type_identifier", "wallet_pass_")
search_fields = ("serial_number", "pass_type_identifier", "authentication_token", "data")
list_filter = ("pass_type_identifier", "updated_at")
date_hierarchy = "updated_at"
readonly_fields = ("wallet_pass_", "updated_at")

def wallet_pass_(self, obj: Pass):
if obj.data:
svg_icon = '<svg width="13" height="16" viewBox="0 0 13 16" fill="none" xmlns="http://www.w3.org/2000/svg">' \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this icon for? how would it look like? @cherijs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2023-07-10 at 10 50 56

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll copy&paste what I wrote in the other PR. I don't know how I feel about hardcoding the content of a svg file here. Can we maybe load this from a file?

'<path d="M2.86133 15.3926C2.15495 15.3926 1.62402 15.2126 1.26855 14.8525C0.917643 14.4971 0.742188 13.9639 0.742188 13.2529V2.88281C0.742188 2.16732 0.917643 1.63184 1.26855 1.27637C1.62402 0.916341 2.15495 0.736328 2.86133 0.736328H4.88477C5.13997 0.736328 5.26758 0.873047 5.26758 1.14648C5.26758 1.50195 5.37923 1.79362 5.60254 2.02148C5.8304 2.24479 6.12663 2.35645 6.49121 2.35645C6.85579 2.35645 7.14974 2.24479 7.37305 2.02148C7.59635 1.79362 7.70801 1.50195 7.70801 1.14648C7.70801 0.873047 7.83561 0.736328 8.09082 0.736328H10.1211C10.8275 0.736328 11.3561 0.916341 11.707 1.27637C12.0625 1.63184 12.2402 2.16732 12.2402 2.88281V13.2529C12.2402 13.9639 12.0625 14.4971 11.707 14.8525C11.3561 15.2126 10.8275 15.3926 10.1211 15.3926H2.86133ZM2.91602 14.292H10.0664C10.4219 14.292 10.6885 14.1986 10.8662 14.0117C11.0485 13.8294 11.1396 13.5697 11.1396 13.2324V2.90332C11.1396 2.56152 11.0485 2.29948 10.8662 2.11719C10.6885 1.93034 10.4219 1.83691 10.0664 1.83691H8.11816L8.76074 1.44727C8.69694 2.04427 8.45768 2.52279 8.04297 2.88281C7.63281 3.24284 7.11556 3.42285 6.49121 3.42285C5.86686 3.42285 5.34733 3.24284 4.93262 2.88281C4.52246 2.52279 4.28776 2.04427 4.22852 1.44727L4.86426 1.83691H2.91602C2.56055 1.83691 2.29167 1.93034 2.10938 2.11719C1.93164 2.29948 1.84277 2.56152 1.84277 2.90332V13.2324C1.84277 13.5697 1.93164 13.8294 2.10938 14.0117C2.29167 14.1986 2.56055 14.292 2.91602 14.292ZM3.90039 5.8291C3.77734 5.8291 3.6748 5.78809 3.59277 5.70605C3.5153 5.62402 3.47656 5.52148 3.47656 5.39844C3.47656 5.27995 3.5153 5.18197 3.59277 5.10449C3.6748 5.02246 3.77734 4.98145 3.90039 4.98145H9.08887C9.20736 4.98145 9.30534 5.02246 9.38281 5.10449C9.46484 5.18197 9.50586 5.27995 9.50586 5.39844C9.50586 5.52148 9.46484 5.62402 9.38281 5.70605C9.30534 5.78809 9.20736 5.8291 9.08887 5.8291H3.90039ZM3.90039 8.25586C3.77734 8.25586 3.6748 8.21712 3.59277 8.13965C3.5153 8.05762 3.47656 7.95736 3.47656 7.83887C3.47656 7.72038 3.5153 7.62012 3.59277 7.53809C3.6748 7.45605 3.77734 7.41504 3.90039 7.41504H6.35449C6.47754 7.41504 6.5778 7.45605 6.65527 7.53809C6.7373 7.62012 6.77832 7.72038 6.77832 7.83887C6.77832 7.95736 6.7373 8.05762 6.65527 8.13965C6.5778 8.21712 6.47754 8.25586 6.35449 8.25586H3.90039Z" fill="black"/>' \
'</svg>'
return format_html(
"<a href='{url}' alt='{title}'>{icon}</a>",
url=obj.data.url,
title=obj.data.name,
icon=mark_safe(svg_icon),
)
return

wallet_pass_.short_description = "Pass"


@admin.register(Registration)
class PassAdmin(admin.ModelAdmin):
list_display = ("device_library_identifier", "push_token", "pass_")
search_fields = ("device_library_identifier", "push_token", "pazz")
raw_id_fields = ("pazz",)
readonly_fields = ("pass_",)

def pass_(self, obj: Registration):
if obj.pazz_id:
url = reverse(
"admin:%s_%s_change" % (obj.pazz._meta.app_label, obj.pazz._meta.model_name),
args=[obj.pazz_id],
)
return format_html(
"<a href='{url}'>{title}</a>",
url=url,
title=obj.pazz.serial_number,
)
return

pass_.short_description = "Pass"
1 change: 1 addition & 0 deletions django_walletpass/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

class DjangoWalletpassConfig(AppConfig):
name = 'django_walletpass'
verbose_name = 'Django walletpass'

def ready(self):
from django_walletpass import signals as _signals # pylint: disable=import-outside-toplevel
7 changes: 6 additions & 1 deletion django_walletpass/classviews.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ def get_pass(pass_type_id, serial_number):
return get_object_or_404(Pass, pass_type_identifier=pass_type_id, serial_number=serial_number)


def web_service_root_view(request):
Copy link
Collaborator

@patroqueeet patroqueeet Jul 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a classview. should either inherit from View or in views.py I assume. actually, what is the intention of this view? @cherijs

Copy link
Contributor Author

@cherijs cherijs Jul 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, it seems that this view should be taken out.

I understood when this error came about not being able to open web_service_url:
WALLETPASS["SERVICE_URL"] = 'https://xxx/api/passes/'

[2023-07-09 12:18:47 +0300] Register task (for device 6d07103565a61da35b0cb6047736518a, pass type pass.id.lolo.tickets, serial number R1e7lciJx5K_j6_m4iNouABbUXY; with web service url https://xxx/api/passes/) encountered error: Unexpected response code 404

but i was wrong...

this error was about not being able to open paass R1e7lciJx5K_j6_m4iNouABbUXY not SERVICE_URL root

return HttpResponse(status=200)


class RegistrationsViewSet(viewsets.ViewSet):
"""
Gets the Serial Numbers for Passes Associated with a Device
Expand Down Expand Up @@ -142,5 +146,6 @@ class LogViewSet(viewsets.ViewSet):
def create(self, request):
json_body = json.loads(request.body)
for message in json_body['logs']:
Log(message=message).save()
log = Log(message=message)
Log.parse_log(log, message)
return Response({}, status=status.HTTP_200_OK)
66 changes: 66 additions & 0 deletions django_walletpass/migrations/0009_auto_20230709_2143.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Generated by Django 3.2.14 on 2023-07-09 18:43

from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import django_walletpass.storage


class Migration(migrations.Migration):

dependencies = [
('django_walletpass', '0008_alter_pass_data'),
]

operations = [
migrations.AddField(
model_name='log',
name='created_at',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name='log',
name='device_id',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AddField(
model_name='log',
name='msg',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='log',
name='pass_type_identifier',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AddField(
model_name='log',
name='pazz',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='django_walletpass.pass'),
),
migrations.AddField(
model_name='log',
name='serial_number',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AddField(
model_name='log',
name='status',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='log',
name='task_type',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AddField(
model_name='log',
name='web_service_url',
field=models.URLField(blank=True, null=True),
),
migrations.AlterField(
model_name='pass',
name='data',
field=models.FileField(storage=django_walletpass.storage.WalletPassStorage(), upload_to='passes'),
),
]
71 changes: 71 additions & 0 deletions django_walletpass/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import datetime
import os
import re
import uuid
import hashlib
import json
Expand All @@ -7,6 +9,7 @@
import zipfile
from glob import glob
from django.core.exceptions import ValidationError
from django.utils import timezone
from django.utils.module_loading import import_string
from django.db import models
from django.utils.translation import gettext_lazy as _
Expand Down Expand Up @@ -285,6 +288,9 @@ def get_pass_builder(self):
def __unicode__(self):
return self.serial_number

def __str__(self):
return self.serial_number

class Meta:
verbose_name_plural = "passes"
unique_together = (
Expand All @@ -308,12 +314,77 @@ class Registration(models.Model):
def __unicode__(self):
return self.device_library_identifier

def __str__(self):
return self.device_library_identifier


class Log(models.Model):
"""
Log message sent by a device
"""
created_at = models.DateTimeField(default=timezone.now)
status = models.CharField(max_length=100, null=True, blank=True)
task_type = models.CharField(max_length=255, null=True, blank=True)
pass_type_identifier = models.CharField(max_length=255, null=True, blank=True)
serial_number = models.CharField(max_length=255, null=True, blank=True)
pazz = models.ForeignKey(Pass, null=True, blank=True, on_delete=models.CASCADE, related_name='logs')
web_service_url = models.URLField(null=True, blank=True)
device_id = models.CharField(max_length=255, null=True, blank=True)
msg = models.TextField(null=True, blank=True)
message = models.TextField()

def __unicode__(self):
return self.message

def __str__(self):
return self.message

@classmethod
def parse_log(cls, log, message):
pattern_register = r"\[(.*?)\]\s(.*?)\s\(for device (.*?), pass type (.*?), serial number (.*?); with web service url (.*?)\)\s(.*?): (.*$)"
pattern_get = r"\[(.*?)\]\s(.*?)\s\(pass type (.*?), serial number (.*?), if-modified-since \(.*?\); with web service url (.*?)\) (.*?): (.*$)"
pattern_web_service_error = r"\[(.*?)\]\s(.*?)\sfor (.*?)\s\((.*?)\):\s(.*)"

match_register = re.match(pattern_register, message)
match_get = re.match(pattern_get, message)
match_web_service_error = re.match(pattern_web_service_error, message)

if match_register:
timestamp_str, task_type, device_id, pass_type_identifier, serial_number, web_service_url, status, msg = match_register.groups()
elif match_get:
timestamp_str, task_type, pass_type_identifier, serial_number, web_service_url, status, msg = match_get.groups()
device_id = None # 'Get pass task' entries don't include device_id
elif match_web_service_error:
timestamp_str, task_type, pass_type_identifier, web_service_url, msg = match_web_service_error.groups()
serial_number = None
device_id = None
status = "error"
else:
log.status = 'unknown'
log.message = message
log.save()
return # Log entry didn't match any known pattern

if 'error' in status:
status = 'error'
elif 'warning' in status:
status = 'warning'

log.created_at = datetime.datetime.strptime(timestamp_str, "%Y-%m-%d %H:%M:%S %z")
log.status = status
log.task_type = task_type
log.device_id = device_id
log.pass_type_identifier = pass_type_identifier
log.serial_number = serial_number
log.web_service_url = web_service_url
log.msg = msg
log.message = message

if serial_number:
try:
pazz = Pass.objects.get(serial_number=serial_number)
log.pazz = pazz
except Pass.DoesNotExist:
pass

log.save()
3 changes: 2 additions & 1 deletion django_walletpass/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from django.urls import re_path
from django.urls import re_path, path
from . import classviews

urlpatterns = [
path('', classviews.web_service_root_view, name='walletpass_web_service_url'),
re_path(
r'^v1/devices/(?P<device_library_id>.+)/registrations/(?P<pass_type_id>.+)/(?P<serial_number>.+)$',
classviews.RegisterPassViewSet.as_view({
Expand Down