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

1 # Specify filepatterns you want git to ignore. 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 .DS_Store 8 .DS_Store
8 demo/.DS_Store 9 demo/.DS_Store
9 demo/solar_system/.DS_Store 10 demo/solar_system/.DS_Store
1 1
2 # BUGS 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 - 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. 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 - registar last_seen e remover os antigos de cada vez que houver um login. 26 - registar last_seen e remover os antigos de cada vez que houver um login.
6 - initdb da integrity error se no mesmo comando existirem alunos repetidos (p.ex em ficheiros csv diferentes ou entre csv e opcao -a) 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,7 +352,7 @@ class LearnApp(object):
352 topics: Dict[str, Dict] = config.get('topics', {}) 352 topics: Dict[str, Dict] = config.get('topics', {})
353 g.add_nodes_from(topics.keys()) 353 g.add_nodes_from(topics.keys())
354 for tref, attr in topics.items(): 354 for tref, attr in topics.items():
355 - logger.debug(f' + {tref}...') 355 + logger.debug(f' + {tref}')
356 for d in attr.get('deps', []): 356 for d in attr.get('deps', []):
357 if d not in g.nodes(): 357 if d not in g.nodes():
358 logger.error(f'Topic "{tref}" depends on "{d}" but it ' 358 logger.error(f'Topic "{tref}" depends on "{d}" but it '
@@ -370,7 +370,6 @@ class LearnApp(object): @@ -370,7 +370,6 @@ class LearnApp(object):
370 370
371 t['path'] = path.join(g.graph['prefix'], tref) # prefix/topic 371 t['path'] = path.join(g.graph['prefix'], tref) # prefix/topic
372 372
373 -  
374 # ======================================================================== 373 # ========================================================================
375 # methods that do not change state (pure functions) 374 # methods that do not change state (pure functions)
376 # ======================================================================== 375 # ========================================================================
@@ -379,6 +378,7 @@ class LearnApp(object): @@ -379,6 +378,7 @@ class LearnApp(object):
379 # Buils dictionary of question factories 378 # Buils dictionary of question factories
380 # ------------------------------------------------------------------------ 379 # ------------------------------------------------------------------------
381 def make_factory(self) -> Dict[str, QFactory]: 380 def make_factory(self) -> Dict[str, QFactory]:
  381 +
382 logger.info('Building questions factory:') 382 logger.info('Building questions factory:')
383 factory: Dict[str, QFactory] = {} 383 factory: Dict[str, QFactory] = {}
384 g = self.deps 384 g = self.deps
aprendizations/questions.py
@@ -234,7 +234,8 @@ class QuestionText(Question): @@ -234,7 +234,8 @@ class QuestionText(Question):
234 234
235 self.set_defaults(QDict({ 235 self.set_defaults(QDict({
236 'text': '', 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 # make sure its always a list of possible correct answers 241 # make sure its always a list of possible correct answers
@@ -244,12 +245,36 @@ class QuestionText(Question): @@ -244,12 +245,36 @@ class QuestionText(Question):
244 # make sure all elements of the list are strings 245 # make sure all elements of the list are strings
245 self['correct'] = [str(a) for a in self['correct']] 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 def correct(self) -> None: 272 def correct(self) -> None:
249 super().correct() 273 super().correct()
250 274
251 if self['answer'] is not None: 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,7 +434,7 @@ def run_webserver(app,
434 except Exception: 434 except Exception:
435 logger.critical('Failed to start web application.') 435 logger.critical('Failed to start web application.')
436 raise 436 raise
437 - sys.exit(1) 437 + # sys.exit(1)
438 else: 438 else:
439 logger.info('Web application started (tornado.web.Application)') 439 logger.info('Web application started (tornado.web.Application)')
440 440
aprendizations/student.py
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 from datetime import datetime 3 from datetime import datetime
4 import logging 4 import logging
5 import random 5 import random
6 -from typing import Dict, List, Optional, Tuple 6 +from typing import List, Optional, Tuple
7 7
8 # third party libraries 8 # third party libraries
9 import networkx as nx 9 import networkx as nx
@@ -176,7 +176,7 @@ class StudentState(object): @@ -176,7 +176,7 @@ class StudentState(object):
176 dt = now - s['date'] 176 dt = now - s['date']
177 try: 177 try:
178 forgetting_factor = self.deps.nodes[tref]['forgetting_factor'] 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 except KeyError: 180 except KeyError:
181 logger.warning(f'Topic {tref} is not on the graph!') 181 logger.warning(f'Topic {tref} is not on the graph!')
182 182