Put keywords on a separate page
authorCharles Connell <charles@connells.org>
Tue, 22 Apr 2014 20:15:27 +0000 (16:15 -0400)
committerCharles Connell <charles@connells.org>
Tue, 22 Apr 2014 20:15:27 +0000 (16:15 -0400)
karmaworld/apps/notes/models.py
karmaworld/apps/notes/views.py
karmaworld/apps/quizzes/views.py
karmaworld/templates/notes/note_detail.html
karmaworld/urls.py

index 84caec8533ec0d4a0350ad5bc605b726b723e99f..df33bfc22d45059a41a1e08b8725944f4310d373 100644 (file)
@@ -332,6 +332,21 @@ class Note(Document):
             # return a url ending in id
             return reverse('note_detail', args=[self.course.school.slug, self.course.slug, self.id])
 
+    def get_absolute_keywords_url(self):
+        """ Resolve note url, use 'note' route and slug if slug
+            otherwise use note.id
+        """
+        if self.slug is not None:
+            # return a url ending in slug
+            if self.course.school:
+                return reverse('note_keywords', args=[self.course.school.slug, self.course.slug, self.slug])
+            else:
+                return reverse('note_keywords', args=[self.course.department.school.slug, self.course.slug, self.slug])
+        else:
+            # return a url ending in id
+            return reverse('note_keywords', args=[self.course.school.slug, self.course.slug, self.id])
+
+
     def filter_html(self, html):
         """
         Apply all sanitizing filters to HTML.
index da1eaa709b81c172981937fca40139507d6c2c1d..2fd1bc2cee8a70ef1d32a0c89de08cc022757f98 100644 (file)
@@ -32,76 +32,40 @@ FLAG_FIELD = 'flags'
 USER_PROFILE_FLAGS_FIELD = 'flagged_notes'
 
 
+def note_page_context_helper(note, request, context):
+    if note.is_pdf():
+        context['pdf_controls'] = True
+
+    if request.user.is_authenticated():
+        try:
+            request.user.get_profile().thanked_notes.get(pk=note.pk)
+            context['already_thanked'] = True
+        except ObjectDoesNotExist:
+            pass
+
+        try:
+            request.user.get_profile().flagged_notes.get(pk=note.pk)
+            context['already_flagged'] = True
+        except ObjectDoesNotExist:
+            pass
+
+
 class NoteDetailView(DetailView):
     """ Class-based view for the note html page """
     model = Note
     context_object_name = u"note" # name passed to template
-    keyword_form_class = formset_factory(KeywordForm)
-
-    def post(self, request, *args, **kwargs):
-        formset = self.keyword_form_class(request.POST)
-        if formset.is_valid():
-            self.keyword_form_valid(formset)
-            self.keyword_formset = self.keyword_form_class(initial=self.get_initial_keywords())
-            return super(NoteDetailView, self).get(request, *args, **kwargs)
-        else:
-            self.keyword_formset = formset
-            return super(NoteDetailView, self).get(request, *args, **kwargs)
-
-    def get(self, request, *args, **kwargs):
-        self.keyword_formset = self.keyword_form_class(initial=self.get_initial_keywords())
-        return super(NoteDetailView, self).get(request, *args, **kwargs)
 
     def get_context_data(self, **kwargs):
         """ Generate custom context for the page rendering a Note
             + if pdf, set the `pdf` flag
         """
 
-        kwargs['keyword_prototype_form'] = KeywordForm
-        kwargs['keyword_formset'] = self.keyword_formset
-        kwargs['keywords'] = Keyword.objects.filter(note=self.object)
-
-        if self.object.is_pdf():
-            kwargs['pdf_controls'] = True
+        kwargs['show_note_container'] = True
 
-        if self.request.user.is_authenticated():
-            try:
-                self.request.user.get_profile().thanked_notes.get(pk=self.object.pk)
-                kwargs['already_thanked'] = True
-            except ObjectDoesNotExist:
-                pass
-
-            try:
-                self.request.user.get_profile().flagged_notes.get(pk=self.object.pk)
-                kwargs['already_flagged'] = True
-            except ObjectDoesNotExist:
-                pass
+        note_page_context_helper(self.object, self.request, kwargs)
 
         return super(NoteDetailView, self).get_context_data(**kwargs)
 
-    def get_initial_keywords(self):
-        existing_keywords = self.get_object().keyword_set.order_by('id')
-        initial_data = [{'keyword': keyword.word, 'definition': keyword.definition, 'id': keyword.pk}
-                        for keyword in existing_keywords]
-        return initial_data
-
-    def keyword_form_valid(self, formset):
-        for form in formset:
-            word = form['keyword'].data
-            definition = form['definition'].data
-            id = form['id'].data
-            if word == '':
-                continue
-            try:
-                keyword_object = Keyword.objects.get(id=id)
-            except (ValueError, ObjectDoesNotExist):
-                keyword_object = Keyword()
-
-            keyword_object.note = self.get_object()
-            keyword_object.word = word
-            keyword_object.definition = definition
-            keyword_object.save()
-
 
 class NoteSaveView(FormView, SingleObjectMixin):
     """ Save a Note and then view the page, 
@@ -159,13 +123,70 @@ class NoteView(View):
         return view(request, *args, **kwargs)
 
     def post(self, request, *args, **kwargs):
-        if request.POST['action'] == 'tags-form':
-            view = NoteSaveView.as_view()
-        else:
-            view = NoteDetailView.as_view()
+        view = NoteSaveView.as_view()
         return view(request, *args, **kwargs)
 
 
+class NoteKeywordsView(FormView, SingleObjectMixin):
+    """ Class-based view for the note html page """
+    model = Note
+    context_object_name = u"note" # name passed to template
+    form_class = formset_factory(KeywordForm)
+    template_name = 'notes/note_detail.html'
+
+    def get_object(self, queryset=None):
+        return Note.objects.get(slug=self.kwargs['slug'])
+
+    def post(self, request, *args, **kwargs):
+        self.object = self.get_object()
+        formset = self.form_class(request.POST)
+        if formset.is_valid():
+            self.keyword_form_valid(formset)
+            self.keyword_formset = self.form_class(initial=self.get_initial_keywords())
+            return super(NoteKeywordsView, self).get(request, *args, **kwargs)
+        else:
+            self.keyword_formset = formset
+            return super(NoteKeywordsView, self).get(request, *args, **kwargs)
+
+    def get(self, request, *args, **kwargs):
+        self.object = self.get_object()
+        self.keyword_formset = self.form_class(initial=self.get_initial_keywords())
+        return super(NoteKeywordsView, self).get(request, *args, **kwargs)
+
+    def get_context_data(self, **kwargs):
+        kwargs['keyword_prototype_form'] = KeywordForm
+        kwargs['keyword_formset'] = self.keyword_formset
+        kwargs['keywords'] = Keyword.objects.filter(note=self.get_object())
+        kwargs['show_keywords'] = True
+
+        note_page_context_helper(self.get_object(), self.request, kwargs)
+
+        return super(NoteKeywordsView, self).get_context_data(**kwargs)
+
+    def get_initial_keywords(self):
+        existing_keywords = self.get_object().keyword_set.order_by('id')
+        initial_data = [{'keyword': keyword.word, 'definition': keyword.definition, 'id': keyword.pk}
+                        for keyword in existing_keywords]
+        return initial_data
+
+    def keyword_form_valid(self, formset):
+        for form in formset:
+            word = form['keyword'].data
+            definition = form['definition'].data
+            id = form['id'].data
+            if word == '':
+                continue
+            try:
+                keyword_object = Keyword.objects.get(id=id)
+            except (ValueError, ObjectDoesNotExist):
+                keyword_object = Keyword()
+
+            keyword_object.note = self.get_object()
+            keyword_object.word = word
+            keyword_object.definition = definition
+            keyword_object.save()
+
+
 class NoteSearchView(ListView):
     template_name = 'notes/search_results.html'
 
index 1e56f0f1ee83dd4a2adfacd1473f51c8fc2bb2c2..0379a07f7edfaa835a406f53669b14c4af09ae55 100644 (file)
@@ -160,11 +160,11 @@ def process_set_delete_keyword(request):
                                     mimetype="application/json")
 
 
-def set_delete_keyword(request):
+def set_delete_keyword_annotator(request):
     return ajax_base(request, process_set_delete_keyword, ('POST', 'PUT', 'DELETE'))
 
 
-def get_keywords(request):
+def get_keywords_annotator(request):
     annotation_uri = request.GET['uri']
 
     try:
@@ -188,3 +188,28 @@ def get_keywords(request):
         return HttpResponseNotFound(json.dumps({'status': 'fail', 'message': e.message}),
                                     mimetype="application/json")
 
+
+def get_keywords_datatables(request):
+    annotation_uri = request.GET['uri']
+
+    try:
+        keywords = Keyword.objects.filter(note_id=annotation_uri)
+        keywords_data = {
+            'total': len(keywords),
+            'rows': []
+        }
+        for keyword in keywords:
+            keyword_data = {
+                'quote': keyword.word,
+                'text': keyword.definition,
+                'ranges': json.loads(keyword.ranges),
+                'created': keyword.timestamp.isoformat(),
+            }
+            keywords_data['rows'].append(keyword_data)
+
+        return HttpResponse(json.dumps(keywords_data), mimetype='application/json')
+
+    except ObjectDoesNotExist, e:
+        return HttpResponseNotFound(json.dumps({'status': 'fail', 'message': e.message}),
+                                    mimetype="application/json")
+
index 19b4b285cda2a3a0472575f92f8a6b6c1bd1dc56..0c077190c90dd921208beb91394d5ba2f4cadedf 100644 (file)
@@ -65,7 +65,7 @@
             {{ note.course.school.name }}
           {% endif %}
           &nbsp;&nbsp;
-          <span style="display: inline;"><span class="show-for-large-up"><i class="fa fa-thumbs-up"></i> <span id="thank-number">{{ note.thanks }}</span> Thanks</span></span>
+          <span style="display: inline;"><span class="show-for-large-up"><i class="fa fa-thumbs-up"></i> <span id="thank-number">{{ note.thanks }}</span> Thank{{ note.thanks|pluralize }}</span></span>
         </div>
       </div>
 
 
     <div class="row">
       <div id="tabs" class="small-12 columns">
-        <dl class="tabs show-for-large-up" data-tab data-options="deep_linking: true; scroll_to_content: false">
-          <dd class="active"><a href="#note_container">Note</a></dd>
-          <dd id="keywords-tab-button"><a href="#keywords">Key Terms & Definitions</a></dd>
+        <dl class="tabs show-for-large-up">
+          <dd id="note-tab-button" class="{% if show_note_container %}active{% endif %}">
+            <a href="{{ note.get_absolute_url }}">Note</a>
+          </dd>
+          <dd id="keywords-tab-button" class="{% if show_keywords %}active{% endif %}">
+            <a href="{{ note.get_absolute_keywords_url }}">Key Terms & Definitions</a>
+          </dd>
         </dl>
-        <div id="tabs-content" class="tabs-content">
-          <div id="note_container" class="content active">
+        <div class="tabs-content">
+        </div>
+        {% if show_note_container %}
+          <div id="note_container" class="">
             {% if pdf_controls %}
               <div id="zoom-buttons" class="row show-for-medium-up">
                 <div id="outline-btn-wrapper" class="small-1 columns hide show-for-medium-up">
               </div><!-- /body_copy -->
             </div>
           </div><!-- /note_container -->
-
+        {% endif %}
+        {% if show_keywords %}
           <div id="keywords" class="content">
             <div class="row">
               <div class="small-12 columns">
                   {% endfor %}
                 </table>
 
-                <form id="keyword-form" action="{{ note.get_absolute_url }}#keywords" method="post" class="hide">
+                <form id="keyword-form" action="{{ note.get_absolute_keywords_url }}" method="post" class="hide">
                   {% csrf_token %}
                   {{ keyword_formset.management_form }}
                   <div class="hide" id="keyword-form-prototype">
                 </form>
               </div>
             </div>
-          </div>
-        </div>
+          </div> <!-- keywords -->
+        {% endif %}
       </div>
     </div>
 
index b56707574ce72f44289f28c642a07efad092ab1b..b1d0f632327ed3744a05acb8ea8c5a9b77bf7dcf 100644 (file)
@@ -14,11 +14,12 @@ from karmaworld.apps.courses.views import CourseDetailView
 from karmaworld.apps.courses.views import CourseListView
 from karmaworld.apps.courses.views import school_course_list
 from karmaworld.apps.courses.views import school_course_instructor_list
-from karmaworld.apps.notes.views import NoteView, thank_note, NoteSearchView, flag_note, downloaded_note, edit_note_tags
+from karmaworld.apps.notes.views import NoteView, thank_note, NoteSearchView, flag_note, downloaded_note, edit_note_tags, \
+    NoteKeywordsView
 from karmaworld.apps.moderation import moderator
 from karmaworld.apps.document_upload.views import save_fp_upload
-from karmaworld.apps.quizzes.views import QuizView, KeywordEditView, quiz_answer, get_keywords, \
-    set_delete_keyword
+from karmaworld.apps.quizzes.views import QuizView, KeywordEditView, quiz_answer, get_keywords_annotator, \
+    set_delete_keyword_annotator, get_keywords_datatables
 from karmaworld.apps.users.views import ProfileView
 
 from ajax_select import urls as ajax_select_urls
@@ -101,8 +102,9 @@ urlpatterns = patterns('',
     # Check if a quiz answer is correct
     url(r'^ajax/quiz/check/$', quiz_answer, name='quiz_answer'),
 
-    url(r'^ajax/annotations/annotations$', set_delete_keyword, name='set_keyword'),
-    url(r'^ajax/annotations/search/$', get_keywords, name='get_keywords'),
+    url(r'^ajax/annotations/annotations$', set_delete_keyword_annotator, name='set_keyword'),
+    url(r'^ajax/annotations/search/$', get_keywords_annotator, name='get_keywords'),
+
 
     # Valid url cases to the Note page
     # a: school/course/id
@@ -114,6 +116,8 @@ urlpatterns = patterns('',
     # note file by note.slug
     url(r'^note/' + SLUG.format('school_') + '/' + SLUG.format('course_') +'/'+ SLUG.format('') +'$',
         NoteView.as_view(), name='note_detail'),
+    url(r'^note/' + SLUG.format('school_') + '/' + SLUG.format('course_') +'/'+ SLUG.format('') +'/keywords/$',
+        NoteKeywordsView.as_view(), name='note_keywords'),
 
     # Quizzes
     url(r'^quiz/(?P<pk>[\d]+)/$',