merging old commit with current master
[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 "Add Course" button in header
51         context['display_add_course'] = True
52
53         # Include settings constants for honeypot
54         for key in ('HONEYPOT_FIELD_NAME', 'HONEYPOT_VALUE'):
55             context[key] = getattr(settings, key)
56
57         return context
58
59     def get_success_url(self):
60         """ On success, return url based on urls.py definition. """
61         return self.object.get_absolute_url()
62
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))
75
76
77 class CourseDetailView(DetailView):
78     """ Class-based view for the course html page """
79     model = Course
80     context_object_name = u"course" # name passed to template
81
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)
86
87         # Include "Add Note" button in header
88         kwargs['display_add_note'] = True
89
90         # For the Filepicker Partial template
91         kwargs['file_upload_form'] = FileUploadForm()
92
93         if self.request.user.is_authenticated():
94             try:
95                 self.request.user.get_profile().flagged_courses.get(pk=self.object.pk)
96                 kwargs['already_flagged'] = True
97             except ObjectDoesNotExist:
98                 pass
99
100         return kwargs
101
102
103 class AboutView(TemplateView):
104     """ Display the About page with the Schools leaderboard """
105     template_name = "about.html"
106
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]
111         return kwargs
112
113
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")
120
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]
128
129     # return as json
130     return HttpResponse(json.dumps({'status':'success', 'schools': schools}), mimetype="application/json")
131
132
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")
142
143     _query = request.POST['q']
144     try:
145       _school_id = int(request.POST['school_id'])
146     except:
147       return HttpResponseBadRequest(json.dumps({'status': 'fail',
148                                               'message': 'could not convert school id to integer'}),
149                                   mimetype="application/json")
150
151     # Look up the school
152     try:
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")
158
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]
162
163     # return as json
164     return HttpResponse(json.dumps({'status':'success', 'courses': courses}),
165                         mimetype="application/json")
166
167
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")
179
180     _query = request.POST['q']
181     _course_name = request.POST['course_name']
182     try:
183       _school_id = int(request.POST['school_id'])
184     except:
185       return HttpResponseBadRequest(json.dumps({'status': 'fail',
186                                               'message':'could not convert school id to integer'}),
187                                   mimetype="application/json")
188
189     # Look up the school
190     try:
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")
196
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]
202
203     # return as json
204     return HttpResponse(json.dumps({'status':'success', 'instructors': instructors}),
205                         mimetype="application/json")
206
207
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)
212
213
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)
217
218
219 def edit_course(request, pk):
220     """
221     Saves the edited course content
222     """
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)
227
228         if course_form.is_valid():
229             course_form.save()
230
231             course_json = serializers.serialize('json', [course,])
232             resp = json.loads(course_json)[0]
233
234             if (course.name != original_name):
235                 course.set_slug()
236                 resp['fields']['new_url'] = course.get_absolute_url()
237
238             return HttpResponse(json.dumps(resp), mimetype="application/json")
239         else:
240             return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Validation error',
241                                           'errors': course_form.errors}),
242                                           mimetype="application/json")
243     else:
244         return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Invalid request'}),
245                                       mimetype="application/json")