Commit ea4ff4d218842199457631b7c4a09bd7b19caba4

Authored by Miguel Barão
1 parent e5b363cc
Exists in master and in 1 other branch dev

- radio: added support for multiple right/wrong options, where only a random sel…

…ection is taken (with only 1 correct option)
BUGS.md
... ... @@ -11,41 +11,6 @@
11 11  
12 12 # TODO
13 13  
14   -```yaml
15   -# constroi pergunta com opções indicadas e pontuação dada em baixo caso resposta
16   -# marque essa opção ou o seu simétrico se não marcar
17   -type: checkbox
18   -options:
19   - - option1
20   - - option2
21   - - option3
22   -correct: [1, -1, 1]
23   -
24   -
25   -# Para cada opção escolhe aleatoriamente right ou wrong e controi pergunta como
26   -# em cima. A ideia é wrongN ser a mesma pergunta mas ao contrário de rightN.
27   -type: checkbox
28   -options:
29   - - [right1, wrong1]
30   - - [right2, wrong2]
31   - - [right3, wrong3]
32   -correct: [1, 1.5, 1]
33   -
34   -
35   -# randomly choose 1 right_option and choose more 2 from wrong_options.
36   -type: radio
37   -right_options:
38   - - right1
39   - - right2
40   - - right3
41   -wrong_options:
42   - - wrong1
43   - - wrong2
44   - - wrong3
45   - - wrong4
46   -choose: 3
47   -```
48   -
49 14 - servir imagens/ficheiros.
50 15 - session management. close after inactive time.
51 16 - each topic only loads a sample of K questions (max) in random order.
... ... @@ -65,6 +30,7 @@ choose: 3
65 30  
66 31 # FIXED
67 32  
  33 +- radio: suporte para multiplas opcoes correctas e erradas, escolhendo-se uma selecção aleatoria destas (so com 1 certa).
68 34 - checkbox: cada opção pode ser uma dupla (certo, errado) sendo escolhida uma aleatória.
69 35 - async/threadpool no bcrypt do initdb.
70 36 - numero de estrelas depende da proporcao entre certas e erradas.
... ...
README.md
... ... @@ -132,7 +132,7 @@ Edit `rc.conf`
132 132 pflog_flags=""
133 133 pflog_logfile="/var/log/pflog"
134 134  
135   -Reboot.
  135 +Reboot or `sudo service pf start`.
136 136  
137 137 ## Troubleshooting
138 138  
... ...
demo/math/questions.yaml
1   -- ref: numbers
2   - type: checkbox
3   - title: Números pares e primos
4   - text: Indique as afirmações verdadeiras.
  1 +# ---------------------------------------------------------------------------
  2 +- ref: distributive_property
  3 + type: radio
  4 + title: Propriedade distributiva
  5 + text: |
  6 + Qual das seguintes opções usa a propriedade distributiva para calcular $9\times 2 - 2\times 3$?
5 7 options:
6   - - ['3 é primo', '4 é primo']
7   - - ['2 é par', '3 é par']
8   - - ['1 é ímpar', '2 é ímpar']
9   - correct: [1,1,1]
  8 + # correct
  9 + - $2\times(9 - 3)$
  10 + - $(9-3)\times 2$
  11 + # wrong
  12 + - $18 - 6 = 12$
  13 + - $2\times 9 - 2\times 3$
  14 + - $2\times(9-2\times 3)$
  15 + correct: [1,1,0,0,0]
  16 + choose: 3
10 17  
11   --
12   - ref: prime_numbers
13   - type: radio
14   - title: Números primos
15   - text: Qual dos seguintes números é primo?
16   - options:
17   - - 13
18   - - 12
19   - - 14
20   - - 1, a **unidade**
  18 +# - ref: numbers
  19 +# type: checkbox
  20 +# title: Números pares e primos
  21 +# text: Indique as afirmações verdadeiras.
  22 +# options:
  23 +# - ['3 é primo', '4 é primo']
  24 +# - ['2 é par', '3 é par']
  25 +# - ['1 é ímpar', '2 é ímpar']
  26 +# correct: [1,1,1]
21 27  
22   -# # ---------------------------------------------------------------------------
23 28 # -
24   -# ref: distributive_property
  29 +# ref: prime_numbers
25 30 # type: radio
26   -# title: Propriedade distributiva
27   -# text: |
28   -# Qual das seguintes opções usa a propriedade distributiva para calcular $9\times 2 - 2\times 3$?
  31 +# title: Números primos
  32 +# text: Qual dos seguintes números é primo?
29 33 # options:
30   -# - $2\times(9 - 3)$
31   -# - $18 - 6 = 12$
32   -# - $2\times 9 - 2\times 3$
33   -# - $2\times(9-2\times 3)$
  34 +# - 13
  35 +# - 12
  36 +# - 14
  37 +# - 1, a **unidade**
  38 +
34 39  
35 40 # # ---------------------------------------------------------------------------
36 41 # -
... ...
questions.py
... ... @@ -61,16 +61,19 @@ class QuestionRadio(Question):
61 61 type (str)
62 62 text (str)
63 63 options (list of strings)
64   - shuffle (bool, default=True)
65 64 correct (list of floats)
66 65 discount (bool, default=True)
67 66 answer (None or an actual answer)
  67 + shuffle (bool, default=True)
  68 + choose (int) # only used if shuffle=True
68 69 '''
69 70  
70 71 #------------------------------------------------------------------------
71 72 def __init__(self, q):
72 73 super().__init__(q)
73 74  
  75 + n = len(self['options'])
  76 +
74 77 # set defaults if missing
75 78 self.set_defaults({
76 79 'text': '',
... ... @@ -79,23 +82,33 @@ class QuestionRadio(Question):
79 82 'discount': True,
80 83 })
81 84  
82   - n = len(self['options'])
83   -
84 85 # always convert to list, e.g. correct: 2 --> correct: [0,0,1,0,0]
85 86 # correctness levels from 0.0 to 1.0 (no discount here!)
86 87 if isinstance(self['correct'], int):
87 88 self['correct'] = [1.0 if x==self['correct'] else 0.0 for x in range(n)]
88 89  
89   - elif len(self['correct']) != n:
90   - logger.error(f'Number of options and correct mismatch in "{self["ref"]}", file "{self["filename"]}".')
91   -
92   - # generate random permutation, e.g. [2,1,4,0,3]
93   - # and apply to `options` and `correct`
94 90 if self['shuffle']:
95   - perm = list(range(n))
96   - random.shuffle(perm)
97   - self['options'] = [ str(self['options'][i]) for i in perm ]
98   - self['correct'] = [ float(self['correct'][i]) for i in perm ]
  91 + # separate right from wrong options
  92 + right = [i for i in range(n) if self['correct'][i] == 1]
  93 + wrong = [i for i in range(n) if self['correct'][i] < 1]
  94 +
  95 + self.set_defaults({'choose': 1+len(wrong)})
  96 +
  97 + # choose 1 correct option
  98 + r = random.choice(right)
  99 + options = [ self['options'][r] ]
  100 + correct = [ 1.0 ]
  101 +
  102 + # choose remaining wrong options
  103 + random.shuffle(wrong)
  104 + nwrong = self['choose']-1
  105 + options.extend(self['options'][i] for i in wrong[:nwrong])
  106 + correct.extend(self['correct'][i] for i in wrong[:nwrong])
  107 +
  108 + # final shuffle of the options
  109 + perm = random.sample(range(self['choose']), self['choose'])
  110 + self['options'] = [ str(options[i]) for i in perm ]
  111 + self['correct'] = [ float(correct[i]) for i in perm ]
99 112  
100 113 #------------------------------------------------------------------------
101 114 # can return negative values for wrong answers
... ...