Commit 9161ff755aa99eb0ba6ef8e0cd109e56a68422fd

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

choose number of questions per topic in the yaml configuration

@@ -5,10 +5,6 @@ @@ -5,10 +5,6 @@
5 "file: questions.yaml" (default questions.yaml) 5 "file: questions.yaml" (default questions.yaml)
6 "shuffle: True/False" (default False) 6 "shuffle: True/False" (default False)
7 "choose: 6" (default tudo) 7 "choose: 6" (default tudo)
8 -- SQLAlchemy error (rollback em accao no final de um topico):  
9 - Users/mjsb/Library/Python/3.7/lib/python/site-packages/sqlalchemy/sql/crud.py:700: SAWarning: Column 'studenttopic.student_id' is marked as a member of the primary key for table 'studenttopic', but has no Python-side or server-side default generator indicated, nor does it indicate 'autoincrement=True' or 'nullable=True', and no explicit value is passed. Primary key columns typically may not store NULL. Note that as of SQLAlchemy 1.1, 'autoincrement=True' must be indicated explicitly for composite (e.g. multicolumn) primary keys if AUTO_INCREMENT/SERIAL/IDENTITY behavior is expected for one of the columns in the primary key. CREATE TABLE statements are impacted by this change as well on most backends.  
10 - util.warn(msg)  
11 -  
12 - quando a pergunta devolve comments, este é apresentado, mas fica persistente nas tentativas seguintes. devia ser limpo apos a segunda submissao. 8 - quando a pergunta devolve comments, este é apresentado, mas fica persistente nas tentativas seguintes. devia ser limpo apos a segunda submissao.
13 - a opcao max_tries na especificacao das perguntas é cumbersome... usar antes tries? 9 - a opcao max_tries na especificacao das perguntas é cumbersome... usar antes tries?
14 - tabelas nas perguntas radio/checkbox não ocupam todo o espaço como em question. 10 - tabelas nas perguntas radio/checkbox não ocupam todo o espaço como em question.
demo/demo.yaml
1 title: Example 1 title: Example
2 database: students.db 2 database: students.db
3 3
  4 +# default global values
  5 +file: questions.yaml
  6 +shuffle: True
  7 +choose: 6
  8 +
4 # path prefix applied to the topics 9 # path prefix applied to the topics
5 path: ./demo 10 path: ./demo
6 11
@@ -16,6 +21,9 @@ topics: @@ -16,6 +21,9 @@ topics:
16 # topic without dependencies 21 # topic without dependencies
17 math: 22 math:
18 name: Matemática 23 name: Matemática
  24 + file: questions.yaml
  25 + choose: 6
  26 + shuffle: True
19 27
20 # topic with one dependency 28 # topic with one dependency
21 solar_system: 29 solar_system:
@@ -32,8 +32,8 @@ class StudentKnowledge(object): @@ -32,8 +32,8 @@ class StudentKnowledge(object):
32 self.topic_sequence = self.recommend_topic_sequence() # ['a', 'b', ...] 32 self.topic_sequence = self.recommend_topic_sequence() # ['a', 'b', ...]
33 self.unlock_topics() # whose dependencies have been done 33 self.unlock_topics() # whose dependencies have been done
34 self.current_topic = None 34 self.current_topic = None
35 -  
36 - self.MAX_QUESTIONS = 6 # FIXME get from yaml configuration file?? 35 + print(deps.graph)
  36 + self.MAX_QUESTIONS = deps.graph['config'].get('choose', 10)
37 37
38 # ------------------------------------------------------------------------ 38 # ------------------------------------------------------------------------
39 # Updates the proficiency levels of the topics, with forgetting factor 39 # Updates the proficiency levels of the topics, with forgetting factor
@@ -29,11 +29,9 @@ class LearnAppException(Exception): @@ -29,11 +29,9 @@ class LearnAppException(Exception):
29 # ============================================================================ 29 # ============================================================================
30 # helper functions 30 # helper functions
31 # ============================================================================ 31 # ============================================================================
32 -async def check_password(try_pw, password):  
33 - try_pw = try_pw.encode('utf-8') 32 +async def check_password(try_pw, pw):
34 loop = asyncio.get_running_loop() 33 loop = asyncio.get_running_loop()
35 - hashed_pw = await loop.run_in_executor(None, bcrypt.hashpw, try_pw, password)  
36 - return password == hashed_pw 34 + return pw == await loop.run_in_executor(None, bcrypt.hashpw, try_pw.encode('utf-8'), pw)
37 35
38 def bcrypt_hash_gen(pw): 36 def bcrypt_hash_gen(pw):
39 return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) 37 return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt())
@@ -62,16 +60,9 @@ class LearnApp(object): @@ -62,16 +60,9 @@ class LearnApp(object):
62 def __init__(self, config_file): 60 def __init__(self, config_file):
63 # state of online students 61 # state of online students
64 self.online = {} 62 self.online = {}
65 -  
66 - config = load_yaml(config_file)  
67 -  
68 - # connect to database and checks for registered students  
69 - self.db_setup(config['database'])  
70 -  
71 - # dependency graph shared by all students 63 + config = load_yaml(config_file[0])
  64 + self.db_setup(config['database']) # setup and check students
72 self.deps = build_dependency_graph(config) 65 self.deps = build_dependency_graph(config)
73 -  
74 - # add topics from dependency graph to the database, if missing  
75 self.db_add_missing_topics(self.deps.nodes()) 66 self.db_add_missing_topics(self.deps.nodes())
76 67
77 # ------------------------------------------------------------------------ 68 # ------------------------------------------------------------------------
@@ -98,8 +89,7 @@ class LearnApp(object): @@ -98,8 +89,7 @@ class LearnApp(object):
98 with self.db_session() as s: 89 with self.db_session() as s:
99 tt = s.query(StudentTopic).filter_by(student_id=uid) 90 tt = s.query(StudentTopic).filter_by(student_id=uid)
100 91
101 - state = {t.topic_id:  
102 - { 92 + state = {t.topic_id: {
103 'level': t.level, 93 'level': t.level,
104 'date': datetime.strptime(t.date, "%Y-%m-%d %H:%M:%S.%f") 94 'date': datetime.strptime(t.date, "%Y-%m-%d %H:%M:%S.%f")
105 } for t in tt} 95 } for t in tt}
@@ -108,8 +98,7 @@ class LearnApp(object): @@ -108,8 +98,7 @@ class LearnApp(object):
108 'number': uid, 98 'number': uid,
109 'name': name, 99 'name': name,
110 'state': StudentKnowledge(self.deps, state=state), 100 'state': StudentKnowledge(self.deps, state=state),
111 - # 'learning': None, # current topic learning  
112 - } 101 + }
113 102
114 else: 103 else:
115 logger.info(f'User "{uid}" wrong password!') 104 logger.info(f'User "{uid}" wrong password!')
@@ -209,23 +198,6 @@ class LearnApp(object): @@ -209,23 +198,6 @@ class LearnApp(object):
209 logger.info(f'User "{uid}" started "{topic}"') 198 logger.info(f'User "{uid}" started "{topic}"')
210 199
211 # ------------------------------------------------------------------------ 200 # ------------------------------------------------------------------------
212 - # Start new topic  
213 - # ------------------------------------------------------------------------  
214 - # def start_learning(self, uid, topic):  
215 - # try:  
216 - # ok = self.online[uid]['state'].init_learning(topic)  
217 - # except KeyError as e:  
218 - # logger.warning(  
219 - # f'User "{uid}" tried to open nonexistent topic: "{topic}"')  
220 - # raise e  
221 - # else:  
222 - # if ok:  
223 - # logger.info(f'User "{uid}" started "{topic}"')  
224 - # else:  
225 - # logger.warning(f'User "{uid}" denied locked "{topic}"')  
226 - # return ok  
227 -  
228 - # ------------------------------------------------------------------------  
229 # Fill db table 'Topic' with topics from the graph if not already there. 201 # Fill db table 'Topic' with topics from the graph if not already there.
230 # ------------------------------------------------------------------------ 202 # ------------------------------------------------------------------------
231 def db_add_missing_topics(self, nn): 203 def db_add_missing_topics(self, nn):
@@ -330,7 +302,7 @@ def build_dependency_graph(config={}): @@ -330,7 +302,7 @@ def build_dependency_graph(config={}):
330 prefix = config.get('path', '.') 302 prefix = config.get('path', '.')
331 title = config.get('title', '') 303 title = config.get('title', '')
332 database = config.get('database', 'students.db') 304 database = config.get('database', 'students.db')
333 - g = nx.DiGraph(path=prefix, title=title, database=database) 305 + g = nx.DiGraph(path=prefix, title=title, database=database, config=config)
334 306
335 # iterate over topics and build graph 307 # iterate over topics and build graph
336 topics = config.get('topics', {}) 308 topics = config.get('topics', {})
@@ -356,7 +356,7 @@ def main(): @@ -356,7 +356,7 @@ def main():
356 # --- start application 356 # --- start application
357 logging.info('Starting App') 357 logging.info('Starting App')
358 try: 358 try:
359 - learnapp = LearnApp(arg.conffile[0]) 359 + learnapp = LearnApp(arg.conffile)
360 except Exception as e: 360 except Exception as e:
361 logging.critical('Failed to start backend application') 361 logging.critical('Failed to start backend application')
362 raise e 362 raise e