From f4809e6f0f647e10fb039749d838ffe47f913a3f Mon Sep 17 00:00:00 2001 From: Miguel Barão Date: Sun, 3 Nov 2019 21:46:45 +0000 Subject: [PATCH] - code cleaning questions.py - fix image size in solution box - update demo to match the new courses organization --- BUGS.md | 2 ++ aprendizations/learnapp.py | 2 +- aprendizations/main.py | 52 +++++++++++++++++++++++++++------------------------- aprendizations/questions.py | 95 +++++++++++++++++++++++++++++++++++++---------------------------------------------------------- aprendizations/serve.py | 1 + aprendizations/tools.py | 8 ++++---- demo/astronomy.yaml | 26 ++++++++++++++++++++++++++ demo/astronomy/solar-system/correct-first_3_planets.py | 28 ++++++++++++++++++++++++++++ demo/astronomy/solar-system/correct-timeout.py | 11 +++++++++++ demo/astronomy/solar-system/public/earth.jpg | Bin 0 -> 1073096 bytes demo/astronomy/solar-system/public/jupiter.gif | Bin 0 -> 3105318 bytes demo/astronomy/solar-system/public/planets.png | Bin 0 -> 429412 bytes demo/astronomy/solar-system/public/saturn.jpg | Bin 0 -> 120067 bytes demo/astronomy/solar-system/questions.yaml | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ demo/courses.yaml | 28 ++++++++++++++++++++++++++++ demo/demo.yaml | 29 ----------------------------- demo/math.yaml | 31 +++++++++++++++++++++++++++++++ demo/math/addition/addition-two-digits.py | 20 ++++++++++++++++++++ demo/math/addition/questions.yaml | 22 ++++++++++++++++++++++ demo/math/gen-multiples-of-3.py | 29 ----------------------------- demo/math/multiplication/multiplication-table.py | 27 +++++++++++++++++++++++++++ demo/math/multiplication/questions.yaml | 24 ++++++++++++++++++++++++ demo/math/prime-numbers/questions.yaml | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ demo/math/questions.yaml | 87 --------------------------------------------------------------------------------------- demo/solar_system/correct-first_3_planets.py | 28 ---------------------------- demo/solar_system/correct-timeout.py | 11 ----------- demo/solar_system/public/earth.jpg | Bin 1073096 -> 0 bytes demo/solar_system/public/jupiter.gif | Bin 3105318 -> 0 bytes demo/solar_system/public/planets.png | Bin 429412 -> 0 bytes demo/solar_system/public/saturn.jpg | Bin 120067 -> 0 bytes demo/solar_system/questions.yaml | 69 --------------------------------------------------------------------- 31 files changed, 414 insertions(+), 341 deletions(-) create mode 100644 demo/astronomy.yaml create mode 100755 demo/astronomy/solar-system/correct-first_3_planets.py create mode 100755 demo/astronomy/solar-system/correct-timeout.py create mode 100644 demo/astronomy/solar-system/public/earth.jpg create mode 100644 demo/astronomy/solar-system/public/jupiter.gif create mode 100644 demo/astronomy/solar-system/public/planets.png create mode 100644 demo/astronomy/solar-system/public/saturn.jpg create mode 100644 demo/astronomy/solar-system/questions.yaml create mode 100644 demo/courses.yaml delete mode 100644 demo/demo.yaml create mode 100644 demo/math.yaml create mode 100755 demo/math/addition/addition-two-digits.py create mode 100644 demo/math/addition/questions.yaml delete mode 100755 demo/math/gen-multiples-of-3.py create mode 100755 demo/math/multiplication/multiplication-table.py create mode 100644 demo/math/multiplication/questions.yaml create mode 100644 demo/math/prime-numbers/questions.yaml delete mode 100644 demo/math/questions.yaml delete mode 100755 demo/solar_system/correct-first_3_planets.py delete mode 100755 demo/solar_system/correct-timeout.py delete mode 100644 demo/solar_system/public/earth.jpg delete mode 100644 demo/solar_system/public/jupiter.gif delete mode 100644 demo/solar_system/public/planets.png delete mode 100644 demo/solar_system/public/saturn.jpg delete mode 100644 demo/solar_system/questions.yaml diff --git a/BUGS.md b/BUGS.md index a62b4e8..e5f8a17 100644 --- a/BUGS.md +++ b/BUGS.md @@ -1,6 +1,8 @@ # BUGS +- se num topico, a ultima pergunta tem imagens, o servidor nao fornece as imagengs porque o current_topic passa a None antes de carregar no botao continuar. O caminho é prefix+None e dá erro. +- registar last_seen e remover os antigos de cada vez que houver um login. - initdb da integrity error se no mesmo comando existirem alunos repetidos (p.ex em ficheiros csv diferentes ou entre csv e opcao -a) - permite definir goal, mas nao verifica se esta no grafo. rebenta no start_topic. - double click submits twice. diff --git a/aprendizations/learnapp.py b/aprendizations/learnapp.py index 68d4ac1..03490ee 100644 --- a/aprendizations/learnapp.py +++ b/aprendizations/learnapp.py @@ -462,7 +462,7 @@ class LearnApp(object): # ------------------------------------------------------------------------ def get_current_public_dir(self, uid: str) -> str: - topic: str = self.online[uid]['state'].get_current_topic() + topic: str = self.online[uid]['state'].get_current_topic() # FIXME returns None if its the last question in the topic prefix: str = self.deps.graph['prefix'] return path.join(prefix, topic, 'public') diff --git a/aprendizations/main.py b/aprendizations/main.py index 4bde154..857b3e7 100644 --- a/aprendizations/main.py +++ b/aprendizations/main.py @@ -140,21 +140,22 @@ def main(): path.join(certs_dir, 'privkey.pem')) except FileNotFoundError: logging.critical(f'SSL certificates missing in {certs_dir}') - print('--------------------------------------------------------------') - print('Certificates should be issued by a certificate authority (CA),') - print('such as https://letsencrypt.org. ') - print('For testing purposes a selfsigned certificate can be generated') - print('locally by running: ') - print(' ') - print(' openssl req -x509 -newkey rsa:4096 -keyout privkey.pem \\ ') - print(' -out cert.pem -days 365 -nodes ') - print(' ') - print('Copy the cert.pem and privkey.pem files to: ') - print(' ') - print(f' {certs_dir:<62}') - print(' ') - print('(See README.md for more information) ') - print('--------------------------------------------------------------') + print('--------------------------------------------------------------', + 'Certificates should be issued by a certificate authority (CA),', + 'such as https://letsencrypt.org. ', + 'For testing purposes a selfsigned certificate can be generated', + 'locally by running: ', + ' ', + ' openssl req -x509 -newkey rsa:4096 -keyout privkey.pem \\ ', + ' -out cert.pem -days 365 -nodes ', + ' ', + 'Copy the cert.pem and privkey.pem files to: ', + ' ', + f' {certs_dir:<62}', + ' ', + 'See README.md for more information ', + '--------------------------------------------------------------', + sep='\n') sys.exit(1) else: logging.info('SSL certificates loaded') @@ -167,20 +168,21 @@ def main(): check=arg.check) except DatabaseUnusableError: logging.critical('Failed to start application.') - print('--------------------------------------------------------------') - print('Could not find a usable database. Use one of the follwing ') - print('commands to initialize: ') - print(' ') - print(' initdb-aprendizations --admin # add admin ') - print(' initdb-aprendizations -a 86 "Max Smart" # add student ') - print(' initdb-aprendizations students.csv # add many students') - print('--------------------------------------------------------------') + print('--------------------------------------------------------------', + 'Could not find a usable database. Use one of the follwing ', + 'commands to initialize: ', + ' ', + ' initdb-aprendizations --admin # add admin ', + ' initdb-aprendizations -a 86 "Max Smart" # add student ', + ' initdb-aprendizations students.csv # add many students', + '--------------------------------------------------------------', + sep='\n') sys.exit(1) except Exception: - logging.critical('Failed to start application.') + logging.critical('Failed to start backend.') sys.exit(1) else: - logging.info('Backend application started') + logging.info('Backend started') # --- run webserver forever run_webserver(app=learnapp, ssl=ssl_ctx, port=arg.port, debug=arg.debug) diff --git a/aprendizations/questions.py b/aprendizations/questions.py index eed52a6..539342c 100644 --- a/aprendizations/questions.py +++ b/aprendizations/questions.py @@ -1,5 +1,6 @@ # python standard library +import asyncio import random import re from os import path @@ -21,10 +22,10 @@ class QuestionException(Exception): pass -# =========================================================================== +# ============================================================================ # Questions derived from Question are already instantiated and ready to be # presented to students. -# =========================================================================== +# ============================================================================ class Question(dict): ''' Classes derived from this base class are meant to instantiate questions @@ -41,7 +42,7 @@ class Question(dict): 'comments': '', 'solution': '', 'files': {}, - 'max_tries': 3, + # 'max_tries': 3, })) def correct(self) -> None: @@ -57,7 +58,7 @@ class Question(dict): self.setdefault(k, v) -# ========================================================================== +# ============================================================================ class QuestionRadio(Question): '''An instance of QuestionRadio will always have the keys: type (str) @@ -92,7 +93,7 @@ class QuestionRadio(Question): for x in range(n)] if len(self['correct']) != n: - msg = (f'Options and correct mismatch in ' + msg = ('Number of options and correct differ in ' f'"{self["ref"]}", file "{self["filename"]}".') logger.error(msg) raise QuestionException(msg) @@ -138,7 +139,7 @@ class QuestionRadio(Question): self['grade'] = x -# =========================================================================== +# ============================================================================ class QuestionCheckbox(Question): '''An instance of QuestionCheckbox will always have the keys: type (str) @@ -218,7 +219,7 @@ class QuestionCheckbox(Question): self['grade'] = x / sum_abs -# =========================================================================== +# ============================================================================ class QuestionText(Question): '''An instance of QuestionText will always have the keys: type (str) @@ -251,7 +252,7 @@ class QuestionText(Question): self['grade'] = 1.0 if self['answer'] in self['correct'] else 0.0 -# =========================================================================== +# ============================================================================ class QuestionTextRegex(Question): '''An instance of QuestionTextRegex will always have the keys: type (str) @@ -281,7 +282,7 @@ class QuestionTextRegex(Question): self['grade'] = 1.0 if ok else 0.0 -# =========================================================================== +# ============================================================================ class QuestionNumericInterval(Question): '''An instance of QuestionTextNumeric will always have the keys: type (str) @@ -316,7 +317,7 @@ class QuestionNumericInterval(Question): self['grade'] = 1.0 if lower <= answer <= upper else 0.0 -# =========================================================================== +# ============================================================================ class QuestionTextArea(Question): '''An instance of QuestionTextArea will always have the keys: type (str) @@ -397,7 +398,7 @@ class QuestionTextArea(Question): logger.error(f'Invalid grade in "{self["correct"]}".') -# =========================================================================== +# ============================================================================ class QuestionInformation(Question): # ------------------------------------------------------------------------ def __init__(self, q: QDict) -> None: @@ -412,30 +413,38 @@ class QuestionInformation(Question): self['grade'] = 1.0 # always "correct" but points should be zero! -# =========================================================================== +# ============================================================================ +# # QFactory is a class that can generate question instances, e.g. by shuffling # options, running a script to generate the question, etc. # -# To generate an instance of a question we use the method generate() where -# the argument is the reference of the question we wish to produce. -# The generate() method returns a question instance of the correct class. +# To generate an instance of a question we use the method generate(). +# It returns a question instance of the correct class. +# There is also an asynchronous version called gen_async(). This version is +# synchronous for all question types (radio, checkbox, etc) except for generator +# types which run asynchronously. # # Example: # -# # generate a question instance from a dictionary -# qdict = { +# # make a factory for a question +# qfactory = QFactory({ # 'type': 'radio', # 'text': 'Choose one', # 'options': ['a', 'b'] -# } -# qfactory = QFactory(qdict) +# }) +# +# # generate synchronously # question = qfactory.generate() # +# # generate asynchronously +# question = await qfactory.gen_async() +# # # answer one question and correct it # question['answer'] = 42 # set answer # question.correct() # correct answer # grade = question['grade'] # get grade -# =========================================================================== +# +# ============================================================================ class QFactory(object): # Depending on the type of question, a different question class will be # instantiated. All these classes derive from the base class `Question`. @@ -456,44 +465,10 @@ class QFactory(object): def __init__(self, qdict: QDict = QDict({})) -> None: self.question = qdict - # ----------------------------------------------------------------------- - # Given a ref returns an instance of a descendent of Question(), - # i.e. a question object (radio, checkbox, ...). - # ----------------------------------------------------------------------- - def generate(self) -> Question: - # FIXME this is almost the same as the one below - # return asyncio.run(self.gen_async()) - - logger.debug(f'generating {self.question["ref"]}...') - # Shallow copy so that script generated questions will not replace - # the original generators - q = self.question.copy() - q['qid'] = str(uuid.uuid4()) # unique for each generated question - - # If question is of generator type, an external program will be run - # which will print a valid question in yaml format to stdout. This - # output is then yaml parsed into a dictionary `q`. - if q['type'] == 'generator': - logger.debug(f' \\_ Running "{q["script"]}".') - q.setdefault('args', []) - q.setdefault('stdin', '') # FIXME is it really necessary? - script = path.join(q['path'], q['script']) - out = run_script(script=script, args=q['args'], stdin=q['stdin']) - q.update(out) - - # Finally we create an instance of Question() - try: - qinstance = self._types[q['type']](QDict(q)) # of matching class - except QuestionException as e: - logger.error(e) - raise e - except KeyError: - logger.error(f'Invalid type "{q["type"]}" in "{q["ref"]}"') - raise - else: - return qinstance - - # ----------------------------------------------------------------------- + # ------------------------------------------------------------------------ + # generates a question instance of QuestionRadio, QuestionCheckbox, ..., + # which is a descendent of base class Question. + # ------------------------------------------------------------------------ async def gen_async(self) -> Question: logger.debug(f'generating {self.question["ref"]}...') # Shallow copy so that script generated questions will not replace @@ -524,3 +499,7 @@ class QFactory(object): raise else: return qinstance + + # ------------------------------------------------------------------------ + def generate(self) -> Question: + return asyncio.get_event_loop().run_until_complete(self.gen_async()) diff --git a/aprendizations/serve.py b/aprendizations/serve.py index 3588ad5..0784e9d 100644 --- a/aprendizations/serve.py +++ b/aprendizations/serve.py @@ -433,6 +433,7 @@ def run_webserver(app, webapp = WebApplication(app, debug=debug) except Exception: logger.critical('Failed to start web application.') + raise sys.exit(1) else: logger.info('Web application started (tornado.web.Application)') diff --git a/aprendizations/tools.py b/aprendizations/tools.py index 20c7011..f592545 100644 --- a/aprendizations/tools.py +++ b/aprendizations/tools.py @@ -110,14 +110,14 @@ class HighlightRenderer(mistune.Renderer): return highlight(code, lexer, formatter) def table(self, header, body): - return '' \ - + header + '' + body + '
' + return ('' + f'{header}{body}
') def image(self, src, title, alt): alt = mistune.escape(alt, quote=True) title = mistune.escape(title or '', quote=True) - return f'' + return (f'{alt}') # class="img-fluid mx-auto d-block" # Pass math through unaltered - mathjax does the rendering in the browser diff --git a/demo/astronomy.yaml b/demo/astronomy.yaml new file mode 100644 index 0000000..2f2a4a2 --- /dev/null +++ b/demo/astronomy.yaml @@ -0,0 +1,26 @@ +--- +# ---------------------------------------------------------------------------- +# optional values applied to each topic, if undefined there +# ---------------------------------------------------------------------------- + +# defaults: +# file: questions.yaml +# shuffle_questions: true +# choose: 6 +# max_tries: 2 +# forgetting_factor: 0.97 +# min_level: 0.01 +# append_wrong: true + +# ---------------------------------------------------------------------------- +# topics and their dependencies +# ---------------------------------------------------------------------------- + +topics: + astronomy/solar-system: + name: Sistema solar + + # astronomy/milky-way: + # name: Via Láctea + # deps: + # - solar-system diff --git a/demo/astronomy/solar-system/correct-first_3_planets.py b/demo/astronomy/solar-system/correct-first_3_planets.py new file mode 100755 index 0000000..dde8a39 --- /dev/null +++ b/demo/astronomy/solar-system/correct-first_3_planets.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +import re +import sys +import time + +s = sys.stdin.read() + +ans = set(re.findall(r'[\w]+', s.lower())) # convert answer to lowercase +ans.difference_update({'e', 'a', 'o', 'planeta', 'planetas'}) # ignore words + +# correct set of planets +planets = {'mercúrio', 'vénus', 'terra'} + +correct = set.intersection(ans, planets) # the ones I got right +wrong = set.difference(ans, planets) # the ones I got wrong + +grade = (len(correct) - len(wrong)) / len(planets) + +comments = 'Certo' if grade == 1.0 else 'as iniciais dos planetas são M, V e T' + +out = f'''--- +grade: {grade} +comments: {comments}''' + +time.sleep(2) # simulate computation time (may generate timeout) + +print(out) diff --git a/demo/astronomy/solar-system/correct-timeout.py b/demo/astronomy/solar-system/correct-timeout.py new file mode 100755 index 0000000..1098990 --- /dev/null +++ b/demo/astronomy/solar-system/correct-timeout.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 + +import sys +import time + +s = sys.stdin.read() + +# generate timeout +time.sleep(100) + +print(0.5) diff --git a/demo/astronomy/solar-system/public/earth.jpg b/demo/astronomy/solar-system/public/earth.jpg new file mode 100644 index 0000000..b4c9a14 Binary files /dev/null and b/demo/astronomy/solar-system/public/earth.jpg differ diff --git a/demo/astronomy/solar-system/public/jupiter.gif b/demo/astronomy/solar-system/public/jupiter.gif new file mode 100644 index 0000000..f0e2746 Binary files /dev/null and b/demo/astronomy/solar-system/public/jupiter.gif differ diff --git a/demo/astronomy/solar-system/public/planets.png b/demo/astronomy/solar-system/public/planets.png new file mode 100644 index 0000000..69845f4 Binary files /dev/null and b/demo/astronomy/solar-system/public/planets.png differ diff --git a/demo/astronomy/solar-system/public/saturn.jpg b/demo/astronomy/solar-system/public/saturn.jpg new file mode 100644 index 0000000..c2406df Binary files /dev/null and b/demo/astronomy/solar-system/public/saturn.jpg differ diff --git a/demo/astronomy/solar-system/questions.yaml b/demo/astronomy/solar-system/questions.yaml new file mode 100644 index 0000000..f77979e --- /dev/null +++ b/demo/astronomy/solar-system/questions.yaml @@ -0,0 +1,66 @@ +--- +# ---------------------------------------------------------------------------- +- type: text + ref: planet-earth + title: Sistema solar + text: O nosso planeta chama-se planeta... + correct: ['Terra', 'terra'] + # opcional + answer: dos macacos? + solution: | + O nosso planeta é o planeta **Terra**. + + ![Terra](earth.jpg) + +# ---------------------------------------------------------------------------- +- type: radio + ref: largest-planet + title: Sistema solar + text: | + ![planetas](planets.png "Planetas do Sistema Solar") + + Qual é o maior planeta do Sistema Solar? + options: + - Mercúrio + - Marte + - Júpiter + - Saturno + - Têm todos o mesmo tamanho + # opcional + correct: 2 + shuffle: false + solution: | + O maior planeta é Júpiter. Tem uma massa 1000 vezes inferior ao Sol, mas + ainda assim 2.5 vezes maior que a massa de todos os outros planetas juntos. + É um gigante gasoso maioritariamente composto por hidrogénio. + + ![Júpiter](jupiter.gif) + +# ---------------------------------------------------------------------------- +- type: text-regex + ref: saturn + title: Sistema solar + text: O planeta do sistema solar conhecido pelos seus aneis é o planeta... + correct: '[Ss]aturno' + solution: | + O planeta Saturno é famoso pelos seus anéis. + É o segundo maior planeta do Sistema Solar. + Tal como Júpiter, é um gigante gasoso. + Os seus anéis são formados por partículas de gelo. + + ![Saturno](saturn.jpg) + +# ---------------------------------------------------------------------------- +- type: textarea + ref: first_3_planets + title: Sistema solar + text: | + Qual o nome dos três planetas mais próximos do Sol? + Exemplo `Ceres, Krypton e Vulcano` + correct: correct-first_3_planets.py + # correct: correct-timeout.py + # opcional + answer: Ceres, Krypton e Vulcano + timeout: 3 + solution: | + Os 3 planetas mais perto do Sol são Mercúrio, Vénus e Terra. diff --git a/demo/courses.yaml b/demo/courses.yaml new file mode 100644 index 0000000..f8922ea --- /dev/null +++ b/demo/courses.yaml @@ -0,0 +1,28 @@ +--- +# ---------------------------------------------------------------------------- +# import topics and their dependencies +# ---------------------------------------------------------------------------- +topics_from: + - math.yaml + - astronomy.yaml + +# ---------------------------------------------------------------------------- +# course names, their goals and other details +# ---------------------------------------------------------------------------- +courses: + math: + title: Matemática + description: | + Adição, multiplicação e números primos. + goals: + - math/addition + - math/multiplication + - math/prime-numbers + + astronomy: + title: Astronomia + description: | + Sistema Solar e Via Láctea. + goals: + - astronomy/solar-system + # - astronomy/milky-way diff --git a/demo/demo.yaml b/demo/demo.yaml deleted file mode 100644 index 142dffa..0000000 --- a/demo/demo.yaml +++ /dev/null @@ -1,29 +0,0 @@ ---- - -title: Example -database: students.db - -# values applied to each topic, if undefined there -file: questions.yaml -shuffle_questions: true -choose: 6 -max_tries: 2 -forgetting_factor: 0.97 -min_level: 0.01 -append_wrong: true - -# ---------------------------------------------------------------------------- -topics: - # topic without dependencies - math: - name: Matemática - file: questions.yaml - choose: 6 - shuffle: true - - # topic with one dependency - solar_system: - name: Sistema solar - deps: - - math - forgetting_factor: 0.1 diff --git a/demo/math.yaml b/demo/math.yaml new file mode 100644 index 0000000..f1afbb4 --- /dev/null +++ b/demo/math.yaml @@ -0,0 +1,31 @@ +--- +# ---------------------------------------------------------------------------- +# optional values applied to each topic, if undefined there +# ---------------------------------------------------------------------------- + +# defaults: +# file: questions.yaml +# shuffle_questions: true +# choose: 6 +# max_tries: 2 +# forgetting_factor: 0.97 +# min_level: 0.01 +# append_wrong: true + +# ---------------------------------------------------------------------------- +# topics and their dependencies +# ---------------------------------------------------------------------------- + +topics: + math/addition: + name: Adição + + math/multiplication: + name: Multiplicação + deps: + - math/addition + + math/prime-numbers: + name: Números primos + deps: + - math/multiplication diff --git a/demo/math/addition/addition-two-digits.py b/demo/math/addition/addition-two-digits.py new file mode 100755 index 0000000..c9a5519 --- /dev/null +++ b/demo/math/addition/addition-two-digits.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +import random +import sys + +a = int(sys.argv[1]) +b = int(sys.argv[2]) + +x = random.randint(a, b) +y = random.randint(a, b) +r = x + y + +print(f'''--- +type: text +title: Adição de números com 2 algarismos +text: | + Qual o resultado da soma ${x}+{y}$? +correct: ['{r}'] +solution: | + O resultado é {r}.''') diff --git a/demo/math/addition/questions.yaml b/demo/math/addition/questions.yaml new file mode 100644 index 0000000..0ab7d95 --- /dev/null +++ b/demo/math/addition/questions.yaml @@ -0,0 +1,22 @@ +--- +# --------------------------------------------------------------------------- +- type: generator + ref: addition-two-digits + script: addition-two-digits.py + args: [10, 20] + +- type: checkbox + ref: addition-properties + title: Propriedades da adição + text: Indique quais as propriedades que a adição satisfaz. + options: + # right + - Existência de elemento neutro, $x+0=x$. + - Existência de inverso aditivo (simétrico), $x+(-x)=0$. + - Propriedade associativa, $(x+y)+z = x+(y+z)$. + - Propriedade comutativa, $x+y=y+x$. + # wrong + - Existência de elemento absorvente, $x+1=1$. + correct: [1, 1, 1, 1, -1] + solution: | + A adição não tem elemento absorvente. diff --git a/demo/math/gen-multiples-of-3.py b/demo/math/gen-multiples-of-3.py deleted file mode 100755 index e001745..0000000 --- a/demo/math/gen-multiples-of-3.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 - -import random -import sys - -a, b = map(int, sys.argv[1:]) # get command line arguments - -numbers = list(range(a, b)) -random.shuffle(numbers) -numbers = numbers[:8] -correct = [1 if n % 3 == 0 else -1 for n in numbers] - -multiples = [str(n) for n in numbers if n % 3 == 0] -if multiples: - solution = f'Os números múltiplos de 3 são: {", ".join(multiples)}.' -else: - solution = f'Nenhum número mostrado é múltiplo de 3.' - - -q = f'''--- -type: checkbox -title: Múltiplos de 3 -text: Indique quais dos seguintes números são múltiplos de 3. -options: {numbers} -correct: {correct} -solution: | - {solution}''' - -print(q) diff --git a/demo/math/multiplication/multiplication-table.py b/demo/math/multiplication/multiplication-table.py new file mode 100755 index 0000000..b3a9672 --- /dev/null +++ b/demo/math/multiplication/multiplication-table.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +import random +import sys + +# can be repeated +# x = random.randint(2, 9) +# y = random.randint(2, 9) + +# with x != y +x, y = random.sample(range(2,10), k=2) +r = x * y + +yy = '+'.join([str(y)]*x) +xx = '+'.join([str(x)]*y) + +print(f'''--- +type: text +title: Multiplicação (tabuada) +text: | + Qual o resultado da multiplicação ${x}\\times {y}$? +correct: ['{r}'] +solution: | + A multiplicação é a repetição da soma. Podemos fazer de duas maneiras: + $$ {x}\\times {y} = {yy} = {r} $$ + ou + $$ {y}\\times {x} = {xx} = {r}. $$''') diff --git a/demo/math/multiplication/questions.yaml b/demo/math/multiplication/questions.yaml new file mode 100644 index 0000000..cb8ac9e --- /dev/null +++ b/demo/math/multiplication/questions.yaml @@ -0,0 +1,24 @@ +--- +# --------------------------------------------------------------------------- +- type: generator + ref: multiplication-table + script: multiplication-table.py + +- type: checkbox + ref: multiplication-properties + title: Propriedades da multiplicação + text: Indique quais as propriedades que a multiplicação satisfaz. + options: + # right + - Existência de elemento neutro, $1x=x$. + - Propriedade associativa, $(xy)z = x(yz)$. + - Propriedade comutativa, $xy=yx$. + - Existência de elemento absorvente, $0x=0$. + # wrong + - Existência de inverso, todos os números $x$ tem um inverso $1/x$ tal que + $x(1/x)=1$. + correct: [1, 1, 1, 1, -1] + solution: | + Na multiplicação nem todos os números têm inverso. Só têm inverso os números + diferentes de zero. + As outras propriedades são satisfeitas para todos os números. diff --git a/demo/math/prime-numbers/questions.yaml b/demo/math/prime-numbers/questions.yaml new file mode 100644 index 0000000..56aab61 --- /dev/null +++ b/demo/math/prime-numbers/questions.yaml @@ -0,0 +1,59 @@ +--- +# --------------------------------------------------------------------------- +- type: radio + ref: prime-number-definition + title: Números primos + text: | + Qual a definição de número primo? + options: + - Um número $n>1$ é primo se só é divisível por si próprio e pela unidade. + - Um número $n\ge 2$ é primo se só é divisível por si próprio e pela unidade. + - Um número $n>1$ é primo se os seus divisores são apenas $1$ e $n$. + - Um número $n\ge 2$ é primo se os seus divisores são apenas $1$ e $n$. + # wrong + - Um número $n$ é primo se é divisível por si próprio e pela unidade. + - Um número $n>1$ é primo se é divisível por si próprio e pela unidade. + - Um número $n\ge 2$ é primo se não tem divisores. + choose: 3 + correct: [1, 1, 1, 1, 0, 0, 0] + solution: + Um número $n$ é primo se $n\ge 2$ e se os únicos divisores forem $1$ e $n$. + +# --------------------------------------------------------------------------- +- type: checkbox + ref: even-odd-prime + title: Números pares, ímpares e primos + text: Indique as afirmações verdadeiras. + options: + - ['3 é primo', '4 é primo'] + - ['2 é par', '3 é par'] + - ['1 é ímpar', '2 é ímpar'] + correct: [1, 1, 1] + solution: | + A tabela seguinte resume as propriedades dos números 1 a 4: + + Número | ímpar | par | primo + :-----:|:-----:|:---:|:------: + 1 | S | N | N + 2 | N | S | S + 3 | S | N | S + 4 | N | S | N + +# --------------------------------------------------------------------------- +- type: radio + ref: prime-numbers + title: Números primos + text: Qual dos seguintes números é um primo múltiplo de 3? + options: + - 3 + # wrong + - 9 + - 13 + - 21 + - 15 + - Não há primos múltiplos de 3. + max_tries: 1 + solution: | + O único número primo múltiplo de 3 é o próprio 3. + Todos os outros múltiplos (6, 9, 12, 15, ...) têm 3 como divisor e portanto + não são primos. diff --git a/demo/math/questions.yaml b/demo/math/questions.yaml deleted file mode 100644 index 4ac167f..0000000 --- a/demo/math/questions.yaml +++ /dev/null @@ -1,87 +0,0 @@ ---- -# --------------------------------------------------------------------------- -- type: radio - ref: distributive_property - title: Propriedade distributiva - text: | - Qual das seguintes opções usa a propriedade distributiva para calcular - $9\times 2 - 2\times 3$? - options: - # correct - - $2\times(9 - 3)$ - - $(9-3)\times 2$ - # wrong - - $18 - 6 = 12$ - - $2\times 9 - 2\times 3$ - - $2\times(9-2\times 3)$ - correct: [1, 1, 0, 0, 0] - choose: 3 - max_tries: 1 - solution: | - Colocando o 2 em evidência, obtém-se a resposta correcta $2\times(9 - 3)$ - ou $(9-3)\times 2$. - -# --------------------------------------------------------------------------- -- type: checkbox - ref: numbers - title: Números pares e primos - text: Indique as afirmações verdadeiras. - options: - - ['3 é primo', '4 é primo'] - - ['2 é par', '3 é par'] - - ['1 é ímpar', '2 é ímpar'] - correct: [1, 1, 1] - solution: | - A tabela seguinte resume as propriedades dos números 1 a 4: - - Número | ímpar | par | primo - :-----:|:-----:|:---:|:------: - 1 | S | N | N - 2 | N | S | S - 3 | S | N | S - 4 | N | S | N - -# --------------------------------------------------------------------------- -- type: radio - ref: prime_numbers - title: Números primos - text: Qual dos seguintes números é primo? - options: - - 13 - - 12 - - 14 - - 1, a **unidade** - max_tries: 1 - solution: | - O único número primo é o 13. - - O número 1, embora apenas seja divisível por ele próprio e pela unidade - (que neste caso coincide com ele próprio), não é um número primo. Apenas - são considerados números primos a partir do 2. - -# --------------------------------------------------------------------------- -- type: checkbox - ref: math-expressions - title: Expressões matemáticas - text: Quais das seguintes expressões são verdadeiras? - options: - - $1 > 0$ - - $\sqrt{3} > \sqrt{2}$ - - $e^{i\pi} + 1 = 0$ - - $\frac{\partial f(x,y)}{\partial z} = 1$ - - $-1 > 1$ - # how many points for each checkmark (normalized afterwards): - correct: [1, 1, 1, -1, -1] - solution: | - Das 5 opções existem 2 falsas. A derivada parcial é zero uma vez que a - função não varia com $z$. A desigualdade $-1 > 1$ é também falsa, os - números negativos são menores que os positivos. - -# --------------------------------------------------------------------------- -# the program should print a question in yaml format. The args will be -# sent as command line options when the program is run. -- type: generator - ref: overflow - script: gen-multiples-of-3.py - args: [11, 120] - choose: 3 diff --git a/demo/solar_system/correct-first_3_planets.py b/demo/solar_system/correct-first_3_planets.py deleted file mode 100755 index dde8a39..0000000 --- a/demo/solar_system/correct-first_3_planets.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 - -import re -import sys -import time - -s = sys.stdin.read() - -ans = set(re.findall(r'[\w]+', s.lower())) # convert answer to lowercase -ans.difference_update({'e', 'a', 'o', 'planeta', 'planetas'}) # ignore words - -# correct set of planets -planets = {'mercúrio', 'vénus', 'terra'} - -correct = set.intersection(ans, planets) # the ones I got right -wrong = set.difference(ans, planets) # the ones I got wrong - -grade = (len(correct) - len(wrong)) / len(planets) - -comments = 'Certo' if grade == 1.0 else 'as iniciais dos planetas são M, V e T' - -out = f'''--- -grade: {grade} -comments: {comments}''' - -time.sleep(2) # simulate computation time (may generate timeout) - -print(out) diff --git a/demo/solar_system/correct-timeout.py b/demo/solar_system/correct-timeout.py deleted file mode 100755 index 1098990..0000000 --- a/demo/solar_system/correct-timeout.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import time - -s = sys.stdin.read() - -# generate timeout -time.sleep(100) - -print(0.5) diff --git a/demo/solar_system/public/earth.jpg b/demo/solar_system/public/earth.jpg deleted file mode 100644 index b4c9a14..0000000 Binary files a/demo/solar_system/public/earth.jpg and /dev/null differ diff --git a/demo/solar_system/public/jupiter.gif b/demo/solar_system/public/jupiter.gif deleted file mode 100644 index f0e2746..0000000 Binary files a/demo/solar_system/public/jupiter.gif and /dev/null differ diff --git a/demo/solar_system/public/planets.png b/demo/solar_system/public/planets.png deleted file mode 100644 index 69845f4..0000000 Binary files a/demo/solar_system/public/planets.png and /dev/null differ diff --git a/demo/solar_system/public/saturn.jpg b/demo/solar_system/public/saturn.jpg deleted file mode 100644 index c2406df..0000000 Binary files a/demo/solar_system/public/saturn.jpg and /dev/null differ diff --git a/demo/solar_system/questions.yaml b/demo/solar_system/questions.yaml deleted file mode 100644 index 5c2feab..0000000 --- a/demo/solar_system/questions.yaml +++ /dev/null @@ -1,69 +0,0 @@ ---- - -# --------------------------------------------------------------------------- -- type: text - ref: home-planet - title: Sistema solar - text: O nosso planeta chama-se planeta... - correct: ['Terra', 'terra'] - # opcional - answer: Não é Marte... - solution: | - Embora algumas pessoas andem muitas vezes na Lua, o nosso planeta é o - planeta **Terra**! - - ![Terra](earth.jpg) - -# --------------------------------------------------------------------------- -- type: radio - ref: solar-system - title: Sistema solar - text: | - ![planetas](planets.png " Planetas do Sistema Solar") - - Qual é o maior planeta do Sistema Solar? - - options: - - Mercúrio - - Marte - - Júpiter - - Têm todos o mesmo tamanho - # opcional - correct: 2 - shuffle: false - discount: true - solution: | - O maior planeta é Júpiter. Tem uma massa 1000 vezes inferior ao Sol, mas - ainda assim 2.5 vezes maior que a massa de todos os outros planetas juntos. - É um gigante gasoso maioritariamente composto por hidrogénio. - - ![Júpiter](jupiter.gif) - -# --------------------------------------------------------------------------- -- type: text-regex - ref: saturn - title: Sistema solar - text: O planeta do sistema solar conhecido por ter aneis é o planeta... - correct: '[Ss]aturno' - solution: | - O planeta Saturno é famoso pelos seus anéis. - É o segundo maior planeta do Sistema Solar, a seguir a Júpiter. - Tal como Júpiter, é um gigante gasoso. - Os seus anéis são formados por partículas de gelo. - - ![Saturno](saturn.jpg) - -# --------------------------------------------------------------------------- -- type: textarea - ref: first_3_planets - title: Sistema solar - text: | - Qual o nome dos três planetas mais próximos do Sol? - Exemplo `Lua, Ceres e Vulcano` - correct: correct-first_3_planets.py - # correct: correct-timeout.py - # opcional - answer: Vulcano, Krypton, Plutão - timeout: 3 - solution: | - Os 3 planetas mais perto do Sol são: Mercúrio, Vénus e Terra. -- libgit2 0.21.2