3 # Copyright (C) 2012 FinalsClub Foundation
7 from django.contrib import messages
9 from django.core import serializers
10 from django.core.exceptions import ValidationError
11 from django.forms.formsets import formset_factory
12 from karmaworld.apps.courses.models import Course
13 from karmaworld.apps.notes.search import SearchIndex
14 from karmaworld.apps.quizzes.forms import KeywordForm
15 from karmaworld.apps.quizzes.models import Keyword
16 from karmaworld.apps.users.models import NoteKarmaEvent
17 from karmaworld.utils.ajax_utils import *
19 from django.http import HttpResponse, HttpResponseBadRequest
20 from django.views.generic import DetailView, ListView
21 from django.views.generic import FormView
22 from django.views.generic import View
23 from django.views.generic.detail import SingleObjectMixin
25 from karmaworld.apps.notes.models import Note
26 from karmaworld.apps.notes.forms import NoteForm, NoteDeleteForm
29 logger = logging.getLogger(__name__)
31 THANKS_FIELD = 'thanks'
32 USER_PROFILE_THANKS_FIELD = 'thanked_notes'
34 USER_PROFILE_FLAGS_FIELD = 'flagged_notes'
37 def note_page_context_helper(note, request, context):
39 if request.method == 'POST':
40 context['note_edit_form'] = NoteForm(request.POST)
42 tags_string = ','.join([str(tag) for tag in note.tags.all()])
43 context['note_edit_form'] = NoteForm(initial={'name': note.name,
46 context['note_delete_form'] = NoteDeleteForm(initial={'note': note.id})
49 context['pdf_controls'] = True
51 if request.user.is_authenticated():
53 request.user.get_profile().thanked_notes.get(pk=note.pk)
54 context['already_thanked'] = True
55 except ObjectDoesNotExist:
59 request.user.get_profile().flagged_notes.get(pk=note.pk)
60 context['already_flagged'] = True
61 except ObjectDoesNotExist:
65 class NoteDetailView(DetailView):
66 """ Class-based view for the note html page """
68 context_object_name = u"note" # name passed to template
70 def get_context_data(self, **kwargs):
71 """ Generate custom context for the page rendering a Note
72 + if pdf, set the `pdf` flag
75 kwargs['show_note_container'] = True
77 note_page_context_helper(self.object, self.request, kwargs)
79 return super(NoteDetailView, self).get_context_data(**kwargs)
82 class NoteSaveView(FormView, SingleObjectMixin):
83 """ Save a Note and then view the page,
84 behaves the same as NoteDetailView, except for saving the
89 template_name = 'notes/note_detail.html'
91 def post(self, request, *args, **kwargs):
92 self.object = self.get_object()
93 if self.object.user != request.user:
94 raise ValidationError("Only the owner of a note can edit it.")
95 return super(NoteSaveView, self).post(request, *args, **kwargs)
97 def get_success_url(self):
98 return self.object.get_absolute_url()
100 def get_context_data(self, **kwargs):
102 'object': self.get_object(),
104 return super(NoteSaveView, self).get_context_data(**context)
106 def form_valid(self, form):
107 """ Actions to take if the submitted form is valid
108 namely, saving the new data to the existing note object
110 if len(form.cleaned_data['name'].strip()) > 0:
111 self.object.name = form.cleaned_data['name']
112 # use *arg expansion to pass tags a list of tags
113 self.object.tags.set(*form.cleaned_data['tags'])
114 # User has submitted this form, so set the SHOW flag
115 self.object.is_hidden = False
117 return super(NoteSaveView, self).form_valid(form)
119 def form_invalid(self, form):
120 """ Do stuff when the form is invalid !!! TODO """
121 # TODO: implement def form_invalid for returning a form with input and error
122 print "running form_invalid"
127 class NoteView(View):
128 """ Notes superclass that wraps http methods """
130 def get(self, request, *args, **kwargs):
131 view = NoteDetailView.as_view()
132 return view(request, *args, **kwargs)
134 def post(self, request, *args, **kwargs):
135 view = NoteSaveView.as_view()
136 return view(request, *args, **kwargs)
139 class NoteDeleteView(FormView):
140 form_class = NoteDeleteForm
142 def form_valid(self, form):
143 self.note = Note.objects.get(id=form.cleaned_data['note'])
144 self.note.is_hidden = True
147 messages.success(self.request, 'The note "{0}" was deleted successfully.'.format(self.note.name))
149 return super(FormView, self).form_valid(form)
151 def get_success_url(self):
152 return self.note.course.get_absolute_url()
155 class NoteKeywordsView(FormView, SingleObjectMixin):
156 """ Class-based view for the note html page """
158 context_object_name = u"note" # name passed to template
159 form_class = formset_factory(KeywordForm)
160 template_name = 'notes/note_detail.html'
162 def get_object(self, queryset=None):
163 return Note.objects.get(slug=self.kwargs['slug'])
165 def post(self, request, *args, **kwargs):
166 self.object = self.get_object()
167 formset = self.form_class(request.POST)
168 if formset.is_valid():
169 self.keyword_form_valid(formset)
170 self.keyword_formset = self.form_class(initial=self.get_initial_keywords())
171 return super(NoteKeywordsView, self).get(request, *args, **kwargs)
173 self.keyword_formset = formset
174 return super(NoteKeywordsView, self).get(request, *args, **kwargs)
176 def get(self, request, *args, **kwargs):
177 self.object = self.get_object()
178 self.keyword_formset = self.form_class(initial=self.get_initial_keywords())
179 return super(NoteKeywordsView, self).get(request, *args, **kwargs)
181 def get_context_data(self, **kwargs):
182 kwargs['keyword_prototype_form'] = KeywordForm
183 kwargs['keyword_formset'] = self.keyword_formset
184 kwargs['keywords'] = Keyword.objects.filter(note=self.get_object())
185 kwargs['show_keywords'] = True
187 note_page_context_helper(self.get_object(), self.request, kwargs)
189 return super(NoteKeywordsView, self).get_context_data(**kwargs)
191 def get_initial_keywords(self):
192 existing_keywords = self.get_object().keyword_set.order_by('id')
193 initial_data = [{'keyword': keyword.word, 'definition': keyword.definition, 'id': keyword.pk}
194 for keyword in existing_keywords]
197 def keyword_form_valid(self, formset):
199 word = form['keyword'].data
200 definition = form['definition'].data
205 keyword_object = Keyword.objects.get(id=id)
206 except (ValueError, ObjectDoesNotExist):
207 keyword_object = Keyword()
209 keyword_object.note = self.get_object()
210 keyword_object.word = word
211 keyword_object.definition = definition
212 keyword_object.save()
215 class NoteSearchView(ListView):
216 template_name = 'notes/search_results.html'
218 def get_queryset(self):
219 if not 'query' in self.request.GET:
220 return Note.objects.none()
222 if 'page' in self.request.GET:
223 page = int(self.request.GET['page'])
228 index = SearchIndex()
230 if 'course_id' in self.request.GET:
231 raw_results = index.search(self.request.GET['query'],
232 self.request.GET['course_id'],
235 raw_results = index.search(self.request.GET['query'],
239 logger.error("Error with IndexDen:\n" + traceback.format_exc())
241 return Note.objects.none()
245 instances = Note.objects.in_bulk(raw_results.ordered_ids)
247 for id in raw_results.ordered_ids:
249 results.append((instances[id], raw_results.snippet_dict[id]))
250 self.has_more = raw_results.has_more
254 def get_context_data(self, **kwargs):
255 if 'query' in self.request.GET:
256 kwargs['query'] = self.request.GET['query']
258 if 'course_id' in self.request.GET:
259 kwargs['course'] = Course.objects.get(id=self.request.GET['course_id'])
262 kwargs['error'] = True
263 return super(NoteSearchView, self).get_context_data(**kwargs)
265 # If query returned more search results than could
266 # fit on one page, show "Next" button
268 kwargs['has_next'] = True
269 if 'page' in self.request.GET:
270 kwargs['next_page'] = int(self.request.GET['page']) + 1
272 kwargs['next_page'] = 1
274 # If the user is looking at a search result page
275 # that isn't the first one, show "Prev" button
276 if 'page' in self.request.GET and \
277 int(self.request.GET['page']) > 0:
278 kwargs['has_prev'] = True
279 kwargs['prev_page'] = int(self.request.GET['page']) - 1
281 return super(NoteSearchView, self).get_context_data(**kwargs)
284 def handle_edit_note(request):
285 course = Course.objects.get(pk=pk)
286 original_name = course.name
287 course_form = CourseForm(request.POST or None, instance=course)
289 if course_form.is_valid():
292 course_json = serializers.serialize('json', [course,])
293 resp = json.loads(course_json)[0]
295 if (course.name != original_name):
297 resp['fields']['new_url'] = course.get_absolute_url()
299 return HttpResponse(json.dumps(resp), mimetype="application/json")
301 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Validation error',
302 'errors': course_form.errors}),
303 mimetype="application/json")
305 def edit_note(request, pk):
307 Saves the edited note metadata
309 ajax_base(request, edit_note, ['POST'])
312 def process_note_thank_events(request_user, note):
313 # Give points to the person who uploaded this note
314 if note.user != request_user and note.user:
315 NoteKarmaEvent.create_event(note.user, note, NoteKarmaEvent.THANKS)
318 def thank_note(request, pk):
319 """Record that somebody has thanked a note."""
320 return ajax_increment(Note, request, pk, THANKS_FIELD, USER_PROFILE_THANKS_FIELD, process_note_thank_events)
323 def process_note_flag_events(request_user, note):
324 # Take a point away from person flagging this note
325 if request_user.is_authenticated():
326 NoteKarmaEvent.create_event(request_user, note, NoteKarmaEvent.GIVE_FLAG)
327 # If this is the 6th time this note has been flagged,
328 # punish the uploader
329 if note.flags == 6 and note.user:
330 NoteKarmaEvent.create_event(note.user, note, NoteKarmaEvent.GET_FLAGGED)
333 def flag_note(request, pk):
334 """Record that somebody has flagged a note."""
335 return ajax_increment(Note, request, pk, FLAG_FIELD, USER_PROFILE_FLAGS_FIELD, process_note_flag_events)
338 def process_downloaded_note(request_user, note):
339 """Record that somebody has downloaded a note"""
340 if request_user.is_authenticated() and request_user != note.user:
341 NoteKarmaEvent.create_event(request_user, note, NoteKarmaEvent.DOWNLOADED_NOTE)
342 if request_user.is_authenticated() and note.user:
343 NoteKarmaEvent.create_event(note.user, note, NoteKarmaEvent.HAD_NOTE_DOWNLOADED)
346 def downloaded_note(request, pk):
347 """Record that somebody has flagged a note."""
348 return ajax_pk_base(Note, request, pk, process_downloaded_note)
351 def edit_note_tags(request, pk):
353 Saves the posted string of tags
355 if request.method == "POST" and request.is_ajax() and request.user.is_authenticated() and request.user.get_profile().can_edit_items():
356 note = Note.objects.get(pk=pk)
357 note.tags.set(request.body)
359 note_json = serializers.serialize('json', [note,])
360 resp = json.loads(note_json)[0]
361 resp['fields']['tags'] = list(note.tags.names())
363 return HttpResponse(json.dumps(resp), mimetype="application/json")
365 return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Invalid request'}),
366 mimetype="application/json")