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 "Add Course" button in header
51 context['display_add_course'] = True
53 # Include settings constants for honeypot
54 for key in ('HONEYPOT_FIELD_NAME', 'HONEYPOT_VALUE'):
55 context[key] = getattr(settings, key)
59 def get_success_url(self):
60 """ On success, return url based on urls.py definition. """
61 return self.object.get_absolute_url()
63 def form_invalid(self, form, **kwargs):
64 """ override form_invalid to populate object_list on redirect """
65 kwargs['is_error'] = True
66 kwargs['course_form'] = form
67 self.object_list = self.get_queryset()
68 kwargs['object_list'] = self.object_list
69 # hard code errors for the dynamically named honeypot field.
70 # This bit will not be necessary when the form is dynamically
71 # generated by Django rather than hard coded in the template.
72 kwargs['honeypot_errors'] = [x for x in form.errors['honeypot']] \
73 if 'honeypot' in form.errors else []
74 return self.render_to_response(self.get_context_data(**kwargs))
77 class CourseDetailView(DetailView):
78 """ Class-based view for the course html page """
80 context_object_name = u"course" # name passed to template
82 def get_context_data(self, **kwargs):
83 """ filter the Course.note_set to return no Drafts """
84 kwargs = super(CourseDetailView, self).get_context_data()
85 kwargs['note_set'] = self.object.note_set.filter(is_hidden=False)
87 # Include "Add Note" button in header
88 kwargs['display_add_note'] = True
90 # For the Filepicker Partial template
91 kwargs['file_upload_form'] = FileUploadForm()
93 if self.request.user.is_authenticated():
95 self.request.user.get_profile().flagged_courses.get(pk=self.object.pk)
96 kwargs['already_flagged'] = True
97 except ObjectDoesNotExist:
103 class AboutView(TemplateView):
104 """ Display the About page with the Schools leaderboard """
105 template_name = "about.html"
107 def get_context_data(self, **kwargs):
108 """ get the list of schools with the most files for leaderboard """
109 if 'schools' not in kwargs:
110 kwargs['schools'] = School.objects.filter(file_count__gt=0).order_by('-file_count')[:20]
114 def school_list(request):
115 """ Return JSON describing Schools that match q query on name """
116 if not (request.method == 'POST' and request.is_ajax()
117 and request.POST.has_key('q')):
118 #return that the api call failed
119 return HttpResponseBadRequest(json.dumps({'status':'fail'}), mimetype="application/json")
121 # if an ajax get request with a 'q' name query
122 # get the schools as a id name dict,
123 _query = request.POST['q']
124 matching_school_aliases = list(School.objects.filter(alias__icontains=_query))
125 matching_school_names = sorted(list(School.objects.filter(name__icontains=_query)[:20]),key=lambda o:len(o.name))
126 _schools = matching_school_aliases[:2] + matching_school_names
127 schools = [{'id': s.id, 'name': s.name} for s in _schools]
130 return HttpResponse(json.dumps({'status':'success', 'schools': schools}), mimetype="application/json")
133 def school_course_list(request):
134 """Return JSON describing courses we know of at the given school
135 that match the query """
136 if not (request.method == 'POST' and request.is_ajax()
137 and request.POST.has_key('q')
138 and request.POST.has_key('school_id')):
139 # return that the api call failed
140 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'query parameters missing'}),
141 mimetype="application/json")
143 _query = request.POST['q']
145 _school_id = int(request.POST['school_id'])
147 return HttpResponseBadRequest(json.dumps({'status': 'fail',
148 'message': 'could not convert school id to integer'}),
149 mimetype="application/json")
153 school = School.objects.get(id__exact=_school_id)
154 except (MultipleObjectsReturned, ObjectDoesNotExist):
155 return HttpResponseBadRequest(json.dumps({'status': 'fail',
156 'message': 'school id did not match exactly one school'}),
157 mimetype="application/json")
159 # Look up matching courses
160 _courses = Course.objects.filter(school__exact=school.id, name__icontains=_query)
161 courses = [{'name': c.name} for c in _courses]
164 return HttpResponse(json.dumps({'status':'success', 'courses': courses}),
165 mimetype="application/json")
168 def school_course_instructor_list(request):
169 """Return JSON describing instructors we know of at the given school
170 teaching the given course
171 that match the query """
172 if not(request.method == 'POST' and request.is_ajax()
173 and request.POST.has_key('q')
174 and request.POST.has_key('course_name')
175 and request.POST.has_key('school_id')):
176 # return that the api call failed
177 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'query parameters missing'}),
178 mimetype="application/json")
180 _query = request.POST['q']
181 _course_name = request.POST['course_name']
183 _school_id = int(request.POST['school_id'])
185 return HttpResponseBadRequest(json.dumps({'status': 'fail',
186 'message':'could not convert school id to integer'}),
187 mimetype="application/json")
191 school = School.objects.get(id__exact=_school_id)
192 except (MultipleObjectsReturned, ObjectDoesNotExist):
193 return HttpResponseBadRequest(json.dumps({'status': 'fail',
194 'message': 'school id did not match exactly one school'}),
195 mimetype="application/json")
197 # Look up matching courses
198 _courses = Course.objects.filter(school__exact=school.id,
199 name__exact=_course_name,
200 instructor_name__icontains=_query)
201 instructors = [{'name': c.instructor_name, 'url': c.get_absolute_url()} for c in _courses]
204 return HttpResponse(json.dumps({'status':'success', 'instructors': instructors}),
205 mimetype="application/json")
208 def process_course_flag_events(request_user, course):
209 # Take a point away from person flagging this course
210 if request_user.is_authenticated():
211 CourseKarmaEvent.create_event(request_user, course, CourseKarmaEvent.GIVE_FLAG)
214 def flag_course(request, pk):
215 """Record that somebody has flagged a note."""
216 return ajax_increment(Course, request, pk, FLAG_FIELD, USER_PROFILE_FLAGS_FIELD, process_course_flag_events)
219 def edit_course(request, pk):
221 Saves the edited course content
223 if request.method == "POST" and request.is_ajax():
224 course = Course.objects.get(pk=pk)
225 original_name = course.name
226 course_form = CourseForm(request.POST or None, instance=course)
228 if course_form.is_valid():
231 course_json = serializers.serialize('json', [course,])
232 resp = json.loads(course_json)[0]
234 if (course.name != original_name):
236 resp['fields']['new_url'] = course.get_absolute_url()
238 return HttpResponse(json.dumps(resp), mimetype="application/json")
240 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Validation error',
241 'errors': course_form.errors}),
242 mimetype="application/json")
244 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Invalid request'}),
245 mimetype="application/json")