Commit 7a51d4543fbf92340dbb0d5af1c9916fb054bf22

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

- fix check for nonexisting goals

- fix missing database
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   -- detectar se em courses.yaml falta declarar ficheiro. Por exemplo se houver goals que não estao em lado nenhum.
  4 +- nao esta a seguir o max_tries definido no ficheiro de dependencias.
23 5 - registar last_seen e remover os antigos de cada vez que houver um login.
24 6 - initdb da integrity error se no mesmo comando existirem alunos repetidos (p.ex em ficheiros csv diferentes ou entre csv e opcao -a)
25   -- permite definir goal, mas nao verifica se esta no grafo. rebenta no start_topic.
26 7 - double click submits twice.
27   -- nao esta a seguir o max_tries definido no ficheiro de dependencias.
28   -- impedir que quando students.db não é encontrado, crie um ficheiro vazio.
29 8 - classificacoes so devia mostrar os que ja fizeram alguma coisa
30 9 - QFactory.generate() devia fazer run da gen_async, ou remover.
31 10 - marking all options right in a radio question breaks!
... ... @@ -70,6 +49,8 @@ sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in
70 49  
71 50 # FIXED
72 51  
  52 +- impedir que quando students.db não é encontrado, crie um ficheiro vazio.
  53 +- permite definir goal, mas nao verifica se esta no grafo. rebenta no start_topic.
73 54 - 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.
74 55 - caixas com os cursos não se ajustam bem com ecran estreito.
75 56 - obter rankings por curso GET course=course_id
... ...
aprendizations/learnapp.py
... ... @@ -69,28 +69,37 @@ class LearnApp(object):
69 69  
70 70 config: Dict[str, Any] = load_yaml(courses)
71 71  
72   - # --- courses dict
73   - self.courses = config['courses']
74   - logger.info(f'Courses: {", ".join(self.courses.keys())}')
75   -
76 72 # --- topic dependencies are shared between all courses
77 73 self.deps = nx.DiGraph(prefix=prefix)
78   - logger.info('Populating graph:')
  74 + logger.info('Populating topic graph:')
79 75  
80 76 t = config.get('topics', {}) # topics defined directly in courses file
81 77 self.populate_graph(t)
82 78 logger.info(f'{len(t):>6} topics in {courses}')
83 79 for f in config.get('topics_from', []):
84 80 c = load_yaml(f) # course configuration
  81 +
  82 + # FIXME set defaults??
85 83 logger.info(f'{len(c["topics"]):>6} topics imported from {f}')
86 84 self.populate_graph(c)
87 85 logger.info(f'Graph has {len(self.deps)} topics')
88 86  
  87 + # --- courses dict
  88 + self.courses = config['courses']
  89 + logger.info(f'Courses: {", ".join(self.courses.keys())}')
  90 + for c, d in self.courses.items():
  91 + for goal in d['goals']:
  92 + if goal not in self.deps.nodes():
  93 + # logger.error(f'Goal "{goal}" of "{c}"" not in the graph')
  94 + raise LearnException(f'Goal "{goal}" from course "{c}" '
  95 + ' does not exist')
  96 +
89 97 # --- factory is a dict with question generators for all topics
90 98 self.factory: Dict[str, QFactory] = self.make_factory()
91 99  
92 100 # if graph has topics that are not in the database, add them
93   - self.db_add_missing_topics(self.deps.nodes())
  101 + self.add_missing_topics(self.deps.nodes())
  102 +
94 103  
95 104 if check:
96 105 self.sanity_check_questions()
... ... @@ -301,7 +310,7 @@ class LearnApp(object):
301 310 # ------------------------------------------------------------------------
302 311 # Fill db table 'Topic' with topics from the graph if not already there.
303 312 # ------------------------------------------------------------------------
304   - def db_add_missing_topics(self, topics: List[str]) -> None:
  313 + def add_missing_topics(self, topics: List[str]) -> None:
305 314 with self.db_session() as s:
306 315 new_topics = [Topic(id=t) for t in topics
307 316 if (t,) not in s.query(Topic.id)]
... ... @@ -317,6 +326,10 @@ class LearnApp(object):
317 326 def db_setup(self, db: str) -> None:
318 327  
319 328 logger.info(f'Checking database "{db}":')
  329 + if not path.exists(db):
  330 + raise LearnException('Database does not exist. '
  331 + 'Use "initdb-aprendizations" to create')
  332 +
320 333 engine = sa.create_engine(f'sqlite:///{db}', echo=False)
321 334 self.Session = sa.orm.sessionmaker(bind=engine)
322 335 try:
... ...
aprendizations/main.py
... ... @@ -9,7 +9,7 @@ import sys
9 9 from typing import Any, Dict
10 10  
11 11 # this project
12   -from .learnapp import LearnApp, DatabaseUnusableError
  12 +from .learnapp import LearnApp, DatabaseUnusableError, LearnException
13 13 from .serve import run_webserver
14 14 from .tools import load_yaml
15 15 from . import APP_NAME, APP_VERSION
... ... @@ -160,7 +160,7 @@ def main():
160 160 else:
161 161 logging.info('SSL certificates loaded')
162 162  
163   - # --- start application
  163 + # --- start application --------------------------------------------------
164 164 try:
165 165 learnapp = LearnApp(courses=arg.courses,
166 166 prefix=arg.prefix,
... ... @@ -178,13 +178,16 @@ def main():
178 178 '--------------------------------------------------------------',
179 179 sep='\n')
180 180 sys.exit(1)
  181 + except LearnException as e:
  182 + logging.critical(e)
  183 + sys.exit(1)
181 184 except Exception:
182 185 logging.critical('Failed to start backend.')
183 186 sys.exit(1)
184 187 else:
185 188 logging.info('LearnApp started')
186 189  
187   - # --- run webserver forever
  190 + # --- run webserver forever ----------------------------------------------
188 191 run_webserver(app=learnapp, ssl=ssl_ctx, port=arg.port, debug=arg.debug)
189 192  
190 193  
... ...
demo/astronomy.yaml
... ... @@ -3,11 +3,11 @@
3 3 # optional values applied to each topic, if undefined there
4 4 # ----------------------------------------------------------------------------
5 5  
6   -# defaults:
  6 +# defaults: FIXME not working
7 7 # file: questions.yaml
8 8 # shuffle_questions: true
9 9 # choose: 6
10   -# max_tries: 2
  10 +# max_tries: 200
11 11 # forgetting_factor: 0.97
12 12 # min_level: 0.01
13 13 # append_wrong: true
... ...