Commit f865b05a08c5e9acbe0ab92f4b0dad1d776f789f

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

allow admin to reload topic and regenerate QFactory

aprendizations/learnapp.py
@@ -78,7 +78,6 @@ class LearnApp(object): @@ -78,7 +78,6 @@ class LearnApp(object):
78 logger.info(f'{len(t):>6} topics in {courses}') 78 logger.info(f'{len(t):>6} topics in {courses}')
79 for f in config.get('topics_from', []): 79 for f in config.get('topics_from', []):
80 c = load_yaml(f) # course configuration 80 c = load_yaml(f) # course configuration
81 -  
82 # FIXME set defaults?? 81 # FIXME set defaults??
83 logger.info(f'{len(c["topics"]):>6} topics imported from {f}') 82 logger.info(f'{len(c["topics"]):>6} topics imported from {f}')
84 self.populate_graph(c) 83 self.populate_graph(c)
@@ -302,6 +301,10 @@ class LearnApp(object): @@ -302,6 +301,10 @@ class LearnApp(object):
302 # ------------------------------------------------------------------------ 301 # ------------------------------------------------------------------------
303 async def start_topic(self, uid: str, topic: str) -> None: 302 async def start_topic(self, uid: str, topic: str) -> None:
304 student = self.online[uid]['state'] 303 student = self.online[uid]['state']
  304 + if uid == '0':
  305 + logger.warning(f'Reloading "{topic}"')
  306 + self.factory.update(self.factory_for(topic))
  307 +
305 try: 308 try:
306 await student.start_topic(topic) 309 await student.start_topic(topic)
307 except Exception as e: 310 except Exception as e:
@@ -393,66 +396,78 @@ class LearnApp(object): @@ -393,66 +396,78 @@ class LearnApp(object):
393 396
394 # ------------------------------------------------------------------------ 397 # ------------------------------------------------------------------------
395 # Buils dictionary of question factories 398 # Buils dictionary of question factories
  399 + # - visits each topic in the graph,
  400 + # - adds factory for each topic.
396 # ------------------------------------------------------------------------ 401 # ------------------------------------------------------------------------
397 def make_factory(self) -> Dict[str, QFactory]: 402 def make_factory(self) -> Dict[str, QFactory]:
398 403
399 logger.info('Building questions factory:') 404 logger.info('Building questions factory:')
400 - factory = {} 405 + factory = dict()
401 g = self.deps 406 g = self.deps
402 for tref in g.nodes(): 407 for tref in g.nodes():
403 - t = g.nodes[tref] 408 + factory.update(self.factory_for(tref))
404 409
405 - # load questions as list of dicts  
406 - topicpath: str = path.join(g.graph['prefix'], tref)  
407 - try:  
408 - fullpath: str = path.join(topicpath, t['file'])  
409 - except Exception:  
410 - msg1 = f'Invalid topic "{tref}"'  
411 - msg2 = f'Check dependencies of {", ".join(g.successors(tref))}'  
412 - raise LearnException(f'{msg1}. {msg2}') 410 + logger.info(f'Factory has {len(factory)} questions')
  411 + return factory
413 412
414 - logger.debug(f' Loading {fullpath}')  
415 - try:  
416 - questions: List[QDict] = load_yaml(fullpath)  
417 - except Exception:  
418 - msg = f'Failed to load "{fullpath}"'  
419 - logger.error(msg)  
420 - raise LearnException(msg) 413 + # ------------------------------------------------------------------------
  414 + # makes factory for a single topic
  415 + # ------------------------------------------------------------------------
  416 + def factory_for(self, tref: str) -> Dict[str, QFactory]:
  417 + factory = {}
  418 + g = self.deps
  419 + t = g.nodes[tref] # get node
421 420
422 - if not isinstance(questions, list):  
423 - msg = f'File "{fullpath}" must be a list of questions'  
424 - raise LearnException(msg) 421 + # load questions as list of dicts
  422 + topicpath: str = path.join(g.graph['prefix'], tref)
  423 + try:
  424 + fullpath: str = path.join(topicpath, t['file'])
  425 + except Exception:
  426 + msg1 = f'Invalid topic "{tref}"'
  427 + msg2 = f'Check dependencies of {", ".join(g.successors(tref))}'
  428 + raise LearnException(f'{msg1}. {msg2}')
425 429
426 - # update refs to include topic as prefix.  
427 - # refs are required to be unique only within the file.  
428 - # undefined are set to topic:n, where n is the question number  
429 - # within the file  
430 - localrefs: Set[str] = set() # refs in current file  
431 - for i, q in enumerate(questions):  
432 - qref = q.get('ref', str(i)) # ref or number  
433 - if qref in localrefs:  
434 - msg = f'Duplicate ref "{qref}" in "{topicpath}"'  
435 - raise LearnException(msg)  
436 - localrefs.add(qref) 430 + logger.debug(f' Loading {fullpath}')
  431 + try:
  432 + questions: List[QDict] = load_yaml(fullpath)
  433 + except Exception:
  434 + msg = f'Failed to load "{fullpath}"'
  435 + logger.error(msg)
  436 + raise LearnException(msg)
  437 +
  438 + if not isinstance(questions, list):
  439 + msg = f'File "{fullpath}" must be a list of questions'
  440 + raise LearnException(msg)
  441 +
  442 + # update refs to include topic as prefix.
  443 + # refs are required to be unique only within the file.
  444 + # undefined are set to topic:n, where n is the question number
  445 + # within the file
  446 + localrefs: Set[str] = set() # refs in current file
  447 + for i, q in enumerate(questions):
  448 + qref = q.get('ref', str(i)) # ref or number
  449 + if qref in localrefs:
  450 + msg = f'Duplicate ref "{qref}" in "{topicpath}"'
  451 + raise LearnException(msg)
  452 + localrefs.add(qref)
437 453
438 - q['ref'] = f'{tref}:{qref}'  
439 - q['path'] = topicpath  
440 - q.setdefault('append_wrong', t['append_wrong']) 454 + q['ref'] = f'{tref}:{qref}'
  455 + q['path'] = topicpath
  456 + q.setdefault('append_wrong', t['append_wrong'])
441 457
442 - # if questions are left undefined, include all.  
443 - if not t['questions']:  
444 - t['questions'] = [q['ref'] for q in questions] 458 + # if questions are left undefined, include all.
  459 + if not t['questions']:
  460 + t['questions'] = [q['ref'] for q in questions]
445 461
446 - t['choose'] = min(t['choose'], len(t['questions'])) 462 + t['choose'] = min(t['choose'], len(t['questions']))
447 463
448 - for q in questions:  
449 - if q['ref'] in t['questions']:  
450 - factory[q['ref']] = QFactory(q)  
451 - logger.debug(f' + {q["ref"]}') 464 + for q in questions:
  465 + if q['ref'] in t['questions']:
  466 + factory[q['ref']] = QFactory(q)
  467 + logger.debug(f' + {q["ref"]}')
452 468
453 - logger.info(f'{len(t["questions"]):6} questions in {tref}') 469 + logger.info(f'{len(t["questions"]):6} questions in {tref}')
454 470
455 - logger.info(f'Factory has {len(factory)} questions')  
456 return factory 471 return factory
457 472
458 # ------------------------------------------------------------------------ 473 # ------------------------------------------------------------------------
@@ -501,7 +516,7 @@ class LearnApp(object): @@ -501,7 +516,7 @@ class LearnApp(object):
501 516
502 # ------------------------------------------------------------------------ 517 # ------------------------------------------------------------------------
503 def get_current_public_dir(self, uid: str) -> str: 518 def get_current_public_dir(self, uid: str) -> str:
504 - topic: str = self.online[uid]['state'].get_current_topic() # FIXME returns None if its the last question in the topic 519 + topic: str = self.online[uid]['state'].get_current_topic()
505 prefix: str = self.deps.graph['prefix'] 520 prefix: str = self.deps.graph['prefix']
506 return path.join(prefix, topic, 'public') 521 return path.join(prefix, topic, 'public')
507 522
aprendizations/student.py
@@ -75,12 +75,12 @@ class StudentState(object): @@ -75,12 +75,12 @@ class StudentState(object):
75 logger.debug(f'start topic "{topic}"') 75 logger.debug(f'start topic "{topic}"')
76 76
77 # avoid regenerating questions in the middle of the current topic 77 # avoid regenerating questions in the middle of the current topic
78 - if self.current_topic == topic: 78 + if self.current_topic == topic and self.uid != '0':
79 logger.info('Restarting current topic is not allowed.') 79 logger.info('Restarting current topic is not allowed.')
80 return 80 return
81 81
82 # do not allow locked topics 82 # do not allow locked topics
83 - if self.is_locked(topic): 83 + if self.is_locked(topic) and self.uid != '0':
84 logger.debug(f'is locked "{topic}"') 84 logger.debug(f'is locked "{topic}"')
85 return 85 return
86 86