3 # Copyright (C) 2012 FinalsClub Foundation
4 """ Views for the KarmaNotes Courses app """
8 from django.core.exceptions import MultipleObjectsReturned
9 from django.core.exceptions import ObjectDoesNotExist
11 from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound
12 from django.views.generic import DetailView
13 from django.views.generic import TemplateView
14 from django.views.generic.edit import ProcessFormView
15 from django.views.generic.edit import ModelFormMixin
16 from django.views.generic.list import ListView
18 from karmaworld.apps.courses.forms import CourseForm
19 from karmaworld.apps.courses.models import Course
20 from karmaworld.apps.courses.models import School
21 from karmaworld.apps.notes.models import Note
24 class CourseListView(ListView, ModelFormMixin, ProcessFormView):
25 """ Simple ListView for the front page that includes the CourseForm """
27 form_class = CourseForm
30 def get_context_data(self, **kwargs):
31 """ Add the CourseForm to ListView context """
32 # get the original context
33 context = super(CourseListView, self).get_context_data(**kwargs)
34 # get the total number of notes
35 context['note_count'] = Note.objects.count()
36 # get the course form for the form at the bottom of the homepage
37 context['course_form'] = kwargs.get('course_form', CourseForm())
38 if context['course_form'].errors:
39 # if there was an error in the form
40 context['jump_to_form'] = True
42 # Include "Add Course" button in header
43 context['display_add_course'] = True
47 def get_success_url(self):
48 """ On form submission success, redirect to what url """
49 return u'/{school_slug}/{course_slug}'.format(
50 school_slug=self.object.school.slug,
51 course_slug=self.object.slug)
53 def form_invalid(self, form, **kwargs):
54 """ override form_invalid to populate object_list on redirect """
55 kwargs['is_error'] = True
56 kwargs['course_form'] = form
57 self.object_list = self.get_queryset()
58 kwargs['object_list'] = self.object_list
59 return self.render_to_response(self.get_context_data(**kwargs))
63 class CourseDetailView(DetailView):
64 """ Class-based view for the course html page """
66 context_object_name = u"course" # name passed to template
68 def get_context_data(self, **kwargs):
69 """ filter the Course.note_set to return no Drafts """
70 kwargs = super(CourseDetailView, self).get_context_data()
71 kwargs['note_set'] = self.object.note_set.filter(is_hidden=False)
73 # Include "Add Note" button in header
74 kwargs['display_add_note'] = True
79 class AboutView(TemplateView):
80 """ Display the About page with the Schools leaderboard """
81 template_name = "about.html"
83 def get_context_data(self, **kwargs):
84 """ get the list of schools with the most files for leaderboard """
85 context = { 'params': kwargs,
86 'schools': School.objects.filter(file_count__gt=0).order_by('-file_count')[:20] }
90 def school_list(request):
91 """ Return JSON describing Schools that match q query on name """
92 if not (request.method == 'POST' and request.is_ajax()
93 and request.POST.has_key('q')):
94 #return that the api call failed
95 return HttpResponseBadRequest(json.dumps({'status':'fail'}), mimetype="application/json")
97 # if an ajax get request with a 'q' name query
98 # get the schools as a id name dict,
99 _query = request.POST['q']
100 matching_school_aliases = list(School.objects.filter(alias__icontains=_query))
101 matching_school_names = list(School.objects.filter(name__icontains=_query)[:20])
102 _schools = matching_school_aliases[:2] + matching_school_names
103 schools = [{'id': s.id, 'name': s.name} for s in _schools]
106 return HttpResponse(json.dumps({'status':'success', 'schools': schools}), mimetype="application/json")
109 def school_course_list(request):
110 """Return JSON describing courses we know of at the given school
111 that match the query """
112 if not (request.method == 'POST' and request.is_ajax()
113 and request.POST.has_key('q')
114 and request.POST.has_key('school_id')):
115 # return that the api call failed
116 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'query parameters missing'}),
117 mimetype="application/json")
119 _query = request.POST['q']
121 _school_id = int(request.POST['school_id'])
123 return HttpResponseBadRequest(json.dumps({'status': 'fail',
124 'message': 'could not convert school id to integer'}),
125 mimetype="application/json")
129 school = School.objects.get(id__exact=_school_id)
130 except (MultipleObjectsReturned, ObjectDoesNotExist):
131 return HttpResponseBadRequest(json.dumps({'status': 'fail',
132 'message': 'school id did not match exactly one school'}),
133 mimetype="application/json")
135 # Look up matching courses
136 _courses = Course.objects.filter(school__exact=school.id, name__icontains=_query)
137 courses = [{'name': c.name} for c in _courses]
140 return HttpResponse(json.dumps({'status':'success', 'courses': courses}),
141 mimetype="application/json")
144 def school_course_instructor_list(request):
145 """Return JSON describing instructors we know of at the given school
146 teaching the given course
147 that match the query """
148 if not(request.method == 'POST' and request.is_ajax()
149 and request.POST.has_key('q')
150 and request.POST.has_key('course_name')
151 and request.POST.has_key('school_id')):
152 # return that the api call failed
153 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'query parameters missing'}),
154 mimetype="application/json")
156 _query = request.POST['q']
157 _course_name = request.POST['course_name']
159 _school_id = int(request.POST['school_id'])
161 return HttpResponseBadRequest(json.dumps({'status': 'fail',
162 'message':'could not convert school id to integer'}),
163 mimetype="application/json")
167 school = School.objects.get(id__exact=_school_id)
168 except (MultipleObjectsReturned, ObjectDoesNotExist):
169 return HttpResponseBadRequest(json.dumps({'status': 'fail',
170 'message': 'school id did not match exactly one school'}),
171 mimetype="application/json")
173 # Look up matching courses
174 _courses = Course.objects.filter(school__exact=school.id,
175 name__exact=_course_name,
176 instructor_name__icontains=_query)
177 instructors = [{'name': c.instructor_name, 'url': c.get_absolute_url()} for c in _courses]
180 return HttpResponse(json.dumps({'status':'success', 'instructors': instructors}),
181 mimetype="application/json")
183 def ajaxIncrementBase(request, pk, field):
184 """Increment a note's field by one."""
185 if not (request.method == 'POST' and request.is_ajax()):
186 # return that the api call failed
187 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'must be a POST ajax request'}),
188 mimetype="application/json")
191 note = Course.objects.get(pk=pk)
192 note.__dict__[field] += 1
194 except ObjectDoesNotExist:
195 return HttpResponseNotFound(json.dumps({'status': 'fail', 'message': 'note id does not match a note'}),
196 mimetype="application/json")
198 return HttpResponse(status=204)
200 def flag_course(request, pk):
201 """Record that somebody has flagged a note."""
202 return ajaxIncrementBase(request, pk, 'flags')