3 # Copyright (C) 2012 FinalsClub Foundation
4 """ Views for the KarmaNotes Courses app """
8 from django.conf import settings
9 from django.core import serializers
10 from django.core.exceptions import MultipleObjectsReturned
11 from django.core.exceptions import ObjectDoesNotExist
13 from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound
14 from django.views.generic import DetailView
15 from django.views.generic import TemplateView
16 from django.views.generic.edit import ProcessFormView
17 from django.views.generic.edit import ModelFormMixin
18 from django.views.generic.list import ListView
20 from karmaworld.apps.courses.forms import CourseForm
21 from karmaworld.apps.courses.models import Course
22 from karmaworld.apps.courses.models import School
23 from karmaworld.apps.notes.models import Note
24 from karmaworld.apps.users.models import CourseKarmaEvent
25 from karmaworld.apps.notes.forms import FileUploadForm
26 from karmaworld.utils import ajax_increment, format_session_increment_field
29 USER_PROFILE_FLAGS_FIELD = 'flagged_courses'
32 class CourseListView(ListView, ModelFormMixin, ProcessFormView):
33 """ Simple ListView for the front page that includes the CourseForm """
35 form_class = CourseForm
38 def get_context_data(self, **kwargs):
39 """ Add the CourseForm to ListView context """
40 # get the original context
41 context = super(CourseListView, self).get_context_data(**kwargs)
42 # get the total number of notes
43 context['note_count'] = Note.objects.count()
44 # get the course form for the form at the bottom of the homepage
45 context['course_form'] = kwargs.get('course_form', CourseForm())
46 if context['course_form'].errors:
47 # if there was an error in the form
48 context['jump_to_form'] = True
50 # Include settings constants for honeypot
51 for key in ('HONEYPOT_FIELD_NAME', 'HONEYPOT_VALUE'):
52 context[key] = getattr(settings, key)
56 def get_success_url(self):
57 """ On success, return url based on urls.py definition. """
58 return self.object.get_absolute_url()
60 def form_invalid(self, form, **kwargs):
61 """ override form_invalid to populate object_list on redirect """
62 kwargs['is_error'] = True
63 kwargs['course_form'] = form
64 self.object_list = self.get_queryset()
65 kwargs['object_list'] = self.object_list
66 # hard code errors for the dynamically named honeypot field.
67 # This bit will not be necessary when the form is dynamically
68 # generated by Django rather than hard coded in the template.
69 kwargs['honeypot_errors'] = [x for x in form.errors['honeypot']] \
70 if 'honeypot' in form.errors else []
71 return self.render_to_response(self.get_context_data(**kwargs))
74 class CourseDetailView(DetailView):
75 """ Class-based view for the course html page """
77 context_object_name = u"course" # name passed to template
79 def get_context_data(self, **kwargs):
80 """ filter the Course.note_set to return no Drafts """
81 kwargs = super(CourseDetailView, self).get_context_data()
82 kwargs['note_set'] = self.object.note_set.filter(is_hidden=False)
84 # For the Filepicker Partial template
85 kwargs['file_upload_form'] = FileUploadForm()
87 if self.request.user.is_authenticated():
89 self.request.user.get_profile().flagged_courses.get(pk=self.object.pk)
90 kwargs['already_flagged'] = True
91 except ObjectDoesNotExist:
96 class AboutView(TemplateView):
97 """ Display the About page with the Schools leaderboard """
98 template_name = "about.html"
100 def get_context_data(self, **kwargs):
101 """ get the list of schools with the most files for leaderboard """
102 if 'schools' not in kwargs:
103 kwargs['schools'] = School.objects.filter(file_count__gt=0).order_by('-file_count')[:20]
107 def school_list(request):
108 """ Return JSON describing Schools that match q query on name """
109 if not (request.method == 'POST' and request.is_ajax()
110 and request.POST.has_key('q')):
111 #return that the api call failed
112 return HttpResponseBadRequest(json.dumps({'status':'fail'}), mimetype="application/json")
114 # if an ajax get request with a 'q' name query
115 # get the schools as a id name dict,
116 _query = request.POST['q']
117 matching_school_aliases = list(School.objects.filter(alias__icontains=_query))
118 matching_school_names = sorted(list(School.objects.filter(name__icontains=_query)[:20]),key=lambda o:len(o.name))
119 _schools = matching_school_aliases[:2] + matching_school_names
120 schools = [{'id': s.id, 'name': s.name} for s in _schools]
123 return HttpResponse(json.dumps({'status':'success', 'schools': schools}), mimetype="application/json")
126 def school_course_list(request):
127 """Return JSON describing courses we know of at the given school
128 that match the query """
129 if not (request.method == 'POST' and request.is_ajax()
130 and request.POST.has_key('q')
131 and request.POST.has_key('school_id')):
132 # return that the api call failed
133 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'query parameters missing'}),
134 mimetype="application/json")
136 _query = request.POST['q']
138 _school_id = int(request.POST['school_id'])
140 return HttpResponseBadRequest(json.dumps({'status': 'fail',
141 'message': 'could not convert school id to integer'}),
142 mimetype="application/json")
146 school = School.objects.get(id__exact=_school_id)
147 except (MultipleObjectsReturned, ObjectDoesNotExist):
148 return HttpResponseBadRequest(json.dumps({'status': 'fail',
149 'message': 'school id did not match exactly one school'}),
150 mimetype="application/json")
152 # Look up matching courses
153 _courses = Course.objects.filter(school__exact=school.id, name__icontains=_query)
154 courses = [{'name': c.name} for c in _courses]
157 return HttpResponse(json.dumps({'status':'success', 'courses': courses}),
158 mimetype="application/json")
161 def school_course_instructor_list(request):
162 """Return JSON describing instructors we know of at the given school
163 teaching the given course
164 that match the query """
165 if not(request.method == 'POST' and request.is_ajax()
166 and request.POST.has_key('q')
167 and request.POST.has_key('course_name')
168 and request.POST.has_key('school_id')):
169 # return that the api call failed
170 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'query parameters missing'}),
171 mimetype="application/json")
173 _query = request.POST['q']
174 _course_name = request.POST['course_name']
176 _school_id = int(request.POST['school_id'])
178 return HttpResponseBadRequest(json.dumps({'status': 'fail',
179 'message':'could not convert school id to integer'}),
180 mimetype="application/json")
184 school = School.objects.get(id__exact=_school_id)
185 except (MultipleObjectsReturned, ObjectDoesNotExist):
186 return HttpResponseBadRequest(json.dumps({'status': 'fail',
187 'message': 'school id did not match exactly one school'}),
188 mimetype="application/json")
190 # Look up matching courses
191 _courses = Course.objects.filter(school__exact=school.id,
192 name__exact=_course_name,
193 instructor_name__icontains=_query)
194 instructors = [{'name': c.instructor_name, 'url': c.get_absolute_url()} for c in _courses]
197 return HttpResponse(json.dumps({'status':'success', 'instructors': instructors}),
198 mimetype="application/json")
201 def process_course_flag_events(request_user, course):
202 # Take a point away from person flagging this course
203 if request_user.is_authenticated():
204 CourseKarmaEvent.create_event(request_user, course, CourseKarmaEvent.GIVE_FLAG)
207 def flag_course(request, pk):
208 """Record that somebody has flagged a note."""
209 return ajax_increment(Course, request, pk, FLAG_FIELD, USER_PROFILE_FLAGS_FIELD, process_course_flag_events)
211 def edit_course(request, pk):
213 Saves the edited course content
215 if request.method == "POST" and request.is_ajax():
216 course = Course.objects.get(pk=pk)
217 original_name = course.name
218 course_form = CourseForm(request.POST or None, instance=course)
220 if course_form.is_valid():
223 course_json = serializers.serialize('json', [course,])
224 resp = json.loads(course_json)[0]
226 if (course.name != original_name):
228 resp['fields']['new_url'] = course.get_absolute_url()
230 return HttpResponse(json.dumps(resp), mimetype="application/json")
232 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Validation error',
233 'errors': course_form.errors}),
234 mimetype="application/json")
236 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Invalid request'}),
237 mimetype="application/json")