Commit dbdd58fe15e1cd88fb28cf6a13f13b5447cdca79
1 parent
bf8ade7a
Exists in
master
and in
1 other branch
- added option 'append_wrong' to enable/disable appending new instance of questi…
…ons when answered wrong. - added option 'max_tries'. - added option 'shuffle' to que order of the questions in a topic. - images are no longer in a html figure tag, and are now centered.
Showing
6 changed files
with
41 additions
and
21 deletions
Show diff stats
BUGS.md
1 | 1 | ||
2 | # BUGS | 2 | # BUGS |
3 | 3 | ||
4 | +- nos topicos learn.yaml, qd falha acrescenta no fim. nao faz sentido. | ||
5 | +- ocorreu uma vez o sqlalchemy dar mesg erro a indicar que as threads sao diferents quando se faz o get da primeira pergunta do topico. Muitas vezes nao mostar erro, mas a pagina da erro ou fica em branco... | ||
6 | + | ||
7 | +- mathjax, formulas $$f(x)$$ nas opções de escolha multipla, não ficam centradas em toda a coluna mas apenas na largura do parágrafo. | ||
4 | - mostrar feedback/solucoes quando acerta, ou excede max tries. | 8 | - mostrar feedback/solucoes quando acerta, ou excede max tries. |
5 | - default prefix should be obtained from each course (yaml conf)? | 9 | - default prefix should be obtained from each course (yaml conf)? |
6 | - tabelas nas perguntas radio/checkbox não ocupam todo o espaço como em question. | 10 | - tabelas nas perguntas radio/checkbox não ocupam todo o espaço como em question. |
knowledge.py
@@ -71,13 +71,14 @@ class StudentKnowledge(object): | @@ -71,13 +71,14 @@ class StudentKnowledge(object): | ||
71 | # questions: list of generated questions to do in the topic | 71 | # questions: list of generated questions to do in the topic |
72 | # current_question: the current question to be presented | 72 | # current_question: the current question to be presented |
73 | # ------------------------------------------------------------------------ | 73 | # ------------------------------------------------------------------------ |
74 | - async def init_topic(self, topic): | ||
75 | - logger.debug(f'StudentKnowledge.init_topic({topic})') | 74 | + # FIXME async mas nao tem awaits... |
75 | + async def start_topic(self, topic): | ||
76 | + logger.debug(f'StudentKnowledge.start_topic({topic})') | ||
76 | 77 | ||
77 | # do not allow locked topics | 78 | # do not allow locked topics |
78 | if self.is_locked(topic): | 79 | if self.is_locked(topic): |
79 | logger.debug(f'Topic {topic} is locked') | 80 | logger.debug(f'Topic {topic} is locked') |
80 | - return | 81 | + return False |
81 | 82 | ||
82 | # starting new topic | 83 | # starting new topic |
83 | self.current_topic = topic | 84 | self.current_topic = topic |
@@ -85,7 +86,11 @@ class StudentKnowledge(object): | @@ -85,7 +86,11 @@ class StudentKnowledge(object): | ||
85 | self.wrong_answers = 0 | 86 | self.wrong_answers = 0 |
86 | 87 | ||
87 | t = self.deps.node[topic] | 88 | t = self.deps.node[topic] |
88 | - questions = random.sample(t['questions'], k=t['choose']) | 89 | + k = t['choose'] |
90 | + if t['shuffle']: | ||
91 | + questions = random.sample(t['questions'], k=k) | ||
92 | + else: | ||
93 | + questions = t['questions'][:k] | ||
89 | logger.debug(f'Questions: {", ".join(questions)}') | 94 | logger.debug(f'Questions: {", ".join(questions)}') |
90 | 95 | ||
91 | # generate instances of questions | 96 | # generate instances of questions |
@@ -94,6 +99,7 @@ class StudentKnowledge(object): | @@ -94,6 +99,7 @@ class StudentKnowledge(object): | ||
94 | 99 | ||
95 | # get first question | 100 | # get first question |
96 | self.next_question() | 101 | self.next_question() |
102 | + return True | ||
97 | 103 | ||
98 | 104 | ||
99 | # ------------------------------------------------------------------------ | 105 | # ------------------------------------------------------------------------ |
@@ -140,8 +146,10 @@ class StudentKnowledge(object): | @@ -140,8 +146,10 @@ class StudentKnowledge(object): | ||
140 | logger.debug(f'Wrong answers = {self.wrong_answers}; Tries = {self.current_question["tries"]}') | 146 | logger.debug(f'Wrong answers = {self.wrong_answers}; Tries = {self.current_question["tries"]}') |
141 | 147 | ||
142 | if self.current_question['tries'] <= 0: | 148 | if self.current_question['tries'] <= 0: |
143 | - logger.debug("Appending new instance of this question to the end") | ||
144 | - self.questions.append(self.factory[q['ref']].generate()) | 149 | + if self.current_question['append_wrong']: |
150 | + logger.debug("Appending new instance of this question to the end") | ||
151 | + self.questions.append(self.factory[q['ref']].generate()) | ||
152 | + | ||
145 | if self.next_question() is None: | 153 | if self.next_question() is None: |
146 | action = 'finished_topic' | 154 | action = 'finished_topic' |
147 | else: | 155 | else: |
@@ -165,7 +173,8 @@ class StudentKnowledge(object): | @@ -165,7 +173,8 @@ class StudentKnowledge(object): | ||
165 | self.finish_topic() | 173 | self.finish_topic() |
166 | else: | 174 | else: |
167 | self.current_question['start_time'] = datetime.now() | 175 | self.current_question['start_time'] = datetime.now() |
168 | - self.current_question['tries'] = self.current_question.get('max_tries', 3) # FIXME hardcoded 3 | 176 | + default_maxtries = self.deps.nodes[self.current_topic]['max_tries'] |
177 | + self.current_question['tries'] = self.current_question.get('max_tries', default_maxtries) | ||
169 | logger.debug(f'Next question is "{self.current_question["ref"]}"') | 178 | logger.debug(f'Next question is "{self.current_question["ref"]}"') |
170 | 179 | ||
171 | return self.current_question # question or None | 180 | return self.current_question # question or None |
learnapp.py
@@ -186,7 +186,7 @@ class LearnApp(object): | @@ -186,7 +186,7 @@ class LearnApp(object): | ||
186 | async def start_topic(self, uid, topic): | 186 | async def start_topic(self, uid, topic): |
187 | student = self.online[uid]['state'] | 187 | student = self.online[uid]['state'] |
188 | try: | 188 | try: |
189 | - await student.init_topic(topic) | 189 | + await student.start_topic(topic) |
190 | except KeyError as e: | 190 | except KeyError as e: |
191 | logger.warning(f'User "{uid}" tried to open nonexistent topic: "{topic}"') | 191 | logger.warning(f'User "{uid}" tried to open nonexistent topic: "{topic}"') |
192 | raise e | 192 | raise e |
@@ -242,6 +242,8 @@ class LearnApp(object): | @@ -242,6 +242,8 @@ class LearnApp(object): | ||
242 | default_shuffle = config.get('shuffle', True) | 242 | default_shuffle = config.get('shuffle', True) |
243 | default_choose = config.get('choose', 9999) | 243 | default_choose = config.get('choose', 9999) |
244 | default_forgetting_factor = config.get('forgetting_factor', 1.0) | 244 | default_forgetting_factor = config.get('forgetting_factor', 1.0) |
245 | + default_maxtries = config.get('max_tries', 3) | ||
246 | + default_append_wrong = config.get('append_wrong', True) | ||
245 | 247 | ||
246 | # iterate over topics and populate graph | 248 | # iterate over topics and populate graph |
247 | topics = config.get('topics', {}) | 249 | topics = config.get('topics', {}) |
@@ -257,8 +259,10 @@ class LearnApp(object): | @@ -257,8 +259,10 @@ class LearnApp(object): | ||
257 | t['path'] = path.join(g.graph['prefix'], tref) # prefix/topic | 259 | t['path'] = path.join(g.graph['prefix'], tref) # prefix/topic |
258 | t['file'] = attr.get('file', default_file) # questions.yaml | 260 | t['file'] = attr.get('file', default_file) # questions.yaml |
259 | t['shuffle'] = attr.get('shuffle', default_shuffle) | 261 | t['shuffle'] = attr.get('shuffle', default_shuffle) |
262 | + t['max_tries'] = attr.get('max_tries', default_maxtries) | ||
260 | t['forgetting_factor'] = attr.get('forgetting_factor', default_forgetting_factor) | 263 | t['forgetting_factor'] = attr.get('forgetting_factor', default_forgetting_factor) |
261 | t['choose'] = attr.get('choose', default_choose) | 264 | t['choose'] = attr.get('choose', default_choose) |
265 | + t['append_wrong'] = attr.get('append_wrong', default_append_wrong) | ||
262 | t['questions'] = attr.get('questions', []) | 266 | t['questions'] = attr.get('questions', []) |
263 | 267 | ||
264 | logger.info(f'Loaded {g.number_of_nodes()} topics') | 268 | logger.info(f'Loaded {g.number_of_nodes()} topics') |
@@ -286,6 +290,7 @@ class LearnApp(object): | @@ -286,6 +290,7 @@ class LearnApp(object): | ||
286 | qref = q.get('ref', str(i)) # ref or number | 290 | qref = q.get('ref', str(i)) # ref or number |
287 | q['ref'] = tref + ':' + qref | 291 | q['ref'] = tref + ':' + qref |
288 | q['path'] = topicpath | 292 | q['path'] = topicpath |
293 | + q.setdefault('append_wrong', t['append_wrong']) | ||
289 | 294 | ||
290 | # if questions are left undefined, include all. | 295 | # if questions are left undefined, include all. |
291 | if not t['questions']: | 296 | if not t['questions']: |
serve.py
@@ -149,6 +149,8 @@ class RootHandler(BaseHandler): | @@ -149,6 +149,8 @@ class RootHandler(BaseHandler): | ||
149 | # Start a given topic: /topic/... | 149 | # Start a given topic: /topic/... |
150 | # ---------------------------------------------------------------------------- | 150 | # ---------------------------------------------------------------------------- |
151 | class TopicHandler(BaseHandler): | 151 | class TopicHandler(BaseHandler): |
152 | + SUPPORTED_METHODS = ['GET'] | ||
153 | + | ||
152 | @tornado.web.authenticated | 154 | @tornado.web.authenticated |
153 | async def get(self, topic): | 155 | async def get(self, topic): |
154 | uid = self.current_user | 156 | uid = self.current_user |
templates/question.html
tools.py
@@ -102,18 +102,18 @@ class HighlightRenderer(mistune.Renderer): | @@ -102,18 +102,18 @@ class HighlightRenderer(mistune.Renderer): | ||
102 | def image(self, src, title, alt): | 102 | def image(self, src, title, alt): |
103 | alt = mistune.escape(alt, quote=True) | 103 | alt = mistune.escape(alt, quote=True) |
104 | title = mistune.escape(title or '', quote=True) | 104 | title = mistune.escape(title or '', quote=True) |
105 | - if title: | ||
106 | - caption = f'<figcaption class="figure-caption">{title}</figcaption>' | ||
107 | - else: | ||
108 | - caption = '' | ||
109 | - | ||
110 | - return f''' | ||
111 | - <figure class="figure"> | ||
112 | - <img src="/file/{src}" class="figure-img img-fluid rounded" alt="{alt}" title="{title}"> | ||
113 | - {caption} | ||
114 | - </figure> | ||
115 | - ''' | ||
116 | - # return f'<img src="/file/{src}" class="img-fluid mx-auto d-block" alt="{alt}" title="{title}">' | 105 | + # if title: |
106 | + # caption = f'<figcaption class="figure-caption">{title}</figcaption>' | ||
107 | + # else: | ||
108 | + # caption = '' | ||
109 | + | ||
110 | + # return f''' | ||
111 | + # <figure class="figure"> | ||
112 | + # <img src="/file/{src}" class="figure-img img-fluid rounded" alt="{alt}" title="{title}"> | ||
113 | + # {caption} | ||
114 | + # </figure> | ||
115 | + # ''' | ||
116 | + return f'<img src="/file/{src}" class="img-fluid mx-auto d-block" alt="{alt}" title="{title}">' | ||
117 | 117 | ||
118 | # Pass math through unaltered - mathjax does the rendering in the browser | 118 | # Pass math through unaltered - mathjax does the rendering in the browser |
119 | def block_math(self, text): | 119 | def block_math(self, text): |