From 939be670c9e323d2967031b2ea4ba56c32c4d729 Mon Sep 17 00:00:00 2001 From: Miguel Barão Date: Fri, 23 Dec 2022 18:05:13 +0000 Subject: [PATCH] update jquery. move links to include-libs.html --- aprendizations/__init__.py | 6 +++--- aprendizations/serve.py | 80 ++++++++++++++++++++++++++++++++++++++++---------------------------------------- aprendizations/templates/include-libs.html | 13 ++++++++++++- aprendizations/templates/question-checkbox.html | 15 ++++++++------- aprendizations/templates/question-radio.html | 10 +++++----- aprendizations/templates/topic.html | 23 +---------------------- aprendizations/tools.py | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------------------------------------------------------------------------- setup.py | 2 +- 8 files changed, 188 insertions(+), 197 deletions(-) diff --git a/aprendizations/__init__.py b/aprendizations/__init__.py index dceaf80..401ae60 100644 --- a/aprendizations/__init__.py +++ b/aprendizations/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2019 Miguel Barão +# Copyright © 2022 Miguel Barão # # THE MIT License # @@ -30,10 +30,10 @@ are progressively uncovered as the students progress. ''' APP_NAME = 'aprendizations' -APP_VERSION = '2022.08.dev1' +APP_VERSION = '2022.12.dev1' APP_DESCRIPTION = __doc__ __author__ = 'Miguel Barão' -__copyright__ = 'Copyright 2021, Miguel Barão' +__copyright__ = 'Copyright © 2022, Miguel Barão' __license__ = 'MIT license' __version__ = APP_VERSION diff --git a/aprendizations/serve.py b/aprendizations/serve.py index c45a40a..e34b772 100644 --- a/aprendizations/serve.py +++ b/aprendizations/serve.py @@ -11,6 +11,7 @@ import mimetypes from os.path import join, dirname, expanduser import signal import sys +import re from typing import List, Optional, Union import uuid @@ -21,7 +22,7 @@ import tornado.web from tornado.escape import to_unicode # this project -from aprendizations.tools import md_to_html +from aprendizations.renderer_markdown import md_to_html from aprendizations.learnapp import LearnException from aprendizations import APP_NAME @@ -82,6 +83,7 @@ class BaseHandler(tornado.web.RequestHandler): ''' Base handler common to all handlers. ''' + @property def learn(self): '''easier access to learnapp''' @@ -105,6 +107,7 @@ class RankingsHandler(BaseHandler): ''' Handles rankings page ''' + @tornado.web.authenticated def get(self) -> None: ''' @@ -130,33 +133,30 @@ class LoginHandler(BaseHandler): ''' Handles /login ''' + def get(self) -> None: ''' - Renders login page + Render login page ''' - self.render('login.html', - appname=APP_NAME, - error='') + self.render('login.html', appname=APP_NAME, error='') async def post(self): ''' - Perform authentication and redirects to application if successful + Authenticate and redirect to application if successful ''' - - userid = (self.get_body_argument('uid') or '').lstrip('l') + userid = self.get_body_argument('uid') or '' passwd = self.get_body_argument('pw') - - login_ok = await self.learn.login(userid, passwd) - - if login_ok: - counter = str(self.learn.get_login_counter(userid)) - self.set_secure_cookie('aprendizations_user', userid) - self.set_secure_cookie('counter', counter) - self.redirect('/') - else: - self.render('login.html', - appname=APP_NAME, - error='Número ou senha incorrectos') + match = re.search(r'[0-9]+', userid) # extract number + if match is not None: + userid = match.group(0) # get string with number + if await self.learn.login(userid, passwd): + counter = str(self.learn.get_login_counter(userid)) + self.set_secure_cookie('aprendizations_user', userid) + self.set_secure_cookie('counter', counter) + self.redirect('/') + self.render('login.html', + appname=APP_NAME, + error='Número ou senha incorrectos') # ---------------------------------------------------------------------------- @@ -167,7 +167,7 @@ class LogoutHandler(BaseHandler): @tornado.web.authenticated def get(self) -> None: ''' - clears cookies and removes user session + clear cookies and user session ''' self.clear_cookie('user') self.clear_cookie('counter') @@ -182,14 +182,14 @@ class ChangePasswordHandler(BaseHandler): ''' Handles password change ''' + @tornado.web.authenticated async def post(self) -> None: ''' - Tries to perform password change and then replies success/fail status + Try to change password and show success/fail status ''' userid = self.current_user passwd = self.get_body_arguments('new_password')[0] - changed_ok = await self.learn.change_password(userid, passwd) if changed_ok: notification = self.render_string( @@ -203,7 +203,6 @@ class ChangePasswordHandler(BaseHandler): type='danger', msg='A password não foi alterada!' ) - self.write({'msg': to_unicode(notification)}) @@ -212,9 +211,10 @@ class RootHandler(BaseHandler): ''' Handles root / ''' + @tornado.web.authenticated def get(self) -> None: - '''Simply redirects to the main entrypoint''' + '''Redirect to main entrypoint''' self.redirect('/courses') @@ -223,12 +223,13 @@ class CoursesHandler(BaseHandler): ''' Handles /courses ''' + def set_default_headers(self, *_) -> None: self.set_header('Cache-Control', 'no-cache') @tornado.web.authenticated def get(self) -> None: - '''Renders list of available courses''' + '''Render available courses''' uid = self.current_user self.render('courses.html', appname=APP_NAME, @@ -285,7 +286,7 @@ class TopicHandler(BaseHandler): Starts a given topic ''' uid = self.current_user - + logger.debug('[TopicHandler.get] %s', topic) try: await self.learn.start_topic(uid, topic) # FIXME GET should not modify state... except KeyError: @@ -304,15 +305,18 @@ class FileHandler(BaseHandler): ''' Serves files from the /public subdir of the topics. ''' + @tornado.web.authenticated async def get(self, filename) -> None: ''' - Serves files from /public subdirectories of a particular topic + Serve file from the /public subdirectory of a particular topic ''' uid = self.current_user public_dir = self.learn.get_current_public_dir(uid) filepath = expanduser(join(public_dir, filename)) + logger.debug('uid=%s, public_dir=%s, filepath=%s', uid, public_dir, + filepath) try: with open(filepath, 'rb') as file: data = file.read() @@ -350,10 +354,9 @@ class QuestionHandler(BaseHandler): @tornado.web.authenticated async def get(self) -> None: ''' - Gets question to render. - Shows an animated trophy if there are no more questions in the topic. + Get question to render or an animated trophy if no more questions. ''' - logger.debug('[QuestionHandler]') + logger.debug('[QuestionHandler.get]') user = self.current_user question = await self.learn.get_question(user) @@ -387,7 +390,7 @@ class QuestionHandler(BaseHandler): @tornado.web.authenticated async def post(self) -> None: ''' - Corrects answer and returns status: right, wrong, try_again + Correct answer and return status: right, wrong, try_again Does not move to next question. ''' user = self.current_user @@ -422,18 +425,16 @@ class QuestionHandler(BaseHandler): # --- check answer (nonblocking) and get corrected question and action question = await self.learn.check_answer(user, ans) - # --- built response to return + # --- build response response = {'method': question['status'], 'params': {}} if question['status'] == 'right': # get next question in the topic comments = self.render_string('comments-right.html', comments=question['comments'], md=md_to_html) - solution = self.render_string('solution.html', solution=question['solution'], md=md_to_html) - response['params'] = { 'type': question['type'], 'progress': self.learn.get_student_progress(user), @@ -445,7 +446,6 @@ class QuestionHandler(BaseHandler): comments = self.render_string('comments.html', comments=question['comments'], md=md_to_html) - response['params'] = { 'type': question['type'], 'progress': self.learn.get_student_progress(user), @@ -456,10 +456,8 @@ class QuestionHandler(BaseHandler): comments = self.render_string('comments.html', comments=question['comments'], md=md_to_html) - solution = self.render_string( 'solution.html', solution=question['solution'], md=md_to_html) - response['params'] = { 'type': question['type'], 'progress': self.learn.get_student_progress(user), @@ -501,7 +499,7 @@ def run_webserver(app, ssl, port: int = 8443, debug: bool = False) -> None: sys.exit(1) logger.info('Web application started (tornado.web.Application)') - # --- create tornado webserver + # --- create tornado http server try: httpserver = tornado.httpserver.HTTPServer(webapp, ssl_options=ssl) except ValueError: @@ -515,9 +513,11 @@ def run_webserver(app, ssl, port: int = 8443, debug: bool = False) -> None: logger.critical('Cannot bind port %d. Already in use?', port) sys.exit(1) logger.info('Webserver listening on %d... (Ctrl-C to stop)', port) + + # --- set signal handler for Control-C signal.signal(signal.SIGINT, signal_handler) - # --- run webserver + # --- run tornado webserver try: tornado.ioloop.IOLoop.current().start() # running... except Exception: diff --git a/aprendizations/templates/include-libs.html b/aprendizations/templates/include-libs.html index 2745272..f9590d6 100644 --- a/aprendizations/templates/include-libs.html +++ b/aprendizations/templates/include-libs.html @@ -1,5 +1,5 @@ - + @@ -7,3 +7,14 @@ + + + + + + + + + + + diff --git a/aprendizations/templates/question-checkbox.html b/aprendizations/templates/question-checkbox.html index 612ceed..6a7bfc0 100644 --- a/aprendizations/templates/question-checkbox.html +++ b/aprendizations/templates/question-checkbox.html @@ -3,13 +3,14 @@ {% block answer %}
-
- {% for n,opt in enumerate(question['options']) %} - - {% end %} +
+ {% for n,opt in enumerate(question['options']) %} +
  • + + + {% end %}
  • diff --git a/aprendizations/templates/question-radio.html b/aprendizations/templates/question-radio.html index 5dad5f5..559ef14 100644 --- a/aprendizations/templates/question-radio.html +++ b/aprendizations/templates/question-radio.html @@ -5,12 +5,12 @@
    {% for n,opt in enumerate(question['options']) %} -
    diff --git a/aprendizations/templates/topic.html b/aprendizations/templates/topic.html index 5e87553..c6e14bb 100644 --- a/aprendizations/templates/topic.html +++ b/aprendizations/templates/topic.html @@ -8,27 +8,6 @@ {% include include-libs.html %} - - - - - - - - - - - - @@ -99,7 +78,7 @@
    - +
    diff --git a/aprendizations/tools.py b/aprendizations/tools.py index 62c6510..77fca0e 100644 --- a/aprendizations/tools.py +++ b/aprendizations/tools.py @@ -3,15 +3,15 @@ import asyncio import logging from os import path -import re +# import re import subprocess from typing import Any, List # third party libraries -import mistune -from pygments import highlight -from pygments.lexers import get_lexer_by_name -from pygments.formatters import HtmlFormatter +# import mistune +# from pygments import highlight +# from pygments.lexers import get_lexer_by_name +# from pygments.formatters import HtmlFormatter import yaml @@ -28,119 +28,119 @@ logger = logging.getLogger(__name__) # Block math: # $$x$$ or \begin{equation}x\end{equation} # ------------------------------------------------------------------------- -class MathBlockGrammar(mistune.BlockGrammar): - block_math = re.compile(r'^\$\$(.*?)\$\$', re.DOTALL) - latex_environment = re.compile(r'^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}', - re.DOTALL) - - -class MathBlockLexer(mistune.BlockLexer): - default_rules = ['block_math', 'latex_environment'] \ - + mistune.BlockLexer.default_rules - - def __init__(self, rules=None, **kwargs): - if rules is None: - rules = MathBlockGrammar() - super().__init__(rules, **kwargs) - - def parse_block_math(self, m): - '''Parse a $$math$$ block''' - self.tokens.append({ - 'type': 'block_math', - 'text': m.group(1) - }) - - def parse_latex_environment(self, m): - r'''Parse an environment \begin{name}text\end{name}''' - self.tokens.append({ - 'type': 'latex_environment', - 'name': m.group(1), - 'text': m.group(2) - }) - - -# ------------------------------------------------------------------------- -# Inline math: $x$ -# ------------------------------------------------------------------------- -class MathInlineGrammar(mistune.InlineGrammar): - math = re.compile(r'^\$(.+?)\$', re.DOTALL) - block_math = re.compile(r'^\$\$(.+?)\$\$', re.DOTALL) - text = re.compile(r'^[\s\S]+?(?=[\\' - f'{header}{body}') - - def image(self, src, title, alt): - alt = mistune.escape(alt, quote=True) - title = mistune.escape(title or '', quote=True) - return (f'{alt}') # class="img-fluid mx-auto d-block" - - # Pass math through unaltered - mathjax does the rendering in the browser - def block_math(self, text): - return fr'\[ {text} \]' - - def latex_environment(self, name, text): - return fr'\begin{{{name}}} {text} \end{{{name}}}' - - def inline_math(self, text): - return fr'\( {text} \)' - - -# hard_wrap=True to insert
    on newline -markdown = MarkdownWithMath(HighlightRenderer(escape=True)) - - -def md_to_html(text: str, strip_p_tag: bool = False) -> str: - md: str = markdown(text) - if strip_p_tag and md.startswith('

    ') and md.endswith('

    \n'): - return md[3:-5] - else: - return md - +# class MathBlockGrammar(mistune.BlockGrammar): +# block_math = re.compile(r'^\$\$(.*?)\$\$', re.DOTALL) +# latex_environment = re.compile(r'^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}', +# re.DOTALL) +# +# +# class MathBlockLexer(mistune.BlockLexer): +# default_rules = ['block_math', 'latex_environment'] \ +# + mistune.BlockLexer.default_rules +# +# def __init__(self, rules=None, **kwargs): +# if rules is None: +# rules = MathBlockGrammar() +# super().__init__(rules, **kwargs) +# +# def parse_block_math(self, m): +# '''Parse a $$math$$ block''' +# self.tokens.append({ +# 'type': 'block_math', +# 'text': m.group(1) +# }) +# +# def parse_latex_environment(self, m): +# r'''Parse an environment \begin{name}text\end{name}''' +# self.tokens.append({ +# 'type': 'latex_environment', +# 'name': m.group(1), +# 'text': m.group(2) +# }) +# +# +# # ------------------------------------------------------------------------- +# # Inline math: $x$ +# # ------------------------------------------------------------------------- +# class MathInlineGrammar(mistune.InlineGrammar): +# math = re.compile(r'^\$(.+?)\$', re.DOTALL) +# block_math = re.compile(r'^\$\$(.+?)\$\$', re.DOTALL) +# text = re.compile(r'^[\s\S]+?(?=[\\' +# f'{header}{body}') +# +# def image(self, src, title, alt): +# alt = mistune.escape(alt, quote=True) +# title = mistune.escape(title or '', quote=True) +# return (f'{alt}') # class="img-fluid mx-auto d-block" +# +# # Pass math through unaltered - mathjax does the rendering in the browser +# def block_math(self, text): +# return fr'\[ {text} \]' +# +# def latex_environment(self, name, text): +# return fr'\begin{{{name}}} {text} \end{{{name}}}' +# +# def inline_math(self, text): +# return fr'\( {text} \)' +# +# +# # hard_wrap=True to insert
    on newline +# markdown = MarkdownWithMath(HighlightRenderer(escape=True)) +# +# +# def md_to_html(text: str, strip_p_tag: bool = False) -> str: +# md: str = markdown(text) +# if strip_p_tag and md.startswith('

    ') and md.endswith('

    \n'): +# return md[3:-5] +# else: +# return md +# # --------------------------------------------------------------------------- # load data from yaml file diff --git a/setup.py b/setup.py index 17f2b63..94f523d 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ setup( python_requires='>=3.9.*', install_requires=[ 'tornado>=6.0', - 'mistune<2', + 'mistune>=3', 'pyyaml>=5.1', 'pygments', 'sqlalchemy>=1.4', -- libgit2 0.21.2