Commit 1d65b0b71481e46943b1baa64534f1d507ff8db0
Exists in
master
and in
1 other branch
Merge branch 'dev'
Showing
4 changed files
with
30 additions
and
44 deletions
Show diff stats
BUGS.md
| 1 | -# BUGS | 1 | +# Bugs and Wishlist |
| 2 | + | ||
| 3 | +## BUGS | ||
| 2 | 4 | ||
| 3 | - nao esta a respeitar o numero de tentativas `max_tries`. | 5 | - nao esta a respeitar o numero de tentativas `max_tries`. |
| 4 | - se na especificacao de um curso, a referencia do topico nao existir como | 6 | - se na especificacao de um curso, a referencia do topico nao existir como |
| 5 | directorio, rebenta. | 7 | directorio, rebenta. |
| 6 | - internal server error ao fazer logout no macos python3.8 | 8 | - internal server error ao fazer logout no macos python3.8 |
| 7 | -- GET can get filtered by browser cache | ||
| 8 | - topicos chapter devem ser automaticamente completos assim que as dependencias | 9 | - topicos chapter devem ser automaticamente completos assim que as dependencias |
| 9 | são satisfeitas. Nao devia ser necessario (ou possivel?) clicar neles. | 10 | são satisfeitas. Nao devia ser necessario (ou possivel?) clicar neles. |
| 10 | - topicos do tipo learn deviam por defeito nao ser randomizados e assumir | 11 | - topicos do tipo learn deviam por defeito nao ser randomizados e assumir |
| @@ -23,11 +24,11 @@ | @@ -23,11 +24,11 @@ | ||
| 23 | - shift-enter não está a funcionar | 24 | - shift-enter não está a funcionar |
| 24 | - default prefix should be obtained from each course (yaml conf)? | 25 | - default prefix should be obtained from each course (yaml conf)? |
| 25 | 26 | ||
| 26 | -# TODO | 27 | +## TODO |
| 27 | 28 | ||
| 28 | -- shuffle das perguntas dentro de um topico | ||
| 29 | - alterar tabelas para incluir email de recuperacao de password (e outros | 29 | - alterar tabelas para incluir email de recuperacao de password (e outros |
| 30 | avisos) | 30 | avisos) |
| 31 | +- shuffle das perguntas dentro de um topico | ||
| 31 | - registar `last_seen` e remover os antigos de cada vez que houver um login. | 32 | - registar `last_seen` e remover os antigos de cada vez que houver um login. |
| 32 | - indicar qtos topicos faltam (>=50%) para terminar o curso. | 33 | - indicar qtos topicos faltam (>=50%) para terminar o curso. |
| 33 | - ao fim de 3 tentativas com password errada, envia email com nova password. | 34 | - ao fim de 3 tentativas com password errada, envia email com nova password. |
| @@ -50,8 +51,9 @@ | @@ -50,8 +51,9 @@ | ||
| 50 | mais falhadas, tempo médio por pergunta. | 51 | mais falhadas, tempo médio por pergunta. |
| 51 | - normalizar com perguntations. | 52 | - normalizar com perguntations. |
| 52 | 53 | ||
| 53 | -# FIXED | 54 | +## FIXED |
| 54 | 55 | ||
| 56 | +- rankings rebenta se nunhum aluno tiver feito nenhum topico. | ||
| 55 | - templates question-*.html tem input hidden question_ref que não é usado. | 57 | - templates question-*.html tem input hidden question_ref que não é usado. |
| 56 | remover? | 58 | remover? |
| 57 | - goals se forem do tipo chapter deve importar todas as dependencias do chapter. | 59 | - goals se forem do tipo chapter deve importar todas as dependencias do chapter. |
aprendizations/learnapp.py
| @@ -288,7 +288,9 @@ class LearnApp(): | @@ -288,7 +288,9 @@ class LearnApp(): | ||
| 288 | logger.info('User "%s" finished "%s" (level=%.2f)', | 288 | logger.info('User "%s" finished "%s" (level=%.2f)', |
| 289 | uid, topic_id, level) | 289 | uid, topic_id, level) |
| 290 | 290 | ||
| 291 | - query = select(StudentTopic).where(StudentTopic.student_id == uid).where(StudentTopic.topic_id == topic_id) | 291 | + query = select(StudentTopic) \ |
| 292 | + .where(StudentTopic.student_id == uid) \ | ||
| 293 | + .where(StudentTopic.topic_id == topic_id) | ||
| 292 | with Session(self._engine, future=True) as session: | 294 | with Session(self._engine, future=True) as session: |
| 293 | student_topic = session.execute(query).scalar_one_or_none() | 295 | student_topic = session.execute(query).scalar_one_or_none() |
| 294 | 296 | ||
| @@ -599,9 +601,9 @@ class LearnApp(): | @@ -599,9 +601,9 @@ class LearnApp(): | ||
| 599 | return self.courses[course_id] | 601 | return self.courses[course_id] |
| 600 | 602 | ||
| 601 | # ------------------------------------------------------------------------ | 603 | # ------------------------------------------------------------------------ |
| 602 | - def get_rankings(self, uid: str, course_id: str) -> Iterable[Tuple[str, str, float, float]]: | 604 | + def get_rankings(self, uid: str, cid: str) -> List[Tuple[str, str, float]]: |
| 603 | ''' | 605 | ''' |
| 604 | - Returns rankings for a certain course_id. | 606 | + Returns rankings for a certain cid (course_id). |
| 605 | User where uid have <=2 chars are considered ghosts are hidden from | 607 | User where uid have <=2 chars are considered ghosts are hidden from |
| 606 | the rankings. This is so that there can be users for development or | 608 | the rankings. This is so that there can be users for development or |
| 607 | testing purposes, which are not real users. | 609 | testing purposes, which are not real users. |
| @@ -609,16 +611,13 @@ class LearnApp(): | @@ -609,16 +611,13 @@ class LearnApp(): | ||
| 609 | This should be modified to have a "visible" flag | 611 | This should be modified to have a "visible" flag |
| 610 | ''' | 612 | ''' |
| 611 | 613 | ||
| 612 | - logger.info('User "%s" rankings for "%s"', uid, course_id) | 614 | + logger.info('User "%s" rankings for "%s"', uid, cid) |
| 615 | + query_students = select(Student.id, Student.name) | ||
| 616 | + query_student_topics = select(StudentTopic.student_id, | ||
| 617 | + StudentTopic.topic_id, | ||
| 618 | + StudentTopic.level, | ||
| 619 | + StudentTopic.date) | ||
| 613 | with Session(self._engine, future=True) as session: | 620 | with Session(self._engine, future=True) as session: |
| 614 | - query_students = select(Student.id, Student.name) | ||
| 615 | - query_student_topics = select(StudentTopic.student_id, | ||
| 616 | - StudentTopic.topic_id, | ||
| 617 | - StudentTopic.level, | ||
| 618 | - StudentTopic.date) | ||
| 619 | - query_total = select(Answer.student_id, func.count(Answer.ref)) | ||
| 620 | - query_right = select(Answer.student_id, func.count(Answer.ref)) \ | ||
| 621 | - .where(Answer.grade == 1.0) | ||
| 622 | 621 | ||
| 623 | # all students in the database FIXME only with answers of this course | 622 | # all students in the database FIXME only with answers of this course |
| 624 | students = session.execute(query_students).all() | 623 | students = session.execute(query_students).all() |
| @@ -626,28 +625,17 @@ class LearnApp(): | @@ -626,28 +625,17 @@ class LearnApp(): | ||
| 626 | # topic levels FIXME only topics of this course | 625 | # topic levels FIXME only topics of this course |
| 627 | student_topics = session.execute(query_student_topics).all() | 626 | student_topics = session.execute(query_student_topics).all() |
| 628 | 627 | ||
| 629 | - # answer performance | ||
| 630 | - | ||
| 631 | - # FIXME this does not work when nobody has done anything... | ||
| 632 | - # FIXME row to dict seems to be deprecated in 1.4+ | ||
| 633 | - total = dict(session.execute(query_total).all()) | ||
| 634 | - right = dict(session.execute(query_right).all()) | ||
| 635 | - | ||
| 636 | - # compute percentage of right answers | ||
| 637 | - perf: Dict[str, float] = {u: right.get(u, 0.0) / total[u] | ||
| 638 | - for u in total} | ||
| 639 | - | ||
| 640 | # compute topic progress | 628 | # compute topic progress |
| 641 | now = datetime.now() | 629 | now = datetime.now() |
| 642 | - goals = self.courses[course_id]['goals'] | 630 | + goals = self.courses[cid]['goals'] |
| 643 | progress: DefaultDict[str, float] = defaultdict(int) | 631 | progress: DefaultDict[str, float] = defaultdict(int) |
| 644 | 632 | ||
| 645 | - for student, topic, level, date in student_topics: | 633 | + for student, topic, level, datestr in student_topics: |
| 646 | if topic in goals: | 634 | if topic in goals: |
| 647 | - date = datetime.strptime(date, "%Y-%m-%d %H:%M:%S.%f") | 635 | + date = datetime.strptime(datestr, "%Y-%m-%d %H:%M:%S.%f") |
| 648 | progress[student] += level**(now - date).days / len(goals) | 636 | progress[student] += level**(now - date).days / len(goals) |
| 649 | 637 | ||
| 650 | - return sorted(((u, name, progress[u], perf.get(u, 0.0)) | 638 | + return sorted(((u, name, progress[u]) |
| 651 | for u, name in students | 639 | for u, name in students |
| 652 | if u in progress and (len(u) > 2 or len(uid) <= 2)), | 640 | if u in progress and (len(u) > 2 or len(uid) <= 2)), |
| 653 | key=lambda x: x[2], reverse=True) | 641 | key=lambda x: x[2], reverse=True) |
aprendizations/templates/rankings.html
| @@ -61,7 +61,6 @@ | @@ -61,7 +61,6 @@ | ||
| 61 | <tr> | 61 | <tr> |
| 62 | <th scope="col" class="text-center">Posição</th> | 62 | <th scope="col" class="text-center">Posição</th> |
| 63 | <th scope="col">Aluno</th> | 63 | <th scope="col">Aluno</th> |
| 64 | - <th scope="col"></th> | ||
| 65 | <th scope="col">Progresso</th> | 64 | <th scope="col">Progresso</th> |
| 66 | </tr> | 65 | </tr> |
| 67 | </thead> | 66 | </thead> |
| @@ -76,9 +75,6 @@ | @@ -76,9 +75,6 @@ | ||
| 76 | <td> <!-- student name --> | 75 | <td> <!-- student name --> |
| 77 | {{ ' '.join(r[1].split()[n] for n in (0,-1)) }} | 76 | {{ ' '.join(r[1].split()[n] for n in (0,-1)) }} |
| 78 | </td> | 77 | </td> |
| 79 | - <td> <!-- nice --> | ||
| 80 | - {{ '<i class="far fa-thumbs-up text-success" title="Mais de 75% de respostas correctas"></i>' if r[3] > 0.75 else '' }} | ||
| 81 | - </td> | ||
| 82 | <td> <!-- progress --> | 78 | <td> <!-- progress --> |
| 83 | <div class="progress" style="height: 24px;"> | 79 | <div class="progress" style="height: 24px;"> |
| 84 | <div class="progress-bar" role="progressbar" style="width: {{ 100*r[2] }}%;" aria-valuenow="{{round(100*r[2])}}%" aria-valuemin="0" aria-valuemax="100">{{round(100*r[2])}}%</div> | 80 | <div class="progress-bar" role="progressbar" style="width: {{ 100*r[2] }}%;" aria-valuenow="{{round(100*r[2])}}%" aria-valuemin="0" aria-valuemax="100">{{round(100*r[2])}}%</div> |
mypy.ini
| @@ -2,14 +2,14 @@ | @@ -2,14 +2,14 @@ | ||
| 2 | python_version = 3.9 | 2 | python_version = 3.9 |
| 3 | plugins = sqlalchemy.ext.mypy.plugin | 3 | plugins = sqlalchemy.ext.mypy.plugin |
| 4 | 4 | ||
| 5 | -[mypy-pygments.*] | ||
| 6 | -ignore_missing_imports = True | 5 | +; [mypy-pygments.*] |
| 6 | +; ignore_missing_imports = True | ||
| 7 | 7 | ||
| 8 | -[mypy-networkx.*] | ||
| 9 | -ignore_missing_imports = True | 8 | +; [mypy-networkx.*] |
| 9 | +; ignore_missing_imports = True | ||
| 10 | 10 | ||
| 11 | -[mypy-bcrypt.*] | ||
| 12 | -ignore_missing_imports = True | 11 | +; [mypy-bcrypt.*] |
| 12 | +; ignore_missing_imports = True | ||
| 13 | 13 | ||
| 14 | -[mypy-mistune.*] | ||
| 15 | -ignore_missing_imports = True | 14 | +; [mypy-mistune.*] |
| 15 | +; ignore_missing_imports = True |