Add tournaments #38

Merged
s470631 merged 1 commits from faeture/tournaments into master 2022-05-25 00:07:08 +02:00
12 changed files with 418 additions and 13 deletions

View File

@ -1,14 +1,19 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load rest_framework %}
{% block title %}Stwórz turniej{% endblock %} {% block title %}Stwórz turniej{% endblock %}
{% block content %} {% block content %}
<h1>Stwórz turniej</h1> <h1>Stwórz turniej</h1>
<form method="post" novalidate> <form method="post" novalidate>
<span>name:<input id="name" type="text" name="name" value="{{ tournament.name }}"></span><br>
<span>passing score:<input id="passing_score" type="text" name="passing_score" value="{{ tournament.passing_score }}"></span>
{% for question in questions %} {% for question in questions %}
<div class="mainTestName"> <div class="mainTestName">
<div class="question_title" style="padding-top:15px; padding-bottom:10px; padding-left:5px;"> <div class="question_title" style="padding-top:15px; padding-bottom:10px; padding-left:5px;">
<input class="form-check-input me-1" type="radio" value={{ question.id }}> <input class="form-check-input me-1" type="radio" name={{ question.id }} value={{ question.id }}>
{{ question.description }} {{ question.description }}
</div> </div>
<div class="list-group"> <div class="list-group">
@ -20,9 +25,10 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</form> <div class="testContent">
<div class="testContent"> <input type="submit" value="Stwórz turniej">
<input type="submit" value="Wyślij odpowiedzi">
</div> </div>
</form>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,54 @@
{% extends "base.html" %}
{% load rest_framework %}
{% block title %}{{ test.name }}{% endblock %}
{% block additional_head %}
<meta charset="UTF-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<style>
.test_title {
font-size: 40px;
}
.test_body {
width: 50%;
}
.question_title {
font-size: 20px;
}
</style>
{% endblock %}
{% block content %}
<div class="card" style="border: none;">
<div class="card-body test_body">
<div class="card-header test_title" style="background:#00916E; color: #FFF;">
{{ tournament.name }}
</div>
<form method="post" novalidate>
{% for question in questions %}
<div class="question_title" style="padding-top:15px; padding-bottom:10px; padding-left:5px;">
{{ question.description }}
</div>
<div class="list-group">
{% for answer in question.answers.all %}
<label class="list-group-item">
<input class="form-check-input me-1" type="radio" name={{ question.id }} value={{ answer.id }}>
{{ answer.description }}
</label>
{% endfor %}
</div>
{% endfor %}
<div class="testContent">
<input type="submit" value="Wyślij odpowiedzi">
</div>
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,56 @@
{% extends "base.html" %}
{% load social_share %}
{% block title %}Test result{% endblock %}
{% block content %}
<div class="resultContainer ">
<img class="resultImage" src="http://kmit.in/emagazine/wp-content/uploads/2018/02/karnataka-results.jpg" alt="Card image cap">
<div class="resultBody">
<h5 class="resultMsg">
<!-- Quite good! All the best for next quiz!-->
{% if request.session.percentage == 100 %}
Idealnie, widzę że ten temat nie ma dla ciebie tajemnic!
{% elif request.session.percentage >= 75 %}
Bardzo dobrze, ale są jeszcze pewne braki ;)
{% elif request.session.percentage >= 50 %}
Nie jest źle, wiedziałeś więcej niż mniej
{% 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 %}
</h5>
<h5 class="resultScore">Rezultat: {{ request.session.status }}</h5>
<h5 class="resultText">Twój wynik: {{ request.session.points }}</h5>
<h5 class="resultText">Próg zaliczenia: {{ request.session.passing }}</h5>
<h5 class="resultText">Maksymalny wynik: {{ request.session.max }}</h5>
<h5 class="resultText">Wynik procentowy: {{ request.session.percentage }}%</h5>
<div class="resultContainerSapce"><br></div>
<span><label for="rate"><h5>Jak oceniasz test:</h5></label></span>
<select name="rate" id="rate">
<option value="1">Tragiczny</option>
<option value="2">Słaby</option>
<option value="3">Przeciętny</option>
<option value="4">Dobry</option>
<option value="5">Genialny</option>
</select>
<span><button class="defaultButton">
<a href='' onclick="this.href='rateTest?rate='+document.getElementById('rate').value">Oceń</a>
</button></span>
<div class="resultContainerSapce"><br></div>
{% if request.session.password == "" %}
<h5>Udostępnij:</h5>
{% post_to_facebook object_or_url %}
{% post_to_linkedin object_or_url %}
{% endif %}
<div class="resultContainerSapce"><br></div>
<button class="defaultButton"><a href="{% url 'home' %}">Strona główna</a></button>
</div>
</div>
{% endblock %}

View File

@ -3,8 +3,8 @@
{% block title %}Turnieje{% endblock %} {% block title %}Turnieje{% endblock %}
{% block content %} {% block content %}
<h1>Twoje testy</h1> <h1>Turnieje</h1>
{% for test in tests %} {% for tournament in tournaments %}
<div class="mainTestContainer"> <div class="mainTestContainer">
<div class="mainTestName"> <div class="mainTestName">
{{tournament.name}} {{tournament.name}}
@ -15,7 +15,7 @@
<!-- <div class="mainTestDesc">--> <!-- <div class="mainTestDesc">-->
<!-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus volutpat scelerisque tortor, id sodales leo finibus id. Vivamus id viverra nunc, ac faucibus metus. Nulla a mauris imperdiet sapien lobortis dapibus. Quisque ornare posuere pulvinar.--> <!-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus volutpat scelerisque tortor, id sodales leo finibus id. Vivamus id viverra nunc, ac faucibus metus. Nulla a mauris imperdiet sapien lobortis dapibus. Quisque ornare posuere pulvinar.-->
<!-- </div>--> <!-- </div>-->
<button><a href="/tests/{{tournament.id}}/show">Rozwiąż</a></button> <button><a href="/tests/{{tournament.id}}/tournament/show">Rozwiąż</a></button>
</div> </div>
<br> <br>
{% endfor %} {% endfor %}

View File

@ -38,3 +38,24 @@ class TestManager(Manager):
) )
return instance return instance
class TournamentManager(Manager):
def create(
self, *,
name="",
questions=[],
passing_score=0,
**kwargs
):
Question = apps.get_model("questions", "Question")
instance = super().create(
name=name,
passing_score=passing_score
)
for q in questions:
qq = Question.objects.get(id=q)
qq.tournament.add(instance.id)
return instance

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-05-24 20:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('trials', '0023_tournament'),
]
operations = [
migrations.AlterField(
model_name='tournament',
name='password',
field=models.CharField(default='', max_length=100),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-05-24 21:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('trials', '0024_alter_tournament_password'),
]
operations = [
migrations.AddField(
model_name='tournament',
name='passing_score',
field=models.PositiveSmallIntegerField(default=0),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-05-24 21:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('trials', '0025_tournament_passing_score'),
]
operations = [
migrations.AddField(
model_name='tournament',
name='completions',
field=models.IntegerField(default=0),
),
]

View File

@ -0,0 +1,43 @@
# Generated by Django 3.2.9 on 2022-05-24 21:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('trials', '0026_tournament_completions'),
]
operations = [
migrations.AddField(
model_name='tournament',
name='avg_difficulty',
field=models.FloatField(default=0.0),
),
migrations.AddField(
model_name='tournament',
name='avg_rating',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='tournament',
name='difficulty_label',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='tournament',
name='rates_amount',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='tournament',
name='total_percentage_scored_by_users',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='tournament',
name='total_rating',
field=models.IntegerField(default=0),
),
]

View File

@ -2,7 +2,7 @@ from django.db import models
from questions.models import Question from questions.models import Question
from users.models import User from users.models import User
from .managers import TestManager from .managers import TestManager, TournamentManager
class Test(models.Model): class Test(models.Model):
@ -114,4 +114,70 @@ class Tournament(models.Model):
related_name="tournaments", related_name="tournaments",
on_delete=models.CASCADE on_delete=models.CASCADE
) )
password = models.CharField(max_length=100) password = models.CharField(max_length=100, default="")
passing_score = models.PositiveSmallIntegerField(default=0)
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 = TournamentManager()
def get_score(self, answers):
"""
[
{
"question": 1,
"answer": 1
},
{
"question": 2,
"answer": 1
}
]
"""
points = 0
for answer in answers:
question = Question.objects.filter(tournament=self.id).get(id=answer["question"])
if question.answers.get(id=answer["answer"]).is_correct:
points += question.points
return points
def get_maxscore(self):
"""
[
{
"question": 1,
"answer": 1
},
{
"question": 2,
"answer": 1
}
]
"""
points = 0
for question in Question.objects.filter(tournament=self.id):
points += question.points
return points
def question_count(self):
return Question.objects.filter(tournament=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,
"passing_score": self.passing_score
}

View File

@ -1,7 +1,7 @@
from django.urls import path from django.urls import path
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
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 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, TournamentTemplateView, TournamentResultView
router = DefaultRouter(trailing_slash=False) router = DefaultRouter(trailing_slash=False)
router.register("items", TestModelViewSet) router.register("items", TestModelViewSet)
@ -26,7 +26,9 @@ urlpatterns = [
path('solved', solvedTests, name="solvedTests"), path('solved', solvedTests, name="solvedTests"),
path('solved/<int:test_id>', solvedTestsDetailed, name="solvedTests"), path('solved/<int:test_id>', solvedTestsDetailed, name="solvedTests"),
path('tournamets', TournamentView, name="tournaments"), path('tournamets', TournamentView, name="tournaments"),
path('add/tournament', CreateTournamentView, name="CreateTournament") path('add/tournament', CreateTournamentView, name="CreateTournament"),
path('<int:tournament_id>/tournament/show', TournamentTemplateView.as_view()),
path('<int:tournament_id>/tournament/result', TournamentResultView.as_view())
] ]
urlpatterns += router.urls urlpatterns += router.urls

View File

@ -85,14 +85,28 @@ def addQuestions(request, **kwargs):
def TournamentView(request): def TournamentView(request):
context = {} context = {}
context['tournament'] = Tournament.objects.all() context['tournaments'] = Tournament.objects.all()
return render(request, 'tournaments.html', context) return render(request, 'tournaments.html', context)
def CreateTournamentView(request): def CreateTournamentView(request, **kwargs):
if request.POST:
question_ids = []
dictt = dict(request.POST)
pyk = dictt.pop("name")
pyk2 = dictt.pop("passing_score")
name = request.POST.get("name")
passing_score = request.POST.get("passing_score")
for k, v in dictt.items():
question_ids.append(k)
Tournament.objects.create(name=name, questions=question_ids, passing_score=passing_score)
return render(request, 'home.html')
context = {} context = {}
context['questions'] = Question.objects.all() context['questions'] = Question.objects.all()
return render(request, 'createTournament.html', context) return render(request, 'createTournament.html', context)
class AddQuestionToExistingTest(TemplateView): class AddQuestionToExistingTest(TemplateView):
template_name = settings.BASE_DIR + f"/templates/addQuestionToExistingTest.html" template_name = settings.BASE_DIR + f"/templates/addQuestionToExistingTest.html"
@ -411,6 +425,91 @@ class TestTemplateView(TemplateView):
return HttpResponseRedirect(f'result') return HttpResponseRedirect(f'result')
class TournamentTemplateView(TemplateView):
PASSED = "Zaliczony"
FAILED = "Niezaliczony"
UNKNOWN = "nieznany"
PASSED = {
True: PASSED,
False: FAILED
}
permission_classes = []
template_name = settings.BASE_DIR + f"/templates/generic_tournament.html"
test_id = None
def get_queryset(self):
return Tournament.objects.all()
def get_context_data(self, tournament_id, **kwargs):
self.test_id = tournament_id
context = super().get_context_data(**kwargs)
context["tournament"] = self.get_queryset().filter(id=tournament_id)
context["questions"] = Question.objects.filter(tournament=tournament_id)
return context
def get_score(self, tournament: Tournament, answers):
return tournament.get_score(answers)
def get_maxscore(self, tournament: Tournament):
return tournament.get_maxscore()
def formatted_responses(self, unformatted_json):
formatted_response = list()
for question, answer in unformatted_json.items():
formatted_response.append(
{
"question": question,
"answer": answer
}
)
return formatted_response
def post(self, request, *args, **kwargs):
tournament = Tournament.objects.get(id=kwargs.get("tournament_id"))
score = self.get_score(tournament, self.formatted_responses(request.POST))
max = self.get_maxscore(tournament)
status = score >= tournament.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
# }
request.session["status"] = self.PASSED.get(status, self.UNKNOWN)
request.session["points"] = score
request.session["max"] = max
request.session["passing"] = tournament.passing_score
request.session["percentage"] = int(score / max * 100)
request.session["password"] = tournament.password
# SolvedTest.objects.create(
# score=score,
# max=max,
# percentage=int(score / max * 100),
# user=request.user,
# test=test
# )
tournament.completions += 1
tournament.total_percentage_scored_by_users += int(score / max * 100)
if tournament.completions >= 5:
tournament.avg_difficulty = float(tournament.total_percentage_scored_by_users) / float(tournament.completions)
if tournament.avg_difficulty > 90.0:
tournament.difficulty_label = 1
elif tournament.avg_difficulty > 75.0:
tournament.difficulty_label = 2
elif tournament.avg_difficulty > 50.0:
tournament.difficulty_label = 3
elif tournament.avg_difficulty > 25.0:
tournament.difficulty_label = 4
else:
tournament.difficulty_label = 5
tournament.save()
return HttpResponseRedirect(f'result')
@login_required @login_required
def TestPasswordTemplateView(request, test_id): def TestPasswordTemplateView(request, test_id):
test = Test.objects.get(id=test_id) test = Test.objects.get(id=test_id)
@ -471,3 +570,7 @@ class TestValidateAPIView(views.APIView):
class TestResultView(TemplateView): class TestResultView(TemplateView):
permission_classes = [] permission_classes = []
template_name = settings.BASE_DIR + f"/templates/result.html" template_name = settings.BASE_DIR + f"/templates/result.html"
class TournamentResultView(TemplateView):
permission_classes = []
template_name = settings.BASE_DIR + f"/templates/tournament_result.html"