Remove NLP approach to finding keywords, start on quiz interface
authorCharles Connell <charles@connells.org>
Fri, 9 May 2014 14:55:41 +0000 (10:55 -0400)
committerCharles Connell <charles@connells.org>
Fri, 9 May 2014 14:55:41 +0000 (10:55 -0400)
17 files changed:
karmaworld/apps/notes/gdrive.py
karmaworld/apps/notes/models.py
karmaworld/apps/notes/views.py
karmaworld/apps/quizzes/admin.py
karmaworld/apps/quizzes/create_quiz.py [new file with mode: 0644]
karmaworld/apps/quizzes/find_keywords.py [deleted file]
karmaworld/apps/quizzes/models.py
karmaworld/assets/css/note_course_pages.css
karmaworld/assets/js/course-list.js
karmaworld/assets/js/note-detail.js
karmaworld/templates/courses/course_list.html
karmaworld/templates/notes/note_base.html [new file with mode: 0644]
karmaworld/templates/notes/note_detail.html
karmaworld/templates/notes/note_keywords.html [new file with mode: 0644]
karmaworld/templates/notes/note_quiz.html [new file with mode: 0644]
karmaworld/urls.py
reqs/common.txt

index 4f4df55fa3a58463e732ed82de52381a394dc453..7bb183f897797773f1baf88b4f965212192a8ab6 100644 (file)
@@ -9,7 +9,6 @@ from django.conf import settings
 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
 from karmaworld.apps.notes.models import UserUploadMapping
 from karmaworld.apps.notes.models import NoteMarkdown
-from karmaworld.apps.quizzes.find_keywords import find_keywords
 from karmaworld.apps.quizzes.models import Keyword
 from karmaworld.apps.users.models import NoteKarmaEvent
 import os
@@ -240,11 +239,6 @@ def convert_raw_document(raw_document, user=None):
         note_markdown = NoteMarkdown(note=note, markdown=markdown)
         note_markdown.save()
 
-    # Guess some keywords from the note text
-    keywords = find_keywords(note.text)
-    for word in keywords:
-        Keyword.objects.create(word=word, note=note)
-
     # If we know the user who uploaded this,
     # associate them with the note
     if user:
index 3f8e9f05821dad53c8d0b28a247d51468e7f75db..760b87ceaba40e8c9dc99c83663427dc71d9d3b8 100644 (file)
@@ -315,6 +315,19 @@ class Note(Document):
             # return a url ending in id
             return reverse('note_keywords', args=[self.course.school.slug, self.course.slug, self.id])
 
+    def get_absolute_quiz_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_quiz', args=[self.course.school.slug, self.course.slug, self.slug])
+            else:
+                return reverse('note_quiz', args=[self.course.department.school.slug, self.course.slug, self.slug])
+        else:
+            # return a url ending in id
+            return reverse('note_quiz', args=[self.course.school.slug, self.course.slug, self.id])
 
     def filter_html(self, html):
         """
index a0b3d8ac97198d770c4650713a984499a16eb2d3..a3c571decc23f76bfead18f6296f3e510576d90a 100644 (file)
@@ -11,6 +11,7 @@ from django.core.exceptions import ValidationError
 from django.forms.formsets import formset_factory
 from karmaworld.apps.courses.models import Course
 from karmaworld.apps.notes.search import SearchIndex
+from karmaworld.apps.quizzes.create_quiz import quiz_from_keywords
 from karmaworld.apps.quizzes.find_keywords import find_keywords
 from karmaworld.apps.quizzes.forms import KeywordForm
 from karmaworld.apps.quizzes.models import Keyword
@@ -18,7 +19,7 @@ from karmaworld.apps.users.models import NoteKarmaEvent
 from karmaworld.utils.ajax_utils import *
 
 from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
-from django.views.generic import DetailView, ListView
+from django.views.generic import DetailView, ListView, TemplateView
 from django.views.generic import FormView
 from django.views.generic import View
 from django.views.generic.detail import SingleObjectMixin
@@ -67,6 +68,7 @@ class NoteDetailView(DetailView):
     """ Class-based view for the note html page """
     model = Note
     context_object_name = u"note" # name passed to template
+    template_name = 'notes/note_base.html'
 
     def get_context_data(self, **kwargs):
         """ Generate custom context for the page rendering a Note
@@ -87,7 +89,7 @@ class NoteSaveView(FormView, SingleObjectMixin):
     """
     form_class = NoteForm
     model = Note
-    template_name = 'notes/note_detail.html'
+    template_name = 'notes/note_base.html'
 
     def post(self, request, *args, **kwargs):
         self.object = self.get_object()
@@ -158,7 +160,7 @@ class NoteKeywordsView(FormView, SingleObjectMixin):
     model = Note
     context_object_name = u"note" # name passed to template
     form_class = formset_factory(KeywordForm)
-    template_name = 'notes/note_detail.html'
+    template_name = 'notes/note_base.html'
 
     def get_object(self, queryset=None):
         return Note.objects.get(slug=self.kwargs['slug'])
@@ -216,6 +218,21 @@ class NoteKeywordsView(FormView, SingleObjectMixin):
             keyword_object.save()
 
 
+class NoteQuizView(TemplateView):
+    template_name = 'notes/note_base.html'
+
+    def get_context_data(self, **kwargs):
+        note = Note.objects.get(slug=self.kwargs['slug'])
+
+        note_page_context_helper(note, self.request, kwargs)
+
+        kwargs['note'] = note
+        kwargs['questions'] = quiz_from_keywords(note)
+        kwargs['show_quiz'] = True
+
+        return super(NoteQuizView, self).get_context_data(**kwargs)
+
+
 class NoteSearchView(ListView):
     template_name = 'notes/search_results.html'
 
index ea1b8b8577280ad56e2fe7eadc538242a31d8e45..de5a7afd78937babeeb5d4275c49d225800e3fcc 100644 (file)
@@ -2,45 +2,6 @@
 # -*- coding:utf8 -*-
 # Copyright (C) 2014  FinalsClub Foundation
 from django.contrib import admin
-from karmaworld.apps.quizzes.models import MultipleChoiceQuestion, FlashCardQuestion, MultipleChoiceOption, Quiz, \
-    TrueFalseQuestion, Keyword
-from nested_inlines.admin import NestedTabularInline, NestedModelAdmin, NestedStackedInline
+from karmaworld.apps.quizzes.models import Keyword
 
-
-class MultipleChoiceOptionInlineAdmin(NestedTabularInline):
-    model = MultipleChoiceOption
-
-
-class MultipleChoiceQuestionAdmin(NestedModelAdmin):
-    model = MultipleChoiceQuestion
-    inlines = [MultipleChoiceOptionInlineAdmin]
-    list_display = ('question_text', 'quiz')
-
-
-class MultipleChoiceQuestionInlineAdmin(NestedStackedInline):
-    model = MultipleChoiceQuestion
-    list_display = ('question_text', 'quiz')
-
-
-class FlashCardQuestionInlineAdmin(NestedStackedInline):
-    model = FlashCardQuestion
-    list_display = ('keyword_side', 'definition_side', 'quiz')
-
-
-class TrueFalseQuestionInlineAdmin(NestedStackedInline):
-    model = TrueFalseQuestion
-    list_display = ('question_text', 'quiz')
-
-
-class QuizAdmin(NestedModelAdmin):
-    search_fields = ['name', 'note__name']
-    list_display = ('name', 'note')
-    inlines = [MultipleChoiceQuestionInlineAdmin, TrueFalseQuestionInlineAdmin, FlashCardQuestionInlineAdmin]
-
-
-admin.site.register(Quiz, QuizAdmin)
-admin.site.register(MultipleChoiceQuestion, MultipleChoiceQuestionAdmin)
-admin.site.register(MultipleChoiceOption)
-admin.site.register(FlashCardQuestion)
-admin.site.register(TrueFalseQuestion)
 admin.site.register(Keyword)
diff --git a/karmaworld/apps/quizzes/create_quiz.py b/karmaworld/apps/quizzes/create_quiz.py
new file mode 100644 (file)
index 0000000..13e76e5
--- /dev/null
@@ -0,0 +1,147 @@
+import random
+from karmaworld.apps.quizzes.models import Keyword
+
+
+class BaseQuizQuestion(object):
+
+    def question_type(self):
+        return self.__class__.__name__
+
+
+class MultipleChoiceQuestion(BaseQuizQuestion):
+    def __init__(self, question_text, choices):
+        self.question_text = question_text
+        self.choices = choices
+
+    def __unicode__(self):
+        return "Multiple choice: {0}: {1}".format(self.question_text, ", ".join(map(str, self.choices)))
+
+    def __str__(self):
+        return unicode(self)
+
+    def __repr__(self):
+        return str(self)
+
+
+class MultipleChoiceOption(object):
+    def __init__(self, text, correct):
+        self.text = text
+        self.correct = correct
+
+    def __unicode__(self):
+        return self.text
+
+    def __str__(self):
+        return unicode(self)
+
+    def __repr__(self):
+        return str(self)
+
+
+class FlashCardQuestion(BaseQuizQuestion):
+    def __init__(self, keyword_side, definition_side):
+        self.keyword_side = keyword_side
+        self.definition_side = definition_side
+
+    def __unicode__(self):
+        return "Flash card: {0} / {1}".format(self.keyword_side, self.definition_side)
+
+    def __str__(self):
+        return unicode(self)
+
+    def __repr__(self):
+        return str(self)
+
+
+class TrueFalseQuestion(BaseQuizQuestion):
+    def __init__(self, question_text, true):
+        self.question_text = question_text
+        self.true = true
+
+    def __unicode__(self):
+        return "True or false: {0}".format(self.question_text)
+
+    def __str__(self):
+        return unicode(self)
+
+    def __repr__(self):
+        return str(self)
+
+
+KEYWORD_MULTIPLE_CHOICE = 1
+DEFINITION_MULTIPLE_CHOICE = 2
+KEYWORD_DEFINITION_TRUE_FALSE = 3
+GENERATED_QUESTION_TYPE = (
+    KEYWORD_MULTIPLE_CHOICE,
+    DEFINITION_MULTIPLE_CHOICE,
+    KEYWORD_DEFINITION_TRUE_FALSE,
+)
+
+MULTIPLE_CHOICE_CHOICES = 4
+
+
+def _create_keyword_multiple_choice(keyword, keywords):
+    choices = [MultipleChoiceOption(text=keyword.word, correct=True)]
+
+    for other_keyword in random.sample(keywords.exclude(id=keyword.id), MULTIPLE_CHOICE_CHOICES - 1):
+        choices.append(MultipleChoiceOption(
+                       text=other_keyword.word,
+                       correct=False))
+
+    question_text = 'Pick the keyword to match "{d}"'.format(d=keyword.definition)
+
+    return MultipleChoiceQuestion(question_text, choices)
+
+
+def _create_definition_multiple_choice(keyword, keywords):
+    choices = [MultipleChoiceOption(text=keyword.definition, correct=True)]
+
+    for other_keyword in random.sample(keywords.exclude(id=keyword.id), MULTIPLE_CHOICE_CHOICES - 1):
+        choices.append(MultipleChoiceOption(
+                       text=other_keyword.definition,
+                       correct=False))
+
+    question_text = 'Pick the definition to match "{w}"'.format(w=keyword.word)
+
+    return MultipleChoiceQuestion(question_text, choices)
+
+
+def _create_keyword_definition_true_false(keyword, keywords):
+    true = random.choice((True, False))
+
+    if true:
+        definition = keyword.definition
+    else:
+        other_keyword = random.choice(keywords.exclude(id=keyword.id))
+        definition = other_keyword.definition
+
+    question_text = 'The keyword "{w}" matches the definition "{d}"'. \
+        format(w=keyword.word, d=definition)
+
+    return TrueFalseQuestion(question_text, true)
+
+
+def _create_keyword_flashcard_blank(keyword):
+    return FlashCardQuestion(keyword.definition, keyword.word)
+
+
+def quiz_from_keywords(note):
+    keywords = Keyword.objects.filter(note=note)
+    questions = []
+
+    for keyword in keywords:
+        if keyword.word and keyword.definition:
+            question_type = random.choice(GENERATED_QUESTION_TYPE)
+
+            if question_type is KEYWORD_MULTIPLE_CHOICE:
+                questions.append(_create_keyword_multiple_choice(keyword, keywords))
+
+            elif question_type is DEFINITION_MULTIPLE_CHOICE:
+                questions.append(_create_definition_multiple_choice(keyword, keywords))
+
+            elif question_type is KEYWORD_DEFINITION_TRUE_FALSE:
+                questions.append(_create_keyword_definition_true_false(keyword, keywords))
+
+    return questions
+
+
diff --git a/karmaworld/apps/quizzes/find_keywords.py b/karmaworld/apps/quizzes/find_keywords.py
deleted file mode 100644 (file)
index ea6f340..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-#!/usr/bin/env python
-# -*- coding:utf8 -*-
-# Copyright (C) 2014  FinalsClub Foundation
-from __future__ import division
-from collections import defaultdict
-import nltk
-import itertools
-from operator import itemgetter
-from pygraph.classes.digraph import digraph
-from pygraph.algorithms.pagerank import pagerank
-from pygraph.classes.exceptions import AdditionError
-
-
-def _filter(tagged, tags=('NN', 'JJ', 'NNP')):
-    pos_filtered = [item[0] for item in tagged if item[1] in tags]
-    stopwords_filtered = [word.lower() for word in pos_filtered if not word.lower() in nltk.corpus.stopwords.words('english')]
-    remove_punc = [item.replace('.', '') for item in stopwords_filtered]
-    return remove_punc
-
-
-def _normalize(words):
-    lower = [word.lower() for word in words]
-    remove_punc = [item.replace('.', '') for item in lower]
-    return remove_punc
-
-
-def _unique_everseen(iterable, key=None):
-    "List unique elements, preserving order. Remember all elements ever seen."
-    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
-    # unique_everseen('ABBCcAD', str.lower) --> A B C D
-    seen = set()
-    seen_add = seen.add
-    if key is None:
-        for element in itertools.ifilterfalse(seen.__contains__, iterable):
-            seen_add(element)
-            yield element
-    else:
-        for element in iterable:
-            k = key(element)
-            if k not in seen:
-                seen_add(k)
-                yield element
-
-
-def _common_ngrams(normalized_words, top_ordered_keywords, n):
-    ngrams_in_top_keywords = set()
-    common_ngrams = []
-
-    for ngram_words in itertools.product(top_ordered_keywords, repeat=n):
-        target_ngram = list(ngram_words)
-        for i in range(len(normalized_words)):
-            ngram = normalized_words[i:i+n]
-            if target_ngram == ngram:
-                ngrams_in_top_keywords.add(tuple(target_ngram))
-
-    for words in ngrams_in_top_keywords:
-        words_usage_in_ngram = 0
-        individual_word_usage = defaultdict(lambda: 0)
-        for i in range(len(normalized_words)):
-            for word in words:
-                if normalized_words[i] == word:
-                    individual_word_usage[word] += 1
-            if normalized_words[i:i+n] == list(words):
-                words_usage_in_ngram += 1
-
-        for word in words:
-            ratio = words_usage_in_ngram / individual_word_usage[word]
-            if ratio > 0.5:
-                common_ngrams.append(words)
-                break
-
-    return common_ngrams
-
-
-def find_keywords(document, word_count=10):
-    """
-    Credit to https://gist.github.com/voidfiles/1646117
-    and http://acl.ldc.upenn.edu/acl2004/emnlp/pdf/Mihalcea.pdf
-    """
-    sentences = nltk.sent_tokenize(document)
-    candidate_words = []
-    all_words = []
-    for sentence in sentences:
-        words = nltk.word_tokenize(sentence)
-        all_words.extend(words)
-        tagged_words = nltk.pos_tag(words)
-        filtered_words = _filter(tagged_words)
-        candidate_words.extend(filtered_words)
-
-    unique_word_set = _unique_everseen(candidate_words)
-
-    gr = digraph()
-    gr.add_nodes(list(unique_word_set))
-
-    window_start = 0
-    window_end = 2
-
-    while 1:
-        window_words = candidate_words[window_start:window_end]
-        if len(window_words) == 2:
-            try:
-                gr.add_edge((window_words[0], window_words[1]))
-            except AdditionError:
-                pass
-        else:
-            break
-
-        window_start += 1
-        window_end += 1
-
-    calculated_page_rank = pagerank(gr)
-    di = sorted(calculated_page_rank.iteritems(), key=itemgetter(1), reverse=True)
-    all_ordered_keywords = [w[0] for w in di]
-    top_ordered_keywords = all_ordered_keywords[:word_count]
-
-    normalized_words = _normalize(all_words)
-
-    common_bigrams = _common_ngrams(normalized_words, top_ordered_keywords, 2)
-    common_trigrams = _common_ngrams(normalized_words, top_ordered_keywords, 3)
-    for words in common_bigrams + common_trigrams:
-        for word in words:
-            top_ordered_keywords.remove(word)
-        top_ordered_keywords.insert(0, ' '.join(words))
-
-    return top_ordered_keywords
index 77a86a535c1bae90ff71c4754b93c891e63627bf..b618209f88f99555645172e01763864c840b8d66 100644 (file)
@@ -21,98 +21,3 @@ class Keyword(models.Model):
     def __unicode__(self):
         return self.word
 
-
-class Quiz(models.Model):
-    name = models.CharField(max_length=512)
-    note = models.ForeignKey('notes.Note', blank=True, null=True)
-    timestamp = models.DateTimeField(default=datetime.datetime.utcnow)
-
-    class Meta:
-        verbose_name_plural = 'quizzes'
-
-    def __unicode__(self):
-        return self.name
-
-
-class BaseQuizQuestion(models.Model):
-    quiz = models.ForeignKey(Quiz)
-    timestamp = models.DateTimeField(default=datetime.datetime.utcnow)
-
-    explanation = models.CharField(max_length=2048, blank=True, null=True)
-
-    EASY = 'EASY'
-    MEDIUM = 'MEDIUM'
-    HARD = 'HARD'
-    DIFFICULTY_CHOICES = (
-        (EASY, 'Easy'),
-        (MEDIUM, 'Medium'),
-        (HARD, 'Hard'),
-    )
-
-    difficulty = models.CharField(max_length=50, choices=DIFFICULTY_CHOICES, blank=True, null=True)
-
-    UNDERSTAND = 'UNDERSTAND'
-    REMEMBER = 'REMEMBER'
-    ANALYZE = 'ANALYZE'
-    KNOWLEDGE = 'KNOWLEDGE'
-    CATEGORY_CHOICES = (
-        (UNDERSTAND, 'Understand'),
-        (REMEMBER, 'Remember'),
-        (ANALYZE, 'Analyze'),
-        (KNOWLEDGE, 'Knowledge'),
-    )
-
-    category = models.CharField(max_length=50, choices=CATEGORY_CHOICES, blank=True, null=True)
-
-
-    class Meta:
-        abstract = True
-
-
-class MultipleChoiceQuestion(BaseQuizQuestion):
-    question_text = models.CharField(max_length=2048)
-
-    class Meta:
-        verbose_name = 'Multiple choice question'
-
-    def __unicode__(self):
-        return self.question_text
-
-
-class MultipleChoiceOption(models.Model):
-    text = models.CharField(max_length=2048)
-    correct = models.BooleanField()
-    question = models.ForeignKey(MultipleChoiceQuestion, related_name="choices")
-
-    def __unicode__(self):
-        return self.text
-
-
-class FlashCardQuestion(BaseQuizQuestion):
-    keyword_side = models.CharField(max_length=2048, verbose_name='Keyword')
-    definition_side = models.CharField(max_length=2048, verbose_name='Definition')
-
-    class Meta:
-        verbose_name = 'Flash card question'
-
-    def __unicode__(self):
-        return self.keyword_side + ' / ' + self.definition_side
-
-
-class TrueFalseQuestion(BaseQuizQuestion):
-    text = models.CharField(max_length=2048)
-    true = models.BooleanField(verbose_name='True?')
-
-    class Meta:
-        verbose_name = 'True/False question'
-
-    def __unicode__(self):
-        return self.text
-
-ALL_QUESTION_CLASSES = (MultipleChoiceQuestion, FlashCardQuestion, TrueFalseQuestion)
-ALL_QUESTION_CLASSES_NAMES = {
-    MultipleChoiceQuestion.__name__: MultipleChoiceQuestion,
-    FlashCardQuestion.__name__: FlashCardQuestion,
-    TrueFalseQuestion.__name__: TrueFalseQuestion,
-}
-
index eca5bbd925ec28bc77e635d10be7a0cfb106988f..1dec4fda1849d75b9d167a32e5e14f697fa0f911 100644 (file)
@@ -133,11 +133,63 @@ button.add-note-btn {
   padding: 5px;
 }
 
-#keyword-intro {
+#keyword-intro,
+#quiz-intro {
   font-size: 0.8em;
 }
 
+#quiz-intro {
+  margin-bottom: 10px;
+}
+
 #note-category {
   margin-bottom: 10px;
 }
 
+.quiz-question {
+  margin-bottom: 20px;
+}
+
+.quiz-question-inner {
+  padding: 10px 0 10px 0;
+}
+
+.question-text {
+  margin: 0 0 10px 0;
+}
+
+.quiz-question input[type="radio"]:checked+label {
+  font-weight: bold;
+}
+
+.correct {
+  background-color: #e5ffdc;
+}
+
+.incorrect {
+  background-color: #ffdcdc;
+}
+
+.correct-label,
+.incorrect-label {
+  padding-left: 0;
+}
+
+.correct-label-inner,
+.incorrect-label-inner {
+  color: #ffffff;
+  font-weight: bold;
+  padding: 5px 10px 5px 10px;
+  font-size: 0.8em;
+  width: intrinsic;                    /* Safari/WebKit uses a non-standard name */
+  width: -moz-max-content;     /* Firefox/Gecko */
+}
+
+.correct-label-inner {
+  background-color: #4f7342;
+}
+
+.incorrect-label-inner {
+  background-color: #740b00;
+}
+
index e526ef30b5bc6d485e367cb341efdd7f606a4d2e..31d3c144f8d2ecee9f79fab4572acfe51c617682 100644 (file)
@@ -36,7 +36,7 @@ $(function() {
   });
 
   // wire up search box
-  $('input.search-courses').keyup(function() {
+  $('#search-courses').keyup(function() {
     dataTable.fnFilter($(this).val());
   });
 
index 81f2b96e394a1b4d3368bc177daa3f09e6ec3fea..1718ee0699aea42df5bf913bb6fe5359ca26e6fe 100644 (file)
@@ -292,4 +292,33 @@ function initNoteKeywordsPage() {
 
 }
 
+function markQuestionCorrect(question) {
+  question.find('.quiz-question').addClass('correct');
+  question.find('.quiz-question').removeClass('incorrect');
+  question.find('.correct-label').show();
+  question.find('.incorrect-label').hide();
+}
+
+function markQuestionIncorrect(question) {
+  question.find('.quiz-question').addClass('incorrect');
+  question.find('.quiz-question').removeClass('correct');
+  question.find('.incorrect-label').show();
+  question.find('.correct-label').hide();
+}
+
+function initQuizPage() {
+  $('#check-answers-button').click(function() {
+    $('.quiz-question-wrapper').each(function() {
+      var choice = $(this).find('input:checked');
+      if (choice.length) {
+        console.log(choice);
+        if (choice.data('correct') == true) {
+          markQuestionCorrect($(this));
+        } else {
+          markQuestionIncorrect($(this));
+        }
+      }
+    });
+  });
+}
 
index 96fd589f1454f18e57aff27d72a4657089487678..c33d71833d817f55efa57147a8d1de1f77e0a90e 100644 (file)
@@ -52,7 +52,7 @@
           <button id="add-course-btn" class="inline-button important museo700" data-reveal-id="add-course-form">Add a Course</button>
         </div>
         <div class="small-12 large-9 columns">
-          <span><input class="search adelle-sans" type="text" placeholder="Search Courses and Schools" />
+          <span><input id="search-courses" class="search adelle-sans" type="text" placeholder="Search Courses and Schools" />
           <i class="fa fa-search inline search-icon"></i></span>
         </div>
       </div>
diff --git a/karmaworld/templates/notes/note_base.html b/karmaworld/templates/notes/note_base.html
new file mode 100644 (file)
index 0000000..bb46f9d
--- /dev/null
@@ -0,0 +1,285 @@
+{% extends "base.html" %}
+{% load url from future %}
+{% load compress %}
+
+{% block title %}
+  {{ note.name }}
+{% endblock %}
+
+{% block pagestyle %}
+  {% compress css %}
+    <link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/note_course_pages.css">
+    <link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/annotator.min.css" />
+  {% endcompress %}
+{% endblock %}
+
+{% block pagescripts %}
+  <script type="text/javascript">
+    var note_id = {{ note.id }};
+    var note_thank_url = "{% url 'thank_note' note.id %}"
+    var note_flag_url = "{% url 'flag_note' note.id %}"
+    var edit_note_tags_url = "{% url 'edit_note_tags' note.id %}"
+    var note_downloaded_url = "{% url 'downloaded_note' note.id %}"
+    var note_contents_url = "{{ S3_URL }}{{ note.get_relative_s3_path }}"
+    var pdfControls = {% if pdf_controls %} true {% else %} false {% endif %};
+    var csrf_token = "{{ csrf_token }}";
+    var annotator_js_url = "{{ STATIC_URL }}js/annotator-full.min.js";
+    var annotator_css_url = "{{ STATIC_URL }}css/annotator.min.css";
+    var setup_ajax_url = "{{ STATIC_URL }}js/setup-ajax.js";
+    var note_edit_url = "{% url 'edit_note' note.id %}";
+    var user_authenticated = {% if user.is_authenticated %}true{% else %}false{% endif %};
+  </script>
+  {% compress js %}
+    <script src="{{ STATIC_URL }}js/setup-ajax.js"></script>
+    <script src="{{ STATIC_URL }}js/note-detail.js" ></script>
+    <script src="{{ STATIC_URL }}js/pxem.jQuery.js"></script>
+    <script src="{{ STATIC_URL }}js/marked.js" ></script>
+    <script src="{{ STATIC_URL }}js/annotator-full.min.js"></script>
+  {% endcompress %}
+  <script>
+    {% if show_note_container %}
+      $(function() {
+        $(document).foundation('joyride', 'start');
+        initNoteContentPage();
+      });
+    {% endif %}
+
+    {% if show_keywords %}
+      $(function() {
+        initNoteKeywordsPage();
+      });
+    {% endif %}
+
+    {% if show_quiz %}
+      $(function() {
+        initQuizPage();
+      });
+    {% endif %}
+  </script>
+{% endblock %}
+
+{% block raw_content %}
+  <section id="note_content">
+
+    <div class="return-to-course show-for-large-up">
+      <div class="row">
+        <div class="small-12 columns">
+          <a href="{{ note.course.get_absolute_url }}" class="inherit-color">
+            <i class="fa fa-angle-double-left"></i> See all notes for {{ note.course.name }}
+          </a>
+        </div>
+      </div>
+    </div>
+
+    <div id="note_header">
+
+      <div class="row header-byline">
+        <div class="small-12 columns">
+          <strong>Lecture note for {{ note.course.name }} </strong>
+          at
+          {% if note.course.department.school %}
+            {{ note.course.department.school.name }}
+          {% else %}
+            {{ 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> Thank{{ note.thanks|pluralize }}</span></span>
+        </div>
+      </div>
+
+      <div class="row museo700">
+        <div class="small-12 columns header-title-row">
+          <span class="header-title">{{ note.name }} </span>
+          <span style="display: inline;">
+            <span class="show-for-large-up">
+              {% if user.is_authenticated %}
+                {% if already_thanked %}
+                  <button id="thank-button-disabled" class="modify-button disabled opentip"
+                          data-ot="You've already thanked this note"
+                           {% include 'partial/opentip_settings.html' %}>
+                    <i class="fa fa-thumbs-up"></i> Thank Note</button>
+                {% else %}
+                  <button id="thank-button" class="modify-button"><i class="fa fa-thumbs-up"></i> Thank Note</button>
+                  <button id="thank-button-disabled" class="modify-button disabled opentip hide"
+                          data-ot="You've already thanked this note"
+                           {% include 'partial/opentip_settings.html' %}>
+                    <i class="fa fa-thumbs-up"></i> Thank Note</button>
+                {% endif %}
+              {% else %}
+                <button id="thank-button-disabled" class="modify-button disabled opentip"
+                          data-ot="Log in to thank this note"
+                           {% include 'partial/opentip_settings.html' %}>
+                    <i class="fa fa-thumbs-up"></i> Thank Note</button>
+              {% endif %}
+
+              {% if user.is_authenticated %}
+                {% if already_flagged %}
+                  <button id="flag-button-disabled" class="modify-button disabled opentip"
+                          data-ot="You've already reported this note"
+                           {% include 'partial/opentip_settings.html' %}>
+                    <i class="fa fa-flag"></i> Report Note</button>
+                {% else %}
+                  <button id="flag-button" class="modify-button"><i class="fa fa-flag"></i> Report Note</button>
+                  <button id="flag-button-disabled" class="modify-button disabled opentip hide"
+                          data-ot="You've already reported this note"
+                           {% include 'partial/opentip_settings.html' %}>
+                    <i class="fa fa-flag"></i> Report Note</button>
+                {% endif %}
+              {% else %}
+                <button id="flag-button-disabled" class="modify-button disabled opentip"
+                          data-ot="Log in to report this note"
+                           {% include 'partial/opentip_settings.html' %}>
+                    <i class="fa fa-flag"></i> Report Note</button>
+              {% endif %}
+
+              {% if user.is_authenticated %}
+                <a href="{{ note.get_fp_url }}">
+                  <button id="note-download-button" class="modify-button opentip"
+                          data-ot="It costs 2 karma points to download a note"
+                          {% include 'partial/opentip_settings.html' %}>
+                    <i class="fa fa-download"></i> Download Note</button></a>
+              {% else %}
+                <button id="note-download-button-disabled" class="modify-button disabled opentip"
+                          data-ot="Log in to download this note"
+                           {% include 'partial/opentip_settings.html' %}>
+                  <i class="fa fa-download"></i> Download Note</button>
+              {% endif %}
+
+              {% if user.get_profile.can_edit_items and note.user != user %}
+                <button id="edit-note-tags" class="modify-button" data-reveal-id="note-tag-dialog">
+                  <i class="fa fa-pencil-square-o"></i> Edit Tags
+                </button>
+              {% endif %}
+
+              {% if note.user == request.user %}
+                <button id="edit-button" data-reveal-id="note-edit-dialog" class="modify-button"> <i class="fa fa-edit"></i> Edit This Note</button>&nbsp;&nbsp;
+              {% endif %}
+
+              {% if note.license %}
+                {{ note.license.html|safe }} {% if note.upstream_link %}<a href="{{ note.upstream_link }}" target="_blank">{{ note.upstream_link|slice:":80" }}</a>{% endif %}
+              {% endif %}
+            </span>
+          </span>
+        </div>
+      </div>
+
+      {% if note.category %}
+      <div id="note-category" class="row show-for-large-up">
+        <div class="small-12 columns">
+          <em>{{ note.get_category_display }}</em>
+        </div>
+      </div>
+      {% endif %}
+
+      <div id="note-tags" class="row show-for-large-up">
+        <div class="small-12 columns">
+          <strong>Tags: </strong>
+          <span class="tags">
+            {% if note.tags.all %}
+              {% for tag in note.tags.all %}
+                <span class="tag-span">
+                  {{ tag.name }}{% if not forloop.last %}, {% endif %}
+                </span>
+              {% endfor %}
+            {% else %}
+              <em class="light">(none defined yet)</em>
+            {% endif %}
+          </span>
+        </div><!-- /note_tags -->
+      </div>
+
+      <div id="note-tag-dialog" class="reveal-modal" data-reveal>
+        <a class="close-reveal-modal">&#215;</a>
+        <div class="row">
+          <div class="small-12 columns">
+            <h3>Edit this note's tags</h3>
+            <input id="note_tags_input" type="text" value="{% for tag in note.tags.all %}{{ tag.name }}{% if not forloop.last %}, {% endif %}{% endfor %}">
+            <button id="save_note_tags" type="submit" value="tags-form"><i class="fa fa-save"></i> Save</button>
+          </div>
+        </div>
+      </div>
+
+      <div id="note-edit-dialog" class="reveal-modal" data-reveal>
+        <a class="close-reveal-modal">&#215;</a>
+        <div class="row">
+          <div class="small-8 columns">
+            <h3>Edit Your Note</h3>
+          </div>
+          <div class="small-4 columns text-right">
+            <form method="POST" action="{% url 'note_delete' %}">
+              {% csrf_token %}
+              {{ note_delete_form }}
+              <button id="delete-note-button" type="submit" class="scary"><i class="fa fa-trash-o"></i> Delete Note</button>
+            </form>
+          </div>
+        </div>
+        <div class="row">
+          <form method="POST" action="{{ note.get_absolute_url }}">
+            {% csrf_token %}
+            <div class="small-12 large-6 columns">
+              {% with note_edit_form.name as field %}
+                {{ field.errors|safe }}
+                <label for="{{ field.id_for_label }}">{{ field.label }}:</label>
+                {{ field }}
+                <p>{{ field.help_text }}</p>
+              {% endwith %}
+            </div>
+            <div class="small-12 large-6 columns">
+              {% with note_edit_form.tags as field %}
+                {{ field.errors|safe }}
+                <label for="{{ field.id_for_label }}">{{ field.label }}:</label>
+                {{ field }}
+                <p>{{ field.help_text }}</p>
+              {% endwith %}
+            </div>
+            <div class="small-12 columns text-center">
+              <button type="submit"><i class="fa fa-save"></i> Save</button>
+            </div>
+          </form>
+        </div>
+      </div>
+
+    </div><!-- /note header -->
+
+    <div class="row">
+      <div id="tabs" class="small-12 columns">
+        <dl class="tabs">
+          <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>
+          <dd id="quiz-tab-button" class="{% if show_quiz %}active{% endif %}">
+            <a href="{{ note.get_absolute_quiz_url }}">Quiz Questions</a>
+          </dd>
+        </dl>
+        <div class="tabs-content">
+        </div>
+        {% if show_note_container %}
+          {% include 'notes/note_detail.html' %}
+        {% endif %}
+        {% if show_keywords %}
+          {% include 'notes/note_keywords.html' %}
+        {% endif %}
+        {% if show_quiz %}
+          {% include 'notes/note_quiz.html' %}
+        {% endif %}
+      </div>
+    </div>
+
+  </section><!--/note_content-->
+
+  <ol class="joyride-list" data-joyride
+        data-options="cookie_monster: true; cookie_name: note_detail_joyride">
+    <li data-id="note-content-wrapper" data-text="Awesome!" data-options="tip_location: top">
+      <p>You can highlight important words or phrases in this note to add definitions for them.</p>
+    </li>
+    <li data-id="keywords-tab-button" data-text="Awesome!" data-options="tip_location: top">
+      <p>Keywords you define will appear here, and you can define new ones here too.</p>
+    </li>
+  </ol>
+
+{% endblock %}
+
+
index 7fd36a0f8db8a92596bb61e7f8714c3fd6d579bd..0e8a1259579e9152f90bf764e5c32894d631d615 100644 (file)
-{% extends "base.html" %}
-{% load url from future %}
-{% load compress %}
-
-{% block title %}
-  {{ note.name }}
-{% endblock %}
-
-{% block pagestyle %}
-  {% compress css %}
-    <link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/note_course_pages.css">
-    <link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/annotator.min.css" />
-  {% endcompress %}
-{% endblock %}
-
-{% block pagescripts %}
-  <script type="text/javascript">
-    var note_id = {{ note.id }};
-    var note_thank_url = "{% url 'thank_note' note.id %}"
-    var note_flag_url = "{% url 'flag_note' note.id %}"
-    var edit_note_tags_url = "{% url 'edit_note_tags' note.id %}"
-    var note_downloaded_url = "{% url 'downloaded_note' note.id %}"
-    var note_contents_url = "{{ S3_URL }}{{ note.get_relative_s3_path }}"
-    var pdfControls = {% if pdf_controls %} true {% else %} false {% endif %};
-    var csrf_token = "{{ csrf_token }}";
-    var annotator_js_url = "{{ STATIC_URL }}js/annotator-full.min.js";
-    var annotator_css_url = "{{ STATIC_URL }}css/annotator.min.css";
-    var setup_ajax_url = "{{ STATIC_URL }}js/setup-ajax.js";
-    var note_edit_url = "{% url 'edit_note' note.id %}";
-    var user_authenticated = {% if user.is_authenticated %}true{% else %}false{% endif %};
-  </script>
-  {% compress js %}
-    <script src="{{ STATIC_URL }}js/setup-ajax.js"></script>
-    <script src="{{ STATIC_URL }}js/note-detail.js" ></script>
-    <script src="{{ STATIC_URL }}js/pxem.jQuery.js"></script>
-    <script src="{{ STATIC_URL }}js/marked.js" ></script>
-    <script src="{{ STATIC_URL }}js/annotator-full.min.js"></script>
-  {% endcompress %}
-  <script>
-    {% if show_note_container %}
-      $(function() {
-        $(document).foundation('joyride', 'start');
-        initNoteContentPage();
-      });
-    {% endif %}
-
-    {% if show_keywords %}
-      $(function() {
-        initNoteKeywordsPage();
-      });
-    {% endif %}
-  </script>
-{% endblock %}
-
-{% block raw_content %}
-  <section id="note_content">
-
-    <div class="return-to-course show-for-large-up">
-      <div class="row">
-        <div class="small-12 columns">
-          <a href="{{ note.course.get_absolute_url }}" class="inherit-color">
-            <i class="fa fa-angle-double-left"></i> See all notes for {{ note.course.name }}
-          </a>
-        </div>
-      </div>
-    </div>
-
-    <div id="note_header">
-
-      <div class="row header-byline">
-        <div class="small-12 columns">
-          <strong>Lecture note for {{ note.course.name }} </strong>
-          at
-          {% if note.course.department.school %}
-            {{ note.course.department.school.name }}
-          {% else %}
-            {{ 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> Thank{{ note.thanks|pluralize }}</span></span>
-        </div>
-      </div>
-
-      <div class="row museo700">
-        <div class="small-12 columns header-title-row">
-          <span class="header-title">{{ note.name }} </span>
-          <span style="display: inline;">
-            <span class="show-for-large-up">
-              {% if user.is_authenticated %}
-                {% if already_thanked %}
-                  <button id="thank-button-disabled" class="modify-button disabled opentip"
-                          data-ot="You've already thanked this note"
-                           {% include 'partial/opentip_settings.html' %}>
-                    <i class="fa fa-thumbs-up"></i> Thank Note</button>
-                {% else %}
-                  <button id="thank-button" class="modify-button"><i class="fa fa-thumbs-up"></i> Thank Note</button>
-                  <button id="thank-button-disabled" class="modify-button disabled opentip hide"
-                          data-ot="You've already thanked this note"
-                           {% include 'partial/opentip_settings.html' %}>
-                    <i class="fa fa-thumbs-up"></i> Thank Note</button>
-                {% endif %}
-              {% else %}
-                <button id="thank-button-disabled" class="modify-button disabled opentip"
-                          data-ot="Log in to thank this note"
-                           {% include 'partial/opentip_settings.html' %}>
-                    <i class="fa fa-thumbs-up"></i> Thank Note</button>
-              {% endif %}
-
-              {% if user.is_authenticated %}
-                {% if already_flagged %}
-                  <button id="flag-button-disabled" class="modify-button disabled opentip"
-                          data-ot="You've already reported this note"
-                           {% include 'partial/opentip_settings.html' %}>
-                    <i class="fa fa-flag"></i> Report Note</button>
-                {% else %}
-                  <button id="flag-button" class="modify-button"><i class="fa fa-flag"></i> Report Note</button>
-                  <button id="flag-button-disabled" class="modify-button disabled opentip hide"
-                          data-ot="You've already reported this note"
-                           {% include 'partial/opentip_settings.html' %}>
-                    <i class="fa fa-flag"></i> Report Note</button>
-                {% endif %}
-              {% else %}
-                <button id="flag-button-disabled" class="modify-button disabled opentip"
-                          data-ot="Log in to report this note"
-                           {% include 'partial/opentip_settings.html' %}>
-                    <i class="fa fa-flag"></i> Report Note</button>
-              {% endif %}
-
-              {% if user.is_authenticated %}
-                <a href="{{ note.get_fp_url }}">
-                  <button id="note-download-button" class="modify-button opentip"
-                          data-ot="It costs 2 karma points to download a note"
-                          {% include 'partial/opentip_settings.html' %}>
-                    <i class="fa fa-download"></i> Download Note</button></a>
-              {% else %}
-                <button id="note-download-button-disabled" class="modify-button disabled opentip"
-                          data-ot="Log in to download this note"
-                           {% include 'partial/opentip_settings.html' %}>
-                  <i class="fa fa-download"></i> Download Note</button>
-              {% endif %}
-
-              {% if user.get_profile.can_edit_items and note.user != user %}
-                <button id="edit-note-tags" class="modify-button" data-reveal-id="note-tag-dialog">
-                  <i class="fa fa-pencil-square-o"></i> Edit Tags
-                </button>
-              {% endif %}
-
-              {% if note.user == request.user %}
-                <button id="edit-button" data-reveal-id="note-edit-dialog" class="modify-button"> <i class="fa fa-edit"></i> Edit This Note</button>&nbsp;&nbsp;
-              {% endif %}
-
-              {% if note.license %}
-                {{ note.license.html|safe }} {% if note.upstream_link %}<a href="{{ note.upstream_link }}" target="_blank">{{ note.upstream_link|slice:":80" }}</a>{% endif %}
-              {% endif %}
-            </span>
-          </span>
-        </div>
-      </div>
-
-      {% if note.category %}
-      <div id="note-category" class="row show-for-large-up">
-        <div class="small-12 columns">
-          <em>{{ note.get_category_display }}</em>
-        </div>
+<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">
+        <i id="outline-btn" class="zoom-button fa fa-bars fa-2x"></i>
       </div>
-      {% endif %}
-
-      <div id="note-tags" class="row show-for-large-up">
-        <div class="small-12 columns">
-          <strong>Tags: </strong>
-          <span class="tags">
-            {% if note.tags.all %}
-              {% for tag in note.tags.all %}
-                <span class="tag-span">
-                  {{ tag.name }}{% if not forloop.last %}, {% endif %}
-                </span>
-              {% endfor %}
-            {% else %}
-              <em class="light">(none defined yet)</em>
-            {% endif %}
-          </span>
-        </div><!-- /note_tags -->
+      <div class="small-4 columns">
+        <span>Jump to page:
+        <input id="scroll-to" type="text" style="width: 3em; display: inline" /></span>
       </div>
-
-      <div id="note-tag-dialog" class="reveal-modal" data-reveal>
-        <a class="close-reveal-modal">&#215;</a>
-        <div class="row">
-          <div class="small-12 columns">
-            <h3>Edit this note's tags</h3>
-            <input id="note_tags_input" type="text" value="{% for tag in note.tags.all %}{{ tag.name }}{% if not forloop.last %}, {% endif %}{% endfor %}">
-            <button id="save_note_tags" type="submit" value="tags-form"><i class="fa fa-save"></i> Save</button>
-          </div>
-        </div>
-      </div>
-
-      <div id="note-edit-dialog" class="reveal-modal" data-reveal>
-        <a class="close-reveal-modal">&#215;</a>
-        <div class="row">
-          <div class="small-8 columns">
-            <h3>Edit Your Note</h3>
-          </div>
-          <div class="small-4 columns text-right">
-            <form method="POST" action="{% url 'note_delete' %}">
-              {% csrf_token %}
-              {{ note_delete_form }}
-              <button id="delete-note-button" type="submit" class="scary"><i class="fa fa-trash-o"></i> Delete Note</button>
-            </form>
-          </div>
-        </div>
-        <div class="row">
-          <form method="POST" action="{{ note.get_absolute_url }}">
-            {% csrf_token %}
-            <div class="small-12 large-6 columns">
-              {% with note_edit_form.name as field %}
-                {{ field.errors|safe }}
-                <label for="{{ field.id_for_label }}">{{ field.label }}:</label>
-                {{ field }}
-                <p>{{ field.help_text }}</p>
-              {% endwith %}
-            </div>
-            <div class="small-12 large-6 columns">
-              {% with note_edit_form.tags as field %}
-                {{ field.errors|safe }}
-                <label for="{{ field.id_for_label }}">{{ field.label }}:</label>
-                {{ field }}
-                <p>{{ field.help_text }}</p>
-              {% endwith %}
-            </div>
-            <div class="small-12 columns text-center">
-              <button type="submit"><i class="fa fa-save"></i> Save</button>
-            </div>
-          </form>
-        </div>
-      </div>
-
-    </div><!-- /note header -->
-
-    <div class="row">
-      <div id="tabs" class="small-12 columns">
-        <dl class="tabs">
-          <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 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">
-                  <i id="outline-btn" class="zoom-button fa fa-bars fa-2x"></i>
-                </div>
-                <div class="small-4 columns">
-                  <span>Jump to page:
-                  <input id="scroll-to" type="text" style="width: 3em; display: inline" /></span>
-                </div>
-                <div class="small-2 small-centered columns center">
-                  <i id="minus-btn" class="zoom-button fa fa-search-minus fa-2x"></i>
-                  <i id="plus-btn" class="zoom-button fa fa-search-plus fa-2x"></i>
-                </div>
-              </div>
-            {% endif %}
-
-            <div class="row">
-              <div class="small-12 small-centered columns medium-12 large-12 body_copy">
-                <div id="note-content-wrapper" class="note-text">
-                  {% if note.has_markdown %}
-                    <span id="note-markdown" data-markdown="{{note.notemarkdown.markdown}}"></span>
-                  {% else %}
-                    <iframe style="border:none; width:100%; min-height: 1000px;"
-                          id="noteframe"></iframe>
-                    <noscript>
-                      {{ note.text }}
-                    </noscript>
-                  {% endif %}
-                </div> <!-- .note-text -->
-              </div><!-- /body_copy -->
-            </div>
-          </div><!-- /note_container -->
-        {% endif %}
-        {% if show_keywords %}
-          <div id="keywords" class="content">
-            <div class="row">
-              <div class="small-12 columns">
-                {% if user.is_authenticated %}
-                  <p id="keyword-intro">These key terms and definitions have been defined by KarmaNotes users.
-                  You can edit them for accuracy and add more if you like.</p>
-                  <p><button id="edit-keywords-button" class="museo700"><i class="fa fa-edit"></i> Edit Key Terms & Definitions</button></p>
-                {% else %}
-                  <p id="keyword-intro">These key terms and definitions have been defined by KarmaNotes users.</p>
-                {% endif %}
-                <table id="keywords-data-table">
-                  <thead>
-                    <tr>
-                      <td>Key Terms</td>
-                      <td>Definitions</td>
-                    </tr>
-                  </thead>
-                  {% for keyword in keywords %}
-                    <tr>
-                      <td>{{ keyword.word }}</td>
-                      <td>
-                        {% if keyword.definition %}
-                          {{ keyword.definition }}
-                        {% else %}
-                          <em class="light">(not defined yet)</em>
-                        {% endif %}
-                      </td>
-                    </tr>
-                  {% endfor %}
-                </table>
-
-                <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">
-                    <div class="row keyword-form-row">
-                      <div class="small-12 large-4 columns">
-                        {{ keyword_prototype_form.keyword }}
-                      </div>
-                      <div class="small-12 large-8 columns">
-                        {{ keyword_prototype_form.definition }}
-                        {{ keyword_prototype_form.id }}
-                      </div>
-                    </div>
-                    <hr class="hide-for-large-up" />
-                  </div>
-                  <div id="keyword-form-rows">
-                    {% for form_row in keyword_formset %}
-                      <div class="row keyword-form-row" data-index="{{ forloop.counter0 }}">
-                        <div class="small-12 large-4 columns">
-                          {{ form_row.keyword }}
-                        </div>
-                        <div class="small-12 large-8 columns">
-                          {{ form_row.definition }}
-                          {{ form_row.id }}
-                        </div>
-                      </div>
-                      <hr class="hide-for-large-up" />
-                    {% endfor %}
-                  </div>
-                  <div class="row hide-for-medium-down">
-                    <div class="small-12 columns">
-                      <p class="keywords-hint">(Click <i class="fa fa-plus"></i> or press TAB in the last definition for another row)</p>
-                    </div>
-                  </div>
-                  <div class="row">
-                    <div class="small-2 columns">
-                      <button type="submit" name="action" value="keyword-form">Save</button>
-                    </div>
-                    <div class="small-9 large-10 columns center">
-                      <i id="add-row-btn" class="fa fa-plus fa-2x"></i>
-                    </div>
-                  </div>
-                </form>
-              </div>
-            </div>
-          </div> <!-- keywords -->
-        {% endif %}
+      <div class="small-2 small-centered columns center">
+        <i id="minus-btn" class="zoom-button fa fa-search-minus fa-2x"></i>
+        <i id="plus-btn" class="zoom-button fa fa-search-plus fa-2x"></i>
       </div>
     </div>
-
-  </section><!--/note_content-->
-
-  <ol class="joyride-list" data-joyride
-        data-options="cookie_monster: true; cookie_name: note_detail_joyride">
-    <li data-id="note-content-wrapper" data-text="Awesome!" data-options="tip_location: top">
-      <p>You can highlight important words or phrases in this note to add definitions for them.</p>
-    </li>
-    <li data-id="keywords-tab-button" data-text="Awesome!" data-options="tip_location: top">
-      <p>Keywords you define will appear here, and you can define new ones here too.</p>
-    </li>
-  </ol>
-
-{% endblock %}
-
-
+  {% endif %}
+
+  <div class="row">
+    <div class="small-12 small-centered columns medium-12 large-12 body_copy">
+      <div id="note-content-wrapper" class="note-text">
+        {% if note.has_markdown %}
+          <span id="note-markdown" data-markdown="{{note.notemarkdown.markdown}}"></span>
+        {% else %}
+          <iframe style="border:none; width:100%; min-height: 1000px;"
+                id="noteframe"></iframe>
+          <noscript>
+            {{ note.text }}
+          </noscript>
+        {% endif %}
+      </div> <!-- .note-text -->
+    </div><!-- /body_copy -->
+  </div>
+</div><!-- /note_container -->
\ No newline at end of file
diff --git a/karmaworld/templates/notes/note_keywords.html b/karmaworld/templates/notes/note_keywords.html
new file mode 100644 (file)
index 0000000..483f3f5
--- /dev/null
@@ -0,0 +1,78 @@
+
+<div id="keywords" class="content">
+  <div class="row">
+    <div class="small-12 columns">
+      {% if user.is_authenticated %}
+        <p id="keyword-intro">These key terms and definitions have been defined by KarmaNotes users.
+        You can edit them for accuracy and add more if you like.</p>
+        <p><button id="edit-keywords-button" class="museo700"><i class="fa fa-edit"></i> Edit Key Terms & Definitions</button></p>
+      {% else %}
+        <p id="keyword-intro">These key terms and definitions have been defined by KarmaNotes users.</p>
+      {% endif %}
+      <table id="keywords-data-table">
+        <thead>
+          <tr>
+            <td>Key Terms</td>
+            <td>Definitions</td>
+          </tr>
+        </thead>
+        {% for keyword in keywords %}
+          <tr>
+            <td>{{ keyword.word }}</td>
+            <td>
+              {% if keyword.definition %}
+                {{ keyword.definition }}
+              {% else %}
+                <em class="light">(not defined yet)</em>
+              {% endif %}
+            </td>
+          </tr>
+        {% endfor %}
+      </table>
+
+      <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">
+          <div class="row keyword-form-row">
+            <div class="small-12 large-4 columns">
+              {{ keyword_prototype_form.keyword }}
+            </div>
+            <div class="small-12 large-8 columns">
+              {{ keyword_prototype_form.definition }}
+              {{ keyword_prototype_form.id }}
+            </div>
+          </div>
+          <hr class="hide-for-large-up" />
+        </div>
+        <div id="keyword-form-rows">
+          {% for form_row in keyword_formset %}
+            <div class="row keyword-form-row" data-index="{{ forloop.counter0 }}">
+              <div class="small-12 large-4 columns">
+                {{ form_row.keyword }}
+              </div>
+              <div class="small-12 large-8 columns">
+                {{ form_row.definition }}
+                {{ form_row.id }}
+              </div>
+            </div>
+            <hr class="hide-for-large-up" />
+          {% endfor %}
+        </div>
+        <div class="row hide-for-medium-down">
+          <div class="small-12 columns">
+            <p class="keywords-hint">(Click <i class="fa fa-plus"></i> or press TAB in the last definition for another row)</p>
+          </div>
+        </div>
+        <div class="row">
+          <div class="small-2 columns">
+            <button type="submit" name="action" value="keyword-form">Save</button>
+          </div>
+          <div class="small-9 large-10 columns center">
+            <i id="add-row-btn" class="fa fa-plus fa-2x"></i>
+          </div>
+        </div>
+      </form>
+    </div>
+  </div>
+</div> <!-- keywords -->
diff --git a/karmaworld/templates/notes/note_quiz.html b/karmaworld/templates/notes/note_quiz.html
new file mode 100644 (file)
index 0000000..64d7a0c
--- /dev/null
@@ -0,0 +1,74 @@
+<div id="quiz" class="content">
+  <div class="row">
+    <div class="small-12 columns">
+      <div id="quiz-intro">
+        {% if questions %}
+          These quiz questions have been randomly generated from the keywords and definitions associated
+          with this note. Refresh to get different questions.
+        {% else %}
+          No keywords with definitions have been supplied, so we can't generate any quiz questions.
+          Provide some key terms with their definitions, and we'll create questions based on them.
+        {% endif %}
+      </div>
+    </div>
+  </div>
+
+  {% for question in questions %}
+    <div class="quiz-question-wrapper">
+      <div class="row">
+        <div class="small-12 columns correct-label hide">
+          <div class="correct-label-inner">Correct</div>
+        </div>
+      </div>
+      <div class="row">
+        <div class="small-12 columns incorrect-label hide">
+          <div class="incorrect-label-inner">Incorrect</div>
+        </div>
+      </div>
+      <div class="row">
+        <div class="small-12 columns quiz-question">
+          <div class="quiz-question-inner">
+            <div class="question-text"><strong>{{ forloop.counter }}. {{ question.question_text }}</strong></div>
+            {% if question.question_type == 'MultipleChoiceQuestion' %}
+              {% for choice in question.choices %}
+                <div class="question-choices">
+                  <input type="radio"
+                         name="group_{{ forloop.parentloop.counter0 }}"
+                         id="choice_{{ forloop.parentloop.counter0 }}_{{ forloop.counter0 }}"
+                         value="choice_{{ forloop.parentloop.counter0 }}_{{ forloop.counter0 }}"
+                         data-correct="{% if choice.correct %}true{% else %}false{% endif %}" />
+                  <label for="choice_{{ forloop.parentloop.counter0 }}_{{ forloop.counter0 }}">{{ choice.text }}</label>
+                </div>
+              {% endfor %}
+            {% endif %}
+            {% if question.question_type == 'TrueFalseQuestion' %}
+              <div class="question-choices">
+                <input type="radio"
+                       name="group_{{ forloop.counter0 }}"
+                       id="choice_{{ forloop.counter0 }}_true"
+                       value="choice_{{ forloop.counter0 }}_true"
+                       data-correct="{% if question.true %}true{% else %}false{% endif %}" />
+                <label for="choice_{{ forloop.counter0 }}_true">True</label>
+              </div>
+              <div class="question-choices">
+                <input type="radio"
+                       name="group_{{ forloop.counter0 }}"
+                       id="choice_{{ forloop.counter0 }}_false"
+                       value="choice_{{ forloop.counter0 }}_false"
+                       data-correct="{% if question.false %}true{% else %}false{% endif %}" />
+                <label for="choice_{{ forloop.counter0 }}_false">False</label>
+              </div>
+            {% endif %}
+          </div>
+        </div>
+      </div>
+    </div>
+  {% endfor %}
+
+  <div class="row">
+    <div class="small-12 columns">
+      <button id="check-answers-button">Check Answers</button>
+    </div>
+  </div>
+
+</div>
\ No newline at end of file
index 6df80416bc3d2c03e1aef2ec327982adc2cb3fed..6f6e85582cf945f56c8ed10e3e01b6f2cc36196e 100644 (file)
@@ -14,7 +14,7 @@ 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, \
-    NoteKeywordsView, edit_note, NoteDeleteView
+    NoteKeywordsView, NoteQuizView, edit_note, NoteDeleteView
 from karmaworld.apps.moderation import moderator
 from karmaworld.apps.document_upload.views import save_fp_upload
 from karmaworld.apps.quizzes.views import set_delete_keyword_annotator, get_keywords_annotator
@@ -111,6 +111,8 @@ urlpatterns = patterns('',
         NoteView.as_view(), name='note_detail'),
     url(r'^note/' + SLUG.format('school_') + '/' + SLUG.format('course_') +'/'+ SLUG.format('') +'/keywords/$',
         NoteKeywordsView.as_view(), name='note_keywords'),
+    url(r'^note/' + SLUG.format('school_') + '/' + SLUG.format('course_') +'/'+ SLUG.format('') +'/quiz/$',
+        NoteQuizView.as_view(), name='note_quiz'),
 
     url(r'note/delete/$', NoteDeleteView.as_view(), name='note_delete'),
 
index c03a7fb593eb379e4188c04c25d62cf44ea80894..55dbbda8a5b68296d08e2375240b6cc5043c7a7e 100644 (file)
@@ -25,8 +25,4 @@ django-reversion
 django-ajax-selects
 git+https://github.com/btbonval/django-ajax-selects-cascade.git
 psycopg2
-git+https://github.com/Soaa-/django-nested-inlines.git
 pyth
-http://python-graph.googlecode.com/files/python-graph-core-1.8.2.tar.gz
-nltk
-numpy