From c5713d3887dd772c7181e7d72b0146a4326fa1eb Mon Sep 17 00:00:00 2001 From: ix <0x00fi@protonmail.com> Date: Wed, 8 Sep 2021 00:32:44 +0200 Subject: [PATCH 1/2] Charts added --- src/accounts/templates/accounts/profile.html | 84 +++++++++++++++++++- src/accounts/views/views.py | 21 ++++- src/static/css/style.css | 2 +- src/statics/css/style.css | 2 +- src/templates/base.html | 1 + 5 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/accounts/templates/accounts/profile.html b/src/accounts/templates/accounts/profile.html index c80fe1f..fa00efd 100644 --- a/src/accounts/templates/accounts/profile.html +++ b/src/accounts/templates/accounts/profile.html @@ -5,9 +5,15 @@

Challenges Solved by {{ user.username }}

- {% if solves%} - - + {% if solves%} + +
+
+ +
+
+
+ @@ -44,7 +50,79 @@ {% endif %}
  • {% trans "Member since" %} {{ user.date_joined|date:"Y-m-d" }}
  • + + + + + {% endblock %} diff --git a/src/accounts/views/views.py b/src/accounts/views/views.py index a791f7b..c6f04b4 100644 --- a/src/accounts/views/views.py +++ b/src/accounts/views/views.py @@ -2,7 +2,7 @@ from django.shortcuts import render, redirect, get_object_or_404 from django.utils.translation import gettext_lazy as _ from django import forms -from ctfs.models import CTF_flags +from ctfs.models import Category, CTF_flags from ..forms import UserForm,UserProfileInfoForm, UserInfosUpdateForm, UserUpdateForm from django.contrib.auth import authenticate, login, logout from django.contrib.auth.models import User @@ -113,9 +113,26 @@ def edit(request): @login_required def profile(request, user_name): + globalLabels= [] + globalDatas = [] + timeLabels = [] + timeDatas= [] + user_obj = get_object_or_404(User, username=user_name) + cats = Category.objects.all() + for cat in cats: + globalLabels.append(cat.name) + solved_count = CTF_flags.objects.filter(user=user_obj, ctf__category__name=cat).order_by('-flag_date').count() + globalDatas.append(solved_count) + + solves = CTF_flags.objects.filter(user=user_obj).order_by('flag_date') + somme = 0 + for flag in solves: + timeLabels.append(flag.flag_date.strftime('%Y-%m-%d')) + somme += flag.ctf.points + timeDatas.append(somme) solves = CTF_flags.objects.filter(user=user_obj).order_by('-flag_date') - return render(request,'accounts/profile.html', {'user':user_obj, 'solves':solves}) + return render(request,'accounts/profile.html', {'user':user_obj, 'solves':solves,'globalLabels': globalLabels, 'globalDatas': globalDatas, 'timeLabels': timeLabels, 'timeDatas': timeDatas}) # Create your views here. def rank(request, token): diff --git a/src/static/css/style.css b/src/static/css/style.css index 6c2877a..5ba7b0a 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -17,7 +17,7 @@ pre {background-color: #000; color: #cecece; padding-left: 15px; font-weight: bo .dropdown-item:hover {background-color: #1D1D1D; color: #FFFFFF} .flag_link {margin-right: 6px} -.flag_img {margin-top: 10px;width: 28px;border-radius: 100%;} +.flag_img {margin-top: 10px;width: 28px;} .table-dark {background-color: #1D1D1D} .table-dark td, .table-dark th, .table-dark thead th {border: none} diff --git a/src/statics/css/style.css b/src/statics/css/style.css index 6c2877a..5ba7b0a 100644 --- a/src/statics/css/style.css +++ b/src/statics/css/style.css @@ -17,7 +17,7 @@ pre {background-color: #000; color: #cecece; padding-left: 15px; font-weight: bo .dropdown-item:hover {background-color: #1D1D1D; color: #FFFFFF} .flag_link {margin-right: 6px} -.flag_img {margin-top: 10px;width: 28px;border-radius: 100%;} +.flag_img {margin-top: 10px;width: 28px;} .table-dark {background-color: #1D1D1D} .table-dark td, .table-dark th, .table-dark thead th {border: none} diff --git a/src/templates/base.html b/src/templates/base.html index b9b5012..2db024b 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -106,6 +106,7 @@ + From c5dedbfc2614c83a6b2d9448754c4602c2d81d6e Mon Sep 17 00:00:00 2001 From: ix <0x00fi@protonmail.com> Date: Wed, 8 Sep 2021 03:59:35 +0200 Subject: [PATCH 2/2] Charts changed to highcharts --- src/accounts/templates/accounts/profile.html | 335 +++++++++++++++---- src/accounts/templatetags/__init__.py | 0 src/accounts/templatetags/key_value.py | 11 + src/accounts/views/views.py | 231 +++++++------ src/templates/base.html | 1 - 5 files changed, 396 insertions(+), 182 deletions(-) create mode 100644 src/accounts/templatetags/__init__.py create mode 100644 src/accounts/templatetags/key_value.py diff --git a/src/accounts/templates/accounts/profile.html b/src/accounts/templates/accounts/profile.html index fa00efd..9be17d5 100644 --- a/src/accounts/templates/accounts/profile.html +++ b/src/accounts/templates/accounts/profile.html @@ -1,6 +1,7 @@ {% extends 'base.html' %} {% block content %} {% load i18n %} +{% load key_value %}
    @@ -8,8 +9,8 @@ {% if solves%}
    -
    - +
    +
    {% trans "Challenge Name" %} {% trans "Category" %}
    @@ -50,79 +51,273 @@ {% endif %}
  • {% trans "Member since" %} {{ user.date_joined|date:"Y-m-d" }}
  • - - - - - var pie_conf= { - type: 'pie', - data: { - datasets: [{ - data: {{ globalDatas|safe }}, - backgroundColor: [ - '#696969', '#808080', '#A9A9A9', '#C0C0C0', '#D3D3D3' - ], - label: 'Solved by category' - }], - labels: {{ globalLabels|safe }} - }, - options: { - legend: {display: false}, - responsive: true - } - }; - var time_conf = { - type: 'line', - data: { - datasets: [{ - data: {{ timeDatas|safe }}, - fill: false, - borderColor: 'rgb(75, 192, 192)', - pointBackgroundColor: '#fff', - pointBorderColor: '#fff', - tension: 0.1, - label: 'Challenge solved' - }, { - }], - labels: {{ timeLabels|safe }} + + {% for cat in cats %} + { + name: '{{ cat.name }}', + data: {{ pointDatas|keyvalue:cat.name|safe }}, + visible: false, + }, + {% endfor %} + ], + responsive: { + rules: [{ + condition: { + maxWidth: 500 + }, + chartOptions: { + legend: { + layout: 'horizontal', + align: 'center', + verticalAlign: 'bottom' + } + } + }] + } + }); + {% endblock %} diff --git a/src/accounts/templatetags/__init__.py b/src/accounts/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/accounts/templatetags/key_value.py b/src/accounts/templatetags/key_value.py new file mode 100644 index 0000000..3594c8b --- /dev/null +++ b/src/accounts/templatetags/key_value.py @@ -0,0 +1,11 @@ +from django import template + +register = template.Library() + +@register.filter +def keyvalue(dict, key): + return dict[key] + +@register.filter +def timestamp_fromdate(date): + return str(date.timestamp() * 1000).replace(',','.') \ No newline at end of file diff --git a/src/accounts/views/views.py b/src/accounts/views/views.py index c6f04b4..125c8c5 100644 --- a/src/accounts/views/views.py +++ b/src/accounts/views/views.py @@ -18,130 +18,139 @@ from accounts.models import UserProfileInfo from . import connection def signin(request): - if not request.user.is_authenticated: - if request.method == 'POST': - username = request.POST.get('username') - password = request.POST.get('password') - user = authenticate(username=username, password=password) - if user: - if user.is_active: - login(request,user) - return HttpResponseRedirect(reverse('home')) - else: - return HttpResponse(_("Your account was inactive.")) - else: - return render(request, 'accounts/login.html', {'error': True}) - else: - return render(request, 'accounts/login.html', {}) - else: - return HttpResponseRedirect(reverse('home')) + if not request.user.is_authenticated: + if request.method == 'POST': + username = request.POST.get('username') + password = request.POST.get('password') + user = authenticate(username=username, password=password) + if user: + if user.is_active: + login(request,user) + return HttpResponseRedirect(reverse('home')) + else: + return HttpResponse(_("Your account was inactive.")) + else: + return render(request, 'accounts/login.html', {'error': True}) + else: + return render(request, 'accounts/login.html', {}) + else: + return HttpResponseRedirect(reverse('home')) def signup(request): - if not request.user.is_authenticated: - user_form = UserForm() - profile_form = UserProfileInfoForm() - registered = False - if request.method == 'POST': - pass1 = request.POST.get('password') - if len(pass1) < 8: - return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':"The new password must be at least %d characters long." % 8}) - first_isalpha = pass1[0].isalpha() - if not any(c.isdigit() for c in pass1) or not any(c.isalpha() for c in pass1): - return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("The password must contain at least one letter and at least one digit or punctuation character.")}) - if User.objects.filter(email=request.POST.get('email')).exists(): - return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that email already exists.")}) - user_form = UserForm(data=request.POST) - profile_form = UserProfileInfoForm(data=request.POST) - if user_form.is_valid() and profile_form.is_valid(): - user = user_form.save() - user.set_password(user.password) - user.save() - profile = profile_form.save(commit=False) - profile.user = user - profile.token = token_hex(16) - profile.save() - registered = True - else: - return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that username already exists.")}) - return render(request,'accounts/register.html', - {'user_form':user_form, - 'profile_form':profile_form, - 'registered':registered}) - else: - return HttpResponseRedirect(reverse('home')) + if not request.user.is_authenticated: + user_form = UserForm() + profile_form = UserProfileInfoForm() + registered = False + if request.method == 'POST': + pass1 = request.POST.get('password') + if len(pass1) < 8: + return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':"The new password must be at least %d characters long." % 8}) + first_isalpha = pass1[0].isalpha() + if not any(c.isdigit() for c in pass1) or not any(c.isalpha() for c in pass1): + return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("The password must contain at least one letter and at least one digit or punctuation character.")}) + if User.objects.filter(email=request.POST.get('email')).exists(): + return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that email already exists.")}) + user_form = UserForm(data=request.POST) + profile_form = UserProfileInfoForm(data=request.POST) + if user_form.is_valid() and profile_form.is_valid(): + user = user_form.save() + user.set_password(user.password) + user.save() + profile = profile_form.save(commit=False) + profile.user = user + profile.token = token_hex(16) + profile.save() + registered = True + else: + return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that username already exists.")}) + return render(request,'accounts/register.html', + {'user_form':user_form, + 'profile_form':profile_form, + 'registered':registered}) + else: + return HttpResponseRedirect(reverse('home')) @login_required def out(request): - logout(request) - return HttpResponseRedirect(reverse('home')) + logout(request) + return HttpResponseRedirect(reverse('home')) @login_required def edit(request): - if request.method == 'POST': - umail = request.user.email - uuser = request.user.username - p_form = UserInfosUpdateForm(request.POST, instance=request.user.userprofileinfo) - u_form = UserUpdateForm(request.POST, instance=request.user) - error = None - success = None - if p_form.is_valid() and u_form.is_valid(): - pmail = u_form.cleaned_data['email'] - if pmail == umail: - pass - else: - if User.objects.filter(email=pmail).exists(): - error = _("Email already taken.") - puser = u_form.cleaned_data['username'] - if puser == uuser: - pass - else: - if User.objects.filter(username=puser).exists(): - error = _("Username already taken.") - if error is None: - u_form.save() - p_form.save() - success = _("Updated.") - request.user.username = uuser - - context={'p_form': p_form, 'u_form': u_form, 'error':error, 'success' : success} - return render(request, 'accounts/edit.html', context) - else: - p_form = UserInfosUpdateForm(instance=request.user.userprofileinfo) - u_form = UserUpdateForm(instance=request.user) - context={'p_form': p_form, 'u_form': u_form, 'token': request.user.userprofileinfo.token} - return render(request, 'accounts/edit.html',context ) + if request.method == 'POST': + umail = request.user.email + uuser = request.user.username + p_form = UserInfosUpdateForm(request.POST, instance=request.user.userprofileinfo) + u_form = UserUpdateForm(request.POST, instance=request.user) + error = None + success = None + if p_form.is_valid() and u_form.is_valid(): + pmail = u_form.cleaned_data['email'] + if pmail == umail: + pass + else: + if User.objects.filter(email=pmail).exists(): + error = _("Email already taken.") + puser = u_form.cleaned_data['username'] + if puser == uuser: + pass + else: + if User.objects.filter(username=puser).exists(): + error = _("Username already taken.") + if error is None: + u_form.save() + p_form.save() + success = _("Updated.") + request.user.username = uuser + + context={'p_form': p_form, 'u_form': u_form, 'error':error, 'success' : success} + return render(request, 'accounts/edit.html', context) + else: + p_form = UserInfosUpdateForm(instance=request.user.userprofileinfo) + u_form = UserUpdateForm(instance=request.user) + context={'p_form': p_form, 'u_form': u_form, 'token': request.user.userprofileinfo.token} + return render(request, 'accounts/edit.html',context ) @login_required def profile(request, user_name): - globalLabels= [] - globalDatas = [] - timeLabels = [] - timeDatas= [] + globalLabels= [] + globalDatas = [] - user_obj = get_object_or_404(User, username=user_name) - cats = Category.objects.all() - for cat in cats: - globalLabels.append(cat.name) - solved_count = CTF_flags.objects.filter(user=user_obj, ctf__category__name=cat).order_by('-flag_date').count() - globalDatas.append(solved_count) + user_obj = get_object_or_404(User, username=user_name) + cats = Category.objects.all() + pointDatas = {} - solves = CTF_flags.objects.filter(user=user_obj).order_by('flag_date') - somme = 0 - for flag in solves: - timeLabels.append(flag.flag_date.strftime('%Y-%m-%d')) - somme += flag.ctf.points - timeDatas.append(somme) - solves = CTF_flags.objects.filter(user=user_obj).order_by('-flag_date') - return render(request,'accounts/profile.html', {'user':user_obj, 'solves':solves,'globalLabels': globalLabels, 'globalDatas': globalDatas, 'timeLabels': timeLabels, 'timeDatas': timeDatas}) + for cat in cats: + # prepare categories + globalLabels.append(cat.name) + solved_count = CTF_flags.objects.filter(user=user_obj, ctf__category__name=cat.name).order_by('-flag_date').count() + globalDatas.append(solved_count) + # get datas + somme = 0 + solved = CTF_flags.objects.filter(user=user_obj, ctf__category__name=cat.name).order_by('flag_date') + pointDatas[cat.name] = [] + pointDatas[cat.name].append([user_obj.date_joined.timestamp() * 1000, 0]) + for flag in solved: + somme += flag.ctf.points + pointDatas[cat.name].append([flag.flag_date.timestamp() * 1000, somme]) + + solves = CTF_flags.objects.filter(user=user_obj).order_by('-flag_date') + solved = [] + somme = 0 + solved.append([user_obj.date_joined.timestamp() * 1000, 0]) + for s in solves.reverse(): + somme += s.ctf.points + solved.append([s.flag_date.timestamp() * 1000,somme]) + return render(request,'accounts/profile.html', {'user':user_obj, 'solves':solves,'solved':solved,'globalLabels': globalLabels, 'globalDatas': globalDatas, 'pointDatas': pointDatas}) # Create your views here. def rank(request, token): - all_users = UserProfileInfo.objects.filter(score__gt=0).select_related().order_by('-score', 'last_submission_date', 'user__username') - - rank = 1 - for elem in all_users: - if elem.token == token: - break - rank += 1 - data = {"rank": rank} - return JsonResponse(data) + all_users = UserProfileInfo.objects.filter(score__gt=0).select_related().order_by('-score', 'last_submission_date', 'user__username') + + rank = 1 + for elem in all_users: + if elem.token == token: + break + rank += 1 + data = {"rank": rank} + return JsonResponse(data) diff --git a/src/templates/base.html b/src/templates/base.html index 2db024b..b9b5012 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -106,7 +106,6 @@ -