-
Notifications
You must be signed in to change notification settings - Fork 3
/
welcome.py
381 lines (341 loc) · 18.1 KB
/
welcome.py
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
from tkinter import *
from tkinter import messagebox
from tkinter import simpledialog
from dateutil import parser
import datetime
import TakeTest, Feedback, CreateTest, login, Test
import csv
import os
import shelve
#Note for later self: check if a test name with the same name exists when creating a test. Maybe also add a timer so it gets deleted automatically
test_list = []
class Welcome(Frame):
# GUI Setup
def __init__ (self, master):
# Initialise Questionnaire Class
Frame.__init__(self, master)
self.grid()
self.overView()
self.createButtons()
def overView(self):
lblQuestion = Label(self, text = 'WELCOME '+login.name, font=('MS', 8,'bold'))
lblQuestion.grid(row=0, column= 4, rowspan=2)
# Create widgets to select a module from a list
string = ""
if login.is_teacher:
string = "Modules you can create an assesment in"
else:
string = "Modules you may have assesments in"
lblModules = Label(self, text=string, font=('MS', 8,'bold'))
lblModules.grid(row=2, column=0, columnspan=2, sticky=NE)
self.listProg = Listbox(self, height= 3)
scroll = Scrollbar(self, command= self.listProg.yview)
self.listProg.configure(yscrollcommand=scroll.set)
self.listProg.grid(row=3, column=0, columnspan=2, sticky=NE)
scroll.grid(row=3, column=4, sticky=W)
modules_list = self.retrieveModules()
for module in modules_list:
self.listProg.insert(END, module)
#self.listProg.selection_set(END)
self.listTest = Listbox(self, height= 3)
scroll = Scrollbar(self, command= self.listTest.yview)
self.listTest.configure(yscrollcommand=scroll.set)
self.listTest.grid(row=7, column=0, columnspan=2, sticky=NE)
scroll.grid(row=7, column=4, sticky=W)
def retrieveModules(self):
modules_list = []
with open('user_modules.csv') as csvfile:
rdr = csv.reader(csvfile)
for row in rdr:
#print(row)
#print("Does {} == {}".format(row[0], login.username))
if row[0] == login.username:
for i in range(1,len(row)):
if row[i]!= "":
modules_list.append(row[i])
return modules_list
def retrieveTests(self, module):
global test_list # setting it to be global so i can access it within Create_Test
test_list = []
with open('tests_overview.csv') as csvfile:
rdr = csv.reader(csvfile)
for row in rdr:
if row[0] == module:#Might need to add in the fact that a test gets taken
#> row[4] is TEST DURATION, adding it to test_list mean the duration vars are more accessible
#> row[5] is ATTEMPTS allowed - 1 for summative, 3 for formative
#> row[2] is TEST TYPE
#print("row 6:", row[6])
#print("row 6 type", type(row[6]))
print(row)
if row[2] == "F":
test_list.append((row[1], row[4], row[5], row[2]))
else:
#> STORE THE DUEDATE AT INDEX 4 OF THE TUPLE
test_list.append((row[1], row[4], row[5], row[2], row[6]))
#else:
# print("row 6:", row[6])
# test_list.append((row[1], row[4], row[5], row[2], row[6]))
if len(test_list) == 0:
# rather than return empty list, return -1
return -1
else:
return test_list
def getIndividualResults(self):
import viewResult
viewResult.View_Results(Toplevel())
def createButtons(self):
butCheck = Button(self, text='Check for Tests',font=('MS', 8,'bold'), command=self.checkTest)
butCheck.grid(row=4, column=0, columnspan=2)
if login.is_teacher:
butCreate = Button(self, text='Create TEST!',font=('MS', 8,'bold'), command=self.createTest)
butCreate.grid(row=8, column=0, columnspan=2)
butEdit = Button(self, text='Edit Test', font=('MS', 8,'bold'), command=self.editTest)
butEdit.grid(row = 8, column = 3, columnspan=2)
butView = Button(self, text='View Class results', font=('MS', 8,'bold'), command=self.viewClassResults)
butView.grid(row = 8, column = 6, columnspan=2)
butIndv = Button(self, text='View Individual Results', font=('MS', 8,'bold'), command=self.getIndividualResults)
butIndv.grid(row = 8, column = 9, columnspan=2)
else:
butTake = Button(self, text='Take TEST!',font=('MS', 8,'bold'), command = self.takeTest)#rename me to thing depending on whether or not you are a teacher
butTake.grid(row=8, column=0, columnspan=2)
butResult = Button(self, text='View Result',font=('MS', 8,'bold'), command = self.getResult)
butResult.grid(row=8, column=3, columnspan=2)
def viewClassResults(self):
if self.listTest.curselection() != ():
index = self.listTest.curselection()[0]
testname = str(self.listTest.get(index))
db = shelve.open("test_results/"+testname+"_results")
students = []
attempts = []
all_students = [[]]
position = 0
total = 0
for item in db:
students.append(item)
for i in range(len(students)):
results = db.get(students[i]).toString()[2]
#attempts.append(db.get(students[i]).toString()[1])
score = 0
correctAnswers = []
result = db.get(students[i]).toString()
#> Get the answers to the questions and store in var correctAnswers
with open(testname+".csv") as testfile:
rdr = csv.reader(testfile)
for row in rdr:
correctAnswers.append((int(row[5]),int(row[6]),int(row[7]),int(row[8])))
#> iterate through the answers and enumerate them,
for b, answer in enumerate(correctAnswers):
if result[2][b] == answer:
score += 1
all_students[position].append(1)
else:
all_students[position].append(0)
all_students.append([])
position += 1
students[i] = (students[i], score)
question_x = [0]*(len(all_students[0]))
all_students.pop(len(all_students)-1)
try:
for i in range(len(all_students[0])):
for student in all_students:
question_x[i] += student[i]
question_list = []
for i in range(len(question_x)):
question_x[i] = round(question_x[i]/len(all_students)*100, 1)
question_list.append(i+1)
print(all_students)
print(question_x)
import testgrades
import ClassResults
classResult = ClassResults.class_results(Tk(), students, testname)
testgrades.display_graph(question_x, question_list)
except IndexError:
messagebox.showwarning("Note", "No students have taken this test yet!")
def checkTest(self):
""" This function appends the tests available for a give
module to the Listbox listTest
"""
if self.listProg.curselection() != ():#Check if the user has selected something
index = self.listProg.curselection()[0]
strModule = str(self.listProg.get(index))
#retrieve tests for that module
test_list = self.retrieveTests(strModule)
# i.e. if retrieveTests doesn't return -1
if test_list != -1:
self.listTest.delete(0,END)
for test in test_list:
self.listTest.insert(END, test[0])
self.listTest.selection_set(END)
else:
#clear list box and show message
self.listTest.delete(0,END)
messagebox.showwarning("Note!","There are no tests for that module. ")
else:
messagebox.showwarning("ERROR","Please select a module!")
def editTest(self):
if self.listTest.curselection() != ():
t1 = Toplevel()
t1.title("Test")
index = self.listTest.curselection()[0]
testfile = str(self.listTest.get(index))
# Try - Except can be used if neccessary
#try:
CreateTest.create_file = 1
CreateTest.Create_Test(t1, testfile+'.csv')
#except FileNotFoundError:
# messagebox.showwarning("ERROR", "Test only exists in tests_overview.csv")
# t1.destroy()
else:
messagebox.showwarning("ERROR", "Please a pick an existing test to edit.")
def editTestFast(self, testfile):
if self.listTest.curselection() != ():
t1 = Toplevel()
t1.title("Test")
CreateTest.Create_Test(t1, testfile+'.csv')
def createTest(self):
""" This method creates an empty test csv file with a filename specified by the user in a
dialog box that appears. It then appends the test's metadata (teacher, testname, module)
to the tests_overview.csv file """
if self.listProg.curselection() != ():
index = self.listProg.curselection()[0]
strModule = str(self.listProg.get(index))
#print(strModule)
name = login.name
testName = simpledialog.askstring("Input", "Enter test name")
#if testname == None or contains forbidden characters
if not testName or len([i for i in testName if i in ['/', '\\', '?', '%', '*', ':', '|', '"', '<', '>', '.']]) != 0:
messagebox.showinfo("Error", "You didn't enter a name or you \nused a forbidden character!")
testType = simpledialog.askstring("Input", "Formative Test: F, Summative Test: S")
duedate = False
if testType and testType.upper() == 'S':
invalid_input=True
while(invalid_input==True):
dueDate = simpledialog.askstring("Input", "Please enter the date the assesment is due in a following format 'Aug 28 1999 12:00AM'")
try:
duedate = parser.parse(dueDate)
now = datetime.datetime.now()
print((now-duedate).total_seconds())
if (now-duedate).total_seconds() > 0:
messagebox.showwarning("ERROR", "Please enter a date that's in the future")
else:
invalid_input=False
except:
messagebox.showwarning("ERROR", "Enter the due date in a valid format")
testDuration = simpledialog.askinteger("Input", "Enter the test duration time in minutes.\n (Min: 15, Max: 120)")
if testDuration < 15:
#> change testDuration to 15
#testDuration = 15
pass
elif testDuration > 120:
testDuration = 120
elif testType and testType.upper() == 'F':
#> Formative tests don't need time limits so setting testDuration
#> to be several years is a solution for now
testDuration = 'No' # in milliseconds
else:
messagebox.showwarning("ERROR", "Enter F or S!")
return
# check if the file already exists in the folder or if testName for the selected module is already in tests_overview
if os.path.isfile('.\\{}.csv'.format(testName)) == False and testName not in test_list:
if duedate == False:
Test.test_file(testName, testType.upper(), strModule, name, testDuration)
else:
Test.test_file(testName, testType.upper(), strModule, name, testDuration, duedate)
print('...Test Created...\n'+120*'-'+'\nTest Name: {0:30}|Type: {1:10}|Teacher: {2:25}|Duration: {3:9}\n'.format(testName, 'Formative' if testType.upper() == 'F' else 'Summative',
name, 'No time limit' if testType.upper() == 'F' else str(testDuration) + ' minutes' + str(duedate)))
self.checkTest()
self.editTestFast(testName)
#print(test_list)
elif testName:
messagebox.showwarning("ERROR", "Test with that name already exists!")
else:
messagebox.showwarning("ERROR", "Something went wrong?!")
else:
messagebox.showwarning("ERROR", "Please select a module!")
def takeTest(self):
if self.listTest.curselection() != ():
index = self.listTest.curselection()[0]
testName= str(self.listTest.get(index))
testType = [i[3] for i in test_list if i[0] == testName]
testType = str(testType[0]) #> convert it to a string
timeLimit = [i[1] for i in test_list if i[0] == testName]
attemptsAllowed = [i[2] for i in test_list if i[0] == testName]
if messagebox.askokcancel("Are you sure", "This is a test "+testType+ " test and you only get "+ attemptsAllowed[0]+" attempts in total"):
print("Taking Test:", testName)
print("attemptsAllowed =",attemptsAllowed[0])
print("testType =",testType)
db = shelve.open("test_results/"+testName+"_results")
#check if students ID exists in database, if it returns True then do not allow student to take test if test (if test is summative)
try:
if testType == 'S':
"""WE only have test name to work on :( so i have to check all of the test_overview file for the datetime
#> Duedate is saved in test_list as the 4th index so don't need this
with open('tests_overview.csv') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
for row in csv_reader:
if row[1] == testName:
dueDate = row[6]
"""
#duedate = parser.parse(dueDate)
duedate = self.turnDueDateToObject(testName) #> Dis be quicker!
now = datetime.datetime.now()
if now>duedate:#current due date has passed :( therefore don't allow student to take test
messagebox.showinfo("TOO LATE!!!","Sorry, the due date for this assesment has passed already")
else:
db[login.username]
messagebox.showinfo("Can't take summative test!", "You have already sat this test")
db.close()
elif testType == 'F':
db[login.username]
attempts = db.get(str(login.username)).toString()[1]
print("You have made {} attempts so far".format(attempts))
if attempts == 3:
messagebox.showinfo("Can't take formative test!", "You have already used your final attempt")
db.close()
else:
db.close()
t1 = Toplevel()
t1.geometry("700x300")
app = TakeTest.Take_Test(t1, testName, timeLimit, login.username, testType, attempts)
except KeyError: # if database for test doesn't contain student's userID, it will return KeyError
db.close()
t1 = Toplevel()
t1.geometry("700x300")
app = TakeTest.Take_Test(t1, testName, timeLimit, login.username, testType)
else:
messagebox.showwarning("ERROR", "Please select a test to take!")
def getResult(self):
if self.listTest.curselection() != ():
index = self.listTest.curselection()[0]
testname = str(self.listTest.get(index))
testType = [i[3] for i in test_list if i[0] == testname]
testType = str(testType[0])
t1 = Toplevel()
if testType == 'S':
#> this is horrible...
duedate = self.turnDueDateToObject(testname)
print(duedate)
fdbck = Feedback.Show_Results(t1, login.username, testname, testType, duedate)
elif testType == 'F':
fdbck = Feedback.Show_Results(t1, login.username, testname, testType)
else:
messagebox.showwarning("Error", "Is your testtype a string other than 'F' or 'S'?")
def turnDueDateToObject(self, testname):
#""" This function converts the test's duedate to a datetime.datetime object """
print("Getting duedate and doing stuff...........")
#> get the duedate
duedate = [i[4] for i in test_list if i[0] == testname][0].split()
#> split the string in to date and time
theDate = duedate[0].split("-") #> [year, month, day]
theTime = duedate[1].split(":") #> [hour, minute, second]
#> create datetime object, convert values to ints as they are strings
dueDate = datetime.datetime(int(theDate[0]), int(theDate[1]), int(theDate[2]),
int(theTime[0]), int(theTime[1]), int(theTime[2]))
return dueDate
#mainloop
#if login.username != "":
if login.loggedIn != False:
root = Tk()
root.title("HOME "+ str(login.username))
app = Welcome(root)
root.mainloop()