test.py
4.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
'''
Test - instances of this class are individual tests
'''
# python standard library
from datetime import datetime
import json
import logging
from math import nan
from os import path
# Logger configuration
logger = logging.getLogger(__name__)
# ============================================================================
class Test(dict):
'''
Each instance Test() is a concrete test of a single student.
'''
# ------------------------------------------------------------------------
def __init__(self, d):
super().__init__(d)
self['grade'] = nan
self['comment'] = ''
# ------------------------------------------------------------------------
def start(self, student: dict) -> None:
'''
Write student id in the test and register start time
'''
self['student'] = student
self['start_time'] = datetime.now()
self['finish_time'] = None
self['state'] = 'ACTIVE'
# ------------------------------------------------------------------------
def reset_answers(self) -> None:
'''Removes all answers from the test (clean)'''
for question in self['questions']:
question['answer'] = None
# ------------------------------------------------------------------------
def update_answer(self, ref: str, ans) -> None:
'''updates one answer in the test'''
self['questions'][ref].set_answer(ans)
# ------------------------------------------------------------------------
def submit(self, answers_dict) -> None:
'''
Given a dictionary ans={'ref': 'some answer'} updates the answers of
multiple questions in the test.
Only affects the questions referred in the dictionary.
'''
self['finish_time'] = datetime.now()
for ref, ans in answers_dict.items():
self['questions'][ref].set_answer(ans)
self['state'] = 'SUBMITTED'
# ------------------------------------------------------------------------
async def correct_async(self) -> None:
'''Corrects all the answers of the test and computes the final grade'''
grade = 0.0
for question in self['questions']:
await question.correct_async()
grade += question['grade'] * question['points']
logger.debug('Correcting %30s: %3g%%',
question['ref'], question['grade']*100)
# truncate to avoid negative final grade and adjust scale
self['grade'] = max(0.0, grade) + self['scale'][0]
self['state'] = 'CORRECTED'
# ------------------------------------------------------------------------
def correct(self) -> None:
'''Corrects all the answers of the test and computes the final grade'''
grade = 0.0
for question in self['questions']:
question.correct()
grade += question['grade'] * question['points']
logger.debug('Correcting %30s: %3g%%',
question['ref'], question['grade']*100)
# truncate to avoid negative final grade and adjust scale
self['grade'] = max(0.0, grade) + self['scale'][0]
self['state'] = 'CORRECTED'
# ------------------------------------------------------------------------
def giveup(self) -> None:
'''Test is marqued as QUIT and is not corrected'''
self['finish_time'] = datetime.now()
self['state'] = 'QUIT'
self['grade'] = 0.0
# ------------------------------------------------------------------------
def save_json(self, pathfile) -> None:
'''save test in JSON format'''
with open(pathfile, 'w') as file:
json.dump(self, file, indent=2, default=str) # str for datetime
# ------------------------------------------------------------------------
def __str__(self) -> str:
return '\n'.join([f'{k}: {v}' for k,v in self.items()])
# return ('Test:\n'
# f' student: {self.get("student", "--")}\n'
# f' start_time: {self.get("start_time", "--")}\n'
# f' questions: {", ".join(q["ref"] for q in self["questions"])}\n')