From 2721421c1dbaf45feb315000d4503784f8ea8fea Mon Sep 17 00:00:00 2001 From: Piotr Kopycki Date: Sat, 16 Apr 2022 17:05:39 +0200 Subject: [PATCH] New metadata, statistics and icons for tests --- config/settings.py | 1 + palette.txt | 4 +- requirements.txt | 1 + static/style.css | 59 ++++++++-- templates/base.html | 4 + templates/category.html | 99 ++++++++++++++++- templates/home.html | 101 +++++++++++++++++- templates/myTests.html | 99 ++++++++++++++++- templates/result.html | 20 +++- trials/migrations/0016_test_completions.py | 18 ++++ trials/migrations/0017_auto_20220416_1431.py | 33 ++++++ trials/migrations/0018_test_rates_amount.py | 18 ++++ ...9_rename_rates_amount_test_rates_amount.py | 18 ++++ trials/migrations/0020_auto_20220416_1634.py | 23 ++++ .../0021_remove_test_difficulty_label.py | 17 +++ trials/models.py | 21 +++- trials/urls.py | 3 +- trials/views.py | 32 +++++- 18 files changed, 542 insertions(+), 29 deletions(-) create mode 100644 trials/migrations/0016_test_completions.py create mode 100644 trials/migrations/0017_auto_20220416_1431.py create mode 100644 trials/migrations/0018_test_rates_amount.py create mode 100644 trials/migrations/0019_rename_rates_amount_test_rates_amount.py create mode 100644 trials/migrations/0020_auto_20220416_1634.py create mode 100644 trials/migrations/0021_remove_test_difficulty_label.py diff --git a/config/settings.py b/config/settings.py index fb0204c..f1ba50f 100644 --- a/config/settings.py +++ b/config/settings.py @@ -55,6 +55,7 @@ INSTALLED_APPS = [ "rest_framework_simplejwt", "django_extensions", "django_social_share", + 'fontawesomefree', "users", "trials", diff --git a/palette.txt b/palette.txt index 634cc57..91540aa 100644 --- a/palette.txt +++ b/palette.txt @@ -8,4 +8,6 @@ Buttons and links #00916E - Illuminating Emerald Main color: -#FF0B7E - Winter Sky \ No newline at end of file +#FF0B7E - Winter Sky + +#19647E - Blue Sapphire \ 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/style.css b/static/style.css index 338666a..ce253be 100644 --- a/static/style.css +++ b/static/style.css @@ -83,8 +83,8 @@ border-radius: 25px; border: 2px solid #FF0B7E; padding: 20px; - width: 800px; - height: 150px; + width: 750px; + height: 200px; } .mainTestName { @@ -115,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 { @@ -340,6 +366,15 @@ background-color:#FF0B7E padding-bottom: 15px; } +.resultContainer label { + font-weight: bold; + font-size: 20px; +} + +.resultContainerSpace { + padding-bottom: 15px; +} + .resultImage{ } @@ -498,4 +533,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..a462f5f 100644 --- a/templates/base.html +++ b/templates/base.html @@ -6,6 +6,10 @@ SOITA | {% block title %}{% endblock %} + + + + {% block additional_head %} {% endblock %} diff --git a/templates/category.html b/templates/category.html index d47d7ca..4deab7d 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.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/home.html b/templates/home.html index 7b00cfc..e6d7280 100644 --- a/templates/home.html +++ b/templates/home.html @@ -8,17 +8,108 @@
{{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/myTests.html b/templates/myTests.html index cd9edd2..e91a92c 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/result.html b/templates/result.html index cc9d086..80b072c 100644 --- a/templates/result.html +++ b/templates/result.html @@ -26,13 +26,29 @@
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/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/models.py b/trials/models.py index e91e07d..d6c499f 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,12 @@ 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) + avg_difficulty = models.FloatField(default=0.0) objects = TestManager() def get_score(self, answers): @@ -45,7 +51,7 @@ class Test(models.Model): points += question.points return points - def get_maxscore(self, answers): + def get_maxscore(self): """ [ { @@ -59,9 +65,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 +72,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, diff --git a/trials/urls.py b/trials/urls.py index fab316a..c689e6f 100644 --- a/trials/urls.py +++ b/trials/urls.py @@ -1,7 +1,7 @@ from django.urls import path from rest_framework.routers import DefaultRouter -from trials.views import TestModelViewSet, TestTemplateView, TestValidateAPIView, TestResultView, addTest, addQuestions, myTests, solvedTests, solvedTestsDetailed, 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 router = DefaultRouter(trailing_slash=False) router.register("items", TestModelViewSet) @@ -11,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()), diff --git a/trials/views.py b/trials/views.py index 4781553..1908b23 100644 --- a/trials/views.py +++ b/trials/views.py @@ -301,6 +301,17 @@ def editPassword(request, test_id): return redirect(f'/tests/{test_id}/edit') +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() serializer_class = TestSerializer @@ -332,8 +343,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() @@ -349,7 +360,7 @@ 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), @@ -372,6 +383,21 @@ class TestTemplateView(TemplateView): user=request.user, test=test ) + 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')