Commit 2290803dba73b806dfb492d8df0396dceeaff342
1 parent
c74f28c0
Exists in
master
and in
1 other branch
- added demo questions, test and scripts
- changed default question type to 'information'.
Showing
7 changed files
with
178 additions
and
13 deletions
Show diff stats
MANUAL.md
@@ -260,7 +260,6 @@ A generator question will run an external program that is expected to print a qu | @@ -260,7 +260,6 @@ A generator question will run an external program that is expected to print a qu | ||
260 | 260 | ||
261 | An example of a question generator is the following | 261 | An example of a question generator is the following |
262 | 262 | ||
263 | - :::python linenums="True" | ||
264 | #!/usr/bin/env python3.4 | 263 | #!/usr/bin/env python3.4 |
265 | from random import randint | 264 | from random import randint |
266 | 265 |
@@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
1 | +#!/usr/bin/env python3.4 | ||
2 | + | ||
3 | +import sys | ||
4 | +s = sys.stdin.read() | ||
5 | + | ||
6 | +# set of words converted to lowercase lowercase | ||
7 | +answer = set(re.findall(r'[\w]+', s.lower())) | ||
8 | + | ||
9 | +# correct set of colors | ||
10 | +rgb_colors = set(['red', 'green', 'blue']) | ||
11 | + | ||
12 | +if answer == rgb_colors: | ||
13 | + print(1.0) | ||
14 | +else: | ||
15 | + print(0.0) | ||
16 | +exit(0) | ||
0 | \ No newline at end of file | 17 | \ No newline at end of file |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +#!/usr/bin/env python3.4 | ||
2 | + | ||
3 | +from random import randint | ||
4 | + | ||
5 | + | ||
6 | +q = ''' | ||
7 | +type: checkbox | ||
8 | +text: Indique quais das seguintes adições resultam em overflow quando se considera a adição de números com sinal (complemento para 2) em registos de 8 bits. | ||
9 | +options: | ||
10 | +''' | ||
11 | + | ||
12 | +correct = [] | ||
13 | +for i in range(5): | ||
14 | + x = randint(11,120) | ||
15 | + y = randint(11,120) | ||
16 | + q += '- "`{} + {}`"\n'.format(x, y) | ||
17 | + correct.append(1 if x + y > 127 else -1) | ||
18 | + | ||
19 | +q += 'correct: ' + str(correct) | ||
20 | + | ||
21 | +print(q) |
@@ -0,0 +1,72 @@ | @@ -0,0 +1,72 @@ | ||
1 | +- | ||
2 | + ref: solar-system-mars | ||
3 | + type: radio | ||
4 | + text: Choose the correct answer | ||
5 | + options: | ||
6 | + - This one is correct | ||
7 | + - Wrong | ||
8 | + - Very wrong! | ||
9 | + # optional | ||
10 | + correct: 0 | ||
11 | + shuffle: True | ||
12 | + discount: True | ||
13 | + hint: Just don't choose the wrong ones | ||
14 | +# --------------------------------------------------------------------------- | ||
15 | +- | ||
16 | + ref: solar-system-jupiter | ||
17 | + type: checkbox | ||
18 | + text: Which ones are correct? | ||
19 | + options: | ||
20 | + - Yes | ||
21 | + - No | ||
22 | + - Yes | ||
23 | + - Obvious one, not very important | ||
24 | + correct: [1, -1, 1, 0.5] | ||
25 | + hint: There are three. | ||
26 | +# --------------------------------------------------------------------------- | ||
27 | +- | ||
28 | + ref: solar-system-venus | ||
29 | + type: text | ||
30 | + text: What's your favorite basic color? | ||
31 | + correct: ['blue', 'green'] | ||
32 | + hint: It's not red. | ||
33 | +# --------------------------------------------------------------------------- | ||
34 | +- | ||
35 | + ref: question-v1 | ||
36 | + type: text_regex | ||
37 | + text: What's your favorite basic color? | ||
38 | + correct: '[bB]lue' | ||
39 | + hint: It's not red. | ||
40 | +# --------------------------------------------------------------------------- | ||
41 | +- | ||
42 | + ref: question-v2 | ||
43 | + type: textarea | ||
44 | + text: Write names of the three basic colors. | ||
45 | + correct: demo/correct-question.py | ||
46 | + hint: They start by RGB and order does not matter. | ||
47 | +# --------------------------------------------------------------------------- | ||
48 | +- | ||
49 | + ref: question-v3 | ||
50 | + type: generator | ||
51 | + script: demo/generate-question.py | ||
52 | + # the script should print a question in yaml format like the ones above. | ||
53 | + # Print only the dictionary, not the list (hiffen). | ||
54 | +# --------------------------------------------------------------------------- | ||
55 | +- | ||
56 | + ref: one-question | ||
57 | + type: information | ||
58 | + text: Please do not cheat. | ||
59 | +# --------------------------------------------------------------------------- | ||
60 | +- | ||
61 | + ref: another-question | ||
62 | + # type: information (default) | ||
63 | + text: | | ||
64 | + The text of questions is parsed as __markdown__ and can include | ||
65 | + LaTeX formulas $\sqrt{2\pi}$ and pretty code | ||
66 | + | ||
67 | + ```.C | ||
68 | + int main() { | ||
69 | + return 0; | ||
70 | + } | ||
71 | + ``` | ||
72 | + |
@@ -0,0 +1,56 @@ | @@ -0,0 +1,56 @@ | ||
1 | +#============================================================================= | ||
2 | +ref: demo | ||
3 | +title: Teste de Demonstração | ||
4 | + | ||
5 | +# database contains students+passwords, final results of the tests, and questions done | ||
6 | +# database: path/to/students.db | ||
7 | +database: db/asc1-students.db | ||
8 | + | ||
9 | +# this will generate a file for each test done. The file is like a replacement | ||
10 | +# for a test done in paper. | ||
11 | +save_answers: False | ||
12 | +# answers_dir: path/to/directory | ||
13 | + | ||
14 | +show_points: True | ||
15 | +show_hints: True | ||
16 | +practice: True | ||
17 | +# debug: False | ||
18 | + | ||
19 | +# When in practice mode and an answer is wrong (i.e. less that 0.5 correct), | ||
20 | +# an insult is chosen from the list (optional) | ||
21 | +offensive: | ||
22 | + - Ó meu grande asno, então não sabes esta? | ||
23 | + - Pois, pois... não estudes não... | ||
24 | + - E eu sou o Elvis Presley... | ||
25 | + - Pois, e bróculos também. | ||
26 | + | ||
27 | +#----------------------------------------------------------------------------- | ||
28 | +# List of files containing questions in yaml format. | ||
29 | +# Selected questions will be obtained from these files. | ||
30 | +files: | ||
31 | + - demo/questions.yaml | ||
32 | +#----------------------------------------------------------------------------- | ||
33 | +# This is the list of questions. If a "ref:" has a list of keys, then | ||
34 | +# one question is selected from the list. | ||
35 | +# The following example will generate a test with 3 questions. | ||
36 | +questions: | ||
37 | + # first | ||
38 | + - ref: | ||
39 | + # choose 1 from the following 3 questions | ||
40 | + - solar-system-mars | ||
41 | + - solar-system-jupiter | ||
42 | + - solar-system-venus | ||
43 | + points: 0.5 | ||
44 | + | ||
45 | + # second | ||
46 | + - ref: | ||
47 | + - question-v1 | ||
48 | + - question-v2 | ||
49 | + - question-v3 | ||
50 | + # points: 1.0 is the default, if not defined here. | ||
51 | + | ||
52 | + # third | ||
53 | + - ref: one-question | ||
54 | + | ||
55 | + # fourth | ||
56 | + - another-question |
questions.py
@@ -3,6 +3,7 @@ import yaml | @@ -3,6 +3,7 @@ import yaml | ||
3 | import random | 3 | import random |
4 | import re | 4 | import re |
5 | import subprocess | 5 | import subprocess |
6 | +import os | ||
6 | 7 | ||
7 | # Example usage: | 8 | # Example usage: |
8 | # | 9 | # |
@@ -29,8 +30,8 @@ class QuestionsPool(dict): | @@ -29,8 +30,8 @@ class QuestionsPool(dict): | ||
29 | with open(filename, 'r') as f: | 30 | with open(filename, 'r') as f: |
30 | questions = yaml.load(f) | 31 | questions = yaml.load(f) |
31 | except(FileNotFoundError): | 32 | except(FileNotFoundError): |
32 | - print(' * Questions file "%s" not found. Ignoring...' % filename) | ||
33 | - return | 33 | + print(' * Questions file "%s" not found. Aborting...' % filename) |
34 | + os.sys.exit(1) | ||
34 | 35 | ||
35 | # add defaults if missing from sources | 36 | # add defaults if missing from sources |
36 | for i, q in enumerate(questions): | 37 | for i, q in enumerate(questions): |
@@ -45,8 +46,8 @@ class QuestionsPool(dict): | @@ -45,8 +46,8 @@ class QuestionsPool(dict): | ||
45 | # ref (if missing, add 'filename.yaml:3') | 46 | # ref (if missing, add 'filename.yaml:3') |
46 | q['ref'] = str(q.get('ref', filename + ':' + str(i))) | 47 | q['ref'] = str(q.get('ref', filename + ':' + str(i))) |
47 | 48 | ||
48 | - # type (default type is just '') | ||
49 | - q['type'] = str(q.get('type', '')) | 49 | + # type (default type is 'information') |
50 | + q['type'] = str(q.get('type', 'information')) | ||
50 | 51 | ||
51 | # add question to the pool | 52 | # add question to the pool |
52 | self[q['ref']] = q | 53 | self[q['ref']] = q |
@@ -78,19 +79,19 @@ def create_question(q): | @@ -78,19 +79,19 @@ def create_question(q): | ||
78 | 'checkbox' : QuestionCheckbox, | 79 | 'checkbox' : QuestionCheckbox, |
79 | 'text' : QuestionText, | 80 | 'text' : QuestionText, |
80 | 'text_regex': QuestionTextRegex, | 81 | 'text_regex': QuestionTextRegex, |
81 | - 'text-regex': QuestionTextRegex, | ||
82 | - 'regex' : QuestionTextRegex, | 82 | + # 'text-regex': QuestionTextRegex, |
83 | + # 'regex' : QuestionTextRegex, | ||
83 | 'textarea' : QuestionTextArea, | 84 | 'textarea' : QuestionTextArea, |
84 | 'information': QuestionInformation, | 85 | 'information': QuestionInformation, |
85 | - 'info' : QuestionInformation, | ||
86 | - '' : QuestionInformation, # default | 86 | + # 'info' : QuestionInformation, |
87 | + # '' : QuestionInformation, # default | ||
87 | } | 88 | } |
88 | 89 | ||
89 | # create instance of given type | 90 | # create instance of given type |
90 | try: | 91 | try: |
91 | questiontype = types[q['type']] | 92 | questiontype = types[q['type']] |
92 | except KeyError: | 93 | except KeyError: |
93 | - print(' * unsupported question type in "%s:%s".' % (q['filename'], q['ref'])) | 94 | + print(' * unsupported question type "{0}" in "{1}:{2}".'.format(q['type'], q['filename'], q['ref'])) |
94 | questiontype = Question | 95 | questiontype = Question |
95 | 96 | ||
96 | # create question instance and return | 97 | # create question instance and return |
test.py
@@ -8,7 +8,7 @@ from datetime import datetime | @@ -8,7 +8,7 @@ from datetime import datetime | ||
8 | import questions | 8 | import questions |
9 | import database | 9 | import database |
10 | 10 | ||
11 | -# ============================================================================ | 11 | +# =========================================================================== |
12 | def read_configuration(filename, debug=False, show_points=False, show_hints=False, practice=False, save_answers=False): | 12 | def read_configuration(filename, debug=False, show_points=False, show_hints=False, practice=False, save_answers=False): |
13 | # FIXME validar se ficheiros e directorios existem??? | 13 | # FIXME validar se ficheiros e directorios existem??? |
14 | if not os.path.isfile(filename): | 14 | if not os.path.isfile(filename): |
@@ -35,7 +35,7 @@ def read_configuration(filename, debug=False, show_points=False, show_hints=Fals | @@ -35,7 +35,7 @@ def read_configuration(filename, debug=False, show_points=False, show_hints=Fals | ||
35 | 35 | ||
36 | if 'database' not in test: | 36 | if 'database' not in test: |
37 | print(' * Missing database in the test configuration.') | 37 | print(' * Missing database in the test configuration.') |
38 | - sys.exit(1) | 38 | + os.sys.exit(1) |
39 | 39 | ||
40 | if isinstance(test['files'], str): | 40 | if isinstance(test['files'], str): |
41 | test['files'] = [test['files']] | 41 | test['files'] = [test['files']] |
@@ -67,7 +67,7 @@ def read_configuration(filename, debug=False, show_points=False, show_hints=Fals | @@ -67,7 +67,7 @@ def read_configuration(filename, debug=False, show_points=False, show_hints=Fals | ||
67 | 67 | ||
68 | return test | 68 | return test |
69 | 69 | ||
70 | -# ============================================================================ | 70 | +# =========================================================================== |
71 | class Test(dict): | 71 | class Test(dict): |
72 | # ----------------------------------------------------------------------- | 72 | # ----------------------------------------------------------------------- |
73 | def __init__(self, d): | 73 | def __init__(self, d): |