Commit 61c38250 authored by Sébastiaan Versteeg's avatar Sébastiaan Versteeg Committed by Jelle Besseling

Refactor education views to class based

parent 0408ac16
......@@ -12,9 +12,10 @@
{% trans "Submit Exam" %}
</h1>
{% if saved %}
{% trans "Exam submitted successfully." as success_text %}
{% alert 'success' success_text dismissable=True %}
{% if messages %}
{% for message in messages %}
{% alert message.tags message dismissable=True %}
{% endfor %}
{% endif %}
<form method="post" enctype="multipart/form-data"
......
......@@ -12,9 +12,10 @@
{% trans "Submit Summary" %}
</h1>
{% if saved %}
{% trans "Summary submitted successfully." as success_text %}
{% alert 'success' success_text dismissable=True %}
{% if messages %}
{% for message in messages %}
{% alert message.tags message dismissable=True %}
{% endfor %}
{% endif %}
<form method="post" enctype="multipart/form-data"
......
"""The routes defined by the education package"""
from django.conf.urls import include, url
from django.views.generic.base import RedirectView, TemplateView
from django.conf.urls import include
from django.urls import path
from django.views.generic.base import RedirectView
from . import views
from education.views import (
StudentParticipantView, BookInfoView,
CourseIndexView, CourseDetailView, ExamCreateView,
SummaryCreateView,
ExamDetailView, SummaryDetailView
)
app_name = "education"
urlpatterns = [
url(r'^books/$', views.books, name="books"),
url(r'^courses/', include([
url(r'^$', views.courses, name="courses"),
url(r'^(?P<id>[0-9]*)/', include([
url(r'^$', views.course, name="course"),
url(r'^upload-exam/$', views.submit_exam, name="submit-exam"),
url(r'^upload-summary/$', views.submit_summary, name="submit-summary"),
path('education/', include([
path('books/', BookInfoView.as_view(), name="books"),
path('courses/', include([
path('', CourseIndexView.as_view(), name="courses"),
path('<int:pk>/', include([
path('', CourseDetailView.as_view(), name="course"),
path('exam/upload/', ExamCreateView.as_view(),
name="submit-exam"),
path('summary/upload/', SummaryCreateView.as_view(),
name="submit-summary"),
])),
path('exam/<int:pk>/', ExamDetailView.as_view, name="exam"),
path('summary/(<int:pk>/', SummaryDetailView.as_view(),
name="summary"),
path('exam/upload/', ExamCreateView.as_view(),
name="submit-exam"),
path('summary/upload/', SummaryCreateView.as_view(),
name="submit-summary"),
])),
path('student-participation/',
StudentParticipantView.as_view(), name="student-participation"),
path('', RedirectView.as_view(
pattern_name='education:courses', permanent=True), name="index"),
])),
url(r'^exams/(?P<id>[0-9]*)/$', views.exam, name="exam"),
url(r'^summaries/(?P<id>[0-9]*)/$', views.summary, name="summary"),
url(r'^upload-exam/$', views.submit_exam, name="submit-exam"),
url(r'^upload-summary/$', views.submit_summary, name="submit-summary"),
url('^student-participation/$', TemplateView.as_view(
template_name='education/student_participation.html'),
name="student-participation"),
url(r'^$',
RedirectView.as_view(pattern_name='education:courses',
permanent=True), name="index"),
]
......@@ -4,9 +4,12 @@ from datetime import datetime, date
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _, get_language
from django.views.generic import ListView, DetailView, CreateView, TemplateView
from sendfile import sendfile
from members.decorators import membership_required
......@@ -14,198 +17,196 @@ from .forms import AddExamForm, AddSummaryForm
from .models import Category, Course, Exam, Summary
def courses(request):
class CourseIndexView(ListView):
"""
Renders an overview of the courses
:param request: the request object
:return: HttpResponse 200 containing the HTML as body
"""
categories = Category.objects.all()
courses = [
{
'course_code': x.course_code,
'name': x.name,
'categories': x.categories.all(),
'document_count': sum([x.summary_set.filter(accepted=True).count(),
x.exam_set.filter(accepted=True).count()] +
[c.summary_set.filter(accepted=True).count()
+ c.exam_set.filter(accepted=True).count()
for c in
x.old_courses.all()]),
'url': x.get_absolute_url()
} for x in
Course.objects.order_by(f'name_{ get_language() }').filter(
until=None)
]
return render(request, 'education/courses.html',
{'courses': courses, 'categories': categories})
def course(request, id):
"""
queryset = Course.objects.filter(until=None)
template_name = 'education/courses.html'
def get_ordering(self) -> str:
return f'name_{get_language()}'
def get_context_data(self, **kwargs) -> dict:
context = super().get_context_data(**kwargs)
context.update({
'courses': ({
'course_code': x.course_code,
'name': x.name,
'categories': x.categories.all(),
'document_count': sum(
[x.summary_set.filter(accepted=True).count(),
x.exam_set.filter(accepted=True).count()] +
[c.summary_set.filter(accepted=True).count()
+ c.exam_set.filter(accepted=True).count()
for c in x.old_courses.all()]),
'url': x.get_absolute_url()
} for x in context['object_list']),
'categories': Category.objects.all(),
})
return context
class CourseDetailView(DetailView):
"""
Renders the detail page of one specific course
:param request: the request object
:param id: the primary key of the selected course
:return: HttpResponse 200 containing the HTML as body
"""
obj = get_object_or_404(Course, pk=id)
courses = list(obj.old_courses.all())
courses.append(obj)
items = {}
for course in courses:
for summary in course.summary_set.filter(accepted=True):
if summary.year not in items:
items[summary.year] = {'summaries': [], 'exams': [],
'legacy': course if course.pk != obj.pk
else None}
items[summary.year]['summaries'].append({
"year": summary.year,
"name": f'{ _("Summary") } { summary.name }',
"language": summary.language,
"id": summary.id
})
for exam in course.exam_set.filter(accepted=True):
if exam.year not in items:
items[exam.year] = {'summaries': [], 'exams': [],
'legacy': course if course.pk != obj.pk
else None}
items[exam.year]['exams'].append({
"type": "exam",
"year": exam.year, "name":
f"{ exam.get_type_display() } { exam.name }",
"language": exam.language,
"id": exam.id
})
return render(request, 'education/course.html',
{'course': obj, 'items': sorted(items.items(),
key=lambda x: x[0])})
@login_required
@membership_required
def exam(request, id):
"""
model = Course
context_object_name = 'course'
template_name = 'education/course.html'
def get_context_data(self, **kwargs) -> dict:
context = super().get_context_data(**kwargs)
obj = context['course']
courses = list(obj.old_courses.all())
courses.append(obj)
items = {}
for course in courses:
for summary in course.summary_set.filter(accepted=True):
if summary.year not in items:
items[summary.year] = {
'summaries': [],
'exams': [],
'legacy': course if course.pk != obj.pk else None
}
items[summary.year]['summaries'].append({
"year": summary.year,
"name": f'{_("Summary")} {summary.name}',
"language": summary.language,
"id": summary.id
})
for exam in course.exam_set.filter(accepted=True):
if exam.year not in items:
items[exam.year] = {
'summaries': [],
'exams': [],
'legacy': course if course.pk != obj.pk else None
}
items[exam.year]['exams'].append({
"type": "exam",
"year": exam.year, "name":
f"{exam.get_type_display()} {exam.name}",
"language": exam.language,
"id": exam.id
})
context.update({
'items': sorted(items.items(), key=lambda x: x[0])
})
return context
@method_decorator(login_required, 'dispatch')
@method_decorator(membership_required, 'dispatch')
class ExamDetailView(DetailView):
"""
Fetches and outputs the specified exam
:param request: the request object
:param id: the id of the exam
:return: 302 if not authenticated else 200 with the file as body
"""
exam = get_object_or_404(Exam, id=int(id))
model = Exam
exam.download_count += 1
exam.save()
def get(self, request, *args, **kwargs) -> HttpResponse:
response = super().get(request, *args, **kwargs)
exam = response.context_data['object']
exam.download_count += 1
exam.save()
ext = os.path.splitext(exam.file.path)[1]
filename = f'{ exam.course.name }-exam{ exam.year }{ ext }'
return sendfile(request, exam.file.path,
attachment=True, attachment_filename=filename)
ext = os.path.splitext(exam.file.path)[1]
filename = f'{exam.course.name}-exam{exam.year}{ext}'
return sendfile(request, exam.file.path,
attachment=True, attachment_filename=filename)
@login_required
@membership_required
def summary(request, id):
@method_decorator(login_required, 'dispatch')
@method_decorator(membership_required, 'dispatch')
class SummaryDetailView(DetailView):
"""
Fetches and outputs the specified summary
:param request: the request object
:param id: the id of the summary
:return: 302 if not authenticated else 200 with the file as body
"""
obj = get_object_or_404(Summary, id=int(id))
model = Summary
obj.download_count += 1
obj.save()
def get(self, request, *args, **kwargs) -> HttpResponse:
response = super().get(request, *args, **kwargs)
obj = response.context_data['object']
obj.download_count += 1
obj.save()
ext = os.path.splitext(obj.file.path)[1]
filename = f'{ obj.course.name }-summary{ obj.year }{ ext }'
return sendfile(request, obj.file.path,
attachment=True, attachment_filename=filename)
ext = os.path.splitext(obj.file.path)[1]
filename = f'{obj.course.name}-summary{obj.year}{ext}'
return sendfile(request, obj.file.path,
attachment=True, attachment_filename=filename)
@login_required
def submit_exam(request, id=None):
@method_decorator(login_required, 'dispatch')
@method_decorator(membership_required, 'dispatch')
class ExamCreateView(CreateView):
"""
Renders the form to submit a new exam
:param request: the request object
:param id: the course id (optional)
:return: 302 if not authenticated else 200 with the form HTML as body
"""
saved = False
if request.POST:
form = AddExamForm(request.POST, request.FILES)
if form.is_valid():
saved = True
obj = form.save(commit=False)
obj.uploader = request.member
obj.uploader_date = datetime.now()
obj.save()
form = AddExamForm()
else:
obj = Exam()
obj.exam_date = date.today()
if id is not None:
obj.course = Course.objects.get(id=id)
form = AddExamForm(instance=obj)
return render(request, 'education/add_exam.html',
{'form': form, 'saved': saved})
@login_required
def submit_summary(request, id=None):
model = Exam
form_class = AddExamForm
template_name = 'education/add_exam.html'
success_url = reverse_lazy('education:submit-exam')
success_message = _('Exam submitted successfully.')
def get_initial(self) -> dict:
initial = super().get_initial()
initial['exam_date'] = date.today()
initial['course'] = self.kwargs.get('pk', None)
return initial
def form_valid(self, form) -> HttpResponse:
self.object = form.save(commit=False)
self.object.uploader = self.request.member
self.object.uploader_date = datetime.now()
self.object.save()
return HttpResponseRedirect(self.get_success_url())
@method_decorator(login_required, 'dispatch')
@method_decorator(membership_required, 'dispatch')
class SummaryCreateView(CreateView):
"""
Renders the form to submit a new summary
:param request: the request object
:param id: the course id (optional)
:return: 302 if not authenticated else 200 with the form HTML as body
"""
saved = False
if request.POST:
form = AddSummaryForm(request.POST, request.FILES)
if form.is_valid():
saved = True
obj = form.save(commit=False)
obj.uploader = request.member
obj.uploader_date = datetime.now()
obj.save()
obj = Summary()
obj.author = request.member.get_full_name()
form = AddSummaryForm(instance=obj)
else:
obj = Summary()
if id is not None:
obj.course = Course.objects.get(id=id)
obj.author = request.member.get_full_name()
form = AddSummaryForm(instance=obj)
return render(request, 'education/add_summary.html',
{'form': form, 'saved': saved})
@login_required
def books(request):
model = Summary
form_class = AddSummaryForm
template_name = 'education/add_summary.html'
success_url = reverse_lazy('education:submit-summary')
success_message = _('Summary submitted successfully.')
def get_initial(self):
initial = super().get_initial()
initial['author'] = self.request.member.get_full_name()
initial['course'] = self.kwargs.get('pk', None)
return initial
def form_valid(self, form) -> HttpResponse:
self.object = form.save(commit=False)
self.object.uploader = self.request.member
self.object.uploader_date = datetime.now()
self.object.save()
return HttpResponseRedirect(self.get_success_url())
@method_decorator(login_required, 'dispatch')
class BookInfoView(TemplateView):
"""
Renders a page with information about book sale
Only available to members and to-be members
"""
template_name = 'education/books.html'
def dispatch(self, request, *args, **kwargs) -> HttpResponse:
if (
request.member.has_active_membership() or
(request.member.earliest_membership and
request.member.earliest_membership.since > timezone.now().date())
):
return super().dispatch(request, *args, **kwargs)
raise PermissionDenied
:param request: the request object
:return: 403 if no active membership else 200 with the page HTML as body
class StudentParticipantView(TemplateView):
"""
Renders a page with information about student information
"""
if (request.member and request.member.is_authenticated and
(request.member.current_membership or
(request.member.earliest_membership and
request.member.earliest_membership.since > timezone.now().date())
)):
return render(request, 'education/books.html')
raise PermissionDenied
template_name = 'education/student_participation.html'
......@@ -109,7 +109,6 @@ urlpatterns = [ # pylint: disable=invalid-name
url(r'^', include('pushnotifications.api.urls')),
])),
])),
url(r'^education/', include('education.urls')),
url(r'^announcements/', include('announcements.urls')),
url(r'^pushnotifications/', include('pushnotifications.urls')),
# Default login helpers
......@@ -131,6 +130,7 @@ urlpatterns = [ # pylint: disable=invalid-name
url(r'^media/private/(?P<request_path>.*)$', private_media, name='private-media'),
url('', include('members.urls')),
url('', include('payments.urls')),
url('', include('education.urls')),
url('', include('activemembers.urls')),
url('', include('documents.urls')),
] + static(settings.MEDIA_URL + 'public/',
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment