Compare commits

...

2 commits
master ... lms

Author SHA1 Message Date
Tuan-Dung Bui
0ee89f1c36
Merge pull request #61 from zhaospei/lms
add resource course views
2023-03-01 20:42:41 +07:00
zhaospei
bc2c64f3b8 add resource course views 2023-03-01 20:40:49 +07:00
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"^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(
r"^contests/(?P<year>\d+)/(?P<month>\d+)/$",
contests.ContestCalendar.as_view(),

View file

@ -42,6 +42,8 @@ from judge.models import (
Course,
)
from judge.models.course import CourseResource
admin.site.register(BlogPost, BlogPostAdmin)
admin.site.register(Comment, CommentAdmin)
@ -66,3 +68,4 @@ admin.site.register(Submission, SubmissionAdmin)
admin.site.register(Ticket, TicketAdmin)
admin.site.register(VolunteerProblemVote, VolunteerProblemVoteAdmin)
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
@classmethod
def is_editable_by(course, profile):
if profile.is_superuser:
return True
def is_editable_by(cls, course, profile):
userquery = CourseRole.objects.filter(course=course, user=profile)
if userquery.exists():
if userquery[0].role == "AS" or userquery[0].role == "TE":
@ -68,7 +66,7 @@ class Course(models.Model):
return False
@classmethod
def is_accessible_by(cls,course, profile):
def is_accessible_by(cls, course, profile):
userqueryset = CourseRole.objects.filter(course=course, user=profile)
if userqueryset.exists():
return True
@ -76,35 +74,35 @@ class Course(models.Model):
return False
@classmethod
def get_students(cls,course):
def get_students(cls, course):
return CourseRole.objects.filter(course=course, role="ST").values("user")
@classmethod
def get_assistants(cls,course):
def get_assistants(cls, course):
return CourseRole.objects.filter(course=course, role="AS").values("user")
@classmethod
def get_teachers(cls,course):
def get_teachers(cls, course):
return CourseRole.objects.filter(course=course, role="TE").values("user")
@classmethod
def add_student(cls,course, profiles):
def add_student(cls, course, profiles):
for profile in profiles:
CourseRole.make_role(course=course, user=profile, role="ST")
@classmethod
def add_teachers(cls,course, profiles):
def add_teachers(cls, course, profiles):
for profile in profiles:
CourseRole.make_role(course=course, user=profile, role="TE")
@classmethod
def add_assistants(cls,course, profiles):
def add_assistants(cls, course, profiles):
for profile in profiles:
CourseRole.make_role(course=course, user=profile, role="AS")
class CourseRole(models.Model):
course = models.OneToOneField(
course = models.ForeignKey(
Course,
verbose_name=_("course"),
on_delete=models.CASCADE,
@ -142,7 +140,7 @@ class CourseRole(models.Model):
class CourseResource(models.Model):
course = models.OneToOneField(
course = models.ForeignKey(
Course,
verbose_name=_("course"),
on_delete=models.CASCADE,
@ -165,15 +163,18 @@ class CourseResource(models.Model):
default=False,
)
def __str__(self):
return f"Resource - {self.pk}"
class CourseAssignment(models.Model):
course = models.OneToOneField(
course = models.ForeignKey(
Course,
verbose_name=_("course"),
on_delete=models.CASCADE,
db_index=True,
)
contest = models.OneToOneField(
contest = models.ForeignKey(
Contest,
verbose_name=_("contest"),
on_delete=models.CASCADE,

View file

@ -1,6 +1,22 @@
from django.db import models
from judge.models.course import Course
from django.views.generic import ListView
from django.forms import ModelForm
from django.contrib.auth.mixins import LoginRequiredMixin
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__ = [
"CourseList",
@ -10,22 +26,184 @@ __all__ = [
"CourseStudentResults",
"CourseEdit",
"CourseResourceDetailEdit",
"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):
def get_queryset(self):
return Course.objects.filter(is_open = "true").values()
return Course.objects.filter(is_open="true").values()
class CourseList(ListView):
model = Course
template_name = "course/list.html"
queryset = Course.objects.filter(is_public=True).filter(is_open=True)
def get_context_data(self, **kwargs):
context = super(CourseList,self).get_context_data(**kwargs)
available , enrolling = [] , []
context = super(CourseList, self).get_context_data(**kwargs)
available, enrolling = [], []
for course in Course.objects.filter(is_public=True).filter(is_open=True):
if Course.is_accessible_by(course, self.request.profile):
enrolling.append(course)
@ -34,4 +212,3 @@ class CourseList(ListView):
context["available"] = available
context["enrolling"] = enrolling
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>
<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>
<h1>Enrolling</h1>
{% for course in enrolling %}
<h2> {{ course }} </h2>
{% endfor %}
<ul>
{% for course in enrolling %}
<li>
<a href="/courses/{{ course.pk }}-{{ course.slug }}"> {{ course }} </a>
</li>
{% endfor %}
</ul>
<h1> Available </h1>
{% for course in available %}
<h2> {{ course }} </h2>
{% endfor %}
<ul>
{% for course in available %}
<li>
<a href="/courses/{{ course.pk }}-{{ course.slug }}"> {{ course }} </a>
</li>
{% endfor %}
</ul>
</body>
</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>