Commit 2265a158598c1603fc503e0f79e312c41b0193bc

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

new transform option in text questions

.gitignore
1 1 # Specify filepatterns you want git to ignore.
2   -/aprendizations.egg-info/
3   -/aprendizations/__pycache__/
4   -/demo/students.db
5   -/node_modules/
6   -/.mypy_cache/
  2 +aprendizations.egg-info/
  3 +aprendizations/__pycache__/
  4 +aprendizations/.mypy_cache/
  5 +demo/students.db
  6 +node_modules/
  7 +.mypy_cache/
7 8 .DS_Store
8 9 demo/.DS_Store
9 10 demo/solar_system/.DS_Store
... ...
BUGS.md
1 1  
2 2 # BUGS
3 3  
  4 +
  5 +Traceback (most recent call last):
  6 + File "/home/mjsb/.local/lib/python3.7/site-packages/tornado/web.py", line 1697, in _execute
  7 + result = method(*self.path_args, **self.path_kwargs)
  8 + File "/home/mjsb/.local/lib/python3.7/site-packages/tornado/web.py", line 3174, in wrapper
  9 + return method(self, *args, **kwargs)
  10 + File "/usr/home/mjsb/Work/Projects/aprendizations/aprendizations/serve.py", line 213, in get
  11 + self.learn.start_course(uid, course)
  12 + File "/usr/home/mjsb/Work/Projects/aprendizations/aprendizations/learnapp.py", line 275, in start_course
  13 + student.start_course(course)
  14 + File "/usr/home/mjsb/Work/Projects/aprendizations/aprendizations/student.py", line 57, in start_course
  15 + self.topic_sequence = self.recommend_topic_sequence(topics)
  16 + File "/usr/home/mjsb/Work/Projects/aprendizations/aprendizations/student.py", line 216, in recommend_topic_sequence
  17 + ts.update(nx.ancestors(G, t))
  18 + File "/home/mjsb/.local/lib/python3.7/site-packages/networkx/algorithms/dag.py", line 92, in ancestors
  19 + raise nx.NetworkXError("The node %s is not in the graph." % source)
  20 +networkx.exception.NetworkXError: The node programming/languages/pseudo-tcg/functions-produtorio is not in the graph.
  21 +
  22 +
  23 +
  24 +- detectar se em courses.yaml falta declarar ficheiro. Por exemplo se houver goals que não estao em lado nenhum.
4 25 - se num topico, a ultima pergunta tem imagens, o servidor nao fornece as imagengs porque o current_topic passa a None antes de carregar no botao continuar. O caminho é prefix+None e dá erro.
5 26 - registar last_seen e remover os antigos de cada vez que houver um login.
6 27 - initdb da integrity error se no mesmo comando existirem alunos repetidos (p.ex em ficheiros csv diferentes ou entre csv e opcao -a)
... ...
aprendizations/learnapp.py
... ... @@ -352,7 +352,7 @@ class LearnApp(object):
352 352 topics: Dict[str, Dict] = config.get('topics', {})
353 353 g.add_nodes_from(topics.keys())
354 354 for tref, attr in topics.items():
355   - logger.debug(f' + {tref}...')
  355 + logger.debug(f' + {tref}')
356 356 for d in attr.get('deps', []):
357 357 if d not in g.nodes():
358 358 logger.error(f'Topic "{tref}" depends on "{d}" but it '
... ... @@ -370,7 +370,6 @@ class LearnApp(object):
370 370  
371 371 t['path'] = path.join(g.graph['prefix'], tref) # prefix/topic
372 372  
373   -
374 373 # ========================================================================
375 374 # methods that do not change state (pure functions)
376 375 # ========================================================================
... ... @@ -379,6 +378,7 @@ class LearnApp(object):
379 378 # Buils dictionary of question factories
380 379 # ------------------------------------------------------------------------
381 380 def make_factory(self) -> Dict[str, QFactory]:
  381 +
382 382 logger.info('Building questions factory:')
383 383 factory: Dict[str, QFactory] = {}
384 384 g = self.deps
... ...
aprendizations/questions.py
... ... @@ -234,7 +234,8 @@ class QuestionText(Question):
234 234  
235 235 self.set_defaults(QDict({
236 236 'text': '',
237   - 'correct': [],
  237 + 'correct': [], # no correct answers, always wrong
  238 + 'transform': [], # transformations applied to the answer, in order
238 239 }))
239 240  
240 241 # make sure its always a list of possible correct answers
... ... @@ -244,12 +245,36 @@ class QuestionText(Question):
244 245 # make sure all elements of the list are strings
245 246 self['correct'] = [str(a) for a in self['correct']]
246 247  
  248 + # make sure that the answers are invariant with respect to the filters
  249 + if any(c != self.transform(c) for c in self['correct']):
  250 + logger.warning(f'in "{self["ref"]}", correct answers are not '
  251 + 'invariant wrt transformations')
  252 +
  253 + # ------------------------------------------------------------------------
  254 + # apply optional filters to the answer
  255 + def transform(self, ans):
  256 + for f in self['transform']:
  257 + if f == 'remove_space': # removes all spaces
  258 + ans = ans.replace(' ', '')
  259 + elif f == 'trim': # removes spaces around
  260 + ans = ans.strip()
  261 + elif f == 'normalize_space': # replaces multiple spaces by one
  262 + ans = re.sub(r'\s+', ' ', ans.strip())
  263 + elif f == 'lower': # convert to lowercase
  264 + ans = ans.lower()
  265 + elif f == 'upper': # convert to uppercase
  266 + ans = ans.upper()
  267 + else:
  268 + logger.warning(f'in "{self["ref"]}", unknown transform "{f}"')
  269 + return ans
  270 +
247 271 # ------------------------------------------------------------------------
248 272 def correct(self) -> None:
249 273 super().correct()
250 274  
251 275 if self['answer'] is not None:
252   - self['grade'] = 1.0 if self['answer'] in self['correct'] else 0.0
  276 + answer = self.transform(self['answer']) # apply transformations
  277 + self['grade'] = 1.0 if answer in self['correct'] else 0.0
253 278  
254 279  
255 280 # ============================================================================
... ...
aprendizations/serve.py
... ... @@ -434,7 +434,7 @@ def run_webserver(app,
434 434 except Exception:
435 435 logger.critical('Failed to start web application.')
436 436 raise
437   - sys.exit(1)
  437 + # sys.exit(1)
438 438 else:
439 439 logger.info('Web application started (tornado.web.Application)')
440 440  
... ...
aprendizations/student.py
... ... @@ -3,7 +3,7 @@
3 3 from datetime import datetime
4 4 import logging
5 5 import random
6   -from typing import Dict, List, Optional, Tuple
  6 +from typing import List, Optional, Tuple
7 7  
8 8 # third party libraries
9 9 import networkx as nx
... ... @@ -176,7 +176,7 @@ class StudentState(object):
176 176 dt = now - s['date']
177 177 try:
178 178 forgetting_factor = self.deps.nodes[tref]['forgetting_factor']
179   - s['level'] *= forgetting_factor ** dt.days # forgetting factor
  179 + s['level'] *= forgetting_factor ** dt.days # forgetting factor
180 180 except KeyError:
181 181 logger.warning(f'Topic {tref} is not on the graph!')
182 182  
... ...