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