Commit 7a51d4543fbf92340dbb0d5af1c9916fb054bf22
1 parent
027c621a
Exists in
master
and in
1 other branch
- fix check for nonexisting goals
- fix missing database
Showing
4 changed files
with
31 additions
and
34 deletions
Show diff stats
BUGS.md
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 | -- 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 | - registar last_seen e remover os antigos de cada vez que houver um login. | 5 | - registar last_seen e remover os antigos de cada vez que houver um login. |
24 | - initdb da integrity error se no mesmo comando existirem alunos repetidos (p.ex em ficheiros csv diferentes ou entre csv e opcao -a) | 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 | - double click submits twice. | 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 | - classificacoes so devia mostrar os que ja fizeram alguma coisa | 8 | - classificacoes so devia mostrar os que ja fizeram alguma coisa |
30 | - QFactory.generate() devia fazer run da gen_async, ou remover. | 9 | - QFactory.generate() devia fazer run da gen_async, ou remover. |
31 | - marking all options right in a radio question breaks! | 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,6 +49,8 @@ sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in | ||
70 | 49 | ||
71 | # FIXED | 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 | - 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. | 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 | - caixas com os cursos não se ajustam bem com ecran estreito. | 55 | - caixas com os cursos não se ajustam bem com ecran estreito. |
75 | - obter rankings por curso GET course=course_id | 56 | - obter rankings por curso GET course=course_id |
aprendizations/learnapp.py
@@ -69,28 +69,37 @@ class LearnApp(object): | @@ -69,28 +69,37 @@ class LearnApp(object): | ||
69 | 69 | ||
70 | config: Dict[str, Any] = load_yaml(courses) | 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 | # --- topic dependencies are shared between all courses | 72 | # --- topic dependencies are shared between all courses |
77 | self.deps = nx.DiGraph(prefix=prefix) | 73 | self.deps = nx.DiGraph(prefix=prefix) |
78 | - logger.info('Populating graph:') | 74 | + logger.info('Populating topic graph:') |
79 | 75 | ||
80 | t = config.get('topics', {}) # topics defined directly in courses file | 76 | t = config.get('topics', {}) # topics defined directly in courses file |
81 | self.populate_graph(t) | 77 | self.populate_graph(t) |
82 | logger.info(f'{len(t):>6} topics in {courses}') | 78 | logger.info(f'{len(t):>6} topics in {courses}') |
83 | for f in config.get('topics_from', []): | 79 | for f in config.get('topics_from', []): |
84 | c = load_yaml(f) # course configuration | 80 | c = load_yaml(f) # course configuration |
81 | + | ||
82 | + # FIXME set defaults?? | ||
85 | logger.info(f'{len(c["topics"]):>6} topics imported from {f}') | 83 | logger.info(f'{len(c["topics"]):>6} topics imported from {f}') |
86 | self.populate_graph(c) | 84 | self.populate_graph(c) |
87 | logger.info(f'Graph has {len(self.deps)} topics') | 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 | # --- factory is a dict with question generators for all topics | 97 | # --- factory is a dict with question generators for all topics |
90 | self.factory: Dict[str, QFactory] = self.make_factory() | 98 | self.factory: Dict[str, QFactory] = self.make_factory() |
91 | 99 | ||
92 | # if graph has topics that are not in the database, add them | 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 | if check: | 104 | if check: |
96 | self.sanity_check_questions() | 105 | self.sanity_check_questions() |
@@ -301,7 +310,7 @@ class LearnApp(object): | @@ -301,7 +310,7 @@ class LearnApp(object): | ||
301 | # ------------------------------------------------------------------------ | 310 | # ------------------------------------------------------------------------ |
302 | # Fill db table 'Topic' with topics from the graph if not already there. | 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 | with self.db_session() as s: | 314 | with self.db_session() as s: |
306 | new_topics = [Topic(id=t) for t in topics | 315 | new_topics = [Topic(id=t) for t in topics |
307 | if (t,) not in s.query(Topic.id)] | 316 | if (t,) not in s.query(Topic.id)] |
@@ -317,6 +326,10 @@ class LearnApp(object): | @@ -317,6 +326,10 @@ class LearnApp(object): | ||
317 | def db_setup(self, db: str) -> None: | 326 | def db_setup(self, db: str) -> None: |
318 | 327 | ||
319 | logger.info(f'Checking database "{db}":') | 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 | engine = sa.create_engine(f'sqlite:///{db}', echo=False) | 333 | engine = sa.create_engine(f'sqlite:///{db}', echo=False) |
321 | self.Session = sa.orm.sessionmaker(bind=engine) | 334 | self.Session = sa.orm.sessionmaker(bind=engine) |
322 | try: | 335 | try: |
aprendizations/main.py
@@ -9,7 +9,7 @@ import sys | @@ -9,7 +9,7 @@ import sys | ||
9 | from typing import Any, Dict | 9 | from typing import Any, Dict |
10 | 10 | ||
11 | # this project | 11 | # this project |
12 | -from .learnapp import LearnApp, DatabaseUnusableError | 12 | +from .learnapp import LearnApp, DatabaseUnusableError, LearnException |
13 | from .serve import run_webserver | 13 | from .serve import run_webserver |
14 | from .tools import load_yaml | 14 | from .tools import load_yaml |
15 | from . import APP_NAME, APP_VERSION | 15 | from . import APP_NAME, APP_VERSION |
@@ -160,7 +160,7 @@ def main(): | @@ -160,7 +160,7 @@ def main(): | ||
160 | else: | 160 | else: |
161 | logging.info('SSL certificates loaded') | 161 | logging.info('SSL certificates loaded') |
162 | 162 | ||
163 | - # --- start application | 163 | + # --- start application -------------------------------------------------- |
164 | try: | 164 | try: |
165 | learnapp = LearnApp(courses=arg.courses, | 165 | learnapp = LearnApp(courses=arg.courses, |
166 | prefix=arg.prefix, | 166 | prefix=arg.prefix, |
@@ -178,13 +178,16 @@ def main(): | @@ -178,13 +178,16 @@ def main(): | ||
178 | '--------------------------------------------------------------', | 178 | '--------------------------------------------------------------', |
179 | sep='\n') | 179 | sep='\n') |
180 | sys.exit(1) | 180 | sys.exit(1) |
181 | + except LearnException as e: | ||
182 | + logging.critical(e) | ||
183 | + sys.exit(1) | ||
181 | except Exception: | 184 | except Exception: |
182 | logging.critical('Failed to start backend.') | 185 | logging.critical('Failed to start backend.') |
183 | sys.exit(1) | 186 | sys.exit(1) |
184 | else: | 187 | else: |
185 | logging.info('LearnApp started') | 188 | logging.info('LearnApp started') |
186 | 189 | ||
187 | - # --- run webserver forever | 190 | + # --- run webserver forever ---------------------------------------------- |
188 | run_webserver(app=learnapp, ssl=ssl_ctx, port=arg.port, debug=arg.debug) | 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,11 +3,11 @@ | ||
3 | # optional values applied to each topic, if undefined there | 3 | # optional values applied to each topic, if undefined there |
4 | # ---------------------------------------------------------------------------- | 4 | # ---------------------------------------------------------------------------- |
5 | 5 | ||
6 | -# defaults: | 6 | +# defaults: FIXME not working |
7 | # file: questions.yaml | 7 | # file: questions.yaml |
8 | # shuffle_questions: true | 8 | # shuffle_questions: true |
9 | # choose: 6 | 9 | # choose: 6 |
10 | -# max_tries: 2 | 10 | +# max_tries: 200 |
11 | # forgetting_factor: 0.97 | 11 | # forgetting_factor: 0.97 |
12 | # min_level: 0.01 | 12 | # min_level: 0.01 |
13 | # append_wrong: true | 13 | # append_wrong: true |