Commit 285737793e41c26cb755229456943354128ee906
1 parent
8f643ad6
Exists in
master
and in
1 other branch
- changes checkbox correct list to the same semantics as radio with
interval values in the interval [0,1]
Showing
3 changed files
with
30 additions
and
24 deletions
Show diff stats
BUGS.md
... | ... | @@ -14,7 +14,6 @@ |
14 | 14 | |
15 | 15 | # TODO |
16 | 16 | |
17 | -- checkbox devia ter correct no intervalo [0,1] tal como radio. em caso de desconto a correccção faz 2*x-1. isto permite a mesma semantica nos dois tipos de perguntas. | |
18 | 17 | - registar last_seen e remover os antigos de cada vez que houver um login. |
19 | 18 | - indicar qtos topicos faltam (>=50%) para terminar o curso. |
20 | 19 | - ao fim de 3 tentativas com password errada, envia email com nova password. |
... | ... | @@ -35,6 +34,7 @@ |
35 | 34 | |
36 | 35 | # FIXED |
37 | 36 | |
37 | +- checkbox devia ter correct no intervalo [0,1] tal como radio. em caso de desconto a correccção faz 2*x-1. isto permite a mesma semantica nos dois tipos de perguntas. | |
38 | 38 | - marking all options right in a radio question breaks! |
39 | 39 | - implementar servidor http com redirect para https. |
40 | 40 | - tabelas nas perguntas radio/checkbox não ocupam todo o espaço como em question. | ... | ... |
aprendizations/questions.py
... | ... | @@ -113,8 +113,8 @@ class QuestionRadio(Question): |
113 | 113 | # check grade boundaries |
114 | 114 | if self['discount'] and not all(0.0 <= x <= 1.0 |
115 | 115 | for x in self['correct']): |
116 | - msg = (f'If discount=true, correct values must be in the ' | |
117 | - f'interval [0.0, 1.0] in "{self["ref"]}"') | |
116 | + msg = (f'Correct values must be in the interval [0.0, 1.0] in ' | |
117 | + f'"{self["ref"]}"') | |
118 | 118 | raise QuestionException(msg) |
119 | 119 | |
120 | 120 | # at least one correct option |
... | ... | @@ -217,12 +217,18 @@ class QuestionCheckbox(Question): |
217 | 217 | f'booleans in "{self["ref"]}"') |
218 | 218 | raise QuestionException(msg) |
219 | 219 | |
220 | - # check grade boundaries (FUTURE) | |
221 | - # if self['discount'] and not all(0.0 <= x <= 1.0 | |
222 | - # for x in self['correct']): | |
223 | - # msg = (f'If discount=true, correct values must be in the ' | |
224 | - # f'interval [0.0, 1.0] in "{self["ref"]}"') | |
225 | - # raise QuestionException(msg) | |
220 | + # check grade boundaries | |
221 | + if self['discount'] and not all(0.0 <= x <= 1.0 | |
222 | + for x in self['correct']): | |
223 | + logger.warning(' * * * BEHAVIOR CHANGE NOTICE * * *') | |
224 | + msg = (f'Correct values must be in the interval [0.0, 1.0] in ' | |
225 | + f'"{self["ref"]}"') | |
226 | + logger.warning(msg) | |
227 | + logger.warning('I will convert "correct" to the new behavior, but ' | |
228 | + 'you should change it in the questions') | |
229 | + logger.warning(' * * * END OF NOTICE * * *') | |
230 | + # normalize to [0,1] | |
231 | + self['correct'] = [(x+1)/2 for x in self['correct']] | |
226 | 232 | |
227 | 233 | # if an option is a list of (right, wrong), pick one |
228 | 234 | options = [] |
... | ... | @@ -232,9 +238,10 @@ class QuestionCheckbox(Question): |
232 | 238 | r = random.randint(0, 1) |
233 | 239 | o = o[r] |
234 | 240 | if r == 1: |
235 | - c = -c | |
241 | + # c = -c | |
242 | + c = 1.0 - c | |
236 | 243 | options.append(str(o)) |
237 | - correct.append(float(c)) | |
244 | + correct.append(c) | |
238 | 245 | |
239 | 246 | # generate random permutation, e.g. [2,1,4,0,3] |
240 | 247 | # and apply to `options` and `correct` |
... | ... | @@ -252,21 +259,20 @@ class QuestionCheckbox(Question): |
252 | 259 | super().correct() |
253 | 260 | |
254 | 261 | if self['answer'] is not None: |
255 | - sum_abs = sum(abs(p) for p in self['correct']) | |
256 | - if sum_abs < 1e-6: # case correct [0,...,0] avoid div-by-zero | |
257 | - self['grade'] = 1.0 | |
258 | - | |
262 | + x = 0.0 | |
263 | + if self['discount']: | |
264 | + sum_abs = sum(abs(2*p-1) for p in self['correct']) | |
265 | + for i, p in enumerate(self['correct']): | |
266 | + x += 2*p-1 if str(i) in self['answer'] else 1-2*p | |
259 | 267 | else: |
260 | - x = 0.0 | |
261 | - | |
262 | - if self['discount']: | |
263 | - for i, p in enumerate(self['correct']): | |
264 | - x += p if str(i) in self['answer'] else -p | |
265 | - else: | |
266 | - for i, p in enumerate(self['correct']): | |
267 | - x += p if str(i) in self['answer'] else 0.0 | |
268 | + sum_abs = sum(abs(p) for p in self['correct']) | |
269 | + for i, p in enumerate(self['correct']): | |
270 | + x += p if str(i) in self['answer'] else 0.0 | |
268 | 271 | |
272 | + try: | |
269 | 273 | self['grade'] = x / sum_abs |
274 | + except ZeroDivisionError: | |
275 | + self['grade'] = 1.0 # limit p->0 | |
270 | 276 | |
271 | 277 | |
272 | 278 | # ============================================================================ | ... | ... |
demo/math/addition/questions.yaml