Commit a7d6b9e15cb57ee62aa31ac0f0dae7f6f35ad904

Authored by Miguel Barão
1 parent ac45791a
Exists in dev

renamed LearnApp to Application

and other minor changes
aprendizations/learnapp.py
@@ -10,13 +10,14 @@ import logging @@ -10,13 +10,14 @@ import logging
10 from random import random 10 from random import random
11 from os.path import join, exists 11 from os.path import join, exists
12 from typing import Any, Dict, Iterable, List, Optional, Tuple, Set, DefaultDict 12 from typing import Any, Dict, Iterable, List, Optional, Tuple, Set, DefaultDict
13 -import yaml  
14 13
15 # third party libraries 14 # third party libraries
16 import bcrypt 15 import bcrypt
17 import networkx as nx 16 import networkx as nx
18 -from sqlalchemy import create_engine, select, func 17 +import sqlalchemy as sa
  18 +from sqlalchemy import select
19 from sqlalchemy.orm import Session 19 from sqlalchemy.orm import Session
  20 +import yaml
20 21
21 # this project 22 # this project
22 from aprendizations.models import Student, Answer, Topic, StudentTopic 23 from aprendizations.models import Student, Answer, Topic, StudentTopic
@@ -34,7 +35,7 @@ class LearnException(Exception): @@ -34,7 +35,7 @@ class LearnException(Exception):
34 35
35 36
36 # ============================================================================ 37 # ============================================================================
37 -class LearnApp(): 38 +class Application():
38 ''' 39 '''
39 LearnApp - application logic 40 LearnApp - application logic
40 41
@@ -157,8 +158,8 @@ class LearnApp(): @@ -157,8 +158,8 @@ class LearnApp():
157 # wait random time to minimize timing attacks 158 # wait random time to minimize timing attacks
158 await asyncio.sleep(random()) 159 await asyncio.sleep(random())
159 160
160 - query = select(Student).where(Student.id == uid)  
161 with Session(self._engine) as session: 161 with Session(self._engine) as session:
  162 + query = select(Student).where(Student.id == uid)
162 student = session.execute(query).scalar_one_or_none() 163 student = session.execute(query).scalar_one_or_none()
163 if student is None: 164 if student is None:
164 logger.info('User "%s" does not exist', uid) 165 logger.info('User "%s" does not exist', uid)
@@ -177,8 +178,8 @@ class LearnApp(): @@ -177,8 +178,8 @@ class LearnApp():
177 logger.info('User "%s" logged in', uid) 178 logger.info('User "%s" logged in', uid)
178 179
179 # get topics for this student and set its current state 180 # get topics for this student and set its current state
180 - query = select(StudentTopic).where(StudentTopic.student_id == uid)  
181 with Session(self._engine) as session: 181 with Session(self._engine) as session:
  182 + query = select(StudentTopic).where(StudentTopic.student_id == uid)
182 student_topics = session.execute(query).scalars().all() 183 student_topics = session.execute(query).scalars().all()
183 184
184 state = {t.topic_id: { 185 state = {t.topic_id: {
@@ -220,8 +221,8 @@ class LearnApp(): @@ -220,8 +221,8 @@ class LearnApp():
220 password.encode('utf-8'), 221 password.encode('utf-8'),
221 bcrypt.gensalt()) 222 bcrypt.gensalt())
222 223
223 - query = select(Student).where(Student.id == uid)  
224 with Session(self._engine) as session: 224 with Session(self._engine) as session:
  225 + query = select(Student).where(Student.id == uid)
225 session.execute(query).scalar_one().password = str(hashed_pw) 226 session.execute(query).scalar_one().password = str(hashed_pw)
226 session.commit() 227 session.commit()
227 228
@@ -273,10 +274,10 @@ class LearnApp(): @@ -273,10 +274,10 @@ class LearnApp():
273 date: str = str(student_state.get_topic_date(tid)) 274 date: str = str(student_state.get_topic_date(tid))
274 logger.info('"%s" finished "%s" (level=%.2f)', uid, tid, level) 275 logger.info('"%s" finished "%s" (level=%.2f)', uid, tid, level)
275 276
276 - query = select(StudentTopic) \  
277 - .where(StudentTopic.student_id == uid) \  
278 - .where(StudentTopic.topic_id == tid)  
279 with Session(self._engine) as session: 277 with Session(self._engine) as session:
  278 + query = select(StudentTopic) \
  279 + .where(StudentTopic.student_id == uid) \
  280 + .where(StudentTopic.topic_id == tid)
280 student_topic = session.execute(query).scalar_one_or_none() 281 student_topic = session.execute(query).scalar_one_or_none()
281 if student_topic is None: 282 if student_topic is None:
282 # insert new studenttopic into database 283 # insert new studenttopic into database
@@ -351,11 +352,11 @@ class LearnApp(): @@ -351,11 +352,11 @@ class LearnApp():
351 raise LearnException('Database does not exist') 352 raise LearnException('Database does not exist')
352 353
353 # echo=True enables logging of the SQL emitted by sqlalchemy 354 # echo=True enables logging of the SQL emitted by sqlalchemy
354 - self._engine = create_engine(f'sqlite:///{database}', echo=False) 355 + self._engine = sa.create_engine(f'sqlite:///{database}', echo=False)
355 try: 356 try:
356 - query_students = select(func.count(Student.id))  
357 - query_topics = select(func.count(Topic.id))  
358 - query_answers = select(func.count(Answer.id)) 357 + query_students = select(sa.func.count(Student.id))
  358 + query_topics = select(sa.func.count(Topic.id))
  359 + query_answers = select(sa.func.count(Answer.id))
359 with Session(self._engine) as session: 360 with Session(self._engine) as session:
360 count_students = session.execute(query_students).scalar() 361 count_students = session.execute(query_students).scalar()
361 count_topics = session.execute(query_topics).scalar() 362 count_topics = session.execute(query_topics).scalar()
@@ -566,12 +567,12 @@ class LearnApp(): @@ -566,12 +567,12 @@ class LearnApp():
566 ''' 567 '''
567 568
568 logger.info('User "%s" rankings for "%s"', uid, cid) 569 logger.info('User "%s" rankings for "%s"', uid, cid)
569 - query_students = select(Student.id, Student.name)  
570 - query_student_topics = select(StudentTopic.student_id,  
571 - StudentTopic.topic_id,  
572 - StudentTopic.level,  
573 - StudentTopic.date)  
574 with Session(self._engine) as session: 570 with Session(self._engine) as session:
  571 + query_students = select(Student.id, Student.name)
  572 + query_student_topics = select(StudentTopic.student_id,
  573 + StudentTopic.topic_id,
  574 + StudentTopic.level,
  575 + StudentTopic.date)
575 576
576 # all students in the database FIXME only with answers of this course 577 # all students in the database FIXME only with answers of this course
577 students = session.execute(query_students).all() 578 students = session.execute(query_students).all()
aprendizations/main.py
@@ -16,16 +16,14 @@ import sys @@ -16,16 +16,14 @@ import sys
16 from typing import Dict 16 from typing import Dict
17 17
18 # this project 18 # this project
19 -from .learnapp import LearnApp, LearnException 19 +from .learnapp import Application, LearnException
20 from .serve import webserver 20 from .serve import webserver
21 from . import APP_NAME, APP_VERSION 21 from . import APP_NAME, APP_VERSION
22 22
23 23
24 # ---------------------------------------------------------------------------- 24 # ----------------------------------------------------------------------------
25 def parse_cmdline_arguments(): 25 def parse_cmdline_arguments():
26 - '''  
27 - Parses command line arguments. Uses the argparse package.  
28 - ''' 26 + '''Parses command line arguments. Uses the argparse package'''
29 27
30 parser = argparse.ArgumentParser( 28 parser = argparse.ArgumentParser(
31 description='Webserver for interactive learning and practice. ' 29 description='Webserver for interactive learning and practice. '
@@ -110,9 +108,7 @@ def get_logger_config(debug: bool = False) -> Dict: @@ -110,9 +108,7 @@ def get_logger_config(debug: bool = False) -> Dict:
110 108
111 # ---------------------------------------------------------------------------- 109 # ----------------------------------------------------------------------------
112 def main(): 110 def main():
113 - '''  
114 - Start application and webserver  
115 - ''' 111 + '''Start application and webserver'''
116 112
117 # --- Commandline argument parsing 113 # --- Commandline argument parsing
118 arg = parse_cmdline_arguments() 114 arg = parse_cmdline_arguments()
@@ -162,10 +158,10 @@ def main(): @@ -162,10 +158,10 @@ def main():
162 158
163 # --- start application -------------------------------------------------- 159 # --- start application --------------------------------------------------
164 try: 160 try:
165 - app = LearnApp(courses=arg.courses,  
166 - prefix=arg.prefix,  
167 - dbase=arg.db,  
168 - check=arg.check) 161 + app = Application(courses=arg.courses,
  162 + prefix=arg.prefix,
  163 + dbase=arg.db,
  164 + check=arg.check)
169 except LearnException: 165 except LearnException:
170 logger.critical('Failed to start application') 166 logger.critical('Failed to start application')
171 sys.exit(1) 167 sys.exit(1)
@@ -180,5 +176,5 @@ def main(): @@ -180,5 +176,5 @@ def main():
180 176
181 177
182 # ---------------------------------------------------------------------------- 178 # ----------------------------------------------------------------------------
183 -if __name__ == "__main__": 179 +if __name__ == '__main__':
184 main() 180 main()
aprendizations/serve.py
@@ -32,7 +32,6 @@ logger = getLogger(__name__) @@ -32,7 +32,6 @@ logger = getLogger(__name__)
32 # ============================================================================ 32 # ============================================================================
33 # Handlers 33 # Handlers
34 # ============================================================================ 34 # ============================================================================
35 -# pylint: disable=abstract-method  
36 class BaseHandler(tornado.web.RequestHandler): 35 class BaseHandler(tornado.web.RequestHandler):
37 '''Base handler common to all handlers.''' 36 '''Base handler common to all handlers.'''
38 37
@@ -55,12 +54,12 @@ class LoginHandler(BaseHandler): @@ -55,12 +54,12 @@ class LoginHandler(BaseHandler):
55 54
56 async def post(self): 55 async def post(self):
57 '''Authenticate and redirect to application if successful''' 56 '''Authenticate and redirect to application if successful'''
58 - userid = self.get_body_argument('uid') or ''  
59 - passwd = self.get_body_argument('pw') 57 + uid = self.get_body_argument('uid') or ''
  58 + pw = self.get_body_argument('pw')
60 loop = tornado.ioloop.IOLoop.current() 59 loop = tornado.ioloop.IOLoop.current()
61 - login_ok = await self.app.login(userid, passwd, loop) 60 + login_ok = await self.app.login(uid, pw, loop)
62 if login_ok: 61 if login_ok:
63 - self.set_secure_cookie('aprendizations_user', userid) 62 + self.set_secure_cookie('aprendizations_user', uid)
64 self.redirect('/') 63 self.redirect('/')
65 else: 64 else:
66 self.render('login.html', error='Número ou senha incorrectos') 65 self.render('login.html', error='Número ou senha incorrectos')
@@ -73,22 +72,18 @@ class LogoutHandler(BaseHandler): @@ -73,22 +72,18 @@ class LogoutHandler(BaseHandler):
73 @tornado.web.authenticated 72 @tornado.web.authenticated
74 def get(self) -> None: 73 def get(self) -> None:
75 '''Clear cookies and user session''' 74 '''Clear cookies and user session'''
76 - self.app.logout(self.current_user) # FIXME 75 + self.app.logout(self.current_user)
77 self.clear_cookie('aprendizations_user') 76 self.clear_cookie('aprendizations_user')
78 self.redirect('/') 77 self.redirect('/')
79 78
80 79
81 # ---------------------------------------------------------------------------- 80 # ----------------------------------------------------------------------------
82 class ChangePasswordHandler(BaseHandler): 81 class ChangePasswordHandler(BaseHandler):
83 - '''  
84 - Handles password change  
85 - ''' 82 + '''Handles password change'''
86 83
87 @tornado.web.authenticated 84 @tornado.web.authenticated
88 async def post(self) -> None: 85 async def post(self) -> None:
89 - '''  
90 - Try to change password and show success/fail status  
91 - ''' 86 + '''Try to change password and show success/fail status'''
92 userid = self.current_user 87 userid = self.current_user
93 passwd = self.get_body_arguments('new_password')[0] # FIXME porque [0]? 88 passwd = self.get_body_arguments('new_password')[0] # FIXME porque [0]?
94 ok = await self.app.change_password(userid, passwd) 89 ok = await self.app.change_password(userid, passwd)
@@ -98,9 +93,7 @@ class ChangePasswordHandler(BaseHandler): @@ -98,9 +93,7 @@ class ChangePasswordHandler(BaseHandler):
98 93
99 # ---------------------------------------------------------------------------- 94 # ----------------------------------------------------------------------------
100 class RootHandler(BaseHandler): 95 class RootHandler(BaseHandler):
101 - '''  
102 - Handles root /  
103 - ''' 96 + '''Handle / (root)'''
104 97
105 @tornado.web.authenticated 98 @tornado.web.authenticated
106 def get(self) -> None: 99 def get(self) -> None:
@@ -110,9 +103,7 @@ class RootHandler(BaseHandler): @@ -110,9 +103,7 @@ class RootHandler(BaseHandler):
110 103
111 # ---------------------------------------------------------------------------- 104 # ----------------------------------------------------------------------------
112 class CoursesHandler(BaseHandler): 105 class CoursesHandler(BaseHandler):
113 - '''  
114 - Handles /courses  
115 - ''' 106 + '''Handles /courses'''
116 def set_default_headers(self, *_) -> None: 107 def set_default_headers(self, *_) -> None:
117 self.set_header('Cache-Control', 'no-cache') 108 self.set_header('Cache-Control', 'no-cache')
118 109
@@ -132,10 +123,7 @@ class CoursesHandler(BaseHandler): @@ -132,10 +123,7 @@ class CoursesHandler(BaseHandler):
132 class CourseHandler2(BaseHandler): 123 class CourseHandler2(BaseHandler):
133 @tornado.web.authenticated 124 @tornado.web.authenticated
134 def get(self, course_id) -> None: 125 def get(self, course_id) -> None:
135 - '''  
136 - Handles get /course/...  
137 - Starts a given course and show list of topics  
138 - ''' 126 + ''' Handles /course/... - start course and show topics'''
139 uid = self.current_user 127 uid = self.current_user
140 logger.debug('[CourseHandler2] uid="%s", course_id="%s"', uid, course_id) 128 logger.debug('[CourseHandler2] uid="%s", course_id="%s"', uid, course_id)
141 129
@@ -157,16 +145,11 @@ class CourseHandler2(BaseHandler): @@ -157,16 +145,11 @@ class CourseHandler2(BaseHandler):
157 145
158 # ============================================================================ 146 # ============================================================================
159 class CourseHandler(BaseHandler): 147 class CourseHandler(BaseHandler):
160 - '''  
161 - Show topics for a particular course  
162 - ''' 148 + '''Show topics for a particular course'''
163 149
164 @tornado.web.authenticated 150 @tornado.web.authenticated
165 def get(self, course_id) -> None: 151 def get(self, course_id) -> None:
166 - '''  
167 - Handles get /course/...  
168 - Starts a given course and show list of topics  
169 - ''' 152 + ''' Handles /course/... - start course and show topics'''
170 uid = self.current_user 153 uid = self.current_user
171 logger.debug('[CourseHandler] uid="%s", course_id="%s"', uid, course_id) 154 logger.debug('[CourseHandler] uid="%s", course_id="%s"', uid, course_id)
172 155
@@ -189,18 +172,13 @@ class CourseHandler(BaseHandler): @@ -189,18 +172,13 @@ class CourseHandler(BaseHandler):
189 172
190 # ============================================================================ 173 # ============================================================================
191 class TopicHandler(BaseHandler): 174 class TopicHandler(BaseHandler):
192 - '''  
193 - Handles a topic  
194 - ''' 175 + '''Handle topic'''
195 def set_default_headers(self, *_) -> None: 176 def set_default_headers(self, *_) -> None:
196 self.set_header('Cache-Control', 'no-cache') 177 self.set_header('Cache-Control', 'no-cache')
197 178
198 @tornado.web.authenticated 179 @tornado.web.authenticated
199 async def get(self, topic) -> None: 180 async def get(self, topic) -> None:
200 - '''  
201 - Handles get /topic/...  
202 - Starts a given topic  
203 - ''' 181 + '''Handles get /topic/... - start topic'''
204 uid = self.current_user 182 uid = self.current_user
205 logger.debug('[TopicHandler] %s', topic) 183 logger.debug('[TopicHandler] %s', topic)
206 try: 184 try:
@@ -217,15 +195,11 @@ class TopicHandler(BaseHandler): @@ -217,15 +195,11 @@ class TopicHandler(BaseHandler):
217 195
218 # ============================================================================ 196 # ============================================================================
219 class FileHandler(BaseHandler): 197 class FileHandler(BaseHandler):
220 - '''  
221 - Serves files from the /public subdir of the topics.  
222 - ''' 198 + '''Serves files from the /public directory of a topic'''
223 199
224 @tornado.web.authenticated 200 @tornado.web.authenticated
225 async def get(self, filename) -> None: 201 async def get(self, filename) -> None:
226 - '''  
227 - Serve file from the /public subdirectory of a particular topic  
228 - ''' 202 + '''Serve file from the /public directory of a topic'''
229 uid = self.current_user 203 uid = self.current_user
230 public_dir = self.app.get_current_public_dir(uid) 204 public_dir = self.app.get_current_public_dir(uid)
231 filepath = expanduser(join(public_dir, filename)) 205 filepath = expanduser(join(public_dir, filename))
@@ -248,9 +222,8 @@ class FileHandler(BaseHandler): @@ -248,9 +222,8 @@ class FileHandler(BaseHandler):
248 222
249 # ============================================================================ 223 # ============================================================================
250 class QuestionHandler(BaseHandler): 224 class QuestionHandler(BaseHandler):
251 - '''  
252 - Responds to AJAX to get a JSON question  
253 - ''' 225 + '''Responds to AJAX to get a JSON question'''
  226 +
254 templates = { 227 templates = {
255 'checkbox': 'question-checkbox.html', 228 'checkbox': 'question-checkbox.html',
256 'radio': 'question-radio.html', 229 'radio': 'question-radio.html',
@@ -268,9 +241,7 @@ class QuestionHandler(BaseHandler): @@ -268,9 +241,7 @@ class QuestionHandler(BaseHandler):
268 # ------------------------------------------------------------------------ 241 # ------------------------------------------------------------------------
269 @tornado.web.authenticated 242 @tornado.web.authenticated
270 async def get(self) -> None: 243 async def get(self) -> None:
271 - '''  
272 - Get question to render or an animated trophy if no more questions.  
273 - ''' 244 + '''Get question to render or an animated trophy'''
274 logger.debug('[QuestionHandler]') 245 logger.debug('[QuestionHandler]')
275 user = self.current_user 246 user = self.current_user
276 question = await self.app.get_question(user) 247 question = await self.app.get_question(user)
@@ -306,7 +277,7 @@ class QuestionHandler(BaseHandler): @@ -306,7 +277,7 @@ class QuestionHandler(BaseHandler):
306 async def post(self) -> None: 277 async def post(self) -> None:
307 ''' 278 '''
308 Correct answer and return status: right, wrong, try_again 279 Correct answer and return status: right, wrong, try_again
309 - Does not move to next question. 280 + Does not move to the next question.
310 ''' 281 '''
311 user = self.current_user 282 user = self.current_user
312 answer = self.get_body_arguments('answer') # list 283 answer = self.get_body_arguments('answer') # list
@@ -455,7 +426,7 @@ async def webserver(app, ssl, port: int = 8443, debug: bool = False) -> None: @@ -455,7 +426,7 @@ async def webserver(app, ssl, port: int = 8443, debug: bool = False) -> None:
455 'debug': debug, 426 'debug': debug,
456 } 427 }
457 webapp = tornado.web.Application(handlers, **settings) 428 webapp = tornado.web.Application(handlers, **settings)
458 - logger.info('Web application started (tornado.web.Application)') 429 + logger.info('Web application created (tornado.web.Application)')
459 430
460 # --- create tornado http server 431 # --- create tornado http server
461 try: 432 try: