exam.py 8.65 KB
Newer Older
1
2
3
4
5
"""
Class for exam objects

Author: L. Lamm (lamm@ifam.rwth-aachen.de)
"""
6
7
8
9
10
from src.utils import read_csv, write_csv, get_index_csv_data
from src.exceptions import *
from src.exportable import Exportable
from src.room import Room
from src.participant import Participant
11
import re
12
13
14
15
16
17
18
19
20


class Exam(Exportable):
    """Class for objects of type Exam"""
    def __init__(self, input_file=None, name=None, date=None, examiner=None, rooms=None,
                 points_per_task=None, participants=None):
        """Constructor of Exam object"""
        if input_file is not None:
            self.import_data_RWTHOnline(input_file)
LammLukas's avatar
LammLukas committed
21
        else:
22
23
24
25
            self.Name = name
            self.Date = date
            self.Examiner = examiner
            self.Participants = participants
26
            self.Rooms = rooms
27
28
29
30
31
        self.Exam_data_file = input_file
        self.Points_per_task = points_per_task

    def save_exam(self, file):
        """Save current exam to .json file"""
32
33
        data = self.data2dict()
        self.write_json(file, data)
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

    def load_exam(self, file):
        """Load exam from .json file"""
        data = self.read_json(file)

        self.Name = data['Name']
        self.Date = data['Date']
        self.Examiner = data['Examiner']
        self.Points_per_task = data['Points_per_task']
        self.Rooms = []
        self.Participants = []

        for item in data['Rooms']:
            self.Rooms.append(Room(item['Name'],
                                   item['NumSeats'],
                                   item['SupervisorInCharge'],
                                   item['ExtraSupervisors']))

        for item in data['Participants']:
            self.Participants.append(Participant(item['Firstname'],
                                                 item['Lastname'],
                                                 item['Matriculation'],
                                                 item['NumberOfTrials']))

    def import_data_RWTHOnline(self, file):
        """Imports participant data from .csv file generated in RWTHOnline"""
        self.Participants = []
        data = read_csv(file)
62
        self.Exam_data_file = file
63
64

        "Get indices of data fields in data"
65
66
67
68
69
70
71
72
        indices = get_index_csv_data(data, {'lastname': "FAMILY_NAME_OF_STUDENT",
                                            'firstname': "FIRST_NAME_OF_STUDENT",
                                            'matriculation': "REGISTRATION_NUMBER",
                                            'trials': 'GUEL_U_AKTUELLE_ANTRITTE_SPO',
                                            'date': 'DATE_OF_ASSESSMENT',
                                            'title': 'COURSE_TITLE',
                                            'examiner': 'Examiner',
                                            'rooms': 'TERMIN_ORT'})
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

        "Assign Name, date and examiner to exam"
        self.Name = data[1][indices['title']]
        self.Date = data[1][indices['date']]
        self.Examiner = data[1][indices['examiner']]

        "Create participants for exam"
        iterator = iter(data)
        next(iterator)
        for item in iterator:
            self.Participants.append(Participant(item[indices['firstname']],
                                                 item[indices['lastname']],
                                                 item[indices['matriculation']],
                                                 item[indices['trials']]))

88
        " Create rooms for exam"
89
        self.Rooms = []
90
91
92
        room_str = data[1][indices['rooms']]
        room_str = re.split(", <br>", room_str)
        for index in range(len(room_str)):
93
94
95
96
            room_id = re.findall("([0-9]{4}\|[0-9]{3})", room_str[index])[0]
            room = Room()
            room.load_template(room_id)
            self.Rooms.append(room)
97

98
99
100
101
102
103
104
    def export_data_RWTHOnline(self):
        """Export data to .csv file for upload to RWTHOnline
            todo: Check if rating has been performed properly
        """
        file_data = read_csv(self.Exam_data_file)

        "Get indices of data fields"
105
106
107
108
        indices = get_index_csv_data(file_data,
                                     {'matriculation': "REGISTRATION_NUMBER",
                                      'grade': "GRADE",
                                      'annotation': "FILE_REMARK"})
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

        "Assign grades and annotations"
        iterator = iter(file_data)
        next(iterator)
        for item in iterator:
            matriculation = item[indices['matriculation']]
            for p in self.Participants:
                if p.Matriculation == matriculation:
                    participant = p
                    break
                else:
                    participant = None
            if participant is not None:
                item[indices['grade']] = participant.Grade
                item[indices['annotation']] = participant.Annotation
            else:
                raise DataExportMismatch('Participant with matriculation ' +
                                         matriculation + ' does not exist in list'
                                                         'from RWTHOnline!')

129
        write_csv('./testdata/rwthExportTest.csv', file_data)
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

    def export_rating_list(self, file):
        """Export a simple .csv file for rating of the examination"""
        if not self.is_object_initialized():
            raise UninitializedObject('Not able to export rating list. '
                                      'Please first initialize the exam object properly.', self)

        data = []
        header = ['Matriculation', 'Lastname', 'Firstname', 'Trial']
        for i in range(len(self.Points_per_task)):
            header.append('Pts. task ' + str(i+1))
        header.append('Pts. total')
        header.append('Bonus')
        header.append('Grade')
        header.append('Annotation')
        data.append(header)

        row = []
        for part in self.Participants:
            row = [part.Matriculation,
                   part.Lastname,
                   part.Firstname,
                   part.NumberOfTrials]
            for i in range(len(self.Points_per_task) + 4):
LammLukas's avatar
LammLukas committed
154
                row.append('')
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
            data.append(row)

        write_csv(file, data)

    def import_rating_list(self, file):
        """Import .csv rating list after rating of exam has been performed"""
        if not self.is_object_initialized():
            raise UninitializedObject('Not able to import rating list. '
                                      'Please first initialize the exam object properly.', self)

        data = read_csv(file)
        iterator = iter(data)
        next(iterator)
        for item in iterator:
            matriculation = item[0]
            for part in self.Participants:
                if part.Matriculation == matriculation:
                    pts = []
                    for i in range(len(self.Points_per_task)):
                        pts.append(item[i + 4])
                    part.Points = pts
                    part.Bonus = item[5 + len(self.Points_per_task)]
                    part.Grade = item[6 + len(self.Points_per_task)]
                    part.Annotation = item[7 + len(self.Points_per_task)]

    def assign_participants(self, form='matriculation', rev=False, offset=5):
        """ Assign participants to rooms
        """
        if not self.is_object_initialized():
            raise UninitializedObject('Not able to assign participants. '
                                      'Please first initialize the exam object properly.', self)
186
        self.check_room_capacity(offset)
187
188
189
190
191
192
193
194
195

        if form == 'matriculation':
            self.Participants.sort(key=lambda x: x.Matriculation, reverse=rev)
        elif form == 'lastname':
            self.Participants.sort(key=lambda x: x.Lastname, reverse=rev)

        counter = 0
        for room in self.Rooms:
            room.AssignedParticipants = []
196
            seats_available = int(room.NumSeats) - (1 + offset)
197
198
199
200
201
202
203
204
205
            seat_count = 0
            while seat_count <= seats_available:
                if counter <= (len(self.Participants) - 1):
                    room.AssignedParticipants.append(self.Participants[counter])
                    seat_count = seat_count + 1
                    counter = counter + 1
                else:
                    break

206
    def check_room_capacity(self, offset):
207
208
209
210
211
        """Check if total room capacity is sufficient
        """
        num_part = len(self.Participants)
        num_seats = 0
        for room in self.Rooms:
212
            num_seats = num_seats + int(room.NumSeats) - offset
213
214
215
216

        if not (num_seats - num_part) >= 0:
            raise InsuffientCapacity('Number of participants exceeds number of '
                                     'available seats! Please check your room booking process.')