Commit dbdd58fe15e1cd88fb28cf6a13f13b5447cdca79

Authored by Miguel Barão
1 parent bf8ade7a
Exists in master and in 1 other branch dev

- 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.
BUGS.md
1 1  
2 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 8 - mostrar feedback/solucoes quando acerta, ou excede max tries.
5 9 - default prefix should be obtained from each course (yaml conf)?
6 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 71 # questions: list of generated questions to do in the topic
72 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 78 # do not allow locked topics
78 79 if self.is_locked(topic):
79 80 logger.debug(f'Topic {topic} is locked')
80   - return
  81 + return False
81 82  
82 83 # starting new topic
83 84 self.current_topic = topic
... ... @@ -85,7 +86,11 @@ class StudentKnowledge(object):
85 86 self.wrong_answers = 0
86 87  
87 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 94 logger.debug(f'Questions: {", ".join(questions)}')
90 95  
91 96 # generate instances of questions
... ... @@ -94,6 +99,7 @@ class StudentKnowledge(object):
94 99  
95 100 # get first question
96 101 self.next_question()
  102 + return True
97 103  
98 104  
99 105 # ------------------------------------------------------------------------
... ... @@ -140,8 +146,10 @@ class StudentKnowledge(object):
140 146 logger.debug(f'Wrong answers = {self.wrong_answers}; Tries = {self.current_question["tries"]}')
141 147  
142 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 153 if self.next_question() is None:
146 154 action = 'finished_topic'
147 155 else:
... ... @@ -165,7 +173,8 @@ class StudentKnowledge(object):
165 173 self.finish_topic()
166 174 else:
167 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 178 logger.debug(f'Next question is "{self.current_question["ref"]}"')
170 179  
171 180 return self.current_question # question or None
... ...
learnapp.py
... ... @@ -186,7 +186,7 @@ class LearnApp(object):
186 186 async def start_topic(self, uid, topic):
187 187 student = self.online[uid]['state']
188 188 try:
189   - await student.init_topic(topic)
  189 + await student.start_topic(topic)
190 190 except KeyError as e:
191 191 logger.warning(f'User "{uid}" tried to open nonexistent topic: "{topic}"')
192 192 raise e
... ... @@ -242,6 +242,8 @@ class LearnApp(object):
242 242 default_shuffle = config.get('shuffle', True)
243 243 default_choose = config.get('choose', 9999)
244 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 248 # iterate over topics and populate graph
247 249 topics = config.get('topics', {})
... ... @@ -257,8 +259,10 @@ class LearnApp(object):
257 259 t['path'] = path.join(g.graph['prefix'], tref) # prefix/topic
258 260 t['file'] = attr.get('file', default_file) # questions.yaml
259 261 t['shuffle'] = attr.get('shuffle', default_shuffle)
  262 + t['max_tries'] = attr.get('max_tries', default_maxtries)
260 263 t['forgetting_factor'] = attr.get('forgetting_factor', default_forgetting_factor)
261 264 t['choose'] = attr.get('choose', default_choose)
  265 + t['append_wrong'] = attr.get('append_wrong', default_append_wrong)
262 266 t['questions'] = attr.get('questions', [])
263 267  
264 268 logger.info(f'Loaded {g.number_of_nodes()} topics')
... ... @@ -286,6 +290,7 @@ class LearnApp(object):
286 290 qref = q.get('ref', str(i)) # ref or number
287 291 q['ref'] = tref + ':' + qref
288 292 q['path'] = topicpath
  293 + q.setdefault('append_wrong', t['append_wrong'])
289 294  
290 295 # if questions are left undefined, include all.
291 296 if not t['questions']:
... ...
serve.py
... ... @@ -149,6 +149,8 @@ class RootHandler(BaseHandler):
149 149 # Start a given topic: /topic/...
150 150 # ----------------------------------------------------------------------------
151 151 class TopicHandler(BaseHandler):
  152 + SUPPORTED_METHODS = ['GET']
  153 +
152 154 @tornado.web.authenticated
153 155 async def get(self, topic):
154 156 uid = self.current_user
... ...
templates/question.html
1 1 {% autoescape %}
2 2  
3   -<h4 class="page-header">{{ question['title'] }}</h4>
  3 +<h2 class="page-header">{{ question['title'] }}</h4>
4 4  
5 5 <div id="text">
6 6 {{ md(question['text']) }}
... ...
tools.py
... ... @@ -102,18 +102,18 @@ class HighlightRenderer(mistune.Renderer):
102 102 def image(self, src, title, alt):
103 103 alt = mistune.escape(alt, quote=True)
104 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 118 # Pass math through unaltered - mathjax does the rendering in the browser
119 119 def block_math(self, text):
... ...