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 ('')
def image(self, src, title, alt):
alt = mistune.escape(alt, quote=True)
title = mistune.escape(title or '', quote=True)
- return f'
'
+ return (f'
')
# 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**.
+
+ 
+
+# ----------------------------------------------------------------------------
+- type: radio
+ ref: largest-planet
+ title: Sistema solar
+ text: |
+ 
+
+ 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.
+
+ 
+
+# ----------------------------------------------------------------------------
+- 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.
+
+ 
+
+# ----------------------------------------------------------------------------
+- 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**!
-
- 
-
-# ---------------------------------------------------------------------------
-- type: radio
- ref: solar-system
- title: Sistema solar
- text: |
- 
-
- 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.
-
- 
-
-# ---------------------------------------------------------------------------
-- 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.
-
- 
-
-# ---------------------------------------------------------------------------
-- 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