Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
concrexit
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
70
Issues
70
List
Boards
Labels
Service Desk
Milestones
Merge Requests
10
Merge Requests
10
Operations
Operations
Incidents
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
thalia
concrexit
Commits
61c38250
Commit
61c38250
authored
Jul 03, 2019
by
Sébastiaan Versteeg
Committed by
Jelle Besseling
Jul 03, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor education views to class based
parent
0408ac16
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
208 additions
and
194 deletions
+208
-194
website/education/templates/education/add_exam.html
website/education/templates/education/add_exam.html
+4
-3
website/education/templates/education/add_summary.html
website/education/templates/education/add_summary.html
+4
-3
website/education/urls.py
website/education/urls.py
+31
-20
website/education/views.py
website/education/views.py
+168
-167
website/thaliawebsite/urls.py
website/thaliawebsite/urls.py
+1
-1
No files found.
website/education/templates/education/add_exam.html
View file @
61c38250
...
...
@@ -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"
...
...
website/education/templates/education/add_summary.html
View file @
61c38250
...
...
@@ -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"
...
...
website/education/urls.py
View file @
61c38250
"""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"
),
]
website/education/views.py
View file @
61c38250
...
...
@@ -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
@
me
mbership_required
def
summary
(
request
,
id
):
@
method_decorator
(
login_required
,
'dispatch'
)
@
me
thod_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'
website/thaliawebsite/urls.py
View file @
61c38250
...
...
@@ -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/'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment