Notes have categories, fixes #367
[oweals/karmaworld.git] / karmaworld / apps / quizzes / xml_import.py
1 from StringIO import StringIO
2 from karmaworld.apps.notes.models import Note
3 import re
4 from bs4 import BeautifulSoup
5 from karmaworld.apps.quizzes.models import MultipleChoiceQuestion, Quiz, TrueFalseQuestion, MultipleChoiceOption, \
6     Keyword
7 from pyth.plugins.plaintext.writer import PlaintextWriter
8 from pyth.plugins.rtf15.reader import Rtf15Reader
9
10 FOUR_MULTIPLE_CHOICE = r'^A. (?P<A>.*)[\n]+B. (?P<B>.*)[\n]+C. (?P<C>.*)[\n]+D. (?P<D>.*)$'
11 TRUE_FALSE_CHOICE = r'^A. (?P<A>True|False)[\n]+B. (?P<B>True|False)$'
12
13
14 def _rtf2plain(str):
15     if str:
16         doc = Rtf15Reader.read(StringIO(str))
17         return PlaintextWriter.write(doc).getvalue()
18     else:
19         return str
20
21
22 def _category_from_question(question):
23     category_string = question.find('Category').string
24     if category_string == 'Knowledge':
25         category = MultipleChoiceQuestion.KNOWLEDGE
26     elif category_string == 'Understand':
27         category = MultipleChoiceQuestion.UNDERSTAND
28     elif category_string == 'Remember':
29         category = MultipleChoiceQuestion.REMEMBER
30     elif category_string == 'Analyze':
31         category = MultipleChoiceQuestion.ANALYZE
32     else:
33         category = None
34
35     return category
36
37
38 def _difficulty_from_question(question):
39     difficulty_string = question.find('Difficulty').string
40     if difficulty_string == 'Easy':
41         difficulty = MultipleChoiceQuestion.EASY
42     elif difficulty_string == 'Medium':
43         difficulty = MultipleChoiceQuestion.MEDIUM
44     elif difficulty_string == 'Hard':
45         difficulty = MultipleChoiceQuestion.HARD
46     else:
47         difficulty = None
48
49     return difficulty
50
51
52 def _true_false(question, quiz_object):
53     question_text = _rtf2plain(question.find('QuestionText').string)
54     explanation_text = _rtf2plain(question.find('Explanation').string)
55     category = _category_from_question(question)
56     difficulty = _difficulty_from_question(question)
57
58     correct_answer_letter = question.find('Answer').string
59
60     options_string = question.find('AnswerOptions').string
61     options_string = _rtf2plain(options_string)
62     match_options = re.match(TRUE_FALSE_CHOICE, options_string)
63     option_a = match_options.group('A')
64     option_b = match_options.group('B')
65
66     if correct_answer_letter == 'A':
67         correct_answer_string = option_a
68     else:
69         correct_answer_string = option_b
70
71     if correct_answer_string == 'True':
72         correct_answer = True
73     else:
74         correct_answer = False
75
76     TrueFalseQuestion.objects.create(text=question_text,
77                                      explanation=explanation_text,
78                                      difficulty=difficulty,
79                                      category=category,
80                                      true=correct_answer,
81                                      quiz=quiz_object)
82
83
84 def _multiple_choice(question, quiz_object):
85     question_text = _rtf2plain(question.find('QuestionText').string)
86     explanation_text = _rtf2plain(question.find('Explanation').string)
87     category = _category_from_question(question)
88     difficulty = _difficulty_from_question(question)
89
90     question_object = MultipleChoiceQuestion.objects.create(question_text=question_text,
91                                                             explanation=explanation_text,
92                                                             difficulty=difficulty,
93                                                             category=category,
94                                                             quiz=quiz_object)
95
96     correct_answer = question.find('Answer').string
97
98     options_string = question.find('AnswerOptions').string
99     options_string = _rtf2plain(options_string)
100     match_options = re.match(FOUR_MULTIPLE_CHOICE, options_string)
101     option_a = match_options.group('A')
102     option_b = match_options.group('B')
103     option_c = match_options.group('C')
104     option_d = match_options.group('D')
105
106     MultipleChoiceOption.objects.create(text=option_a,
107                                         correct=(correct_answer == 'A'),
108                                         question=question_object)
109     MultipleChoiceOption.objects.create(text=option_b,
110                                         correct=(correct_answer == 'B'),
111                                         question=question_object)
112     MultipleChoiceOption.objects.create(text=option_c,
113                                         correct=(correct_answer == 'C'),
114                                         question=question_object)
115     MultipleChoiceOption.objects.create(text=option_d,
116                                         correct=(correct_answer == 'D'),
117                                         question=question_object)
118
119
120 def quiz_from_xml(filename, note_id):
121     with open(filename, 'r') as file:
122         soup = BeautifulSoup(file.read(), "xml")
123
124     note_object = Note.objects.get(id=note_id)
125     quiz_name = soup.find('EChapterTitle').string
126     quiz_object = Quiz.objects.create(name=quiz_name, note=note_object)
127
128     questions = soup.find_all('TestBank')
129     for question in questions:
130         type_string = question.find('Type').string
131         if type_string == 'Multiple Choice':
132             _multiple_choice(question, quiz_object)
133
134         elif type_string == 'True/False':
135             _true_false(question, quiz_object)
136
137
138 def keywords_from_xml(filename, note_id):
139     with open(filename, 'r') as file:
140         soup = BeautifulSoup(file.read(), "xml")
141
142     note_object = Note.objects.get(id=note_id)
143
144     keywords = soup.find_all('WordPhrase')
145     for word in keywords:
146         Keyword.objects.create(word=word.string, note=note_object)