add CourseDetail, CourseStudentResults
This commit is contained in:
parent
b629459b46
commit
1797b9fe06
10 changed files with 205 additions and 17 deletions
19
dmoj/urls.py
19
dmoj/urls.py
|
@ -487,7 +487,6 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
url(r"^contests/", paged_list_view(contests.ContestList, "contest_list")),
|
url(r"^contests/", paged_list_view(contests.ContestList, "contest_list")),
|
||||||
url(r"^course/", paged_list_view(course.CourseList, "course_list" )),
|
|
||||||
url(
|
url(
|
||||||
r"^contests/(?P<year>\d+)/(?P<month>\d+)/$",
|
r"^contests/(?P<year>\d+)/(?P<month>\d+)/$",
|
||||||
contests.ContestCalendar.as_view(),
|
contests.ContestCalendar.as_view(),
|
||||||
|
@ -592,6 +591,24 @@ urlpatterns = [
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
url(r"^courses/", paged_list_view(course.CourseList, "course_list" )),
|
||||||
|
url(
|
||||||
|
r"^courses/(?P<slug>[\w-]*)",
|
||||||
|
include(
|
||||||
|
[
|
||||||
|
url(
|
||||||
|
r"^$",
|
||||||
|
course.CourseDetail.as_view(),
|
||||||
|
name="course_detail"
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r"^/grades$",
|
||||||
|
course.CourseStudentResults.as_view(),
|
||||||
|
name="grades"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
url(
|
url(
|
||||||
r"^organizations/$",
|
r"^organizations/$",
|
||||||
organization.OrganizationList.as_view(),
|
organization.OrganizationList.as_view(),
|
||||||
|
|
|
@ -40,6 +40,7 @@ from judge.models import (
|
||||||
Ticket,
|
Ticket,
|
||||||
VolunteerProblemVote,
|
VolunteerProblemVote,
|
||||||
Course,
|
Course,
|
||||||
|
course,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,4 +66,5 @@ admin.site.register(Profile, ProfileAdmin)
|
||||||
admin.site.register(Submission, SubmissionAdmin)
|
admin.site.register(Submission, SubmissionAdmin)
|
||||||
admin.site.register(Ticket, TicketAdmin)
|
admin.site.register(Ticket, TicketAdmin)
|
||||||
admin.site.register(VolunteerProblemVote, VolunteerProblemVoteAdmin)
|
admin.site.register(VolunteerProblemVote, VolunteerProblemVoteAdmin)
|
||||||
admin.site.register(Course)
|
admin.site.register(course.Course)
|
||||||
|
admin.site.register(course.CourseAssignment)
|
||||||
|
|
19
judge/migrations/0151_alter_courseassignment_course.py
Normal file
19
judge/migrations/0151_alter_courseassignment_course.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.2.17 on 2023-02-15 04:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('judge', '0150_alter_profile_timezone'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='courseassignment',
|
||||||
|
name='course',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.course', verbose_name='course'),
|
||||||
|
),
|
||||||
|
]
|
19
judge/migrations/0152_alter_courserole_course.py
Normal file
19
judge/migrations/0152_alter_courserole_course.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.2.17 on 2023-02-22 08:08
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('judge', '0151_alter_courseassignment_course'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='courserole',
|
||||||
|
name='course',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.course', verbose_name='course'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -449,6 +449,13 @@ class Contest(models.Model):
|
||||||
"profile_id", flat=True
|
"profile_id", flat=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def total_points(self):
|
||||||
|
total = 0
|
||||||
|
for problem in self.problems.all():
|
||||||
|
total += problem.points
|
||||||
|
return total
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ class Course(models.Model):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_students(cls,course):
|
def get_students(cls,course):
|
||||||
return CourseRole.objects.filter(course=course, role="ST").values("user")
|
return CourseRole.objects.filter(course=course, role="ST")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_assistants(cls,course):
|
def get_assistants(cls,course):
|
||||||
|
@ -104,7 +104,7 @@ class Course(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class CourseRole(models.Model):
|
class CourseRole(models.Model):
|
||||||
course = models.OneToOneField(
|
course = models.ForeignKey(
|
||||||
Course,
|
Course,
|
||||||
verbose_name=_("course"),
|
verbose_name=_("course"),
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
|
@ -167,7 +167,7 @@ class CourseResource(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class CourseAssignment(models.Model):
|
class CourseAssignment(models.Model):
|
||||||
course = models.OneToOneField(
|
course = models.ForeignKey(
|
||||||
Course,
|
Course,
|
||||||
verbose_name=_("course"),
|
verbose_name=_("course"),
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from judge.models.course import Course
|
from judge.models.course import Course , CourseAssignment , CourseRole
|
||||||
from django.views.generic import ListView
|
from judge.models.contest import ContestParticipation, Contest
|
||||||
|
from django.views.generic import ListView, DetailView
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"CourseList",
|
"CourseList",
|
||||||
|
@ -15,17 +17,14 @@ __all__ = [
|
||||||
|
|
||||||
course_directory_file = ""
|
course_directory_file = ""
|
||||||
|
|
||||||
class CourseListMixin(object):
|
|
||||||
def get_queryset(self):
|
|
||||||
return Course.objects.filter(is_open = "true").values()
|
|
||||||
|
|
||||||
class CourseList(ListView):
|
class CourseList(ListView):
|
||||||
model = Course
|
model = Course
|
||||||
template_name = "course/list.html"
|
template_name = "course/list.html"
|
||||||
queryset = Course.objects.filter(is_public=True).filter(is_open=True)
|
queryset = Course.objects.filter(is_public=True).filter(is_open=True)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(CourseList,self).get_context_data(**kwargs)
|
context = super(CourseList, self).get_context_data(**kwargs)
|
||||||
available , enrolling = [] , []
|
available, enrolling = [], []
|
||||||
for course in Course.objects.filter(is_public=True).filter(is_open=True):
|
for course in Course.objects.filter(is_public=True).filter(is_open=True):
|
||||||
if Course.is_accessible_by(course, self.request.profile):
|
if Course.is_accessible_by(course, self.request.profile):
|
||||||
enrolling.append(course)
|
enrolling.append(course)
|
||||||
|
@ -35,3 +34,60 @@ class CourseList(ListView):
|
||||||
context["enrolling"] = enrolling
|
context["enrolling"] = enrolling
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
class CourseDetail(ListView):
|
||||||
|
model = CourseAssignment
|
||||||
|
template_name = "course/course.html"
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
cur_course = Course.objects.get(slug=self.kwargs['slug'])
|
||||||
|
return CourseAssignment.objects.filter(course=cur_course)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(CourseDetail, self).get_context_data(**kwargs)
|
||||||
|
context['slug'] = self.kwargs['slug']
|
||||||
|
return context
|
||||||
|
|
||||||
|
def best_score_user_contest(user , contest):
|
||||||
|
participated_contests = ContestParticipation.objects.filter(user=user, contest=contest)
|
||||||
|
progress_point = 0
|
||||||
|
for cur_contest in participated_contests:
|
||||||
|
progress_point = max( progress_point , cur_contest.score_final )
|
||||||
|
return progress_point
|
||||||
|
|
||||||
|
def progress_contest(user , contest):
|
||||||
|
return best_score_user_contest(user, contest) / contest.total_points
|
||||||
|
|
||||||
|
def progress_course(user , course):
|
||||||
|
assignments = CourseAssignment.objects.filter(course=course)
|
||||||
|
assignments_total_point = 0
|
||||||
|
for assignment in assignments:
|
||||||
|
assignments_total_point += assignment.points
|
||||||
|
progress = 0
|
||||||
|
for assignment in assignments:
|
||||||
|
progress += progress_contest(user,assignment.contest) * assignment.points / assignments_total_point
|
||||||
|
return progress
|
||||||
|
|
||||||
|
class CourseStudentResults(ListView):
|
||||||
|
model = ContestParticipation
|
||||||
|
template_name = "course/grades.html"
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
cur_course = Course.objects.get(slug=self.kwargs['slug'])
|
||||||
|
contests = CourseAssignment.objects.filter(course=cur_course)
|
||||||
|
students = Course.get_students(cur_course)
|
||||||
|
grades_table = []
|
||||||
|
for student in students:
|
||||||
|
grades_student = [student.user.user.username]
|
||||||
|
for contest in contests:
|
||||||
|
grades_student.append( round(progress_contest(student.user, contest.contest) * 100 ) )
|
||||||
|
grades_student.append( round(progress_course(student.user, cur_course) * 100) )
|
||||||
|
grades_table.append( grades_student )
|
||||||
|
return grades_table
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(CourseStudentResults, self).get_context_data(**kwargs)
|
||||||
|
cur_course = Course.objects.get(slug=self.kwargs['slug'])
|
||||||
|
contests = CourseAssignment.objects.filter(course=cur_course)
|
||||||
|
context['contests'] = contests
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
15
templates/course/course.html
Normal file
15
templates/course/course.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% for course in object_list %}
|
||||||
|
<h1> {{ course.contest }} - {{ course.contest.start_time }} - {{ course.contest.end_time }} </h1>
|
||||||
|
{% endfor %}
|
||||||
|
<a href="{{ url('grades',slug) }}">View grades</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
53
templates/course/grades.html
Normal file
53
templates/course/grades.html
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
table th {
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
table td {
|
||||||
|
border: 1px solid black;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Grades</h1>
|
||||||
|
<!-- {% for something in object_list %}
|
||||||
|
<h2> {{ something }} </h2>
|
||||||
|
{% endfor %} -->
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
{% for contest in contests %}
|
||||||
|
<th>{{ contest }}(%)</th>
|
||||||
|
{% endfor %}
|
||||||
|
<th>Progress</th>
|
||||||
|
</tr>
|
||||||
|
{% for student in object_list %}
|
||||||
|
<tr>
|
||||||
|
{% for data in student %}
|
||||||
|
<td>{{ data }}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -9,11 +9,11 @@
|
||||||
<body>
|
<body>
|
||||||
<h1>Enrolling</h1>
|
<h1>Enrolling</h1>
|
||||||
{% for course in enrolling %}
|
{% for course in enrolling %}
|
||||||
<h2> {{ course }} </h2>
|
<h2><a href="{{ url('course_detail',course.name) }}"> {{ course }} </a></h2>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<h1> Available </h1>
|
<h1> Available </h1>
|
||||||
{% for course in available %}
|
{% for course in available %}
|
||||||
<h2> {{ course }} </h2>
|
<h2><a href="{{ url('course_detail',course.name) }}"> {{ course }} </a></h2>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in a new issue