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,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: |