Commit a7d6b9e15cb57ee62aa31ac0f0dae7f6f35ad904
1 parent
ac45791a
Exists in
dev
renamed LearnApp to Application
and other minor changes
Showing
3 changed files
with
48 additions
and
80 deletions
Show diff stats
aprendizations/learnapp.py
... | ... | @@ -10,13 +10,14 @@ import logging |
10 | 10 | from random import random |
11 | 11 | from os.path import join, exists |
12 | 12 | from typing import Any, Dict, Iterable, List, Optional, Tuple, Set, DefaultDict |
13 | -import yaml | |
14 | 13 | |
15 | 14 | # third party libraries |
16 | 15 | import bcrypt |
17 | 16 | import networkx as nx |
18 | -from sqlalchemy import create_engine, select, func | |
17 | +import sqlalchemy as sa | |
18 | +from sqlalchemy import select | |
19 | 19 | from sqlalchemy.orm import Session |
20 | +import yaml | |
20 | 21 | |
21 | 22 | # this project |
22 | 23 | from aprendizations.models import Student, Answer, Topic, StudentTopic |
... | ... | @@ -34,7 +35,7 @@ class LearnException(Exception): |
34 | 35 | |
35 | 36 | |
36 | 37 | # ============================================================================ |
37 | -class LearnApp(): | |
38 | +class Application(): | |
38 | 39 | ''' |
39 | 40 | LearnApp - application logic |
40 | 41 | |
... | ... | @@ -157,8 +158,8 @@ class LearnApp(): |
157 | 158 | # wait random time to minimize timing attacks |
158 | 159 | await asyncio.sleep(random()) |
159 | 160 | |
160 | - query = select(Student).where(Student.id == uid) | |
161 | 161 | with Session(self._engine) as session: |
162 | + query = select(Student).where(Student.id == uid) | |
162 | 163 | student = session.execute(query).scalar_one_or_none() |
163 | 164 | if student is None: |
164 | 165 | logger.info('User "%s" does not exist', uid) |
... | ... | @@ -177,8 +178,8 @@ class LearnApp(): |
177 | 178 | logger.info('User "%s" logged in', uid) |
178 | 179 | |
179 | 180 | # get topics for this student and set its current state |
180 | - query = select(StudentTopic).where(StudentTopic.student_id == uid) | |
181 | 181 | with Session(self._engine) as session: |
182 | + query = select(StudentTopic).where(StudentTopic.student_id == uid) | |
182 | 183 | student_topics = session.execute(query).scalars().all() |
183 | 184 | |
184 | 185 | state = {t.topic_id: { |
... | ... | @@ -220,8 +221,8 @@ class LearnApp(): |
220 | 221 | password.encode('utf-8'), |
221 | 222 | bcrypt.gensalt()) |
222 | 223 | |
223 | - query = select(Student).where(Student.id == uid) | |
224 | 224 | with Session(self._engine) as session: |
225 | + query = select(Student).where(Student.id == uid) | |
225 | 226 | session.execute(query).scalar_one().password = str(hashed_pw) |
226 | 227 | session.commit() |
227 | 228 | |
... | ... | @@ -273,10 +274,10 @@ class LearnApp(): |
273 | 274 | date: str = str(student_state.get_topic_date(tid)) |
274 | 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 | 277 | with Session(self._engine) as session: |
278 | + query = select(StudentTopic) \ | |
279 | + .where(StudentTopic.student_id == uid) \ | |
280 | + .where(StudentTopic.topic_id == tid) | |
280 | 281 | student_topic = session.execute(query).scalar_one_or_none() |
281 | 282 | if student_topic is None: |
282 | 283 | # insert new studenttopic into database |
... | ... | @@ -351,11 +352,11 @@ class LearnApp(): |
351 | 352 | raise LearnException('Database does not exist') |
352 | 353 | |
353 | 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 | 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 | 360 | with Session(self._engine) as session: |
360 | 361 | count_students = session.execute(query_students).scalar() |
361 | 362 | count_topics = session.execute(query_topics).scalar() |
... | ... | @@ -566,12 +567,12 @@ class LearnApp(): |
566 | 567 | ''' |
567 | 568 | |
568 | 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 | 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 | 577 | # all students in the database FIXME only with answers of this course |
577 | 578 | students = session.execute(query_students).all() | ... | ... |
aprendizations/main.py
... | ... | @@ -16,16 +16,14 @@ import sys |
16 | 16 | from typing import Dict |
17 | 17 | |
18 | 18 | # this project |
19 | -from .learnapp import LearnApp, LearnException | |
19 | +from .learnapp import Application, LearnException | |
20 | 20 | from .serve import webserver |
21 | 21 | from . import APP_NAME, APP_VERSION |
22 | 22 | |
23 | 23 | |
24 | 24 | # ---------------------------------------------------------------------------- |
25 | 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 | 28 | parser = argparse.ArgumentParser( |
31 | 29 | description='Webserver for interactive learning and practice. ' |
... | ... | @@ -110,9 +108,7 @@ def get_logger_config(debug: bool = False) -> Dict: |
110 | 108 | |
111 | 109 | # ---------------------------------------------------------------------------- |
112 | 110 | def main(): |
113 | - ''' | |
114 | - Start application and webserver | |
115 | - ''' | |
111 | + '''Start application and webserver''' | |
116 | 112 | |
117 | 113 | # --- Commandline argument parsing |
118 | 114 | arg = parse_cmdline_arguments() |
... | ... | @@ -162,10 +158,10 @@ def main(): |
162 | 158 | |
163 | 159 | # --- start application -------------------------------------------------- |
164 | 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 | 165 | except LearnException: |
170 | 166 | logger.critical('Failed to start application') |
171 | 167 | sys.exit(1) |
... | ... | @@ -180,5 +176,5 @@ def main(): |
180 | 176 | |
181 | 177 | |
182 | 178 | # ---------------------------------------------------------------------------- |
183 | -if __name__ == "__main__": | |
179 | +if __name__ == '__main__': | |
184 | 180 | main() | ... | ... |
aprendizations/serve.py
... | ... | @@ -32,7 +32,6 @@ logger = getLogger(__name__) |
32 | 32 | # ============================================================================ |
33 | 33 | # Handlers |
34 | 34 | # ============================================================================ |
35 | -# pylint: disable=abstract-method | |
36 | 35 | class BaseHandler(tornado.web.RequestHandler): |
37 | 36 | '''Base handler common to all handlers.''' |
38 | 37 | |
... | ... | @@ -55,12 +54,12 @@ class LoginHandler(BaseHandler): |
55 | 54 | |
56 | 55 | async def post(self): |
57 | 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 | 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 | 61 | if login_ok: |
63 | - self.set_secure_cookie('aprendizations_user', userid) | |
62 | + self.set_secure_cookie('aprendizations_user', uid) | |
64 | 63 | self.redirect('/') |
65 | 64 | else: |
66 | 65 | self.render('login.html', error='Número ou senha incorrectos') |
... | ... | @@ -73,22 +72,18 @@ class LogoutHandler(BaseHandler): |
73 | 72 | @tornado.web.authenticated |
74 | 73 | def get(self) -> None: |
75 | 74 | '''Clear cookies and user session''' |
76 | - self.app.logout(self.current_user) # FIXME | |
75 | + self.app.logout(self.current_user) | |
77 | 76 | self.clear_cookie('aprendizations_user') |
78 | 77 | self.redirect('/') |
79 | 78 | |
80 | 79 | |
81 | 80 | # ---------------------------------------------------------------------------- |
82 | 81 | class ChangePasswordHandler(BaseHandler): |
83 | - ''' | |
84 | - Handles password change | |
85 | - ''' | |
82 | + '''Handles password change''' | |
86 | 83 | |
87 | 84 | @tornado.web.authenticated |
88 | 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 | 87 | userid = self.current_user |
93 | 88 | passwd = self.get_body_arguments('new_password')[0] # FIXME porque [0]? |
94 | 89 | ok = await self.app.change_password(userid, passwd) |
... | ... | @@ -98,9 +93,7 @@ class ChangePasswordHandler(BaseHandler): |
98 | 93 | |
99 | 94 | # ---------------------------------------------------------------------------- |
100 | 95 | class RootHandler(BaseHandler): |
101 | - ''' | |
102 | - Handles root / | |
103 | - ''' | |
96 | + '''Handle / (root)''' | |
104 | 97 | |
105 | 98 | @tornado.web.authenticated |
106 | 99 | def get(self) -> None: |
... | ... | @@ -110,9 +103,7 @@ class RootHandler(BaseHandler): |
110 | 103 | |
111 | 104 | # ---------------------------------------------------------------------------- |
112 | 105 | class CoursesHandler(BaseHandler): |
113 | - ''' | |
114 | - Handles /courses | |
115 | - ''' | |
106 | + '''Handles /courses''' | |
116 | 107 | def set_default_headers(self, *_) -> None: |
117 | 108 | self.set_header('Cache-Control', 'no-cache') |
118 | 109 | |
... | ... | @@ -132,10 +123,7 @@ class CoursesHandler(BaseHandler): |
132 | 123 | class CourseHandler2(BaseHandler): |
133 | 124 | @tornado.web.authenticated |
134 | 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 | 127 | uid = self.current_user |
140 | 128 | logger.debug('[CourseHandler2] uid="%s", course_id="%s"', uid, course_id) |
141 | 129 | |
... | ... | @@ -157,16 +145,11 @@ class CourseHandler2(BaseHandler): |
157 | 145 | |
158 | 146 | # ============================================================================ |
159 | 147 | class CourseHandler(BaseHandler): |
160 | - ''' | |
161 | - Show topics for a particular course | |
162 | - ''' | |
148 | + '''Show topics for a particular course''' | |
163 | 149 | |
164 | 150 | @tornado.web.authenticated |
165 | 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 | 153 | uid = self.current_user |
171 | 154 | logger.debug('[CourseHandler] uid="%s", course_id="%s"', uid, course_id) |
172 | 155 | |
... | ... | @@ -189,18 +172,13 @@ class CourseHandler(BaseHandler): |
189 | 172 | |
190 | 173 | # ============================================================================ |
191 | 174 | class TopicHandler(BaseHandler): |
192 | - ''' | |
193 | - Handles a topic | |
194 | - ''' | |
175 | + '''Handle topic''' | |
195 | 176 | def set_default_headers(self, *_) -> None: |
196 | 177 | self.set_header('Cache-Control', 'no-cache') |
197 | 178 | |
198 | 179 | @tornado.web.authenticated |
199 | 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 | 182 | uid = self.current_user |
205 | 183 | logger.debug('[TopicHandler] %s', topic) |
206 | 184 | try: |
... | ... | @@ -217,15 +195,11 @@ class TopicHandler(BaseHandler): |
217 | 195 | |
218 | 196 | # ============================================================================ |
219 | 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 | 200 | @tornado.web.authenticated |
225 | 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 | 203 | uid = self.current_user |
230 | 204 | public_dir = self.app.get_current_public_dir(uid) |
231 | 205 | filepath = expanduser(join(public_dir, filename)) |
... | ... | @@ -248,9 +222,8 @@ class FileHandler(BaseHandler): |
248 | 222 | |
249 | 223 | # ============================================================================ |
250 | 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 | 227 | templates = { |
255 | 228 | 'checkbox': 'question-checkbox.html', |
256 | 229 | 'radio': 'question-radio.html', |
... | ... | @@ -268,9 +241,7 @@ class QuestionHandler(BaseHandler): |
268 | 241 | # ------------------------------------------------------------------------ |
269 | 242 | @tornado.web.authenticated |
270 | 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 | 245 | logger.debug('[QuestionHandler]') |
275 | 246 | user = self.current_user |
276 | 247 | question = await self.app.get_question(user) |
... | ... | @@ -306,7 +277,7 @@ class QuestionHandler(BaseHandler): |
306 | 277 | async def post(self) -> None: |
307 | 278 | ''' |
308 | 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 | 282 | user = self.current_user |
312 | 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 | 426 | 'debug': debug, |
456 | 427 | } |
457 | 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 | 431 | # --- create tornado http server |
461 | 432 | try: | ... | ... |