Commit 2b30310aa1ed2ecbeadddb8d279177df120d5f0b

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

- path prefix is now given on commandline instead of configuration file. The ide…

…a is to avoid incompatible prefixes on multiple yaml files.
- some changes in async functions
demo/demo.yaml
1 title: Example 1 title: Example
2 database: students.db 2 database: students.db
3 -path: ./demo 3 +# path: ./demo
4 4
5 # values applie to each topic, if undefined there 5 # values applie to each topic, if undefined there
6 # default values are: file=question.yaml, shuffle=True, choose: all 6 # default values are: file=question.yaml, shuffle=True, choose: all
@@ -17,7 +17,6 @@ forgetting_factor: 0.99 @@ -17,7 +17,6 @@ forgetting_factor: 0.99
17 # - B 17 # - B
18 # - C 18 # - C
19 topics: 19 topics:
20 -  
21 # topic without dependencies 20 # topic without dependencies
22 math: 21 math:
23 name: Matemática 22 name: Matemática
demo/solar_system/questions.yaml
@@ -38,13 +38,13 @@ @@ -38,13 +38,13 @@
38 # correct: !regex '[Ss]aturno' 38 # correct: !regex '[Ss]aturno'
39 39
40 # --------------------------------------------------------------------------- 40 # ---------------------------------------------------------------------------
41 -- ref: first_3_planets  
42 - type: textarea  
43 - title: Sistema solar  
44 - text: Escreva o nome dos três planetas mais próximos do Sol. (Exemplo `A, B e C`)  
45 - correct: correct-first_3_planets.py  
46 - # correct: correct-timeout.py  
47 - # opcional  
48 - answer: Vulcano, Krypton, Plutão  
49 - lines: 3  
50 - timeout: 50 41 +# - ref: first_3_planets
  42 +# type: textarea
  43 +# title: Sistema solar
  44 +# text: Escreva o nome dos três planetas mais próximos do Sol. (Exemplo `A, B e C`)
  45 +# correct: correct-first_3_planets.py
  46 +# # correct: correct-timeout.py
  47 +# # opcional
  48 +# answer: Vulcano, Krypton, Plutão
  49 +# lines: 3
  50 +# timeout: 50
@@ -70,7 +70,7 @@ class StudentKnowledge(object): @@ -70,7 +70,7 @@ class StudentKnowledge(object):
70 # questions: list of generated questions to do in the topic 70 # questions: list of generated questions to do in the topic
71 # current_question: the current question to be presented 71 # current_question: the current question to be presented
72 # ------------------------------------------------------------------------ 72 # ------------------------------------------------------------------------
73 - def init_topic(self, topic): 73 + async def init_topic(self, topic):
74 logger.debug(f'StudentKnowledge.init_topic({topic})') 74 logger.debug(f'StudentKnowledge.init_topic({topic})')
75 75
76 # do not allow locked topics 76 # do not allow locked topics
@@ -88,7 +88,7 @@ class StudentKnowledge(object): @@ -88,7 +88,7 @@ class StudentKnowledge(object):
88 self.wrong_answers = 0 88 self.wrong_answers = 0
89 89
90 # select a random set of questions for this topic 90 # select a random set of questions for this topic
91 - size = min(self.MAX_QUESTIONS, len(questionlist)) # number of questions 91 + size = len(questionlist) # number of questions FIXME get from topic config
92 questionlist = random.sample(questionlist, k=size) 92 questionlist = random.sample(questionlist, k=size)
93 logger.debug(f'Questions: {", ".join(questionlist)}') 93 logger.debug(f'Questions: {", ".join(questionlist)}')
94 94
@@ -22,8 +22,8 @@ from tools import load_yaml @@ -22,8 +22,8 @@ from tools import load_yaml
22 logger = logging.getLogger(__name__) 22 logger = logging.getLogger(__name__)
23 23
24 # ============================================================================ 24 # ============================================================================
25 -class LearnAppException(Exception):  
26 - pass 25 +# class LearnAppException(Exception):
  26 +# pass
27 27
28 28
29 # ============================================================================ 29 # ============================================================================
@@ -61,11 +61,11 @@ class LearnApp(object): @@ -61,11 +61,11 @@ class LearnApp(object):
61 session.close() 61 session.close()
62 62
63 # ------------------------------------------------------------------------ 63 # ------------------------------------------------------------------------
64 - def __init__(self, config_files): 64 + def __init__(self, config_files, prefix):
65 self.db_setup() # setup database and check students 65 self.db_setup() # setup database and check students
66 self.online = dict() # online students 66 self.online = dict() # online students
67 67
68 - self.deps = nx.DiGraph() 68 + self.deps = nx.DiGraph(prefix=prefix)
69 for c in config_files: 69 for c in config_files:
70 self.populate_graph(c) 70 self.populate_graph(c)
71 71
@@ -192,9 +192,9 @@ class LearnApp(object): @@ -192,9 +192,9 @@ class LearnApp(object):
192 # Start new topic 192 # Start new topic
193 # ------------------------------------------------------------------------ 193 # ------------------------------------------------------------------------
194 async def start_topic(self, uid, topic): 194 async def start_topic(self, uid, topic):
195 - loop = asyncio.get_running_loop() 195 + student = self.online[uid]['state']
196 try: 196 try:
197 - await loop.run_in_executor(None, self.online[uid]['state'].init_topic, topic) 197 + await student.init_topic(topic)
198 except KeyError as e: 198 except KeyError as e:
199 logger.warning(f'User "{uid}" tried to open nonexistent topic: "{topic}"') 199 logger.warning(f'User "{uid}" tried to open nonexistent topic: "{topic}"')
200 raise e 200 raise e
@@ -277,9 +277,9 @@ class LearnApp(object): @@ -277,9 +277,9 @@ class LearnApp(object):
277 # ------------------------------------------------------------------------ 277 # ------------------------------------------------------------------------
278 def get_current_public_dir(self, uid): 278 def get_current_public_dir(self, uid):
279 topic = self.online[uid]['state'].get_current_topic() 279 topic = self.online[uid]['state'].get_current_topic()
280 - p = self.deps.graph['path']  
281 - return path.join(p, topic, 'public') 280 + p = self.deps.graph['prefix'] # FIXME not defined!!!
282 281
  282 + return path.join(p, topic, 'public')
283 283
284 284
285 # ============================================================================ 285 # ============================================================================
@@ -304,21 +304,12 @@ class LearnApp(object): @@ -304,21 +304,12 @@ class LearnApp(object):
304 g = self.deps # the graph 304 g = self.deps # the graph
305 config = load_yaml(conffile) # course configuration 305 config = load_yaml(conffile) # course configuration
306 306
307 - # set attributes of the graph  
308 - prefix = path.expanduser(config.get('path', '.'))  
309 - # title = config.get('title', '')  
310 - # database = config.get('database', 'students.db')  
311 -  
312 # default attributes that apply to the topics 307 # default attributes that apply to the topics
313 default_file = config.get('file', 'questions.yaml') 308 default_file = config.get('file', 'questions.yaml')
314 default_shuffle = config.get('shuffle', True) 309 default_shuffle = config.get('shuffle', True)
315 default_choose = config.get('choose', 9999) 310 default_choose = config.get('choose', 9999)
316 default_forgetting_factor = config.get('forgetting_factor', 1.0) 311 default_forgetting_factor = config.get('forgetting_factor', 1.0)
317 312
318 - # create graph  
319 - # g = nx.DiGraph(path=prefix, title=title, database=database, config=config)  
320 -  
321 -  
322 # iterate over topics and populate graph 313 # iterate over topics and populate graph
323 topics = config.get('topics', {}) 314 topics = config.get('topics', {})
324 tcount = qcount = 0 # topic and question counters 315 tcount = qcount = 0 # topic and question counters
@@ -331,12 +322,12 @@ class LearnApp(object): @@ -331,12 +322,12 @@ class LearnApp(object):
331 g.add_node(tref) 322 g.add_node(tref)
332 t = g.node[tref] # current topic node 323 t = g.node[tref] # current topic node
333 324
334 - topicpath = path.join(prefix, tref) 325 + topicpath = path.join(g.graph['prefix'], tref)
335 326
336 t['type'] = attr.get('type', 'topic') 327 t['type'] = attr.get('type', 'topic')
337 t['name'] = attr.get('name', tref) 328 t['name'] = attr.get('name', tref)
338 - t['path'] = topicpath  
339 - t['file'] = attr.get('file', default_file) 329 + t['path'] = topicpath # prefix/topic
  330 + t['file'] = attr.get('file', default_file) # questions.yaml
340 t['shuffle'] = attr.get('shuffle', default_shuffle) 331 t['shuffle'] = attr.get('shuffle', default_shuffle)
341 t['forgetting_factor'] = attr.get('forgetting_factor', default_forgetting_factor) 332 t['forgetting_factor'] = attr.get('forgetting_factor', default_forgetting_factor)
342 g.add_edges_from((d,tref) for d in attr.get('deps', [])) 333 g.add_edges_from((d,tref) for d in attr.get('deps', []))
@@ -154,7 +154,7 @@ class QuestionCheckbox(Question): @@ -154,7 +154,7 @@ class QuestionCheckbox(Question):
154 # set defaults if missing 154 # set defaults if missing
155 self.set_defaults({ 155 self.set_defaults({
156 'text': '', 156 'text': '',
157 - 'correct': [0.0] * n, # useful for questionaries 157 + 'correct': [1.0] * n, # Using 0.0 breaks the (right, wrong) options
158 'shuffle': True, 158 'shuffle': True,
159 'discount': True, 159 'discount': True,
160 'choose': n, # number of options 160 'choose': n, # number of options
@@ -304,9 +304,11 @@ def main(): @@ -304,9 +304,11 @@ def main():
304 # --- Commandline argument parsing 304 # --- Commandline argument parsing
305 argparser = argparse.ArgumentParser(description='Server for online learning. Enrolled students and topics have to be previously configured. Please read the documentation included with this software before running the server.') 305 argparser = argparse.ArgumentParser(description='Server for online learning. Enrolled students and topics have to be previously configured. Please read the documentation included with this software before running the server.')
306 argparser.add_argument('conffile', type=str, nargs='+', 306 argparser.add_argument('conffile', type=str, nargs='+',
307 - help='Topics configuration file in YAML format.') # FIXME only one 307 + help='Topics configuration file in YAML format.')
  308 + argparser.add_argument('--prefix', type=str, default='demo',
  309 + help='Path prefix under which the topic directories can be found, e.g. ~/topics')
308 argparser.add_argument('--port', type=int, default=8443, 310 argparser.add_argument('--port', type=int, default=8443,
309 - help='Port to be used by the HTTPS server') 311 + help='Port to be used by the HTTPS server, e.g. 8443')
310 argparser.add_argument('--debug', action='store_true', 312 argparser.add_argument('--debug', action='store_true',
311 help='Enable debug messages') 313 help='Enable debug messages')
312 arg = argparser.parse_args() 314 arg = argparser.parse_args()
@@ -327,7 +329,7 @@ def main(): @@ -327,7 +329,7 @@ def main():
327 # --- start application 329 # --- start application
328 logging.info('Starting App') 330 logging.info('Starting App')
329 try: 331 try:
330 - learnapp = LearnApp(arg.conffile) 332 + learnapp = LearnApp(arg.conffile, prefix=arg.prefix)
331 except Exception as e: 333 except Exception as e:
332 logging.critical('Failed to start backend application') 334 logging.critical('Failed to start backend application')
333 raise e 335 raise e