Skip to content

Commit 3b5829f

Browse files
committedFeb 4, 2016
Improve course lists
1 parent 47abd8d commit 3b5829f

File tree

11 files changed

+98
-118
lines changed

11 files changed

+98
-118
lines changed
 

‎course/models.py

+34-6
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,35 @@ class CourseInstanceManager(models.Manager):
6767
Helpers in CourseInstance.objects
6868
"""
6969

70-
def get_active(self, user=None):
71-
qs = self.filter(ending_time__gte=timezone.now())
70+
def get_queryset(self):
71+
return super().get_queryset().select_related('course').order_by('-starting_time')
72+
73+
def get_enrolled(self, user=None, end_after=None):
74+
if not user or not user.is_authenticated():
75+
return self.none()
76+
qs = self.filter(visible_to_students=True, students=user.userprofile)
77+
if end_after:
78+
qs.exclude(ending_time__gte=end_after)
79+
return qs
80+
81+
def get_on_staff(self, user=None, end_after=None):
82+
if not user or not user.is_authenticated():
83+
return self.none()
84+
qs = self.filter(Q(assistants=user.userprofile) |
85+
Q(course__teachers=user.userprofile)).distinct()
86+
if end_after:
87+
qs.exclude(ending_time__gte=end_after)
88+
return qs
89+
90+
def get_visible(self, user=None):
7291
if not user or not user.is_authenticated():
73-
qs = qs.filter(visible_to_students=True)
74-
elif not user.is_superuser:
75-
qs = qs.filter(Q(visible_to_students=True)
92+
return self.filter(visible_to_students=True)
93+
if not user.is_superuser:
94+
return self.filter(Q(visible_to_students=True)
7695
| Q(assistants=user.userprofile)
7796
| Q(course__teachers=user.userprofile)
7897
).distinct()
79-
return qs
98+
return self.all()
8099

81100

82101
def build_upload_dir(instance, filename):
@@ -264,6 +283,13 @@ def trigger(self, data):
264283
self.hook_type, self.hook_url, self.course_instance)
265284

266285

286+
class CourseModuleManager(models.Manager):
287+
288+
def get_queryset(self):
289+
return super().get_queryset().select_related(
290+
'course_instance', 'course_instance__course')
291+
292+
267293
class CourseModule(models.Model):
268294
"""
269295
CourseModule objects connect chapters and learning objects to logical sets
@@ -301,6 +327,8 @@ class CourseModule(models.Model):
301327
late_submission_penalty = PercentField(default=0.5,
302328
help_text=_("Multiplier of points to reduce, as decimal. 0.1 = 10%"))
303329

330+
objects = CourseModuleManager()
331+
304332
class Meta:
305333
unique_together = ("course_instance", "url")
306334
ordering = ['order', 'closing_time', 'id']

‎course/templates/course/archive.html

+26-44
Original file line numberDiff line numberDiff line change
@@ -7,50 +7,32 @@
77

88
{% block content %}
99
<div class="row">
10-
<div class="col-md-8">
11-
12-
<div class="page-header">
13-
<h1>{% trans "Course archive" %}
14-
</div>
15-
16-
<ul id="course-list"></ul>
17-
18-
</div>
19-
<div class="col-md-4">
20-
<div class="alert alert-info">
21-
{% blocktrans %}
22-
<strong>Note:</strong>
23-
To demonstrate functionality of the system,
24-
the list of courses and course instances is fetched
25-
from the API. And built using JavaScript.
26-
{% endblocktrans %}
27-
</div>
10+
<div class="col-md-8">
11+
<div class="page-header">
12+
<h1>{% trans "Course archive" %}
2813
</div>
14+
<ul>
15+
{% for instance in instances %}
16+
<li>
17+
<h4>
18+
<a href="{{ instance|url }}">{{ instance }}</a>
19+
<small>
20+
<br />{{ instance.starting_time|date:"SHORT_DATE_FORMAT" }}
21+
&ndash; {{ instance.ending_time|date:"SHORT_DATE_FORMAT" }}
22+
{% if not instance.visible_to_students %}
23+
<span class="label label-danger">{% trans "Hidden from students" %}</span>
24+
{% endif %}
25+
{% if instance.enrollment_audience == 1 or instance.enrollment_audience == 3 %}
26+
<span class="label label-success">{{ internal_user_label|safe }}</span>
27+
{% endif %}
28+
{% if instance.enrollment_audience == 2 or instance.enrollment_audience == 3 %}
29+
<span class="label label-info">{{ external_user_label|safe }}</span>
30+
{% endif %}
31+
</small>
32+
</h4>
33+
</li>
34+
{% endfor %}
35+
</ul>
36+
</div>
2937
</div>
3038
{% endblock %}
31-
32-
{% block scripts %}
33-
<script type="text/javascript">
34-
$(function(){
35-
$.getJSON("/api/v1/course/", function(courseResponse) {
36-
$.each(courseResponse.objects, function(i, course) {
37-
var $courseLI = $("<li />").attr('id', 'course'+course.id).appendTo("#course-list");
38-
var $courseHeading = $("<h3 />").text(course.code + " " + course.name).appendTo($courseLI);
39-
$instanceUL = $("<ul />").appendTo($courseLI);
40-
$.each(course.instances, function(j, courseInstance) {
41-
$.getJSON(courseInstance, function(instanceResponse) {
42-
var $instanceLI = $("<li />").appendTo($("#course" + course.id + " > ul"));
43-
var $instanceLink = $("<a />")
44-
.text(instanceResponse.instance_name)
45-
.attr("href", instanceResponse.browser_url)
46-
.appendTo($instanceLI);
47-
if (instanceResponse.is_open) {
48-
$instanceLI.append(" <span class='label label-success'>open</span>");
49-
}
50-
});
51-
});
52-
});
53-
});
54-
});
55-
</script>
56-
{% endblock %}

‎course/templates/course/course.html

-34
This file was deleted.

‎course/templates/course/index.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ <h3>
3636
{% endif %}
3737
<br />
3838
<!--{{ instance.instance_name }}<br />-->
39-
{{ instance.starting_time|date:"N j, Y" }} &ndash;
40-
{{ instance.ending_time|date:"N j, Y" }}
39+
{{ instance.starting_time|date:"SHORT_DATE_FORMAT" }} &ndash;
40+
{{ instance.ending_time|date:"SHORT_DATE_FORMAT" }}
4141
<br />
4242
{% if instance.enrollment_audience == 1 or instance.enrollment_audience == 3 %}
4343
<span class="label label-success">{{ internal_user_label|safe }}</span>

‎course/templatetags/course.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
from datetime import timedelta
12
from django import template
23
from django.conf import settings
4+
from django.utils import timezone
35

46
from course.models import CourseInstance
57

@@ -9,7 +11,12 @@
911

1012
@register.inclusion_tag("course/_course_dropdown_menu.html", takes_context=True)
1113
def course_menu(context):
12-
return { "instances": CourseInstance.objects.get_active(context["user"]) }
14+
if "course_menu" not in context:
15+
six_months_before = timezone.now() - timedelta(days=180)
16+
context["course_menu"] = \
17+
list(CourseInstance.objects.get_enrolled(context["user"], six_months_before)) + \
18+
list(CourseInstance.objects.get_on_staff(context["user"], six_months_before))
19+
return { "instances": context["course_menu"] }
1320

1421

1522
@register.filter

‎course/tests.py

+7-15
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ def test_course_instance_open(self):
144144
self.assertFalse(self.future_course_instance.is_open())
145145

146146
def test_course_url(self):
147-
self.assertEqual("/Course-Url/", self.course.get_absolute_url())
148147
self.assertEqual("/Course-Url/T-00.1000_d1/", self.current_course_instance.get_absolute_url())
149148
self.assertEqual("/Course-Url/T-00.1000_hidden/", self.hidden_course_instance.get_absolute_url())
150149

@@ -220,19 +219,19 @@ def test_course_instance_visibility(self):
220219
self.assertTrue(self.current_course_instance.is_visible_to(self.superuser))
221220
self.assertTrue(self.hidden_course_instance.is_visible_to(self.superuser))
222221

223-
def test_course_instance_get_active(self):
224-
open_course_instances = CourseInstance.objects.get_active()
225-
self.assertEqual(2, len(open_course_instances))
222+
def test_course_instance_get_visible(self):
223+
open_course_instances = CourseInstance.objects.get_visible()
224+
self.assertEqual(3, len(open_course_instances))
226225
self.assertTrue(self.current_course_instance in open_course_instances)
227226
self.assertTrue(self.future_course_instance in open_course_instances)
228227

229-
open_course_instances = CourseInstance.objects.get_active(self.user)
230-
self.assertEqual(2, len(open_course_instances))
228+
open_course_instances = CourseInstance.objects.get_visible(self.user)
229+
self.assertEqual(3, len(open_course_instances))
231230
self.assertTrue(self.current_course_instance in open_course_instances)
232231
self.assertTrue(self.future_course_instance in open_course_instances)
233232

234-
open_course_instances = CourseInstance.objects.get_active(self.superuser)
235-
self.assertEqual(3, len(open_course_instances))
233+
open_course_instances = CourseInstance.objects.get_visible(self.superuser)
234+
self.assertEqual(4, len(open_course_instances))
236235
self.assertTrue(self.current_course_instance in open_course_instances)
237236
self.assertTrue(self.future_course_instance in open_course_instances)
238237
self.assertTrue(self.hidden_course_instance in open_course_instances)
@@ -265,10 +264,6 @@ def test_course_module_after_open(self):
265264
def test_course_views(self):
266265
response = self.client.get('/no_course/test', follow=True)
267266
self.assertEqual(response.status_code, 404)
268-
response = self.client.get(self.course.get_absolute_url(), follow=True)
269-
self.assertTrue(response.redirect_chain)
270-
self.assertEqual(response.status_code, 200)
271-
self.assertTemplateUsed(response, 'userprofile/login.html')
272267
response = self.client.get(self.current_course_instance.get_absolute_url(), follow=True)
273268
self.assertTrue(response.redirect_chain)
274269
self.assertEqual(response.status_code, 200)
@@ -277,9 +272,6 @@ def test_course_views(self):
277272
self.client.login(username="testUser", password="testPassword")
278273
response = self.client.get('/no_course/test', follow=True)
279274
self.assertEqual(response.status_code, 404)
280-
response = self.client.get(self.course.get_absolute_url(), follow=True)
281-
self.assertEqual(response.status_code, 200)
282-
self.assertTemplateUsed(response, 'course/course.html')
283275
response = self.client.get(self.current_course_instance.get_absolute_url(), follow=True)
284276
self.assertEqual(response.status_code, 200)
285277

‎course/urls.py

-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@
1919
url(r'^accounts/$',
2020
views.ProfileView.as_view(),
2121
name="profile"),
22-
url(COURSE_URL_PREFIX + r'$',
23-
views.CourseView.as_view(),
24-
name="course-instances"),
2522
url(INSTANCE_URL_PREFIX + r'$',
2623
views.InstanceView.as_view(),
2724
name="course"),

‎course/views.py

+15-10
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,33 @@ def get_common_objects(self):
3333
self.welcome_text = settings_text(self.request, 'WELCOME_TEXT')
3434
self.internal_user_label = settings_text(self.request, 'INTERNAL_USER_LABEL')
3535
self.external_user_label = settings_text(self.request, 'EXTERNAL_USER_LABEL')
36-
self.instances = CourseInstance.objects.get_active(self.request.user)
36+
self.instances = []
37+
prio2 = []
38+
treshold = timezone.now() - datetime.timedelta(days=10)
39+
for instance in CourseInstance.objects.get_visible(self.request.user)\
40+
.filter(ending_time__gte=timezone.now()):
41+
if instance.starting_time > treshold:
42+
self.instances += [instance]
43+
else:
44+
prio2 += [instance]
45+
self.instances += prio2[::-1]
3746
self.note("welcome_text", "internal_user_label", "external_user_label", "instances")
3847

3948

4049
class ArchiveView(UserProfileView):
4150
access_mode = ACCESS.ANONYMOUS
4251
template_name = "course/archive.html"
4352

44-
45-
class ProfileView(UserProfileView):
46-
template_name = "course/profile.html"
47-
48-
49-
class CourseView(CourseBaseView):
50-
template_name = "course/course.html"
51-
5253
def get_common_objects(self):
5354
super().get_common_objects()
54-
self.instances = self.course.instances.get_active(self.request.user)
55+
self.instances = CourseInstance.objects.get_visible(self.request.user)
5556
self.note("instances")
5657

5758

59+
class ProfileView(UserProfileView):
60+
template_name = "course/profile.html"
61+
62+
5863
class InstanceView(EnrollableViewMixin, BaseTemplateView):
5964
template_name = "course/toc.html"
6065

‎exercise/exercise_models.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
class LearningObjectManager(models.Manager):
3030

3131
def get_queryset(self):
32-
return super().get_queryset().defer('description', 'content', 'content_head')
32+
return super().get_queryset()\
33+
.defer('description', 'content', 'content_head')\
34+
.select_related('course_module', 'course_module__course_instance',
35+
'course_module__course_instance__course')
3336

3437
def find_enrollment_exercise(self, course_instance):
3538
return self.filter(

‎redirect_old_urls/tests.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def test_course(self):
5757
response = self.client.get('/course/Course-Url/', follow=True)
5858
self.assertTrue(response.redirect_chain)
5959
self.assertEqual(response.status_code, 200)
60-
self.assertTemplateUsed(response, 'course/course.html')
60+
self.assertTemplateUsed(response, 'course/toc.html')
6161
response = self.client.get('/another/course/Course-Url/')
6262
self.assertEqual(response.status_code, 404)
6363

‎redirect_old_urls/views.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
def course(request, course_url=None):
66
course = get_object_or_404(Course, url=course_url)
7-
return redirect(course.get_absolute_url(), permanent=True)
7+
return redirect(course.instances.first().get_absolute_url(), permanent=True)
88

99
def instance(request, course_url=None, instance_url=None):
1010
instance = get_object_or_404(CourseInstance, url=instance_url, course__url=course_url)

0 commit comments

Comments
 (0)
Please sign in to comment.