diff --git a/config/settings.py b/config/settings.py index fb0204c..f357760 100644 --- a/config/settings.py +++ b/config/settings.py @@ -55,13 +55,16 @@ INSTALLED_APPS = [ "rest_framework_simplejwt", "django_extensions", "django_social_share", + 'fontawesomefree', + "jquery", "users", "trials", "answers", "questions", "categories", - "tools" + "tools", + "questionnaire" ] # AUTHENTICATION_BACKENDS = ['config.authh.SettingsBackend'] MIDDLEWARE = [ diff --git a/config/urls.py b/config/urls.py index e2c4f31..fd0f475 100644 --- a/config/urls.py +++ b/config/urls.py @@ -16,18 +16,21 @@ Including another URLconf from django.contrib import admin from django.urls import include from django.urls import path -from .views import home, welcome, help +from .views import home, welcome, help, hard, popular from django.contrib.staticfiles.urls import staticfiles_urlpatterns urlpatterns = [ path('', welcome, name='welcome'), path('home', home, name='home'), + path('popular', popular, name='popular'), + path('hard', hard, name='hard'), path('help', help, name='help'), path('users/', include("users.urls")), path('questions/', include("questions.urls")), path('answers/', include("answers.urls")), path('tests/', include("trials.urls")), path('category/', include("categories.urls")), + path('questionnaire/', include("questionnaire.urls")), ] urlpatterns += staticfiles_urlpatterns() diff --git a/config/views.py b/config/views.py index a6157b1..fc02746 100644 --- a/config/views.py +++ b/config/views.py @@ -2,6 +2,7 @@ from django.shortcuts import render, redirect from django.template import loader from django.contrib.auth.decorators import login_required from trials.models import Test +import operator @login_required @@ -18,6 +19,19 @@ def home(request): def help(request): return render(request, 'help.html', ) +@login_required +def popular(request): + context = {} + context['tests'] = Test.objects.filter(avg_rating__gt=0) + context['tests'] = sorted(context['tests'], key=operator.attrgetter('avg_rating'), reverse=True) + return render(request, 'popular.html', context) + +@login_required +def hard(request): + context = {} + context['tests'] = Test.objects.filter(difficulty_label__gt=0) + context['tests'] = sorted(context['tests'], key=operator.attrgetter('difficulty_label'), reverse=True) + return render(request, 'hard.html', context) def welcome(request): return render(request, 'welcome.html') diff --git a/palette.txt b/palette.txt index b1b3199..91540aa 100644 --- a/palette.txt +++ b/palette.txt @@ -1,8 +1,13 @@ Color palette: -#00916E - Illuminating Emerald #FEEFE5 - Linen #FFCF00 - Cyber Yellow #EE6123 - Orange Panteon #FA003F - Red Munsell -#FF0B7E - Winter Sky \ No newline at end of file +Buttons and links +#00916E - Illuminating Emerald + +Main color: +#FF0B7E - Winter Sky + +#19647E - Blue Sapphire \ No newline at end of file diff --git a/questionnaire/__init__.py b/questionnaire/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/questionnaire/apps.py b/questionnaire/apps.py new file mode 100644 index 0000000..e69de29 diff --git a/questionnaire/managers.py b/questionnaire/managers.py new file mode 100644 index 0000000..599759e --- /dev/null +++ b/questionnaire/managers.py @@ -0,0 +1,22 @@ +from django.db.models import Manager +from django.apps import apps +from django.conf import settings + +class QuestionnaireManager(Manager): + + def create( + self, + **kwargs + ): + instance = super().create( + overall=kwargs.get("overall"), + functionality=kwargs.get("functionality"), + look=kwargs.get("look"), + intuitive=kwargs.get("intuitive"), + bugs=kwargs.get("bugs"), + tech=kwargs.get("tech"), + potential=kwargs.get("potential"), + worst=kwargs.get("worst"), + desc=kwargs.get("desc"), + ) + return instance diff --git a/questionnaire/migrations/0001_initial.py b/questionnaire/migrations/0001_initial.py new file mode 100644 index 0000000..4daae12 --- /dev/null +++ b/questionnaire/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.9 on 2022-05-15 21:29 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Questionnaire', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('overall', models.IntegerField(default=0)), + ('functionality', models.IntegerField(default=0)), + ('look', models.IntegerField(default=0)), + ('intuitive', models.IntegerField(default=0)), + ('bugs', models.IntegerField(default=0)), + ('tech', models.IntegerField(default=0)), + ('potential', models.IntegerField(default=0)), + ('worst', models.IntegerField(default=0)), + ('desc', models.CharField(default='', max_length=255)), + ('fill_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='questionnaire', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/questionnaire/migrations/__init__.py b/questionnaire/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/questionnaire/models.py b/questionnaire/models.py new file mode 100644 index 0000000..ecb2d03 --- /dev/null +++ b/questionnaire/models.py @@ -0,0 +1,22 @@ +from django.db import models + +from .managers import QuestionnaireManager + + +class Questionnaire(models.Model): + overall = models.IntegerField(default=0) + functionality = models.IntegerField(default=0) + look = models.IntegerField(default=0) + intuitive = models.IntegerField(default=0) + bugs = models.IntegerField(default=0) + tech = models.IntegerField(default=0) + potential = models.IntegerField(default=0) + worst = models.IntegerField(default=0) + desc = models.CharField(max_length=255, default="") + fill_by = models.ForeignKey( + "users.User", + null=True, + related_name="questionnaire", + on_delete=models.CASCADE + ) + objects = QuestionnaireManager() \ No newline at end of file diff --git a/questionnaire/serializers.py b/questionnaire/serializers.py new file mode 100644 index 0000000..d49b520 --- /dev/null +++ b/questionnaire/serializers.py @@ -0,0 +1,28 @@ +from rest_framework import serializers + +from questionnaire.models import Questionnaire + +class QuestionnaireSerializer(serializers.ModelSerializer): + + class Meta: + model = Questionnaire + fields = ( + "id", + "overall", + "functionality", + "look", + "intuitive", + "bugs", + "tech", + "potential", + "worst", + "desc", + "fill_by" + ) + + def create(self, validated_data): + instance = Questionnaire.objects.create( + **validated_data + ) + return instance + diff --git a/questionnaire/urls.py b/questionnaire/urls.py new file mode 100644 index 0000000..f56770e --- /dev/null +++ b/questionnaire/urls.py @@ -0,0 +1,12 @@ +from django.urls import path +from rest_framework.routers import DefaultRouter + +from .views import questionnaire + +router = DefaultRouter(trailing_slash=False) + +urlpatterns = [ + path('questionnaire', questionnaire, name='questionnaire') +] + +urlpatterns += router.urls \ No newline at end of file diff --git a/questionnaire/views.py b/questionnaire/views.py new file mode 100644 index 0000000..bc2f254 --- /dev/null +++ b/questionnaire/views.py @@ -0,0 +1,22 @@ +from django.shortcuts import render, redirect +from django.template import loader +from django.contrib.auth.decorators import login_required +from questionnaire.models import Questionnaire + + +@login_required +def questionnaire(request): + if request.POST: + overall = request.GET.get("overall") + functionality = request.GET.get("functionality") + look = request.GET.get("look") + intuitive = request.GET.get("intuitive") + bugs = request.GET.get("bugs") + tech = request.GET.get("tech") + potential = request.GET.get("potential") + worst = request.GET.get("worst") + desc = request.GET.get("desc") + Questionnaire.objects.create(overall=overall, functionality=functionality, look=look, intuitive=intuitive, + bugs=bugs, tech=tech, potential=potential, worst=worst, desc=desc) + return redirect('home') + return render(request, 'questionnaire.html') diff --git a/questions/models.py b/questions/models.py index 7e7ac72..fded3f9 100644 --- a/questions/models.py +++ b/questions/models.py @@ -19,4 +19,9 @@ class Question(models.Model): for answer in self.answers.all() ] + tournament = models.ManyToManyField( + "trials.Tournament", + null=True + ) + objects = QuestionManager() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 63369f6..e8d6fc4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -109,3 +109,4 @@ wcwidth==0.2.5 webencodings==0.5.1 widgetsnbextension==3.5.2 zipp==3.6.0 +fontawesomefree==6.1.1 \ No newline at end of file diff --git a/static/script.js b/static/script.js new file mode 100644 index 0000000..0b8e7bd --- /dev/null +++ b/static/script.js @@ -0,0 +1,21 @@ +//$(function() { +// $(".newQuestionSection" ).draggable().resizable(); +//} ); + +var questionId = 1; + +$('.addQuestionButton').click(function(){ + console.log("REEEEEEEE") + // Clone extra box, and remove duplicate ids + let clone = $("#question-copy").clone().removeAttr("hidden"); + clone.id = "question"- + ++questionId; + //clone.find("*").removeAttr("id"); + clone.find("*").each(function() { + $(this).attr("id", "q" + questionId + "-" + $(this).attr("id")); + }); + clone.find("input:radio").each(function() { + $(this).attr("name", questionId); + }); + // append it to div + $('#canvas').append(clone); +}); \ No newline at end of file diff --git a/static/style.css b/static/style.css index ba70f33..080d606 100644 --- a/static/style.css +++ b/static/style.css @@ -1,6 +1,6 @@ .sidenav { height: 100%; - width: 195px; + width: 215px; position: fixed; z-index: 1; top: 0; @@ -44,7 +44,7 @@ } .main { - margin-left: 190px; + margin-left: 215px; padding: 0px 40px; } @@ -83,8 +83,8 @@ border-radius: 25px; border: 2px solid #FF0B7E; padding: 20px; - width: 800px; - height: 150px; + width: 750px; + height: 200px; } .mainTestName { @@ -97,7 +97,15 @@ border-radius: 25px; border: 2px solid #FF0B7E; padding: 20px; - width: 300px; + width: 320px; + height: 250px; +} + +.solvedTestContainerMini { + border-radius: 25px; + border: 2px solid #FF0B7E; + padding: 20px; + width: 320px; height: 200px; } @@ -107,12 +115,38 @@ } .mainTestMeta { - font-size: 13px; - padding-bottom: 40px; - color: #808080; + padding-bottom: 30px; /*transform: translate(150%,0%);*/ width:100%; - text-align:center; +} + +.mainTestMetaLine { + padding-bottom: 10px; + display: flex; + justify-content: space-between; +} + +.mainTestMetaInfoText { + width: 250px; + color: #808080; + font-size: 13px; + text-align: center; +} + +.mainTestMetaLineLabels { + padding-bottom: 10px; + padding-top: 10px; + margin-left: 15%; + margin-right: 15%; + display: flex; + justify-content: space-between; +} + +.mainTestMetaLabels { + font-weight: bold; + font-size: 16px; + color: #000000; + text-align: center; } .left { @@ -332,6 +366,15 @@ background-color:#FF0B7E padding-bottom: 15px; } +.resultContainer label { + font-weight: bold; + font-size: 20px; +} + +.resultContainerSpace { + padding-bottom: 15px; +} + .resultImage{ } @@ -453,6 +496,7 @@ background-color:#FF0B7E .linkDefault a { color: #00916E; text-decoration: none; + font-weight: bold; } .newContainer input[type=submit]{ @@ -471,6 +515,23 @@ background-color:#FF0B7E width: 500px; } +.newQuestionSection { + border-radius: 25px; + border: 2px solid #FF0B7E; + padding: 20px; + width: 750px; + height: 350px; +} + +.questionSectionLabelText { + font-weight: bold; + font-size: 16px; + padding-bottom: 20px; +} + +.addQuestionButton { +} + .editContainer { overflow: scroll; } @@ -489,4 +550,16 @@ background-color:#FF0B7E span { margin-right: 10px; +} + +.starChecked { + color: gold; +} + +.fireChecked { + color: red; +} + +.locked { + color: #19647E; } \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index d67e318..cc6cf8f 100644 --- a/templates/base.html +++ b/templates/base.html @@ -6,17 +6,27 @@ SOITA | {% block title %}{% endblock %} + + + + {% block additional_head %} {% endblock %}
- Strona główna - Stwórz test - Twoje testy - Wyniki testów - Pomoc + Strona główna + Popularne + Trudne + {% if user.type == "admin" %} + Stwórz turniej + {% endif %} + Turnieje +
+ Stwórz test + Twoje testy + Wyniki testów

Kategorie

Język polski Język angielski @@ -30,13 +40,17 @@ Historia Inne

Konto

- Ustawienia - Wyloguj + Ustawienia + Ankieta + Pomoc + Wyloguj
- {% block content %} - {% endblock %} +
+ {% block content %} + {% endblock %} +
diff --git a/templates/category.html b/templates/category.html index d47d7ca..c3b75d7 100644 --- a/templates/category.html +++ b/templates/category.html @@ -14,11 +14,104 @@
{{test.name}} + {% if test.password != "" %} + + {% endif %}
-
Kategoria: {{test.category}}
-
Próg zaliczenia: {{test.passing_score}}
-
Liczba pytań: {{test.question_count}}
+
+
Kategoria: {{test.category}}
+
Autor: {{test.get_author_name}}
+
Rozwiązania: {{test.completions}}
+
+
+
Próg zaliczenia: {{test.passing_score}}
+
Maksymalna ilość punktów: {{test.get_maxscore}}
+
Ilość pytań: {{test.question_count}}
+
+
+
+ Trudność: + {% if test.difficulty_label == 0 %} + + + + + + {% elif test.difficulty_label == 1 %} + + > + > + > + > + {% elif test.difficulty_label == 2 %} + + + + + + {% elif test.difficulty_label == 3 %} + + + + + + {% elif test.difficulty_label == 4 %} + + + + + + {% else %} + + + + + + {% endif %} +
+
+ Ocena: + {% if test.avg_rating == 0 %} + + + + + + {% elif test.avg_rating == 1 %} + + + + + + {% elif test.avg_rating == 2 %} + + + + + + {% elif test.avg_rating == 3 %} + + + + + + {% elif test.avg_rating == 4 %} + + + + + + {% else %} + + + + + + {% endif %} + (Głosy: {{test.rates_amount}}) +
+
diff --git a/templates/changeEmail.html b/templates/changeEmail.html index b9b70a9..23d805c 100644 --- a/templates/changeEmail.html +++ b/templates/changeEmail.html @@ -9,7 +9,7 @@

- Wróc + Wróć
{% endblock %} diff --git a/templates/createTestNew.html b/templates/createTestNew.html new file mode 100644 index 0000000..4bdcfcb --- /dev/null +++ b/templates/createTestNew.html @@ -0,0 +1,10 @@ + + + + + $Title$ + + +$END$ + + \ No newline at end of file diff --git a/templates/createTournament.html b/templates/createTournament.html new file mode 100644 index 0000000..8b531a7 --- /dev/null +++ b/templates/createTournament.html @@ -0,0 +1,28 @@ +{% extends "base.html" %} + +{% block title %}Stwórz turniej{% endblock %} + +{% block content %} +

Stwórz turniej

+
+ {% for question in questions %} +
+
+ + {{ question.description }} +
+
+ {% for answer in question.answers.all %} + + {% endfor %} +
+ {% endfor %} +
+
+
+ +
+ +{% endblock %} diff --git a/templates/hard.html b/templates/hard.html new file mode 100644 index 0000000..f20fdc0 --- /dev/null +++ b/templates/hard.html @@ -0,0 +1,118 @@ +{% extends "base.html" %} + +{% block title %}Najtrudniejsze{% endblock %} + +{% block content %} +

Najtrudniejsze testy:

+ {% for test in tests %} +
+
+ {{test.name}} + {% if test.password != "" %} + + {% endif %} +
+
+
+
Kategoria: {{test.category}}
+
Autor: {{test.get_author_name}}
+
Rozwiązania: {{test.completions}}
+
+
+
Próg zaliczenia: {{test.passing_score}}
+
Maksymalna ilość punktów: {{test.get_maxscore}}
+
Ilość pytań: {{test.question_count}}
+
+
+
+ Trudność: + {% if test.difficulty_label == 0 %} + + + + + + {% elif test.difficulty_label == 1 %} + + > + > + > + > + {% elif test.difficulty_label == 2 %} + + + + + + {% elif test.difficulty_label == 3 %} + + + + + + {% elif test.difficulty_label == 4 %} + + + + + + {% else %} + + + + + + {% endif %} +
+
+ Ocena: + {% if test.avg_rating == 0 %} + + + + + + {% elif test.avg_rating == 1 %} + + + + + + {% elif test.avg_rating == 2 %} + + + + + + {% elif test.avg_rating == 3 %} + + + + + + {% elif test.avg_rating == 4 %} + + + + + + {% else %} + + + + + + {% endif %} + (Głosy: {{test.rates_amount}}) +
+
+
+ + + + +
+
+ {% endfor %} +{% endblock %} + diff --git a/templates/home.html b/templates/home.html index 7b00cfc..d57bd6b 100644 --- a/templates/home.html +++ b/templates/home.html @@ -3,22 +3,113 @@ {% block title %}Główna{% endblock %} {% block content %} -

Rozwiąż jakiś test!

+

Najnowsze testy:

{% for test in tests %}
{{test.name}} - + {% if test.password != "" %} + + {% endif %}
-
Kategoria: {{test.category}}
-
Próg zaliczenia: {{test.passing_score}}
-
Liczba pytań: {{test.question_count}}
+
+
Kategoria: {{test.category}}
+
Autor: {{test.get_author_name}}
+
Rozwiązania: {{test.completions}}
+
+
+
Próg zaliczenia: {{test.passing_score}}
+
Maksymalna ilość punktów: {{test.get_maxscore}}
+
Ilość pytań: {{test.question_count}}
+
+
+
+ Trudność: + {% if test.difficulty_label == 0 %} + + + + + + {% elif test.difficulty_label == 1 %} + + > + > + > + > + {% elif test.difficulty_label == 2 %} + + + + + + {% elif test.difficulty_label == 3 %} + + + + + + {% elif test.difficulty_label == 4 %} + + + + + + {% else %} + + + + + + {% endif %} +
+
+ Ocena: + {% if test.avg_rating == 0 %} + + + + + + {% elif test.avg_rating == 1 %} + + + + + + {% elif test.avg_rating == 2 %} + + + + + + {% elif test.avg_rating == 3 %} + + + + + + {% elif test.avg_rating == 4 %} + + + + + + {% else %} + + + + + + {% endif %} + (Głosy: {{test.rates_amount}}) +
+
-{# #}

diff --git a/templates/myTests.html b/templates/myTests.html index cd9edd2..29abbb3 100644 --- a/templates/myTests.html +++ b/templates/myTests.html @@ -8,11 +8,104 @@
{{test.name}} + {% if test.password != "" %} + + {% endif %}
-
Kategoria: {{test.category}}
-
Próg zaliczenia: {{test.passing_score}}
-
Liczba pytań: {{test.question_count}}
+
+
Kategoria: {{test.category}}
+
Autor: {{test.get_author_name}}
+
Rozwiązania: {{test.completions}}
+
+
+
Próg zaliczenia: {{test.passing_score}}
+
Maksymalna ilość punktów: {{test.get_maxscore}}
+
Ilość pytań: {{test.question_count}}
+
+
+
+ Trudność: + {% if test.avg_difficulty == 0.0 %} + + + + + + {% elif test.avg_difficulty < 20.0 %} + + > + > + > + > + {% elif test.avg_difficulty < 40.0 %} + + + + + + {% elif test.avg_difficulty < 60.0 %} + + + + + + {% elif test.avg_difficulty < 80.0 %} + + + + + + {% else %} + + + + + + {% endif %} +
+
+ Ocena: + {% if test.avg_rating == 0 %} + + + + + + {% elif test.avg_rating == 1 %} + + + + + + {% elif test.avg_rating == 2 %} + + + + + + {% elif test.avg_rating == 3 %} + + + + + + {% elif test.avg_rating == 4 %} + + + + + + {% else %} + + + + + + {% endif %} + (Głosy: {{test.rates_amount}}) +
+
diff --git a/templates/popular.html b/templates/popular.html new file mode 100644 index 0000000..c08771c --- /dev/null +++ b/templates/popular.html @@ -0,0 +1,118 @@ +{% extends "base.html" %} + +{% block title %}Najpopularniejsze{% endblock %} + +{% block content %} +

Najpopularniejsze testy:

+ {% for test in tests %} +
+
+ {{test.name}} + {% if test.password != "" %} + + {% endif %} +
+
+
+
Kategoria: {{test.category}}
+
Autor: {{test.get_author_name}}
+
Rozwiązania: {{test.completions}}
+
+
+
Próg zaliczenia: {{test.passing_score}}
+
Maksymalna ilość punktów: {{test.get_maxscore}}
+
Ilość pytań: {{test.question_count}}
+
+
+
+ Trudność: + {% if test.difficulty_label == 0 %} + + + + + + {% elif test.difficulty_label == 1 %} + + > + > + > + > + {% elif test.difficulty_label == 2 %} + + + + + + {% elif test.difficulty_label == 3 %} + + + + + + {% elif test.difficulty_label == 4 %} + + + + + + {% else %} + + + + + + {% endif %} +
+
+ Ocena: + {% if test.avg_rating == 0 %} + + + + + + {% elif test.avg_rating == 1 %} + + + + + + {% elif test.avg_rating == 2 %} + + + + + + {% elif test.avg_rating == 3 %} + + + + + + {% elif test.avg_rating == 4 %} + + + + + + {% else %} + + + + + + {% endif %} + (Głosy: {{test.rates_amount}}) +
+
+
+ + + + +
+
+ {% endfor %} +{% endblock %} + diff --git a/templates/questionnaire.html b/templates/questionnaire.html new file mode 100644 index 0000000..d6ba905 --- /dev/null +++ b/templates/questionnaire.html @@ -0,0 +1,96 @@ +{% extends "base.html" %} + +{% block title %}Ankieta{% endblock %} + +{% block content %} +
+

Ankieta oceniająca działanie aplikacji:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+

+
+
+{% endblock %} diff --git a/templates/result.html b/templates/result.html index afb7102..80b072c 100644 --- a/templates/result.html +++ b/templates/result.html @@ -9,30 +9,46 @@
- {% if percentage == 100 %} + {% if request.session.percentage == 100 %} Idealnie, widzę że ten temat nie ma dla ciebie tajemnic! - {% elif percentage >= 75 %} + {% elif request.session.percentage >= 75 %} Bardzo dobrze, ale są jeszcze pewne braki ;) - {% elif percentage >= 50 %} + {% elif request.session.percentage >= 50 %} Nie jest źle, wiedziałeś więcej niż mniej - {% elif percentage >= 25 %} + {% elif request.session.percentage >= 25 %} Masz spore braki, powinieneś trochę więcej się pouczyć {% else %} Słabiutko, ale następnym razem będzie lepiej {% endif %}
-
Rezultat: {{ status }}
-
Twój wynik: {{ points }}
-
Próg zaliczenia: {{ passing }}
-
Maksymalny wynik: {{ max }}
-
Wynik procentowy: {{ percentage }}%
- - {% if password == "" %} -
+
Rezultat: {{ request.session.status }}
+
Twój wynik: {{ request.session.points }}
+
Próg zaliczenia: {{ request.session.passing }}
+
Maksymalny wynik: {{ request.session.max }}
+
Wynik procentowy: {{ request.session.percentage }}%
+ +

+ + + +

+ + {% if request.session.password == "" %}
Udostępnij:
{% post_to_facebook object_or_url %} {% post_to_linkedin object_or_url %} {% endif %} + +

+
diff --git a/templates/solvedTests.html b/templates/solvedTests.html index 8d958b6..262e6ef 100644 --- a/templates/solvedTests.html +++ b/templates/solvedTests.html @@ -4,16 +4,21 @@ {% block content %}

Historia rozwiązanych testów

- {% for test in tests %} -
-
- {{test.name}} + {% for test, results in tests_lists.items %} +
+
+ {{results.0.name}} +
+
Twój wynik: {{results.0.score}}
+
Próg zaliczenia: {{results.0.passing_score}}
+
Maksymalny wynik: {{results.0.max}}
+
Wynik procentowy: {{results.0.percentage}}%
+ {% if results|length > 1 %} + + {% endif %}
-
Twój wynik: {{test.score}}
-
Próg zaliczenia: {{test.passing_score}}
-
Maksymalny wynik: {{test.max}}
-
Wynik procentowy: {{test.percentage}}%
-
-
- {% endfor %} +
+ {% endfor %} {% endblock %} \ No newline at end of file diff --git a/templates/solvedTestsDetailed.html b/templates/solvedTestsDetailed.html new file mode 100644 index 0000000..dbeacb7 --- /dev/null +++ b/templates/solvedTestsDetailed.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + +{% block title %}Historia testu{% endblock %} + +{% block content %} +

Historia testu: {{name}}

+ {% for test in tests %} +
+
Twój wynik: {{test.score}}
+
Próg zaliczenia: {{test.passing_score}}
+
Maksymalny wynik: {{test.max}}
+
Wynik procentowy: {{test.percentage}}%
+
+
+ {% endfor %} +{% endblock %} \ No newline at end of file diff --git a/templates/tournaments.html b/templates/tournaments.html new file mode 100644 index 0000000..dd7f7d2 --- /dev/null +++ b/templates/tournaments.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block title %}Turnieje{% endblock %} + +{% block content %} +

Twoje testy

+ {% for test in tests %} +
+
+ {{tournament.name}} + + + +
+ + + + +
+
+ {% endfor %} +{% endblock %} + diff --git a/trials/migrations/0016_test_completions.py b/trials/migrations/0016_test_completions.py new file mode 100644 index 0000000..487812d --- /dev/null +++ b/trials/migrations/0016_test_completions.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-04-16 12:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trials', '0015_test_password'), + ] + + operations = [ + migrations.AddField( + model_name='test', + name='completions', + field=models.IntegerField(default=0), + ), + ] diff --git a/trials/migrations/0017_auto_20220416_1431.py b/trials/migrations/0017_auto_20220416_1431.py new file mode 100644 index 0000000..87f761b --- /dev/null +++ b/trials/migrations/0017_auto_20220416_1431.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.9 on 2022-04-16 12:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trials', '0016_test_completions'), + ] + + operations = [ + migrations.AddField( + model_name='test', + name='difficulty_label', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='test', + name='rating_label', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='test', + name='total_percentage_scored_by_users', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='test', + name='total_rating', + field=models.IntegerField(default=0), + ), + ] diff --git a/trials/migrations/0018_test_rates_amount.py b/trials/migrations/0018_test_rates_amount.py new file mode 100644 index 0000000..2f724fc --- /dev/null +++ b/trials/migrations/0018_test_rates_amount.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-04-16 13:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trials', '0017_auto_20220416_1431'), + ] + + operations = [ + migrations.AddField( + model_name='test', + name='rates_Amount', + field=models.IntegerField(default=0), + ), + ] diff --git a/trials/migrations/0019_rename_rates_amount_test_rates_amount.py b/trials/migrations/0019_rename_rates_amount_test_rates_amount.py new file mode 100644 index 0000000..ba489ee --- /dev/null +++ b/trials/migrations/0019_rename_rates_amount_test_rates_amount.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-04-16 13:23 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('trials', '0018_test_rates_amount'), + ] + + operations = [ + migrations.RenameField( + model_name='test', + old_name='rates_Amount', + new_name='rates_amount', + ), + ] diff --git a/trials/migrations/0020_auto_20220416_1634.py b/trials/migrations/0020_auto_20220416_1634.py new file mode 100644 index 0000000..23fc95e --- /dev/null +++ b/trials/migrations/0020_auto_20220416_1634.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.9 on 2022-04-16 14:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trials', '0019_rename_rates_amount_test_rates_amount'), + ] + + operations = [ + migrations.RenameField( + model_name='test', + old_name='rating_label', + new_name='avg_rating', + ), + migrations.AddField( + model_name='test', + name='avg_difficulty', + field=models.FloatField(default=0.0), + ), + ] diff --git a/trials/migrations/0021_remove_test_difficulty_label.py b/trials/migrations/0021_remove_test_difficulty_label.py new file mode 100644 index 0000000..6570bc5 --- /dev/null +++ b/trials/migrations/0021_remove_test_difficulty_label.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.9 on 2022-04-16 14:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('trials', '0020_auto_20220416_1634'), + ] + + operations = [ + migrations.RemoveField( + model_name='test', + name='difficulty_label', + ), + ] diff --git a/trials/migrations/0022_test_difficulty_label.py b/trials/migrations/0022_test_difficulty_label.py new file mode 100644 index 0000000..becfab6 --- /dev/null +++ b/trials/migrations/0022_test_difficulty_label.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-05-15 21:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trials', '0021_remove_test_difficulty_label'), + ] + + operations = [ + migrations.AddField( + model_name='test', + name='difficulty_label', + field=models.IntegerField(default=0), + ), + ] diff --git a/trials/models.py b/trials/models.py index e91e07d..9894eb1 100644 --- a/trials/models.py +++ b/trials/models.py @@ -1,6 +1,7 @@ from django.db import models from questions.models import Question +from users.models import User from .managers import TestManager @@ -22,7 +23,13 @@ class Test(models.Model): on_delete=models.CASCADE ) password = models.CharField(max_length=100, default="") - + completions = models.IntegerField(default=0) + total_percentage_scored_by_users = models.IntegerField(default=0) + total_rating = models.IntegerField(default=0) + avg_rating = models.IntegerField(default=0) + rates_amount = models.IntegerField(default=0) + difficulty_label = models.IntegerField(default=0) + avg_difficulty = models.FloatField(default=0.0) objects = TestManager() def get_score(self, answers): @@ -45,7 +52,7 @@ class Test(models.Model): points += question.points return points - def get_maxscore(self, answers): + def get_maxscore(self): """ [ { @@ -59,9 +66,6 @@ class Test(models.Model): ] """ points = 0 - # for answer in answers: - # question = self.questions.get(id=answer["question"]) - # points += question.points for question in self.questions.all(): points += question.points return points @@ -69,6 +73,14 @@ class Test(models.Model): def question_count(self): return Question.objects.filter(test_id=self.id).count() + def get_author_name(self): + if self.created_by: + user = User.objects.get(email=self.created_by) + user_fullname = user.first_name + " " + user.last_name + else: + user_fullname = "SOITA" + return user_fullname + def name_and_passing_score(self): return { "name": self.name, @@ -91,4 +103,15 @@ class SolvedTest(models.Model): null=True, related_name="answers", on_delete=models.CASCADE - ) \ No newline at end of file + ) + + +class Tournament(models.Model): + name = models.CharField(max_length=100) + created_by = models.ForeignKey( + "users.User", + null=True, + related_name="tournaments", + on_delete=models.CASCADE + ) + password = models.CharField(max_length=100) diff --git a/trials/serializers.py b/trials/serializers.py index fa5683c..0f938ed 100644 --- a/trials/serializers.py +++ b/trials/serializers.py @@ -19,6 +19,14 @@ class TestSerializer(serializers.ModelSerializer): "questions", "visible", "category", + "created_by", + "completions", + "total_percentage_scored_by_users", + "total_rating", + "avg_rating", + "rates_amount", + "difficulty_label", + "avg_difficulty" ) def create(self, validated_data): diff --git a/trials/urls.py b/trials/urls.py index 44688db..9a8453f 100644 --- a/trials/urls.py +++ b/trials/urls.py @@ -1,10 +1,7 @@ from django.urls import path from rest_framework.routers import DefaultRouter -from trials.views import TestModelViewSet -from trials.views import TestTemplateView -from trials.views import TestValidateAPIView -from trials.views import TestResultView, addTest, addQuestions, myTests, editTest, solvedTests, EditTestTemplateView, deleteTest, AddQuestionToExistingTest, RemoveQuestionFromExistingTest, EditQuestionTemplateView, editName, editVisible, editPassword, TestPasswordTemplateView +from trials.views import TestModelViewSet, TestTemplateView, TestValidateAPIView, TestResultView, rateTest, addTest, addQuestions, myTests, solvedTests, solvedTestsDetailed, EditTestTemplateView, deleteTest, AddQuestionToExistingTest, RemoveQuestionFromExistingTest, EditQuestionTemplateView, editName, editVisible, editPassword, TestPasswordTemplateView, TournamentView, CreateTournamentView router = DefaultRouter(trailing_slash=False) router.register("items", TestModelViewSet) @@ -14,6 +11,7 @@ urlpatterns = [ path('/password', TestPasswordTemplateView), path('/mark', TestValidateAPIView.as_view()), path('/result', TestResultView.as_view()), + path('/rateTest', rateTest, name="rateTest"), path('/edit', EditTestTemplateView.as_view()), path('/add-question', AddQuestionToExistingTest.as_view()), path('/remove-question', RemoveQuestionFromExistingTest.as_view()), @@ -22,11 +20,13 @@ urlpatterns = [ path('/editVisible', editVisible, name="editVisible"), path('/editPassword', editPassword, name="editPassword"), path('/remove', deleteTest, name="deleteTest"), - # path('delete', deleteTest, name="deleteTest"), path('add/test', addTest, name="newTest"), path('add/questions', addQuestions, name="addQuestions"), path('my', myTests, name="myTests"), - path('solved', solvedTests, name="solvedTests") + path('solved', solvedTests, name="solvedTests"), + path('solved/', solvedTestsDetailed, name="solvedTests"), + path('tournamets', TournamentView, name="tournaments"), + path('add/tournament', CreateTournamentView, name="CreateTournament") ] urlpatterns += router.urls diff --git a/trials/views.py b/trials/views.py index a799360..263c9fa 100644 --- a/trials/views.py +++ b/trials/views.py @@ -8,7 +8,7 @@ from rest_framework.response import Response from rest_framework.views import APIView from questions.models import Question -from trials.models import Test, SolvedTest +from trials.models import Test, SolvedTest, Tournament from trials.serializers import TestSerializer from django.conf import settings from django.shortcuts import render, redirect @@ -18,12 +18,12 @@ from django.template import loader from django.template.loader import render_to_string, get_template from django.http import HttpRequest import requests +from django.contrib.auth.decorators import login_required - +@login_required def addTest(request): return render(request, 'createTest.html') - def is_visible(visible): if visible =="public": return True @@ -83,6 +83,15 @@ def addQuestions(request, **kwargs): return redirect('home') return render(request, 'addQuestions.html') +def TournamentView(request): + context = {} + context['tournament'] = Tournament.objects.all() + return render(request, 'tournaments.html', context) + +def CreateTournamentView(request): + context = {} + context['questions'] = Question.objects.all() + return render(request, 'createTournament.html', context) class AddQuestionToExistingTest(TemplateView): template_name = settings.BASE_DIR + f"/templates/addQuestionToExistingTest.html" @@ -144,43 +153,58 @@ class RemoveQuestionFromExistingTest(TemplateView): Question.objects.get(id=request.POST["id"]).delete() return redirect(f'/tests/{kwargs["test_id"]}/edit') - +@login_required def myTests(request): context = {} user = request.user.id context['tests'] = Test.objects.filter(created_by=user) return render(request, 'myTests.html', context) - +@login_required def solvedTests(request): context = {} user_id = request.user.id solved_tests = SolvedTest.objects.filter(user__id=user_id) + dict_with_lists_of_test_results = {} + for solved_test in solved_tests: + if solved_test.test.name_and_passing_score()["name"] in dict_with_lists_of_test_results.keys(): + dict_with_lists_of_test_results[solved_test.test.name_and_passing_score()["name"]].insert(0, { + "name": solved_test.test.name_and_passing_score()["name"], + "passing_score": solved_test.test.name_and_passing_score()["passing_score"], + "score": solved_test.score, + "max": solved_test.max, + "percentage": solved_test.percentage, + "id": solved_test.test.id + }) + else: + dict_with_lists_of_test_results[solved_test.test.name_and_passing_score()["name"]] = [] + dict_with_lists_of_test_results[solved_test.test.name_and_passing_score()["name"]].append({ + "name": solved_test.test.name_and_passing_score()["name"], + "passing_score": solved_test.test.name_and_passing_score()["passing_score"], + "score": solved_test.score, + "max": solved_test.max, + "percentage": solved_test.percentage, + "id": solved_test.test.id + }) + context['tests_lists'] = dict_with_lists_of_test_results + return render(request, 'solvedTests.html', context) + +@login_required +def solvedTestsDetailed(request, test_id): + context = {} + solved_tests = SolvedTest.objects.filter(test__id=test_id) formatted_tests = list() for solved_test in solved_tests: formatted_tests.append({ - "name": solved_test.test.name_and_passing_score()["name"], "passing_score": solved_test.test.name_and_passing_score()["passing_score"], "score": solved_test.score, "max": solved_test.max, - "percentage": solved_test.percentage + "percentage": solved_test.percentage, + "id": solved_test.test.id }) + context['name'] = solved_tests[0].test.name_and_passing_score()["name"] context['tests'] = formatted_tests - return render(request, 'solvedTests.html', context) - - -def editTest(request): - if request.POST: - # TODO here - # firstName = request.POST.get("firstName") - # lastName = request.POST.get("lastName") - # - # u = request.user - # u.first_name = firstName - # u.last_name = lastName - # u.save() - return redirect('myTests') - return render(request, 'editTest.html') + return render(request, 'solvedTestsDetailed.html', context) class EditTestTemplateView(TemplateView): @@ -253,12 +277,12 @@ class EditQuestionTemplateView(TemplateView): Question.objects.EditQuestion(question_id=question_id, name=description, answers=answers) return redirect(f'/tests/{test}/edit') - +@login_required def deleteTest(request, test_id): Test.objects.filter(id=test_id).delete() return redirect('myTests') - +@login_required def editVisible(request, test_id): new_visible = request.GET["visible"] if new_visible == "public": @@ -270,6 +294,7 @@ def editVisible(request, test_id): test.save() return redirect(f'/tests/{test_id}/edit') +@login_required def editName(request, test_id): new_name = request.GET["name"] test = Test.objects.get(id=test_id) @@ -277,7 +302,7 @@ def editName(request, test_id): test.save() return redirect(f'/tests/{test_id}/edit') - +@login_required def editPassword(request, test_id): new_password = request.GET["password"] test = Test.objects.get(id=test_id) @@ -285,6 +310,17 @@ def editPassword(request, test_id): test.save() return redirect(f'/tests/{test_id}/edit') +@login_required +def rateTest(request, test_id): + test = Test.objects.get(id=test_id) + rate = request.GET["rate"] + test.rates_amount += 1 + test.total_rating += int(rate) + avg_rating = test.total_rating / test.rates_amount + test.avg_rating = int(avg_rating) + test.save() + return redirect(f'/tests/{test_id}/result') + class TestModelViewSet(viewsets.ModelViewSet): queryset = Test.objects.all() @@ -317,8 +353,8 @@ class TestTemplateView(TemplateView): def get_score(self, test: Test, answers): return test.get_score(answers) - def get_maxscore(self, test: Test, answers): - return test.get_maxscore(answers) + def get_maxscore(self, test: Test): + return test.get_maxscore() def formatted_responses(self, unformatted_json): formatted_response = list() @@ -334,16 +370,22 @@ class TestTemplateView(TemplateView): def post(self, request, *args, **kwargs): test = Test.objects.get(id=kwargs.get("test_id")) score = self.get_score(test, self.formatted_responses(request.POST)) - max = self.get_maxscore(test, self.formatted_responses(request.POST)) + max = self.get_maxscore(test) status = score >= test.passing_score - context = { - "status": self.PASSED.get(status, self.UNKNOWN), - "points": score, - "max": max, - "passing": test.passing_score, - "percentage": int(score / max * 100), - "password": test.password - } + # context = { + # "status": self.PASSED.get(status, self.UNKNOWN), + # "points": score, + # "max": max, + # "passing": test.passing_score, + # "percentage": int(score / max * 100), + # "password": test.password + # } + request.session["status"] = self.PASSED.get(status, self.UNKNOWN) + request.session["points"] = score + request.session["max"] = max + request.session["passing"] = test.passing_score + request.session["percentage"] = int(score / max * 100) + request.session["password"] = test.password SolvedTest.objects.create( score=score, max=max, @@ -351,11 +393,25 @@ class TestTemplateView(TemplateView): user=request.user, test=test ) - template_name = "result.html" - template = get_template(template_name) - return HttpResponse(template.render(context, request)) + test.completions += 1 + test.total_percentage_scored_by_users += int(score / max * 100) + if test.completions >= 5: + test.avg_difficulty = float(test.total_percentage_scored_by_users) / float(test.completions) + if test.avg_difficulty > 90.0: + test.difficulty_label = 1 + elif test.avg_difficulty > 75.0: + test.difficulty_label = 2 + elif test.avg_difficulty > 50.0: + test.difficulty_label = 3 + elif test.avg_difficulty > 25.0: + test.difficulty_label = 4 + else: + test.difficulty_label = 5 + test.save() + return HttpResponseRedirect(f'result') +@login_required def TestPasswordTemplateView(request, test_id): test = Test.objects.get(id=test_id) if test.password == "": @@ -366,6 +422,7 @@ def TestPasswordTemplateView(request, test_id): return render(request, 'testPassword.html') +@login_required def testView(request): permission_classes = [] template_name = settings.BASE_DIR + f"/templates/generic_test.html" diff --git a/users/migrations/0005_user_type.py b/users/migrations/0005_user_type.py new file mode 100644 index 0000000..3600d67 --- /dev/null +++ b/users/migrations/0005_user_type.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-05-15 21:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0004_alter_user_is_active'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='type', + field=models.CharField(choices=[('admin', 'admin'), ('standard', 'standard')], default='standard', max_length=100), + ), + ] diff --git a/users/models.py b/users/models.py index 7a0311e..3343166 100644 --- a/users/models.py +++ b/users/models.py @@ -5,6 +5,15 @@ from .managers import UserManager class User(AbstractBaseUser): + + ADMIN = "admin" + STANDARD = "standard" + + USER_TYPES = ( + ("admin", ADMIN), + ("standard", STANDARD), + ) + first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) @@ -14,6 +23,7 @@ class User(AbstractBaseUser): confirmation_number = models.CharField(max_length=100) reset_code = models.IntegerField(null=True) avatar = models.ImageField(upload_to="avatars/", null=True) + type = models.CharField(choices=USER_TYPES, default=STANDARD, max_length=100) USERNAME_FIELD = "email" diff --git a/users/views.py b/users/views.py index e685bba..81a0f7a 100644 --- a/users/views.py +++ b/users/views.py @@ -90,7 +90,8 @@ def register(request): email=form.cleaned_data["email"], first_name=form.cleaned_data["first_name"], last_name=form.cleaned_data["last_name"], - password=form.cleaned_data["password1"] + password=form.cleaned_data["password1"], + type="standard" ) return redirect('register_success') else: