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 |