test.py
4.44 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
109
110
111
112
113
'''
Test - instances of this class are individual tests
'''
# python standard library
from datetime import datetime
import json
import logging
from math import nan
logger = logging.getLogger(__name__)
# ============================================================================
class Test(dict):
'''
Each instance Test() is a concrete test of a single student.
A test can be in one of the states: ACTIVE, SUBMITTED, CORRECTED, QUIT
Methods:
t.start(student) - marks start of test (register time and state)
t.reset_answers() - remove all answers from the test
t.update_answer(ref, ans) - update answer of a given question
t.submit(answers_dict) - update answers, register time and state
t.correct_async()
t.correct() - corrects questions and compute grade, register state
t.giveup() - register the test as given up, answers are not corrected
t.save_json(filename) - save the current test to file in JSON format
'''
# ------------------------------------------------------------------------
def __init__(self, d: dict):
super().__init__(d)
self['grade'] = nan
self['comment'] = ''
# ------------------------------------------------------------------------
def start(self, uid: str) -> None:
'''
Register student id and start time in the test
'''
self['student'] = uid
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.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, filename: str) -> None:
'''save test in JSON format'''
with open(filename, 'w', encoding='utf-8') 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()])