add resource course views

This commit is contained in:
zhaospei 2023-03-01 20:40:49 +07:00
parent cf31735e80
commit bc2c64f3b8
11 changed files with 358 additions and 30 deletions

View file

@ -492,7 +492,39 @@ 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(r"^courses/", paged_list_view(course.CourseList, "course_list")),
url(
r"^courses/(?P<pk>\d+)-(?P<slug>[\w-]*)",
include(
[
url(
r"^$",
course.CourseHome.as_view(),
name="course_home",
),
url(
r"^/resource/$",
course.CourseResourceList.as_view(),
name="course_resource",
),
url(
r"^/resource_edit/$",
course.CourseResourceEdit.as_view(),
name="course_resource_edit",
),
url(
r"^/resource/(?P<pk>\d+)/$",
course.CourseResouceDetail.as_view(),
name="course_resource_detail",
),
url(
r"^/resource/(?P<pk>\d+)/edit",
course.CourseResourceDetailEdit.as_view(),
name="course_resource_detail_edit",
),
]
),
),
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(),

View file

@ -42,6 +42,8 @@ from judge.models import (
Course, Course,
) )
from judge.models.course import CourseResource
admin.site.register(BlogPost, BlogPostAdmin) admin.site.register(BlogPost, BlogPostAdmin)
admin.site.register(Comment, CommentAdmin) admin.site.register(Comment, CommentAdmin)
@ -66,3 +68,4 @@ 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)
admin.site.register(CourseResource)

File diff suppressed because one or more lines are too long

View file

@ -58,9 +58,7 @@ class Course(models.Model):
return self.name return self.name
@classmethod @classmethod
def is_editable_by(course, profile): def is_editable_by(cls, course, profile):
if profile.is_superuser:
return True
userquery = CourseRole.objects.filter(course=course, user=profile) userquery = CourseRole.objects.filter(course=course, user=profile)
if userquery.exists(): if userquery.exists():
if userquery[0].role == "AS" or userquery[0].role == "TE": if userquery[0].role == "AS" or userquery[0].role == "TE":
@ -68,7 +66,7 @@ class Course(models.Model):
return False return False
@classmethod @classmethod
def is_accessible_by(cls,course, profile): def is_accessible_by(cls, course, profile):
userqueryset = CourseRole.objects.filter(course=course, user=profile) userqueryset = CourseRole.objects.filter(course=course, user=profile)
if userqueryset.exists(): if userqueryset.exists():
return True return True
@ -76,35 +74,35 @@ class Course(models.Model):
return False return False
@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").values("user")
@classmethod @classmethod
def get_assistants(cls,course): def get_assistants(cls, course):
return CourseRole.objects.filter(course=course, role="AS").values("user") return CourseRole.objects.filter(course=course, role="AS").values("user")
@classmethod @classmethod
def get_teachers(cls,course): def get_teachers(cls, course):
return CourseRole.objects.filter(course=course, role="TE").values("user") return CourseRole.objects.filter(course=course, role="TE").values("user")
@classmethod @classmethod
def add_student(cls,course, profiles): def add_student(cls, course, profiles):
for profile in profiles: for profile in profiles:
CourseRole.make_role(course=course, user=profile, role="ST") CourseRole.make_role(course=course, user=profile, role="ST")
@classmethod @classmethod
def add_teachers(cls,course, profiles): def add_teachers(cls, course, profiles):
for profile in profiles: for profile in profiles:
CourseRole.make_role(course=course, user=profile, role="TE") CourseRole.make_role(course=course, user=profile, role="TE")
@classmethod @classmethod
def add_assistants(cls,course, profiles): def add_assistants(cls, course, profiles):
for profile in profiles: for profile in profiles:
CourseRole.make_role(course=course, user=profile, role="AS") CourseRole.make_role(course=course, user=profile, role="AS")
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,
@ -142,7 +140,7 @@ class CourseRole(models.Model):
class CourseResource(models.Model): class CourseResource(models.Model):
course = models.OneToOneField( course = models.ForeignKey(
Course, Course,
verbose_name=_("course"), verbose_name=_("course"),
on_delete=models.CASCADE, on_delete=models.CASCADE,
@ -165,15 +163,18 @@ class CourseResource(models.Model):
default=False, default=False,
) )
def __str__(self):
return f"Resource - {self.pk}"
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,
db_index=True, db_index=True,
) )
contest = models.OneToOneField( contest = models.ForeignKey(
Contest, Contest,
verbose_name=_("contest"), verbose_name=_("contest"),
on_delete=models.CASCADE, on_delete=models.CASCADE,

View file

@ -1,6 +1,22 @@
from django.db import models from django.forms import ModelForm
from judge.models.course import Course from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView from django.views.generic.edit import UpdateView
from judge.models.course import Course, CourseResource
from django.views.generic import ListView, UpdateView, DetailView
from judge.views.feed import FeedView
from django.http import (
Http404,
HttpResponsePermanentRedirect,
HttpResponseRedirect,
)
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext as _
from judge.utils.views import (
generic_message,
)
from django.urls import reverse_lazy
from django.contrib import messages
__all__ = [ __all__ = [
"CourseList", "CourseList",
@ -13,19 +29,181 @@ __all__ = [
"CourseResourceEdit", "CourseResourceEdit",
] ]
course_directory_file = ""
class CourseBase(object):
def is_editable_by(self, course=None):
if course is None:
course = self.object
if self.request.profile:
return Course.is_editable_by(course, self.request.profile)
return False
def is_accessible_by(self, course):
if course is None:
course = self.object
if self.request.profile:
return Course.is_accessible_by(course, self.request.profile)
return False
class CourseMixin(CourseBase):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["can_edit"] = self.is_editable_by(self.course)
context["can access"] = self.is_accessible_by(self.course)
context["course"] = self.course
return context
def dispatch(self, request, *args, **kwargs):
print(self)
try:
self.course_id = int(kwargs["pk"])
self.course = get_object_or_404(Course, id=self.course_id)
except Http404:
key = None
if hasattr(self, "slug_url_kwarg"):
key = kwargs.get(self.slug_url_kwarg, None)
if key:
return generic_message(
request,
_("No such course"),
_('Could not find a course with the key "%s".') % key,
)
else:
return generic_message(
request,
_("No such course"),
_("Could not find such course."),
)
if self.course.slug != kwargs["slug"]:
return HttpResponsePermanentRedirect(
request.get_full_path().replace(kwargs["slug"], self.course.slug)
)
return super(CourseMixin, self).dispatch(request, *args, **kwargs)
class CourseHomeView(CourseMixin):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if not hasattr(self, "course"):
self.course = self.object
return context
class CourseHome(CourseHomeView, FeedView):
template_name = "course/home.html"
def get_queryset(self):
return CourseResource.objects.filter(
is_public=True,
course=self.course,
).order_by("order")
def get_context_data(self, **kwargs):
context = super(CourseHome, self).get_context_data(**kwargs)
context["title"] = self.course.name
context["description"] = self.course.about
return context
class CourseResourceList(CourseMixin, ListView):
template_name = "course/resource.html"
def get_queryset(self):
return CourseResource.objects.filter(
is_public=True,
course=self.course,
).order_by("order")
def get_context_data(self, **kwargs):
context = super(CourseResourceList, self).get_context_data(**kwargs)
context["title"] = self.course.name
return context
class CourseResouceDetail(DetailView):
template_name = "course/resource-content.html"
model = CourseResource
def get_context_data(self, **kwargs):
context = super(CourseResouceDetail, self).get_context_data(**kwargs)
return context
class CourseAdminMixin(CourseMixin):
def dispatch(self, request, *args, **kwargs):
res = super(CourseAdminMixin, self).dispatch(request, *args, **kwargs)
if not hasattr(self, "course") or self.is_editable_by(self.course):
return res
return generic_message(
request,
_("Can't edit course"),
_("You are not allowed to edit this course."),
status=403,
)
class CourseResourceDetailEditForm(ModelForm):
def __init__(self, *args, **kwargs):
super(CourseResourceDetailEditForm, self).__init__(*args, **kwargs)
class CourseResourceDetailEdit(LoginRequiredMixin, UpdateView):
template_name = "course/resource_detail_edit.html"
model = CourseResource
fields = ["description", "files", "is_public"]
def get_success_url(self):
return self.request.get_full_path()
def form_valid(self, form):
form.save()
return super().form_valid(form)
class CourseResourceEdit(CourseMixin, LoginRequiredMixin, ListView):
template_name = "course/resource_edit.html"
def get_queryset(self):
return CourseResource.objects.filter(
course=self.course,
).order_by("order")
def post(self, request, *args, **kwargs):
queryset = self.get_queryset()
for resource in queryset:
if request.POST.get("resource-" + str(resource.pk) + "-delete") != None:
resource.delete()
else:
if request.POST.get("resource-" + str(resource.pk) + "-public") != None:
resource.is_public = True
else:
resource.is_public = False
resource.order = request.POST.get(
"resource-" + str(resource.pk) + "-order"
)
resource.save()
return HttpResponseRedirect(request.path)
def get_context_data(self, **kwargs):
return super(CourseResourceEdit, self).get_context_data(**kwargs)
class CourseListMixin(object): class CourseListMixin(object):
def get_queryset(self): def get_queryset(self):
return Course.objects.filter(is_open = "true").values() 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)
@ -34,4 +212,3 @@ class CourseList(ListView):
context["available"] = available context["available"] = available
context["enrolling"] = enrolling context["enrolling"] = enrolling
return context return context

View file

@ -0,0 +1,4 @@
<h1> {{ title }} - Home </h1>
<h2> About: {{ description }} </h2>
<a href="/courses/{{ course.pk }}-{{ course.slug }}/resource"> See course resource.</a>

View file

@ -1,19 +1,30 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title> <title>Document</title>
</head> </head>
<body> <body>
<h1>Enrolling</h1> <h1>Enrolling</h1>
{% for course in enrolling %} <ul>
<h2> {{ course }} </h2> {% for course in enrolling %}
{% endfor %} <li>
<a href="/courses/{{ course.pk }}-{{ course.slug }}"> {{ course }} </a>
</li>
{% endfor %}
</ul>
<h1> Available </h1> <h1> Available </h1>
{% for course in available %} <ul>
<h2> {{ course }} </h2> {% for course in available %}
{% endfor %} <li>
<a href="/courses/{{ course.pk }}-{{ course.slug }}"> {{ course }} </a>
</li>
{% endfor %}
</ul>
</body> </body>
</html> </html>

View file

@ -0,0 +1,12 @@
<h1> {{ object }} </h1>
<h2> This resource belong to {{ object.course }}</h2>
<h3> {{ object.description }}</h3>
<div>
{% if object.files != None %}
<div> <a href="{{ object.files.url}}" target="_blank"> See files </a> </div>
{% endif %}
<div> <a href="/courses/{{ object.course.pk }}-{{ object.course.slug }}/resource/{{ object.pk }}/edit"> Edit
Resource </a> </div>
</div>

View file

@ -0,0 +1,11 @@
<h1> {{ course.name }}'s Resource </h1>
<ul>
{% for resource in object_list %}
<li>
<a href="/courses/{{ course.pk }}-{{ course.slug }}/resource/{{ resource.pk }}"> {{ resource }} </a>
</li>
{% endfor %}
</ul>
<a href="/courses/{{ course.pk }}-{{ course.slug }}/resource_edit"> Edit resources</a>

View file

@ -0,0 +1,5 @@
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
<input type="submit" value="Update">
</form>

View file

@ -0,0 +1,33 @@
{{ course }}
<form id="resource-form" method="post" action="">
{% csrf_token %}
<table>
<tr>
<th>Name</th>
<th>Delete</th>
<th>Order</th>
<th>Public</th>
<th>Edit link</th>
</tr>
{% for resource in object_list %}
<tr>
<td> {{ resource }} </td>
<td> <input type="checkbox" name="resource-{{ resource.pk }}-delete" id="resource-{{ resource.pk }}-delete">
</td>
<td> <input type="number" name="resource-{{ resource.pk }}-order" value="{{
resource.order }}" id="resource-{{ resource.pk }}-order"> </a></td>
{% if resource.is_public %}
<td> <input type="checkbox" name="resource-{{ resource.pk }}-public" id="resource-{{ resource.pk }}-public"
checked=""> </a></td>
{% else %}
<td> <input type="checkbox" name="resource-{{ resource.pk }}-public" id="resource-{{ resource.pk }}-public">
</a></td>
{% endif %}
<td> <a href="/courses/{{ course.pk }}-{{ course.slug }}/resource/{{ resource.pk }}/edit"> Edit </a></td>
</tr>
{% endfor %}
</table>
<button type="submit">Save</button>
</form>