211471ed07cb95fb7cedd68daea005a018958628
[oweals/karmaworld.git] / karmaworld / apps / courses / views.py
1 #!/usr/bin/env python
2 # -*- coding:utf8 -*-
3 # Copyright (C) 2012  FinalsClub Foundation
4 """ Views for the KarmaNotes Courses app """
5
6 import json
7
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
12
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
19
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
27
28 FLAG_FIELD = 'flags'
29 USER_PROFILE_FLAGS_FIELD = 'flagged_courses'
30
31
32 class CourseListView(ListView, ModelFormMixin, ProcessFormView):
33     """ Simple ListView for the front page that includes the CourseForm """
34     model = Course
35     form_class = CourseForm
36     object = None
37
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
49
50         # Include settings constants for honeypot
51         for key in ('HONEYPOT_FIELD_NAME', 'HONEYPOT_VALUE'):
52             context[key] = getattr(settings, key)
53
54         return context
55
56     def get_success_url(self):
57         """ On success, return url based on urls.py definition. """
58         return self.object.get_absolute_url()
59
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))
72
73
74 class CourseDetailView(DetailView):
75     """ Class-based view for the course html page """
76     model = Course
77     context_object_name = u"course" # name passed to template
78
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)
83
84         # For the Filepicker Partial template
85         kwargs['file_upload_form'] = FileUploadForm()
86
87         if self.request.user.is_authenticated():
88             try:
89                 self.request.user.get_profile().flagged_courses.get(pk=self.object.pk)
90                 kwargs['already_flagged'] = True
91             except ObjectDoesNotExist:
92                 pass
93
94         return kwargs
95
96 class AboutView(TemplateView):
97     """ Display the About page with the Schools leaderboard """
98     template_name = "about.html"
99
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]
104         return kwargs
105
106
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")
113
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]
121
122     # return as json
123     return HttpResponse(json.dumps({'status':'success', 'schools': schools}), mimetype="application/json")
124
125
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")
135
136     _query = request.POST['q']
137     try:
138       _school_id = int(request.POST['school_id'])
139     except:
140       return HttpResponseBadRequest(json.dumps({'status': 'fail',
141                                               'message': 'could not convert school id to integer'}),
142                                   mimetype="application/json")
143
144     # Look up the school
145     try:
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")
151
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]
155
156     # return as json
157     return HttpResponse(json.dumps({'status':'success', 'courses': courses}),
158                         mimetype="application/json")
159
160
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")
172
173     _query = request.POST['q']
174     _course_name = request.POST['course_name']
175     try:
176       _school_id = int(request.POST['school_id'])
177     except:
178       return HttpResponseBadRequest(json.dumps({'status': 'fail',
179                                               'message':'could not convert school id to integer'}),
180                                   mimetype="application/json")
181
182     # Look up the school
183     try:
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")
189
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]
195
196     # return as json
197     return HttpResponse(json.dumps({'status':'success', 'instructors': instructors}),
198                         mimetype="application/json")
199
200
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)
205
206
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)
210
211 def edit_course(request, pk):
212     """
213     Saves the edited course content
214     """
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)
219
220         if course_form.is_valid():
221             course_form.save()
222
223             course_json = serializers.serialize('json', [course,])
224             resp = json.loads(course_json)[0]
225
226             if (course.name != original_name):
227                 course.set_slug()
228                 resp['fields']['new_url'] = course.get_absolute_url()
229
230             return HttpResponse(json.dumps(resp), mimetype="application/json")
231         else:
232             return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Validation error',
233                                           'errors': course_form.errors}),
234                                           mimetype="application/json")
235     else:
236         return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Invalid request'}),
237                                       mimetype="application/json")