Commit 0a96984cde48d2c23d83b0cb8648e3b5391d7123
1 parent
e447d0c2
Exists in
master
and in
1 other branch
database file is now selected as a command line option, defaults to students.db
Showing
5 changed files
with
81 additions
and
47 deletions
Show diff stats
knowledge.py
@@ -129,8 +129,10 @@ class StudentKnowledge(object): | @@ -129,8 +129,10 @@ class StudentKnowledge(object): | ||
129 | 129 | ||
130 | if grade > 0.999: | 130 | if grade > 0.999: |
131 | self.correct_answers += 1 | 131 | self.correct_answers += 1 |
132 | - self.next_question() | ||
133 | - action = 'new_question' | 132 | + if self.next_question() is None: |
133 | + action = 'finished_topic' | ||
134 | + else: | ||
135 | + action = 'new_question' | ||
134 | 136 | ||
135 | else: | 137 | else: |
136 | self.wrong_answers += 1 | 138 | self.wrong_answers += 1 |
@@ -140,13 +142,15 @@ class StudentKnowledge(object): | @@ -140,13 +142,15 @@ class StudentKnowledge(object): | ||
140 | if self.current_question['tries'] <= 0: | 142 | if self.current_question['tries'] <= 0: |
141 | logger.debug("Appending new instance of this question to the end") | 143 | logger.debug("Appending new instance of this question to the end") |
142 | self.questions.append(self.factory[q['ref']].generate()) | 144 | self.questions.append(self.factory[q['ref']].generate()) |
143 | - self.next_question() | ||
144 | - action = 'new_question' | 145 | + if self.next_question() is None: |
146 | + action = 'finished_topic' | ||
147 | + else: | ||
148 | + action = 'new_question' # FIXME show comments | ||
145 | 149 | ||
146 | else: | 150 | else: |
147 | - action = 'wrong' | 151 | + action = 'try_again' |
148 | 152 | ||
149 | - # returns answered and corrected question (not new one) | 153 | + # returns corrected question (not new one) which might include comments |
150 | return q, action | 154 | return q, action |
151 | 155 | ||
152 | 156 |
learnapp.py
@@ -58,8 +58,8 @@ class LearnApp(object): | @@ -58,8 +58,8 @@ class LearnApp(object): | ||
58 | session.close() | 58 | session.close() |
59 | 59 | ||
60 | # ------------------------------------------------------------------------ | 60 | # ------------------------------------------------------------------------ |
61 | - def __init__(self, config_files, prefix): | ||
62 | - self.db_setup() # setup database and check students | 61 | + def __init__(self, config_files, prefix, db): |
62 | + self.db_setup(db) # setup database and check students | ||
63 | self.online = dict() # online students | 63 | self.online = dict() # online students |
64 | 64 | ||
65 | self.deps = nx.DiGraph(prefix=prefix) | 65 | self.deps = nx.DiGraph(prefix=prefix) |
@@ -152,7 +152,7 @@ class LearnApp(object): | @@ -152,7 +152,7 @@ class LearnApp(object): | ||
152 | topic_id=topic)) | 152 | topic_id=topic)) |
153 | logger.debug(f'Saved "{q["ref"]}" into database') | 153 | logger.debug(f'Saved "{q["ref"]}" into database') |
154 | 154 | ||
155 | - if knowledge.get_current_question() is None: | 155 | + if action == 'finished_topic': |
156 | # finished topic, save into database | 156 | # finished topic, save into database |
157 | logger.info(f'User "{uid}" finished "{topic}"') | 157 | logger.info(f'User "{uid}" finished "{topic}"') |
158 | level = knowledge.get_topic_level(topic) | 158 | level = knowledge.get_topic_level(topic) |
@@ -176,9 +176,8 @@ class LearnApp(object): | @@ -176,9 +176,8 @@ class LearnApp(object): | ||
176 | s.add(a) | 176 | s.add(a) |
177 | 177 | ||
178 | logger.debug(f'Saved topic "{topic}" into database') | 178 | logger.debug(f'Saved topic "{topic}" into database') |
179 | - action = 'finished_topic' # FIXME | ||
180 | 179 | ||
181 | - return action | 180 | + return q, action |
182 | 181 | ||
183 | 182 | ||
184 | # ------------------------------------------------------------------------ | 183 | # ------------------------------------------------------------------------ |
@@ -208,7 +207,7 @@ class LearnApp(object): | @@ -208,7 +207,7 @@ class LearnApp(object): | ||
208 | # ------------------------------------------------------------------------ | 207 | # ------------------------------------------------------------------------ |
209 | # setup and check database | 208 | # setup and check database |
210 | # ------------------------------------------------------------------------ | 209 | # ------------------------------------------------------------------------ |
211 | - def db_setup(self, db='students.db'): | 210 | + def db_setup(self, db): |
212 | logger.info(f'Checking database "{db}":') | 211 | logger.info(f'Checking database "{db}":') |
213 | engine = create_engine(f'sqlite:///{db}', echo=False) | 212 | engine = create_engine(f'sqlite:///{db}', echo=False) |
214 | self.Session = sessionmaker(bind=engine) | 213 | self.Session = sessionmaker(bind=engine) |
questions.py
@@ -42,10 +42,6 @@ class Question(dict): | @@ -42,10 +42,6 @@ class Question(dict): | ||
42 | 'files': {}, | 42 | 'files': {}, |
43 | }) | 43 | }) |
44 | 44 | ||
45 | - # FIXME unused. do childs need do override this? | ||
46 | - # def updateAnswer(answer=None): | ||
47 | - # self['answer'] = answer | ||
48 | - | ||
49 | def correct(self): | 45 | def correct(self): |
50 | self['comments'] = '' | 46 | self['comments'] = '' |
51 | self['grade'] = 0.0 | 47 | self['grade'] = 0.0 |
serve.py
@@ -248,16 +248,17 @@ class QuestionHandler(BaseHandler): | @@ -248,16 +248,17 @@ class QuestionHandler(BaseHandler): | ||
248 | answer = answer[0] | 248 | answer = answer[0] |
249 | 249 | ||
250 | # check answer in another thread (nonblocking) | 250 | # check answer in another thread (nonblocking) |
251 | - action = await self.learn.check_answer(user, answer) | 251 | + # and get corrected question |
252 | + q, action = await self.learn.check_answer(user, answer) | ||
252 | 253 | ||
253 | # get next question (same, new or None) | 254 | # get next question (same, new or None) |
254 | question = self.learn.get_current_question(user) | 255 | question = self.learn.get_current_question(user) |
255 | 256 | ||
256 | - if action == 'wrong': | 257 | + if action == 'try_again': |
257 | comments_html = self.render_string('comments.html', | 258 | comments_html = self.render_string('comments.html', |
258 | comments=question['comments'], md=md_to_html) | 259 | comments=question['comments'], md=md_to_html) |
259 | self.write({ | 260 | self.write({ |
260 | - 'method': action, | 261 | + 'method': 'try_again', # FIXME js |
261 | 'params': { | 262 | 'params': { |
262 | 'progress': self.learn.get_student_progress(user), | 263 | 'progress': self.learn.get_student_progress(user), |
263 | 'comments': tornado.escape.to_unicode(comments_html), # FIXME | 264 | 'comments': tornado.escape.to_unicode(comments_html), # FIXME |
@@ -265,14 +266,20 @@ class QuestionHandler(BaseHandler): | @@ -265,14 +266,20 @@ class QuestionHandler(BaseHandler): | ||
265 | } | 266 | } |
266 | }) | 267 | }) |
267 | 268 | ||
268 | - elif action == 'finished_topic': # right answer, finished topic | ||
269 | - finished_topic_html = self.render_string('finished_topic.html') | ||
270 | - self.write({ | ||
271 | - 'method': 'finished_topic', | ||
272 | - 'params': { | ||
273 | - 'question': tornado.escape.to_unicode(finished_topic_html) | ||
274 | - } | ||
275 | - }) | 269 | + # if action == 'wrong': |
270 | + # comments_html = self.render_string('comments.html', | ||
271 | + # comments=question['comments'], md=md_to_html) | ||
272 | + # template = self.templates[question['type']] | ||
273 | + # question_html = self.render_string(template, question=question, md=md_to_html) | ||
274 | + # self.write({ | ||
275 | + # 'method': 'wrong', # FIXME js | ||
276 | + # 'params': { | ||
277 | + # 'question': tornado.escape.to_unicode(question_html), | ||
278 | + # 'progress': self.learn.get_student_progress(user), | ||
279 | + # 'comments': tornado.escape.to_unicode(comments_html), # FIXME | ||
280 | + # 'tries': question['tries'], | ||
281 | + # } | ||
282 | + # }) | ||
276 | 283 | ||
277 | elif action == 'new_question': # get next question in the topic | 284 | elif action == 'new_question': # get next question in the topic |
278 | template = self.templates[question['type']] | 285 | template = self.templates[question['type']] |
@@ -287,6 +294,15 @@ class QuestionHandler(BaseHandler): | @@ -287,6 +294,15 @@ class QuestionHandler(BaseHandler): | ||
287 | } | 294 | } |
288 | }) | 295 | }) |
289 | 296 | ||
297 | + elif action == 'finished_topic': # right answer, finished topic | ||
298 | + finished_topic_html = self.render_string('finished_topic.html') | ||
299 | + self.write({ | ||
300 | + 'method': 'finished_topic', | ||
301 | + 'params': { | ||
302 | + 'question': tornado.escape.to_unicode(finished_topic_html) | ||
303 | + } | ||
304 | + }) | ||
305 | + | ||
290 | else: | 306 | else: |
291 | logger.error(f'Unknown action {action}') | 307 | logger.error(f'Unknown action {action}') |
292 | 308 | ||
@@ -316,6 +332,8 @@ def main(): | @@ -316,6 +332,8 @@ def main(): | ||
316 | help='Path prefix under which the topic directories can be found, e.g. ~/topics') | 332 | help='Path prefix under which the topic directories can be found, e.g. ~/topics') |
317 | argparser.add_argument('--port', type=int, default=8443, | 333 | argparser.add_argument('--port', type=int, default=8443, |
318 | help='Port to be used by the HTTPS server, e.g. 8443') | 334 | help='Port to be used by the HTTPS server, e.g. 8443') |
335 | + argparser.add_argument('--db', type=str, default='students.db', | ||
336 | + help='SQLite3 database file, e.g. students.db') | ||
319 | argparser.add_argument('--debug', action='store_true', | 337 | argparser.add_argument('--debug', action='store_true', |
320 | help='Enable debug messages') | 338 | help='Enable debug messages') |
321 | arg = argparser.parse_args() | 339 | arg = argparser.parse_args() |
@@ -336,7 +354,7 @@ def main(): | @@ -336,7 +354,7 @@ def main(): | ||
336 | # --- start application | 354 | # --- start application |
337 | logging.info('Starting App') | 355 | logging.info('Starting App') |
338 | try: | 356 | try: |
339 | - learnapp = LearnApp(arg.conffile, prefix=arg.prefix) | 357 | + learnapp = LearnApp(arg.conffile, prefix=arg.prefix, db=arg.db) |
340 | except Exception as e: | 358 | except Exception as e: |
341 | logging.critical('Failed to start backend application') | 359 | logging.critical('Failed to start backend application') |
342 | raise e | 360 | raise e |
static/js/topic.js
@@ -7,31 +7,34 @@ $.fn.extend({ | @@ -7,31 +7,34 @@ $.fn.extend({ | ||
7 | } | 7 | } |
8 | }); | 8 | }); |
9 | 9 | ||
10 | + | ||
11 | +function new_question(question, tries, progress) { | ||
12 | + $("#question_div").html(question); | ||
13 | + $("#comments").html(""); | ||
14 | + $("#tries").html(tries); | ||
15 | + $('#topic_progress').css('width', (100*progress)+'%').attr('aria-valuenow', 100*progress); | ||
16 | + MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]); | ||
17 | + $('#question_div').animateCSS('bounceInDown'); | ||
18 | + | ||
19 | + // enable shift+enter to submit and tab to spaces conversion | ||
20 | + $("input:text, input:radio, input:checkbox").keydown(function (e) { | ||
21 | + if (e.keyCode == 13) { | ||
22 | + e.preventDefault(); | ||
23 | + if (e.shiftKey) postQuestion(); | ||
24 | + return false; | ||
25 | + }}); | ||
26 | +} | ||
27 | + | ||
10 | // updates question according to the response given by the server | 28 | // updates question according to the response given by the server |
11 | function updateQuestion(response){ | 29 | function updateQuestion(response){ |
12 | 30 | ||
13 | switch (response["method"]) { | 31 | switch (response["method"]) { |
14 | case "new_question": | 32 | case "new_question": |
15 | - $("#question_div").html(response["params"]["question"]); | ||
16 | - $("#comments").html(""); | ||
17 | - $("#tries").html(response["params"]["tries"]); | ||
18 | - | ||
19 | - $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]); | ||
20 | - | ||
21 | - MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]); | ||
22 | - | ||
23 | - // enable shift+enter to submit and tab to spaces conversion | ||
24 | - $("input:text, input:radio, input:checkbox").keydown(function (e) { | ||
25 | - if (e.keyCode == 13) { | ||
26 | - e.preventDefault(); | ||
27 | - if (e.shiftKey) postQuestion(); | ||
28 | - return false; | ||
29 | - }}); | ||
30 | - | ||
31 | - $('#question_div').animateCSS('bounceInDown'); | 33 | + params = response["params"]; |
34 | + new_question(params["question"], params["tries"], params["progress"]); | ||
32 | break; | 35 | break; |
33 | 36 | ||
34 | - case "wrong": | 37 | + case "try_again": |
35 | $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]); | 38 | $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]); |
36 | $('#question_div').animateCSS('shake'); | 39 | $('#question_div').animateCSS('shake'); |
37 | $('#comments').html(response['params']['comments']); | 40 | $('#comments').html(response['params']['comments']); |
@@ -39,6 +42,20 @@ function updateQuestion(response){ | @@ -39,6 +42,20 @@ function updateQuestion(response){ | ||
39 | MathJax.Hub.Queue(["Typeset",MathJax.Hub,"#comments"]); | 42 | MathJax.Hub.Queue(["Typeset",MathJax.Hub,"#comments"]); |
40 | break; | 43 | break; |
41 | 44 | ||
45 | + // case "wrong": | ||
46 | + // $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]); | ||
47 | + // $('#question_div').animateCSS('shake'); | ||
48 | + // $('#comments').html(response['params']['comments']); | ||
49 | + // $("#tries").html(response["params"]["tries"]); | ||
50 | + // MathJax.Hub.Queue(["Typeset",MathJax.Hub,"#comments"]); | ||
51 | + | ||
52 | + // // setTimeout(function(){ | ||
53 | + // new_question(response["params"]["question"], response["params"]["tries"], response["params"]["progress"]); | ||
54 | + // // }, 5000); | ||
55 | + // break; | ||
56 | + | ||
57 | + | ||
58 | + | ||
42 | case "finished_topic": | 59 | case "finished_topic": |
43 | $('#submit').css("visibility", "hidden"); | 60 | $('#submit').css("visibility", "hidden"); |
44 | $("#content").html(response["params"]["question"]); | 61 | $("#content").html(response["params"]["question"]); |
@@ -62,7 +79,7 @@ function getQuestion() { | @@ -62,7 +79,7 @@ function getQuestion() { | ||
62 | 79 | ||
63 | // Send answer and receive a response. | 80 | // Send answer and receive a response. |
64 | // The response can be a new_question or a shake if the answer is wrong, which | 81 | // The response can be a new_question or a shake if the answer is wrong, which |
65 | -// is then passed to updateQuestion() | 82 | +// is then passed to updateQuestion() |
66 | function postQuestion() { | 83 | function postQuestion() { |
67 | if (typeof editor === 'object') | 84 | if (typeof editor === 'object') |
68 | editor.save(); | 85 | editor.save(); |