#!/usr/bin/env python3.6 # python standard library import os import json # installed libraries import markdown import tornado.ioloop import tornado.web import tornado.httpserver from tornado import template, gen import concurrent.futures # this project from app import LearnApp # markdown helper def md(text): return markdown.markdown(text, extensions=[ 'markdown.extensions.tables', 'markdown.extensions.fenced_code', 'markdown.extensions.codehilite', 'markdown.extensions.def_list', 'markdown.extensions.sane_lists' ]) # A thread pool to be used for password hashing with bcrypt. FIXME and other things? executor = concurrent.futures.ThreadPoolExecutor(2) # ============================================================================ # WebApplication - Tornado Web Server # ============================================================================ class WebApplication(tornado.web.Application): def __init__(self): handlers = [ (r'/', LearnHandler), (r'/login', LoginHandler), (r'/logout', LogoutHandler), (r'/question', QuestionHandler), ] settings = { 'template_path': os.path.join(os.path.dirname(__file__), 'templates'), 'static_path': os.path.join(os.path.dirname(__file__), 'static'), 'static_url_prefix': '/static/', # this is the default 'xsrf_cookies': False, # FIXME see how to do it... 'cookie_secret': '__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__', # FIXME 'login_url': '/login', 'debug': True, } super().__init__(handlers, **settings) self.learn = LearnApp() # ============================================================================ # Handlers # ============================================================================ # ---------------------------------------------------------------------------- # Base handler common to all handlers. # ---------------------------------------------------------------------------- class BaseHandler(tornado.web.RequestHandler): @property def learn(self): return self.application.learn def get_current_user(self): cookie = self.get_secure_cookie("user") if cookie: user = cookie.decode('utf-8') # FIXME if the cookie exists but user is not in learn.online, this will force new login and store new (duplicate?) cookie. is this correct?? if user in self.learn.online: return user # ---------------------------------------------------------------------------- # /auth/login and /auth/logout # ---------------------------------------------------------------------------- class LoginHandler(BaseHandler): def get(self): self.render('login.html', error='') # @gen.coroutine def post(self): uid = self.get_body_argument('uid') pw = self.get_body_argument('pw') # print(f'login.post: user={uid}, pw={pw}') if self.learn.login_ok(uid, pw): print('login ok') self.set_secure_cookie("user", str(uid)) self.redirect(self.get_argument("next", "/")) else: print('login failed') self.render("login.html", error='NĂºmero ou senha incorrectos') # ---------------------------------------------------------------------------- class LogoutHandler(BaseHandler): @tornado.web.authenticated def get(self): self.learn.logout(self.current_user) self.clear_cookie('user') self.redirect(self.get_argument('next', '/')) # ---------------------------------------------------------------------------- # /learn # ---------------------------------------------------------------------------- class LearnHandler(BaseHandler): @tornado.web.authenticated def get(self): uid = self.current_user self.render('learn.html', uid=uid, name=self.learn.get_student_name(uid) ) # ---------------------------------------------------------------------------- # respond to AJAX to get a JSON question class QuestionHandler(BaseHandler): templates = { 'checkbox': 'question-checkbox.html', 'radio': 'question-radio.html', 'text': 'question-text.html', 'text_regex': 'question-text.html', 'text_numeric': 'question-text.html', 'textarea': 'question-textarea.html', } @tornado.web.authenticated def get(self): self.redirect('/') @tornado.web.authenticated def post(self): print('================= POST ==============') # ref = self.get_body_arguments('question_ref') user = self.current_user answer = self.get_body_arguments('answer') next_question = self.learn.check_answer(user, answer) if next_question is not None: html_out = self.render_string(self.templates[next_question['type']], question=next_question, # dictionary with the question md=md, # function that renders markdown to html ) self.write({ 'params': tornado.escape.to_unicode(html_out), 'method': 'new_question', }) else: self.write({ 'params': 'None', 'method': 'shake' }) # ---------------------------------------------------------------------------- def main(): webapp = WebApplication() http_server = tornado.httpserver.HTTPServer(webapp, ssl_options={ "certfile": "certs/cert.pem", "keyfile": "certs/key.pem" }) http_server.listen(8443) try: print('--- start ---') tornado.ioloop.IOLoop.current().start() except KeyboardInterrupt: tornado.ioloop.IOLoop.current().stop() print('\n--- stop ---') # ---------------------------------------------------------------------------- if __name__ == "__main__": main()