Commit 65a5ad4aceead32255bb3bbd04a3f0d715c63519

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

Small fixes

aprendizations/learnapp.py
@@ -6,7 +6,6 @@ This is the main controller of the application. @@ -6,7 +6,6 @@ This is the main controller of the application.
6 # python standard library 6 # python standard library
7 import asyncio 7 import asyncio
8 from collections import defaultdict 8 from collections import defaultdict
9 -# from contextlib import contextmanager # `with` statement in db sessions  
10 from datetime import datetime 9 from datetime import datetime
11 import logging 10 import logging
12 from random import random 11 from random import random
@@ -45,18 +44,25 @@ class LearnApp(): @@ -45,18 +44,25 @@ class LearnApp():
45 ''' 44 '''
46 LearnApp - application logic 45 LearnApp - application logic
47 46
48 - self.deps - networkx topic dependencies  
49 - self.courses - dict {course_id: {'title': ...,  
50 - 'description': ...,  
51 - 'goals': ...,}, ...}  
52 - self.factory = dict {qref: QFactory()}  
53 - self.online - dict {student_id: {'number': ...,  
54 - 'name': ...,  
55 - 'state': StudentState(),  
56 - 'counter': ...}, ...} 47 + self.deps = networkx topic dependencies
  48 + self.courses = {
  49 + course_id: {
  50 + 'title': ...,
  51 + 'description': ...,
  52 + 'goals': ...,
  53 + }, ...
  54 + }
  55 + self.factory = { qref: QFactory() }
  56 + self.online = {
  57 + student_id: {
  58 + 'number': ...,
  59 + 'name': ...,
  60 + 'state': StudentState(),
  61 + 'counter': ...
  62 + }, ...
  63 + }
57 ''' 64 '''
58 65
59 - # ------------------------------------------------------------------------  
60 def __init__(self, 66 def __init__(self,
61 courses: str, # filename with course configurations 67 courses: str, # filename with course configurations
62 prefix: str, # path to topics 68 prefix: str, # path to topics
@@ -114,7 +120,6 @@ class LearnApp(): @@ -114,7 +120,6 @@ class LearnApp():
114 if check: 120 if check:
115 self._sanity_check_questions() 121 self._sanity_check_questions()
116 122
117 - # ------------------------------------------------------------------------  
118 def _sanity_check_questions(self) -> None: 123 def _sanity_check_questions(self) -> None:
119 ''' 124 '''
120 Unit tests for all questions 125 Unit tests for all questions
@@ -159,7 +164,6 @@ class LearnApp(): @@ -159,7 +164,6 @@ class LearnApp():
159 raise LearnException('Sanity checks') 164 raise LearnException('Sanity checks')
160 logger.info(' 0 errors found.') 165 logger.info(' 0 errors found.')
161 166
162 - # ------------------------------------------------------------------------  
163 async def login(self, uid: str, password: str) -> bool: 167 async def login(self, uid: str, password: str) -> bool:
164 '''user login''' 168 '''user login'''
165 169
@@ -212,13 +216,11 @@ class LearnApp(): @@ -212,13 +216,11 @@ class LearnApp():
212 216
213 return pw_ok 217 return pw_ok
214 218
215 - # ------------------------------------------------------------------------  
216 def logout(self, uid: str) -> None: 219 def logout(self, uid: str) -> None:
217 '''User logout''' 220 '''User logout'''
218 del self.online[uid] 221 del self.online[uid]
219 logger.info('User "%s" logged out', uid) 222 logger.info('User "%s" logged out', uid)
220 223
221 - # ------------------------------------------------------------------------  
222 async def change_password(self, uid: str, password: str) -> bool: 224 async def change_password(self, uid: str, password: str) -> bool:
223 ''' 225 '''
224 Change user Password. 226 Change user Password.
@@ -242,7 +244,6 @@ class LearnApp(): @@ -242,7 +244,6 @@ class LearnApp():
242 logger.info('User "%s" changed password', uid) 244 logger.info('User "%s" changed password', uid)
243 return True 245 return True
244 246
245 - # ------------------------------------------------------------------------  
246 async def check_answer(self, uid: str, answer) -> Question: 247 async def check_answer(self, uid: str, answer) -> Question:
247 ''' 248 '''
248 Checks answer and update database. 249 Checks answer and update database.
@@ -271,7 +272,6 @@ class LearnApp(): @@ -271,7 +272,6 @@ class LearnApp():
271 272
272 return question 273 return question
273 274
274 - # ------------------------------------------------------------------------  
275 async def get_question(self, uid: str) -> Optional[Question]: 275 async def get_question(self, uid: str) -> Optional[Question]:
276 ''' 276 '''
277 Get the question to show (current or new one) 277 Get the question to show (current or new one)
@@ -318,7 +318,6 @@ class LearnApp(): @@ -318,7 +318,6 @@ class LearnApp():
318 318
319 return question 319 return question
320 320
321 - # ------------------------------------------------------------------------  
322 def start_course(self, uid: str, course_id: str) -> None: 321 def start_course(self, uid: str, course_id: str) -> None:
323 '''Start course''' 322 '''Start course'''
324 323
@@ -331,9 +330,6 @@ class LearnApp(): @@ -331,9 +330,6 @@ class LearnApp():
331 else: 330 else:
332 logger.info('User "%s" course "%s"', uid, course_id) 331 logger.info('User "%s" course "%s"', uid, course_id)
333 332
334 - # ------------------------------------------------------------------------  
335 - #  
336 - # ------------------------------------------------------------------------  
337 async def start_topic(self, uid: str, topic: str) -> None: 333 async def start_topic(self, uid: str, topic: str) -> None:
338 '''Start new topic''' 334 '''Start new topic'''
339 335
@@ -350,9 +346,6 @@ class LearnApp(): @@ -350,9 +346,6 @@ class LearnApp():
350 else: 346 else:
351 logger.info('User "%s" started topic "%s"', uid, topic) 347 logger.info('User "%s" started topic "%s"', uid, topic)
352 348
353 - # ------------------------------------------------------------------------  
354 - #  
355 - # ------------------------------------------------------------------------  
356 def _add_missing_topics(self, topics: Iterable[str]) -> None: 349 def _add_missing_topics(self, topics: Iterable[str]) -> None:
357 ''' 350 '''
358 Fill db table 'Topic' with topics from the graph, if new 351 Fill db table 'Topic' with topics from the graph, if new
@@ -365,7 +358,6 @@ class LearnApp(): @@ -365,7 +358,6 @@ class LearnApp():
365 session.commit() 358 session.commit()
366 logger.info('Added %d new topic(s) to the database', len(new)) 359 logger.info('Added %d new topic(s) to the database', len(new))
367 360
368 - # ------------------------------------------------------------------------  
369 def _db_setup(self, database: str) -> None: 361 def _db_setup(self, database: str) -> None:
370 ''' 362 '''
371 Setup and check database contents 363 Setup and check database contents
@@ -395,7 +387,6 @@ class LearnApp(): @@ -395,7 +387,6 @@ class LearnApp():
395 logger.info('%6d topics', count_topics) 387 logger.info('%6d topics', count_topics)
396 logger.info('%6d answers', count_answers) 388 logger.info('%6d answers', count_answers)
397 389
398 - # ------------------------------------------------------------------------  
399 def _populate_graph(self, config: Dict[str, Any]) -> None: 390 def _populate_graph(self, config: Dict[str, Any]) -> None:
400 ''' 391 '''
401 Populates a digraph. 392 Populates a digraph.
@@ -437,12 +428,10 @@ class LearnApp(): @@ -437,12 +428,10 @@ class LearnApp():
437 # prefix/topic 428 # prefix/topic
438 topic['path'] = join(self.deps.graph['prefix'], tref) 429 topic['path'] = join(self.deps.graph['prefix'], tref)
439 430
440 -  
441 - # ======================================================================== 431 + # ------------------------------------------------------------------------
442 # methods that do not change state (pure functions) 432 # methods that do not change state (pure functions)
443 - # ========================================================================  
444 -  
445 # ------------------------------------------------------------------------ 433 # ------------------------------------------------------------------------
  434 +
446 def _make_factory(self) -> Dict[str, QFactory]: 435 def _make_factory(self) -> Dict[str, QFactory]:
447 ''' 436 '''
448 Buils dictionary of question factories 437 Buils dictionary of question factories
@@ -519,38 +508,31 @@ class LearnApp(): @@ -519,38 +508,31 @@ class LearnApp():
519 508
520 return factory 509 return factory
521 510
522 - # ------------------------------------------------------------------------  
523 def get_login_counter(self, uid: str) -> int: 511 def get_login_counter(self, uid: str) -> int:
524 '''login counter''' # FIXME 512 '''login counter''' # FIXME
525 return int(self.online[uid]['counter']) 513 return int(self.online[uid]['counter'])
526 514
527 - # ------------------------------------------------------------------------  
528 def get_student_name(self, uid: str) -> str: 515 def get_student_name(self, uid: str) -> str:
529 '''Get the username''' 516 '''Get the username'''
530 return self.online[uid].get('name', '') 517 return self.online[uid].get('name', '')
531 518
532 - # ------------------------------------------------------------------------  
533 def get_student_state(self, uid: str) -> List[Dict[str, Any]]: 519 def get_student_state(self, uid: str) -> List[Dict[str, Any]]:
534 '''Get the knowledge state of a given user''' 520 '''Get the knowledge state of a given user'''
535 return self.online[uid]['state'].get_knowledge_state() 521 return self.online[uid]['state'].get_knowledge_state()
536 522
537 - # ------------------------------------------------------------------------  
538 def get_student_progress(self, uid: str) -> float: 523 def get_student_progress(self, uid: str) -> float:
539 '''Get the current topic progress of a given user''' 524 '''Get the current topic progress of a given user'''
540 return float(self.online[uid]['state'].get_topic_progress()) 525 return float(self.online[uid]['state'].get_topic_progress())
541 526
542 - # ------------------------------------------------------------------------  
543 def get_current_question(self, uid: str) -> Optional[Question]: 527 def get_current_question(self, uid: str) -> Optional[Question]:
544 '''Get the current question of a given user''' 528 '''Get the current question of a given user'''
545 question: Optional[Question] = self.online[uid]['state'].get_current_question() 529 question: Optional[Question] = self.online[uid]['state'].get_current_question()
546 return question 530 return question
547 531
548 - # ------------------------------------------------------------------------  
549 def get_current_question_id(self, uid: str) -> str: 532 def get_current_question_id(self, uid: str) -> str:
550 '''Get id of the current question for a given user''' 533 '''Get id of the current question for a given user'''
551 return str(self.online[uid]['state'].get_current_question()['qid']) 534 return str(self.online[uid]['state'].get_current_question()['qid'])
552 535
553 - # ------------------------------------------------------------------------  
554 def get_student_question_type(self, uid: str) -> str: 536 def get_student_question_type(self, uid: str) -> str:
555 '''Get type of the current question for a given user''' 537 '''Get type of the current question for a given user'''
556 return str(self.online[uid]['state'].get_current_question()['type']) 538 return str(self.online[uid]['state'].get_current_question()['type'])
@@ -559,12 +541,10 @@ class LearnApp(): @@ -559,12 +541,10 @@ class LearnApp():
559 # def get_student_topic(self, uid: str) -> str: 541 # def get_student_topic(self, uid: str) -> str:
560 # return str(self.online[uid]['state'].get_current_topic()) 542 # return str(self.online[uid]['state'].get_current_topic())
561 543
562 - # ------------------------------------------------------------------------  
563 def get_student_course_title(self, uid: str) -> str: 544 def get_student_course_title(self, uid: str) -> str:
564 '''get the title of the current course for a given user''' 545 '''get the title of the current course for a given user'''
565 return str(self.online[uid]['state'].get_current_course_title()) 546 return str(self.online[uid]['state'].get_current_course_title())
566 547
567 - # ------------------------------------------------------------------------  
568 def get_current_course_id(self, uid: str) -> Optional[str]: 548 def get_current_course_id(self, uid: str) -> Optional[str]:
569 '''get the current course (id) of a given user''' 549 '''get the current course (id) of a given user'''
570 cid: Optional[str] = self.online[uid]['state'].get_current_course_id() 550 cid: Optional[str] = self.online[uid]['state'].get_current_course_id()
@@ -574,7 +554,6 @@ class LearnApp(): @@ -574,7 +554,6 @@ class LearnApp():
574 # def get_topic_name(self, ref: str) -> str: 554 # def get_topic_name(self, ref: str) -> str:
575 # return str(self.deps.nodes[ref]['name']) 555 # return str(self.deps.nodes[ref]['name'])
576 556
577 - # ------------------------------------------------------------------------  
578 def get_current_public_dir(self, uid: str) -> str: 557 def get_current_public_dir(self, uid: str) -> str:
579 ''' 558 '''
580 Get the path for the 'public' directory of the current topic of the 559 Get the path for the 'public' directory of the current topic of the
@@ -586,21 +565,18 @@ class LearnApp(): @@ -586,21 +565,18 @@ class LearnApp():
586 prefix: str = self.deps.graph['prefix'] 565 prefix: str = self.deps.graph['prefix']
587 return join(prefix, topic, 'public') 566 return join(prefix, topic, 'public')
588 567
589 - # ------------------------------------------------------------------------  
590 def get_courses(self) -> Dict[str, Dict[str, Any]]: 568 def get_courses(self) -> Dict[str, Dict[str, Any]]:
591 ''' 569 '''
592 Get dictionary with all courses {'course1': {...}, 'course2': {...}} 570 Get dictionary with all courses {'course1': {...}, 'course2': {...}}
593 ''' 571 '''
594 return self.courses 572 return self.courses
595 573
596 - # ------------------------------------------------------------------------  
597 def get_course(self, course_id: str) -> Dict[str, Any]: 574 def get_course(self, course_id: str) -> Dict[str, Any]:
598 ''' 575 '''
599 Get dictionary {'title': ..., 'description':..., 'goals':...} 576 Get dictionary {'title': ..., 'description':..., 'goals':...}
600 ''' 577 '''
601 return self.courses[course_id] 578 return self.courses[course_id]
602 579
603 - # ------------------------------------------------------------------------  
604 def get_rankings(self, uid: str, cid: str) -> List[Tuple[str, str, float]]: 580 def get_rankings(self, uid: str, cid: str) -> List[Tuple[str, str, float]]:
605 ''' 581 '''
606 Returns rankings for a certain cid (course_id). 582 Returns rankings for a certain cid (course_id).
@@ -639,5 +615,3 @@ class LearnApp(): @@ -639,5 +615,3 @@ class LearnApp():
639 for u, name in students 615 for u, name in students
640 if u in progress and (len(u) > 2 or len(uid) <= 2)), 616 if u in progress and (len(u) > 2 or len(uid) <= 2)),
641 key=lambda x: x[2], reverse=True) 617 key=lambda x: x[2], reverse=True)
642 -  
643 - # ------------------------------------------------------------------------  
aprendizations/templates/maintopics-table.html
@@ -105,23 +105,23 @@ @@ -105,23 +105,23 @@
105 <tr> 105 <tr>
106 <th scope="row" class="text-muted text-center"> 106 <th scope="row" class="text-muted text-center">
107 {% if t['type']=='chapter' %} 107 {% if t['type']=='chapter' %}
108 - <i class="bi bi-flag-fill"></i>&nbsp; 108 + <h5><i class="bi bi-flag-fill"></i></h5>
109 {% elif t['type']=='learn' %} 109 {% elif t['type']=='learn' %}
110 - <i class="bi bi-book"></i>&nbsp; 110 + <h5><i class="bi bi-book"></i></h5>
111 {% else %} 111 {% else %}
112 - <i class="bi bi-pencil"></i>&nbsp; 112 + <h5><i class="bi bi-pencil"></i></h5>
113 {% end %} 113 {% end %}
114 </th> 114 </th>
115 <td> 115 <td>
116 <div class="text-muted"> 116 <div class="text-muted">
117 {% if t['ref'] not in course['goals'] %} 117 {% if t['ref'] not in course['goals'] %}
118 - <i class="bi bi-puzzle-fill"></i>&nbsp; 118 + <h5><i class="bi bi-puzzle-fill"></i></h5>
119 {% end %} 119 {% end %}
120 {{ t['name'] }} 120 {{ t['name'] }}
121 </div> 121 </div>
122 </td> 122 </td>
123 <td class="text-center"> 123 <td class="text-center">
124 - <i class="bi bi-lock-fill text-muted"></i> 124 + <h5><i class="bi bi-lock-fill text-muted"></i></h5>
125 </td> 125 </td>
126 </tr> 126 </tr>
127 127
@@ -131,30 +131,22 @@ @@ -131,30 +131,22 @@
131 <th scope="row" class="text-primary text-center"> 131 <th scope="row" class="text-primary text-center">
132 {% if t['type']=='chapter' %} 132 {% if t['type']=='chapter' %}
133 <span class="text-nowrap" data-toggle="tooltip" data-placement="bottom" title="Fim do capítulo"> 133 <span class="text-nowrap" data-toggle="tooltip" data-placement="bottom" title="Fim do capítulo">
134 - <h5>  
135 - <i class="bi bi-flag-fill"></i>&nbsp;  
136 - </h5> 134 + <h5><i class="bi bi-flag-fill"></i></h5>
137 </span> 135 </span>
138 {% elif t['type']=='learn' %} 136 {% elif t['type']=='learn' %}
139 <span class="text-nowrap" data-toggle="tooltip" data-placement="bottom" title="Texto com matéria"> 137 <span class="text-nowrap" data-toggle="tooltip" data-placement="bottom" title="Texto com matéria">
140 - <h5>  
141 - <i class="bi bi-book"></i>&nbsp;  
142 - </h5> 138 + <h5><i class="bi bi-book"></i></h5>
143 </span> 139 </span>
144 {% else %} 140 {% else %}
145 <span class="text-nowrap" data-toggle="tooltip" data-placement="bottom" title="Exercícios"> 141 <span class="text-nowrap" data-toggle="tooltip" data-placement="bottom" title="Exercícios">
146 - <h5>  
147 - <i class="bi bi-pencil"></i>&nbsp;  
148 - </h5> 142 + <h5><i class="bi bi-pencil"></i></h5>
149 </span> 143 </span>
150 {% end %} 144 {% end %}
151 </th> 145 </th>
152 <td class="text-primary"> 146 <td class="text-primary">
153 {% if t['ref'] not in course['goals'] %} 147 {% if t['ref'] not in course['goals'] %}
154 <span class="text-nowrap" data-toggle="tooltip" data-placement="bottom" title="Pré-requisito para este curso"> 148 <span class="text-nowrap" data-toggle="tooltip" data-placement="bottom" title="Pré-requisito para este curso">
155 - <h5>  
156 - <i class="bi bi-puzzle-fill"></i>  
157 - </h5> 149 + <h5><i class="bi bi-puzzle-fill"></i></h5>
158 </span> 150 </span>
159 {% end %} 151 {% end %}
160 {{ t['name'] }} 152 {{ t['name'] }}
@@ -162,18 +154,12 @@ @@ -162,18 +154,12 @@
162 154
163 <td class="text-center"> 155 <td class="text-center">
164 {% if t['level'] < 0.01 %} 156 {% if t['level'] < 0.01 %}
165 - <h5>  
166 - <i class="bi bi-unlock-fill text-success"></i>  
167 - </h5> 157 + <h5><i class="bi bi-unlock-fill text-success"></i></h5>
168 {% elif t['type']=='chapter' %} 158 {% elif t['type']=='chapter' %}
169 - <h5>  
170 - <i class="bi bi-award-fill"></i>  
171 - </h5> 159 + <h5><i class="bi bi-award-fill"></i></h5>
172 {% else %} 160 {% else %}
173 <span class="text-nowrap text-warning" data-toggle="tooltip" data-placement="bottom" title="{{round(t['level']*5, 3)}}"> 161 <span class="text-nowrap text-warning" data-toggle="tooltip" data-placement="bottom" title="{{round(t['level']*5, 3)}}">
174 - <h5>  
175 - {{ int(t['level']*5+0.25)*'<i class="bi bi-star-fill"></i>' }}{% if int(t['level']*5+0.25) < 5 %}{{'<i class="bi bi-star-half"></i>' if 0.25 <= t['level']*5-int(t['level']*5) < 0.75 else '<i class="bi bi-star"></i>'}}{% end %}{{ (4-int(t['level']*5+0.25))*'<i class="bi bi-star"></i>' }}  
176 - </h5> 162 + <h5>{{ int(t['level']*5+0.25)*'<i class="bi bi-star-fill"></i>' }}{% if int(t['level']*5+0.25) < 5 %}{{'<i class="bi bi-star-half"></i>' if 0.25 <= t['level']*5-int(t['level']*5) < 0.75 else '<i class="bi bi-star"></i>'}}{% end %}{{ (4-int(t['level']*5+0.25))*'<i class="bi bi-star"></i>' }}</h5>
177 </span> 163 </span>
178 {% end %} <!-- if --> 164 {% end %} <!-- if -->
179 </td> 165 </td>