Browse Source

Add delete views and update signup button style

master
Vitor Freitas 3 years ago
parent
commit
00d620ffd6
No known key found for this signature in database GPG Key ID: 11D07CF62D8BE9DE
  1. 9
      django_school/classroom/forms.py
  2. 2
      django_school/classroom/templates/classroom/home.html
  3. 14
      django_school/classroom/templates/classroom/students/_header.html
  4. 13
      django_school/classroom/templates/classroom/students/interests_form.html
  5. 13
      django_school/classroom/templates/classroom/students/quiz_list.html
  6. 2
      django_school/classroom/templates/classroom/students/take_quiz_form.html
  7. 13
      django_school/classroom/templates/classroom/students/taken_quiz_list.html
  8. 2
      django_school/classroom/templates/classroom/teachers/question_add_form.html
  9. 4
      django_school/classroom/templates/classroom/teachers/question_change_form.html
  10. 21
      django_school/classroom/templates/classroom/teachers/question_delete_confirm.html
  11. 2
      django_school/classroom/templates/classroom/teachers/quiz_add_form.html
  12. 8
      django_school/classroom/templates/classroom/teachers/quiz_change_form.html
  13. 20
      django_school/classroom/templates/classroom/teachers/quiz_delete_confirm.html
  14. 6
      django_school/classroom/templates/classroom/teachers/quiz_results.html
  15. 3
      django_school/classroom/urls.py
  16. 27
      django_school/classroom/views/students.py
  17. 50
      django_school/classroom/views/teachers.py
  18. 1
      django_school/django_school/settings.py
  19. 26
      django_school/static/css/app.css
  20. 7
      django_school/templates/base.html
  21. 4
      django_school/templates/registration/signup.html
  22. 2
      django_school/templates/registration/signup_form.html

9
django_school/classroom/forms.py

@ -39,6 +39,15 @@ class StudentSignUpForm(UserCreationForm):
return user
class StudentInterestsForm(forms.ModelForm):
class Meta:
model = Student
fields = ('interests', )
widgets = {
'interests': forms.CheckboxSelectMultiple
}
class QuestionForm(forms.ModelForm):
class Meta:
model = Question

2
django_school/classroom/templates/classroom/home.html

@ -14,5 +14,5 @@
based on their interests.
</p>
<p>Want to run this code locally? <a href="">Read detailed instructions on how to run this project</a>.</p>
<p>Vitor Freitas<br><a href="https://twitter.com/vitorfs/" target="_blank" rel="noopener">@vitorfs</a></p>
<p>Vitor Freitas<br><a href="https://twitter.com/vitorfs/">@vitorfs</a></p>
{% endblock %}

14
django_school/classroom/templates/classroom/students/_header.html

@ -0,0 +1,14 @@
<h2>Quizzes</h2>
<p class="text-muted">
Subjects:{% for subject in user.student.interests.all %} {{ subject.get_html_badge }}{% endfor %}
<a href="{% url 'students:student_interests' %}"><small>(update interests)</small></a>
</p>
<ul class="nav nav-tabs mb-3">
<li class="nav-item">
<a class="nav-link{% if active == 'new' %} active{% endif %}" href="{% url 'students:quiz_list' %}">New</a>
</li>
<li class="nav-item">
<a class="nav-link{% if active == 'taken' %} active{% endif %}" href="{% url 'students:taken_quiz_list' %}">Taken</a>
</li>
</ul>

13
django_school/classroom/templates/classroom/students/interests_form.html

@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<h2 class="mb-3">Update your interests</h2>
<form method="post" novalidate>
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-success">Save changes</button>
<a href="{% url 'students:quiz_list' %}" class="btn btn-outline-secondary">Nevermind</a>
</form>
{% endblock %}

13
django_school/classroom/templates/classroom/students/quiz_list.html

@ -1,18 +1,7 @@
{% extends 'base.html' %}
{% block content %}
<h2>Quizzes</h2>
<p class="text-muted">Subjects:{% for subject in user.student.interests.all %} {{ subject.get_html_badge }}{% endfor %}</p>
<ul class="nav nav-tabs mb-3">
<li class="nav-item">
<a class="nav-link active" href="{% url 'students:quiz_list' %}">New</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'students:taken_quiz_list' %}">Taken</a>
</li>
</ul>
{% include 'classroom/students/_header.html' with active='new' %}
<div class="card">
<table class="table mb-0">
<thead>

2
django_school/classroom/templates/classroom/students/take_quiz_form.html

@ -8,6 +8,6 @@
<form method="post" novalidate>
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Next</button>
<button type="submit" class="btn btn-primary">Next</button>
</form>
{% endblock %}

13
django_school/classroom/templates/classroom/students/taken_quiz_list.html

@ -1,18 +1,7 @@
{% extends 'base.html' %}
{% block content %}
<h2>Quizzes</h2>
<p class="text-muted">Subjects:{% for subject in user.student.interests.all %} {{ subject.get_html_badge }}{% endfor %}</p>
<ul class="nav nav-tabs mb-3">
<li class="nav-item">
<a class="nav-link" href="{% url 'students:quiz_list' %}">New</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="{% url 'students:taken_quiz_list' %}">Taken</a>
</li>
</ul>
{% include 'classroom/students/_header.html' with active='taken' %}
<div class="card">
<table class="table mb-0">
<thead>

2
django_school/classroom/templates/classroom/teachers/question_add_form.html

@ -15,7 +15,7 @@
<form method="post" novalidate>
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Add question</button>
<button type="submit" class="btn btn-success">Save</button>
<a href="{% url 'teachers:quiz_change' quiz.pk %}" class="btn btn-outline-secondary" role="button">Nevermind</a>
</form>
{% endblock %}

4
django_school/classroom/templates/classroom/teachers/question_change_form.html

@ -15,7 +15,6 @@
{% csrf_token %}
{{ formset.management_form }}
{{ form|crispy }}
<div class="card mb-3{% if formset.errors %} border-danger{% endif %}">
<div class="card-header">
<div class="row">
@ -58,7 +57,8 @@
<p>
<small class="form-text text-muted">Your question may have at least <strong>2</strong> answers and maximum <strong>10</strong> answers. Select at least one correct answer.</small>
</p>
<button type="submit" class="btn btn-primary">Save changes</button>
<button type="submit" class="btn btn-success">Save changes</button>
<a href="{% url 'teachers:quiz_change' quiz.pk %}" class="btn btn-outline-secondary" role="button">Nevermind</a>
<a href="{% url 'teachers:question_delete' quiz.pk question.pk %}" class="btn btn-danger float-right">Delete</a>
</form>
{% endblock %}

21
django_school/classroom/templates/classroom/teachers/question_delete_confirm.html

@ -0,0 +1,21 @@
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'teachers:quiz_change_list' %}">My Quizzes</a></li>
<li class="breadcrumb-item"><a href="{% url 'teachers:quiz_change' quiz.pk %}">{{ quiz.name }}</a></li>
<li class="breadcrumb-item"><a href="{% url 'teachers:question_change' quiz.pk question.pk %}">{{ question.text }}</a></li>
<li class="breadcrumb-item active" aria-current="page">Confirm deletion</li>
</ol>
</nav>
<h2 class="mb-3">Confirm deletion</h2>
<p class="lead">Are you sure you want to delete the question <strong>"{{ question.text }}"</strong>? There is no going back.</p>
<form method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger btn-lg">Yes, I'm sure</button>
<a href="{% url 'teachers:question_change' quiz.pk question.pk %}" class="btn btn-outline-secondary btn-lg" role="button">Nevermind</a>
</form>
{% endblock %}

2
django_school/classroom/templates/classroom/teachers/quiz_add_form.html

@ -15,7 +15,7 @@
<form method="post" novalidate>
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Add quiz</button>
<button type="submit" class="btn btn-success">Save</button>
<a href="{% url 'teachers:quiz_change_list' %}" class="btn btn-outline-secondary" role="button">Nevermind</a>
</form>
</div>

8
django_school/classroom/templates/classroom/teachers/quiz_change_form.html

@ -9,14 +9,18 @@
<li class="breadcrumb-item active" aria-current="page">{{ quiz.name }}</li>
</ol>
</nav>
<h2 class="mb-3">{{ quiz.name }}</h2>
<h2 class="mb-3">
{{ quiz.name }}
<a href="{% url 'teachers:quiz_results' quiz.pk %}" class="btn btn-primary float-right">View results</a>
</h2>
<div class="row mb-3">
<div class="col-md-6 col-sm-8 col-12">
<form method="post" novalidate>
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Save changes</button>
<button type="submit" class="btn btn-success">Save changes</button>
<a href="{% url 'teachers:quiz_change_list' %}" class="btn btn-outline-secondary" role="button">Nevermind</a>
<a href="{% url 'teachers:quiz_delete' quiz.pk %}" class="btn btn-danger float-right">Delete</a>
</form>
</div>
</div>

20
django_school/classroom/templates/classroom/teachers/quiz_delete_confirm.html

@ -0,0 +1,20 @@
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'teachers:quiz_change_list' %}">My Quizzes</a></li>
<li class="breadcrumb-item"><a href="{% url 'teachers:quiz_change' quiz.pk %}">{{ quiz.name }}</a></li>
<li class="breadcrumb-item active" aria-current="page">Confirm deletion</li>
</ol>
</nav>
<h2 class="mb-3">Confirm deletion</h2>
<p class="lead">Are you sure you want to delete the quiz <strong>"{{ quiz.name }}"</strong>? There is no going back.</p>
<form method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger btn-lg">Yes, I'm sure</button>
<a href="{% url 'teachers:quiz_change' quiz.pk %}" class="btn btn-outline-secondary btn-lg" role="button">Nevermind</a>
</form>
{% endblock %}

6
django_school/classroom/templates/classroom/teachers/quiz_results.html

@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% load crispy_forms_tags humanize %}
{% block content %}
<nav aria-label="breadcrumb">
@ -15,7 +15,7 @@
<div class="card">
<div class="card-header">
<strong>Taken Quizzes</strong>
<span class="badge badge-pill badge-primary float-right">Average Score: {{ quiz_score.average_score }}</span>
<span class="badge badge-pill badge-primary float-right">Average Score: {{ quiz_score.average_score|default_if_none:0.0 }}</span>
</div>
<table class="table mb-0">
<thead>
@ -29,7 +29,7 @@
{% for taken_quiz in taken_quizzes %}
<tr>
<td>{{ taken_quiz.student.user.username }}</td>
<td>{{ taken_quiz.date }}</td>
<td>{{ taken_quiz.date|naturaltime }}</td>
<td>{{ taken_quiz.score }}</td>
</tr>
{% endfor %}

3
django_school/classroom/urls.py

@ -7,6 +7,7 @@ urlpatterns = [
path('students/', include(([
path('', students.QuizListView.as_view(), name='quiz_list'),
path('interests/', students.StudentInterestsView.as_view(), name='student_interests'),
path('taken/', students.TakenQuizListView.as_view(), name='taken_quiz_list'),
path('quiz/<int:pk>/', students.take_quiz, name='take_quiz'),
], 'classroom'), namespace='students')),
@ -15,8 +16,10 @@ urlpatterns = [
path('', teachers.QuizListView.as_view(), name='quiz_change_list'),
path('quiz/add/', teachers.QuizCreateView.as_view(), name='quiz_add'),
path('quiz/<int:pk>/', teachers.QuizUpdateView.as_view(), name='quiz_change'),
path('quiz/<int:pk>/delete/', teachers.QuizDeleteView.as_view(), name='quiz_delete'),
path('quiz/<int:pk>/results/', teachers.QuizResultsView.as_view(), name='quiz_results'),
path('quiz/<int:pk>/question/add/', teachers.question_add, name='question_add'),
path('quiz/<int:quiz_pk>/question/<int:question_pk>/', teachers.question_change, name='question_change'),
path('quiz/<int:quiz_pk>/question/<int:question_pk>/delete/', teachers.QuestionDeleteView.as_view(), name='question_delete'),
], 'classroom'), namespace='teachers')),
]

27
django_school/classroom/views/students.py

@ -5,11 +5,12 @@ from django.db import transaction
from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.views.generic import CreateView, ListView
from django.views.generic import CreateView, ListView, UpdateView
from django.urls import reverse_lazy
from ..decorators import student_required
from ..forms import StudentSignUpForm, TakeQuizForm
from ..models import Quiz, TakenQuiz, User
from ..forms import StudentSignUpForm, StudentInterestsForm, TakeQuizForm
from ..models import Quiz, Student, TakenQuiz, User
class StudentSignUpView(CreateView):
@ -27,6 +28,21 @@ class StudentSignUpView(CreateView):
return redirect('students:quiz_list')
@method_decorator([login_required, student_required], name='dispatch')
class StudentInterestsView(UpdateView):
model = Student
form_class = StudentInterestsForm
template_name = 'classroom/students/interests_form.html'
success_url = reverse_lazy('students:quiz_list')
def get_object(self):
return self.request.user.student
def form_valid(self, form):
messages.success(self.request, 'Interests updated with success!')
return super().form_valid(form)
@method_decorator([login_required, student_required], name='dispatch')
class QuizListView(ListView):
model = Quiz
@ -86,7 +102,10 @@ def take_quiz(request, pk):
correct_answers = student.quiz_answers.filter(answer__question__quiz=quiz, answer__is_correct=True).count()
score = round((correct_answers / total_questions) * 100.0, 2)
TakenQuiz.objects.create(student=student, quiz=quiz, score=score)
messages.success(request, 'Congratulations! You completed the quiz %s with success!' % quiz.name)
if score < 50.0:
messages.warning(request, 'Better luck next time! Your score for the quiz %s was %s.' % (quiz.name, score))
else:
messages.success(request, 'Congratulations! You completed the quiz %s with success! You scored %s points.' % (quiz.name, score))
return redirect('students:quiz_list')
else:
form = TakeQuizForm(question=question)

50
django_school/classroom/views/teachers.py

@ -5,9 +5,9 @@ from django.db import transaction
from django.db.models import Avg, Count
from django.forms import inlineformset_factory
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.urls import reverse, reverse_lazy
from django.utils.decorators import method_decorator
from django.views.generic import CreateView, DetailView, ListView, UpdateView
from django.views.generic import CreateView, DeleteView, DetailView, ListView, UpdateView
from ..decorators import teacher_required
from ..forms import BaseAnswerInlineFormSet, QuestionForm, TeacherSignUpForm
@ -39,7 +39,8 @@ class QuizListView(ListView):
def get_queryset(self):
queryset = self.request.user.quizzes \
.select_related('subject') \
.annotate(questions_count=Count('questions'), taken_count=Count('taken_quizzes'))
.annotate(questions_count=Count('questions', distinct=True)) \
.annotate(taken_count=Count('taken_quizzes', distinct=True))
return queryset
@ -80,6 +81,22 @@ class QuizUpdateView(UpdateView):
return reverse('teachers:quiz_change', kwargs={'pk': self.object.pk})
@method_decorator([login_required, teacher_required], name='dispatch')
class QuizDeleteView(DeleteView):
model = Quiz
context_object_name = 'quiz'
template_name = 'classroom/teachers/quiz_delete_confirm.html'
success_url = reverse_lazy('teachers:quiz_change_list')
def delete(self, request, *args, **kwargs):
quiz = self.get_object()
messages.success(request, 'The quiz %s was deleted with success!' % quiz.name)
return super().delete(request, *args, **kwargs)
def get_queryset(self):
return self.request.user.quizzes.all()
@method_decorator([login_required, teacher_required], name='dispatch')
class QuizResultsView(DetailView):
model = Quiz
@ -88,7 +105,7 @@ class QuizResultsView(DetailView):
def get_context_data(self, **kwargs):
quiz = self.get_object()
taken_quizzes = quiz.taken_quizzes.select_related('student__user').order_by('date')
taken_quizzes = quiz.taken_quizzes.select_related('student__user').order_by('-date')
total_taken_quizzes = taken_quizzes.count()
quiz_score = quiz.taken_quizzes.aggregate(average_score=Avg('score'))
extra_context = {
@ -168,3 +185,28 @@ def question_change(request, quiz_pk, question_pk):
'form': form,
'formset': formset
})
@method_decorator([login_required, teacher_required], name='dispatch')
class QuestionDeleteView(DeleteView):
model = Question
context_object_name = 'question'
template_name = 'classroom/teachers/question_delete_confirm.html'
pk_url_kwarg = 'question_pk'
def get_context_data(self, **kwargs):
question = self.get_object()
kwargs['quiz'] = question.quiz
return super().get_context_data(**kwargs)
def delete(self, request, *args, **kwargs):
question = self.get_object()
messages.success(request, 'The question %s was deleted with success!' % question.text)
return super().delete(request, *args, **kwargs)
def get_queryset(self):
return Question.objects.filter(quiz__owner=self.request.user)
def get_success_url(self):
question = self.get_object()
return reverse('teachers:quiz_change', kwargs={'pk': question.quiz_id})

1
django_school/django_school/settings.py

@ -38,6 +38,7 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
'crispy_forms',

26
django_school/static/css/app.css

@ -30,3 +30,29 @@ footer a {
line-height: 1.5;
border-radius: .2rem;
}
.btn-student {
color: #fff;
background-color: #91afb6;
border-color: #91afb6;
}
.btn-student:hover,
.btn-student:active {
color: #fff;
background-color: #608993;
border-color: #608993;
}
.btn-teacher {
color: #fff;
background-color: #8980a5;
border-color: #8980a5;
}
.btn-teacher:hover,
.btn-teacher:active {
color: #fff;
background-color: #66598B;
border-color: #66598B;
}

7
django_school/templates/base.html

@ -15,6 +15,7 @@
{% endif %}
</head>
<body>
<a href="https://github.com/you"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/38ef81f8aca64bb9a64448d0d70f1308ef5341ab/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6461726b626c75655f3132313632312e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"></a>
<div class="container my-4">
<div class="row justify-content-center">
<div class="col-md-10 col-sm-12">
@ -57,11 +58,11 @@
</div>
</div>
<footer>
<a href="https://simpleisbetterthancomplex.com">© simpleisbetterthancomplex.com</a>
<a href="https://simpleisbetterthancomplex.com">© SimpleIsBetterThanComplex.com</a>
/
<a href="">Read more about this example</a>
<a href="#">Read more about this example</a>
/
<a href="">Fork the code on GitHub</a>
<a href="https://github.com/sibtc/django-multiple-user-types-example">GitHub repository</a>
</footer>
</div>
</div>

4
django_school/templates/registration/signup.html

@ -3,6 +3,6 @@
{% block content %}
<h2>Sign up for a free account</h2>
<p class="lead">Select below the type of account you want to create</p>
<a href="{% url 'student_signup' %}" class="btn btn-primary btn-lg" role="button">I'm a student</a>
<a href="{% url 'teacher_signup' %}" class="btn btn-success btn-lg" role="button">I'm a teacher</a>
<a href="{% url 'student_signup' %}" class="btn btn-student btn-lg" role="button">I'm a student</a>
<a href="{% url 'teacher_signup' %}" class="btn btn-teacher btn-lg" role="button">I'm a teacher</a>
{% endblock %}

2
django_school/templates/registration/signup_form.html

@ -10,7 +10,7 @@
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Sign up</button>
<button type="submit" class="btn btn-success">Sign up</button>
</form>
</div>
</div>

Loading…
Cancel
Save