@ChrisCheng/

CEP Final Project 2019 (CCA Allocation)

Python

This is some code I, @ChrisCheng, has written with @NigelHeng for a weighted assessment in my school for the Computer Elective Programme.

fork
loading
Files
  • main.py
  • Backups
  • Default Files
  • config.py
  • configurations.csv
  • edgecases.md
  • gui.py
  • README.md
  • TODO.md
main.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
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
#Import external python module(s)
import os, sys, time, pathlib, datetime, openpyxl

#Import own python module(s)
import gui, config

#Functions to convert the excel workbook to simple lists
def workbook_to_sheets(filename_path):
	'''
	This function uses the filename path to find the excel file, and creates a dictionary using the sheetnames as keys and excel worksheet objects from openpyxl as values.

	Parameters:
		filename (pathlib.PosixPath): The path of the excel file (.xlsx) to be converted into a dictionary of excel worksheet objects.
	
	Returns:
		NIL
	'''

	global workbook, dict_of_sheets
	workbook = openpyxl.load_workbook(filename_path)
	dict_of_sheets = {}
	for sheetname in workbook.sheetnames:
		dict_of_sheets[sheetname] = workbook[sheetname]

def sheet_to_2D_list(sheetname):
	'''
	This function uses the sheetname to find an excel file, and creates a dictionary using the sheetnames as keys and excel worksheet objects from openpyxl as values.

	Parameters:
		sheetname (str): The name of the excel sheet to be converted into a 2D list.

	Returns:
		_2D_list (list): The excel sheet that has been converted into a 2D list.
	'''
	
	sheet_ref = dict_of_sheets[sheetname]
	no_of_rows = sheet_ref.max_row
	no_of_columns = sheet_ref.max_column
	_2D_list = []
	for i in range(1, no_of_rows + 1):
		_2D_list.append([])
	for row in range(1, no_of_rows + 1):
		for column in range(1, no_of_columns + 1):
			_2D_list[row - 1].append(sheet_ref.cell(row = row, column = column).value)
	return _2D_list

#Functions for Merit CCA allocation
def merit_allocation_both():
	'''
	This function allocated both merit CCA choices to students.

	Parameters:
		NIL
	
	Returns:
		student_data (list): A list of lists, each containing the NRIC of the student and both of their merit CCA choices. If the choices are empty, it is represented by None.
	'''
	
	student_data = sheet_to_2D_list("choices")
	for i in range(len(student_data)):   
		temp = []
		temp.append(student_data[i][1])
		if len(student_data[i]) >= 15:
			temp.append(student_data[i][14])
		else:
			temp.append("")
		if len(student_data[i]) == 16:
			temp.append(student_data[i][15])
		student_data[i] = temp
	return student_data

def merit_allocation(exceptions):
	'''
	This function uses the list of exceptions to allocate merit CCAs to students.

	If a merit CCA is in the list of exceptions, it should not clash with the timing of any other CCA. Thus, if there are 2 CCAs where the timimg does not clash (at least one in the list of exceptions), both merit CCAs will be allocated. If there are 2 CCAs where the timings clash (both CCAs are not in the list of exceptions), the first choice will be allocated.

	Parameters:
		exceptions (list): The list of merit CCA(s) that does not clash with all the other merit CCAs.
	
	Returns:
		allocations (list): A list of lists, each containing the NRIC of the student and the allocated merit CCA choices. If the choices are empty, it is represented by None.
	'''
	
	student_data = sheet_to_2D_list("choices")
	allocations = []
	for i in range(1, len(student_data)):
		if len(exceptions) != 0:
			for j in exceptions:
				if student_data[i][14] == j or student_data[i][15] == j:
					allocations.append([student_data[i][1], student_data[i][14], student_data[i][15]])
				else:
					allocations.append([student_data[i][1], student_data[i][14], None])
		else:
			allocations.append([student_data[i][1], student_data[i][14], None])
	return allocations

def merit_allocation_with_prompt(spam_count):
	'''
	This function gets the list of exceptions that the user inputs by prompting the user, and uses it in merit_allocation(exceptions).

	Parameters:
		spam_count (int): The number of times the user has spammed the [Enter] button.
	
	Returns:
		spam_count (int): The revised nimber of times the user has spammed the [Enter] button.
		allocations (list): A list of lists, each containing the NRIC of the student and the allocated Merit CCA choices. If the choices are empty, it is represented by None.
	'''

	student_data = sheet_to_2D_list("choices")

	#Get list of Merit CCAs
	merit_ccas = []
	for i in range(1, len(student_data)):
		if student_data[i][14] not in merit_ccas:
			merit_ccas.append(student_data[i][14])
		if student_data[i][15] not in merit_ccas:
			merit_ccas.append(student_data[i][15])
	if None in merit_ccas:
		del merit_ccas[merit_ccas.index(None)]

	#Prompt user for list of exceptions
	exceptions = []
	exit_loop = False
	while exit_loop == False:
		gui.clear()
		print(f"Welcome to the Automatic CCA Allocator.\n\nTo use this option, you will have to enter in which Merit CCAs' timing do not clash with any other Merit CCA's timing, and as a result will allocate both Merit CCA choices if their timings do not clash.\n\nTo add/remove a CCA to/from the list, type its abbreviation as the CCA. Once your are done adding CCAs to the list, type in 'finish' as the CCA.\n\nThe list of CCAs that can be picked is as follows: {merit_ccas}\n\nThe list of CCAs that have been picked is as follows: {exceptions}\n")

		start = time.time()
		merit_cca = input("CCA:")

		if (merit_cca in merit_ccas) and (merit_cca not in exceptions):
			exceptions.append(merit_cca)
		elif (merit_cca in merit_ccas) and (merit_cca in exceptions):
			del exceptions[exceptions.index(merit_cca)]
		elif merit_cca == "finish":
			exit_loop = True
		else:
			spam_count = gui.invalid_input(start, spam_count, 4)
	allocations = merit_allocation(exceptions)
	return spam_count, allocations

#Functions for Core CCA allocation
def cca_quotas_and_choices(spam_count):
	'''
	This function uses the 'ccaquotas' sheet to create a dictionary where the keys are the Core CCAs and the values are the respective CCA's quota, and the 'choices' sheet to ...

	additional explanation

	Parameters:
		spam_count (int): The number of times the user has spammed the [Enter] button.

	Returns:
		spam_count (int): The revised number of times the user has spammed the [Enter] button.
		cca_quotas_dict (dict): A dictionary of the CCA quotas of each CCA, with the keys as the CCA abbreviations as stated in the 'ccaquota' sheet, and the values as the quota of that particular CCA.
		name_ccaquota_choices_sheet (list): A list of lists containing the respective CCA names, abbreviations in the 'ccaquota' and 'choices' tab, and the sheetnames of all the CCAs.
	'''

	#Get list_of_ccas and unallocated_sheetnames
	list_of_ccas = sheet_to_2D_list("ccaquota")
	unallocated_sheetnames = list(dict_of_sheets.keys())

	for i in range(len(list_of_ccas) - 1, -1, -1):
		if list_of_ccas[i][0] == None:
			#Remove un-needed rows from list_of_ccas
			del list_of_ccas[i]
		elif list_of_ccas[i][1] in unallocated_sheetnames:
			#Remove allocated CCAs (CCAs who's abbreviation in the 'ccaquota' tab exists as a sheetname) from unallocated_sheetnames
			del unallocated_sheetnames[unallocated_sheetnames.index(list_of_ccas[i][1])]

	#Remove irrelevant sheetnames from unallocated_sheetnames (sheetnames before 'choices' sheet that are not for CCAs)
	exit_loop = False
	while exit_loop == False:
		if unallocated_sheetnames[0] != "choices":
			del unallocated_sheetnames[0]
		else:
			del unallocated_sheetnames[0]
			exit_loop = True
	
	#Compile the CCA quotas into a dictionary (keys are abbreviations in 'ccaquota' sheet)
	cca_quotas_dict = {}
	for i in range(len(list_of_ccas)):
		cca_quotas_dict[list_of_ccas[i][1]] = list_of_ccas[i][4]

	#Create a reference list of the ways various CCAs are referenced throughout the workbook
	name_ccaquota_choices_sheet = [[], [], [], []]
	for i in range(len(list_of_ccas)):
		name_ccaquota_choices_sheet[0].append(list_of_ccas[i][2])
		name_ccaquota_choices_sheet[1].append(list_of_ccas[i][1])
		name_ccaquota_choices_sheet[2].append(list_of_ccas[i][1])
		name_ccaquota_choices_sheet[3].append(list_of_ccas[i][1])

	#Prompt the user if there are CCAs in the 'ccaquota' sheet that do not have a sheet with the same name
	for i in range(len(list_of_ccas)):
		if list_of_ccas[i][1] not in dict_of_sheets.keys():
			exit_loop = False
			while exit_loop == False:
				if unallocated_sheetnames != []:
					gui.clear()
					print(f"{list_of_ccas[i][2]} is represented by the abbreviation {list_of_ccas[i][1]!r} on the 'ccaquota' sheet of the excel workbook, but no excel sheet has {list_of_ccas[i][1]!r} as its name.\n\nPlease enter the sheetname that represents {list_of_ccas[i][2]}. If no such sheet exists, please type in 'ignore' as the sheetname.\n\nValid sheetnames are as follows: {unallocated_sheetnames}\n")
					start = time.time()
					sheetname = input("Sheetname: ")
				else:
					#If there are no more unallocated sheetnames, the rest of the CCAs that do not have a sheet are ignored
					sheetname = "ignore"

				if sheetname in unallocated_sheetnames:
					#Correct the sheetname if it exists
					name_ccaquota_choices_sheet[3][name_ccaquota_choices_sheet[0].index(list_of_ccas[i][2])] = sheetname
					#Remove the sheetname from unallocated_sheetnames
					del unallocated_sheetnames[unallocated_sheetnames.index(sheetname)]
					exit_loop = True
				elif sheetname == "ignore":
					#Make the sheetname None if sheet doesen't exist
					name_ccaquota_choices_sheet[3][name_ccaquota_choices_sheet[0].index(list_of_ccas[i][2])] = None
					exit_loop = True
				else:
					gui.clear()
					spam_count = gui.invalid_input(start, spam_count, 2)
	
	#Get choices, cca_choices_sheets, unallocated_ccaquota_abbreviations
	dict_of_cca_choices = {}
	choices = sheet_to_2D_list("choices")
	cca_choices_sheets = list(dict_of_sheets.keys())
	unallocated_ccaquota_abbreviations = name_ccaquota_choices_sheet[1].copy()

	#Extract all CCA abbreviations in 'choices' sheet
	ccas_in_choices = []
	for i in range(1, len(choices)):
		for j in range(5, 14):
			if choices[i][j] not in ccas_in_choices:
				ccas_in_choices.append(choices[i][j])
	if None in ccas_in_choices:
		del ccas_in_choices[ccas_in_choices.index(None)]
	
	for i in range(len(ccas_in_choices)):
		if ccas_in_choices[i] in unallocated_ccaquota_abbreviations:
			#Remove allocated CCAs (CCAs who's abbreviation in the 'choices' sheet exists as an abbreviation in the 'ccaquota' sheet) from unallocated_ccaquota_abbreviations
			del unallocated_ccaquota_abbreviations[unallocated_ccaquota_abbreviations.index(ccas_in_choices[i])]

	#Remove irrelevant sheetnames from cca_choices_sheets (sheetnames before 'choices' sheet that are not for CCAs)
	exit_loop = False
	while exit_loop == False:
		if cca_choices_sheets[0] != "choices":
			del cca_choices_sheets[0]
		else:
			del cca_choices_sheets[0]
			exit_loop = True
	
	#Create dict_of_cca_choices, with the CCA sheetnames as the keys and the values as a list of all the shortlisted people for each CCA
	for i in range(len(cca_choices_sheets)):
		temp = sheet_to_2D_list(cca_choices_sheets[i])
		del temp[0]
		for j in range(len(temp)):
			temp[j] = temp[j][1]
		dict_of_cca_choices[cca_choices_sheets[i]] = temp

	#Prompt the user if there are CCAs in the 'choices' sheet that do not have a sheet with the same name
	for i in range(len(ccas_in_choices)):
		if ccas_in_choices[i] not in name_ccaquota_choices_sheet[1]:
			exit_loop = False
			while exit_loop == False:
				if unallocated_ccaquota_abbreviations != []:
					gui.clear()
					print(f"There is a CCA with the abbreviation {ccas_in_choices[i]!r} on the 'choices' sheet of the excel workbook, but no CCA has {ccas_in_choices[i]!r} as its abbreviation on the 'ccaquota' sheet.\n\nPlease enter the abbreviation on the 'ccaquota' sheet that represents this CCA. If no such abbreviation exists, please type 'exit' as the abbreviation.\n\nValid abbreviations are as follows: {unallocated_ccaquota_abbreviations}\n")
					start = time.time()
					abbreviation = input("Abbreviation: ")
				else:
					#If there are no more unallocated abbreviations, exit programme as there is an error with the excel workbook
					abbreviation = "exit"

				if abbreviation in unallocated_ccaquota_abbreviations:
					#Correct the abbreviation if it exists
					name_ccaquota_choices_sheet[2][name_ccaquota_choices_sheet[1].index(abbreviation)] = ccas_in_choices[i]
					#Edit cca_quotas_dict such that the keys are the CCA abbreviations from the 'choices' sheet (insteaad of the CCA abbreviations from the 'ccaquota' sheet)
					cca_quotas_dict[ccas_in_choices[i]] = cca_quotas_dict[abbreviation]
					del cca_quotas_dict[abbreviation]
					#Edit dict_of_cca_choices such that the keys are the CCA abbreviations from the 'choices' sheet (insteaad of the sheetnames)
					dict_of_cca_choices[ccas_in_choices[i]] = dict_of_cca_choices[name_ccaquota_choices_sheet[3][name_ccaquota_choices_sheet[1].index(abbreviation)]]
					del dict_of_cca_choices[name_ccaquota_choices_sheet[3][name_ccaquota_choices_sheet[1].index(abbreviation)]]
					#Remove the abbreviation from unallocated_ccaquota_abbreviations
					del unallocated_ccaquota_abbreviations[unallocated_ccaquota_abbreviations.index(abbreviation)]
					exit_loop = True
				elif abbreviation == "exit":
					#Log here about exiting programme cos error in excel spreadsheet
					sys.exit("\nSince no such abbreviation exists, it means that your excel workbook does not contain such an abbreviation, and the 'ccaquota' sheet lacks the abbreviation for this CCA.\n\nBy missing such an abrreviation, this programme will not be able to process the file and allocated each student a CCA. Please edit and correct the excel workbook, and run this program again.\n\nThank you for using the Automatic CCA Allocator!\n\n© 2019 Christopher Cheng & Nigel Heng")
				else:
					gui.clear()
					spam_count = gui.invalid_input(start, spam_count, 3)

	return spam_count, cca_quotas_dict, dict_of_cca_choices, name_ccaquota_choices_sheet

def quota_discrepancy(spam_count):
	'''
	Concise Explanation

	Detailed Explanation

	Parameters:
		NIL

	Returns:
		NIL
	'''
	classlist = sheet_to_2D_list("classlist")
	no_of_students = len(classlist) - 1
	total_quota = 0
	for i in list(cca_quotas_dict.keys()):
		total_quota += cca_quotas_dict[i]
	if no_of_students > total_quota:
		exit_loop = False
		while exit_loop == False:
			gui.clear()
			print(f"The total sum of the CCA quotas mentioned in the 'ccaquota' sheet in the excel workbook is lower than the amount of students who have to be allocated CCAs. This means that there are not enough slots in all the CCAs for every student to be assigned.\n\nWhich CCA's quota would you like to increase? The list of CCAs you can pick from and their respective quotas are as follows:\n")
			for key, value in cca_quotas_dict:
				print(f"{key}: {value}")

			start = time.time()
			cca_name = input("\nCCA: ")
			
			if cca_name in cca_quotas_dict.keys():
				cca_quotas_dict[cca_name] += 1
				for i in list(cca_quotas_dict.keys()):
					total_quota += cca_quotas_dict[i]
				if no_of_students <= total_quota:
					exit_loop = True
			else:
				spam_count = gui.invalid_input(start, spam_count, 4)
	return cca_quotas_dict



def DSA_allocation():
	'''
	Concise Explanation

	Detailed Explanation

	Parameters:
		NIL

	Returns:
		NIL
	'''
	
	DSA_students_list = sheet_to_2D_list("DSA")
	del DSA_students_list[0]
	for i in range(len(DSA_students_list) - 1, -1, -1):
		if DSA_students_list[i][1] != 'Sport':
			del DSA_students_list[i]
	for i in range(len(DSA_students_list)):
		del DSA_students_list[i][3]
		del DSA_students_list[i][1]
		DSA_students_list[i].append("DSA")
	#at this point, DSA_students_list can be returned. Code below modifies the cca_quotas_dict global variable by removing the allocated students from the quota.
	for i in range(len(DSA_students_list)):
		try:
			cca_quotas_dict[DSA_students_list[i][1]] = cca_quotas_dict[DSA_students_list[i][1]] - 1
		except KeyError:
			pass #Do nothing if key doesnt exist
	return DSA_students_list, cca_quotas_dict

def max_student_and_overall_happiness(cca_quotas_dict, dict_of_cca_choices):
	'''
	Concise Explanation

	Detailed Explanation

	Parameters:
		NIL

	Returns:
		NIL
	'''
	classlist_sheet = sheet_to_2D_list("classlist")
	list_of_all_students = []
	for i in range(1,len(classlist_sheet)):
		list_of_all_students.append(classlist_sheet[i][2])
	student_choices_list = []
	temp_sheet = sheet_to_2D_list("choices")
	del temp_sheet[0]
	for i in range(len(temp_sheet)):
		temp_list = []
		temp_list.append(temp_sheet[i][1])
		for j in range(5, 14):
			temp_list.append(temp_sheet[i][j])
		student_choices_list.append(temp_list)
	for i in range(len(student_choices_list)):
		if student_choices_list[i][0] in list_of_all_students:
			del list_of_all_students[list_of_all_students.index(student_choices_list[i][0])] #this deletes all the students who made choices from all the students in classlist
	DSA = sheet_to_2D_list('DSA')
	for i in range(len(DSA)-1,0,-1):
		if DSA[i][1] == "Acad":
			del DSA[i]
	del DSA[0]
	for i in range(len(DSA)-1,-1,-1):
		if DSA[i][0] in list_of_all_students:
			del list_of_all_students[list_of_all_students.index(DSA[i][0])]
		for j in range(len(student_choices_list)-1,-1,-1):
			if DSA[i][0] == student_choices_list[j][0]:
				del student_choices_list[j]
	contested = {}
	allocated = []
	for i in cca_quotas_dict.keys():
		contested[i] = []
	#to explain briefly, contested is a dictionary with ccanames as keys. This dict holds a list of everyone contesting. the lower the number the higher the priority.
	for i in range(9): #1 repetition for each choice, hence 9
		for j in range(len(student_choices_list)-1,-1,-1): #loops through the cca choices and puts the students into the corresponding contested section, with priority value being x where x is 2*choice no. if not shortlisted and 2*choice no. -1 if shortlisted
			try:
				if student_choices_list[j][0] in dict_of_cca_choices[student_choices_list[j][1+i]]:
					contested[student_choices_list[j][1+i]].append([student_choices_list[j][0],2*(i+1)-1])
				else:
					contested[student_choices_list[j][1+i]].append([student_choices_list[j][0],2*(i+1)])
			except KeyError:
				contested[student_choices_list[j][1+i]].append([student_choices_list[j][0],2*(i+1)])
	for i in contested.keys(): #sorts list based on priority
		contested[i].sort(key = lambda x : x[1])
	for priority in range(18):#runs 18 times, for each priority value.
		for i in contested.keys():#loops through contested
			if len(contested[i]) != 0 or cca_quotas_dict[i] != 0:#if the cca has already been fully allocated do not run
				counter = 0
				for j in range(len(contested[i])):
					if contested[i][j][1] == contested[i][0][1]:
						counter = counter + 1 #counts the number of times the first priority value appears
				if counter <= cca_quotas_dict[i]:#if the counter is less than the quota, allocate all.
					for j in range(counter-1,-1,-1):
						allocated.append([contested[i][j][0],i,contested[i][j][1]])#adds to allocated
						for k in contested.keys():#this segment deletes all occurances of the student's name
							if k != i:
								for l in range(len(contested[k])-1,-1,-1):
									if contested[k][l][0] == contested[i][j][0]:
										del contested[k][l]
						del contested[i][j]
					if counter < cca_quotas_dict[i]: #this if else takes away the allocated students from the quota, or zeroes the quota if all students have been allocated for that CCA
						cca_quotas_dict[i] = cca_quotas_dict[i] - counter
					else:
						cca_quotas_dict[i] = 0
					counter = 0
				elif counter > cca_quotas_dict[i]:#if the counter is higher, pass unless the students who are tied are shortlisted
					if contested[i][0][1]%2 == 1:
						temp = []
						for j in range(counter):
							temp.append([contested[i][j][0],dict_of_cca_choices[i].index(contested[i][j][0]),contested[i][j][1]]) #puts student, priority value and cca's ranking
						temp.sort(key = lambda x : x[1]) #sorts list based on cca ranking
						for j in range(cca_quotas_dict[i]-1,-1,-1):
							allocated.append([temp[j][0],i,temp[j][2]])
							for k in contested.keys():
								if k != i:
									for l in range(len(contested[k])-1,-1,-1):
										if contested[k][l][0] == temp[j][0]:
											del contested[k][l]
							del temp[j] #takes the top few and allocates them, then deletes their names
						cca_quotas_dict[i] = 0
						contested[i] = []
	#the 18 lines below check if any students were not allocated and allocates them a random CCA or their first choice if the number of students exceeds the quota.
	list_of_students_who_were_allocated = []
	left_out = list_of_all_students.copy()
	random_cca = []
	for i in range(len(allocated)):
		list_of_students_who_were_allocated.append(allocated[i][0])
	for i in range(len(student_choices_list)):
		if student_choices_list[i][0] not in list_of_students_who_were_allocated:
			left_out.append(student_choices_list[i])
	for i in cca_quotas_dict.keys():
		if cca_quotas_dict[i] != 0:
			for j in range(cca_quotas_dict[i]):
				random_cca.append(i)
	for i in range(len(left_out)):
		if len(random_cca) != 0:
			allocated.append([left_out[i][0],random_cca[0],"none"])
			del random_cca[0]
		else:
			allocated.append([left_out[i][0],left_out[i][1],1])
	return allocated

#Functions to make main() cleaner
def filename_check(spam_count):
	'''
	This function checks whether the excel file with the filename the user inputs has the correct file type, exists, and can be accessed.

	Parameters:
		spam_count (int): The number of times the user has spammed the [Enter] button.

	Returns:
		spam_count (int): The revised number of times the user has spammed the [Enter] button.
		filename (str): The verified name of the excel file.
	'''
	
	exit_loop = False
	while exit_loop == False:
		gui.clear()
		print("Welcome to the Automatic CCA Allocator.\n\nPlease put the unallocated excel file (ends with .xlsx) in the same folder as this programme if you haven't already done so.\n")

		start = time.time()
		filename = input("Filename: ")
		filename_path = pathlib.Path(filename)
		
		if filename == "":
			spam_count = gui.invalid_input(start, spam_count, 6)
		elif "." not in filename:
			spam_count = gui.invalid_input(start, spam_count, 7)
		elif filename_path.suffix != ".xlsx":
			spam_count = gui.invalid_input(start, spam_count, 8, filename_path.suffix)
		else:
			try:
				filepointer = open(filename_path, "r")
				filepointer.close()
				exit_loop = True
			except FileNotFoundError:
				spam_count = gui.invalid_input(start, spam_count, 9, filename)
			except IOError:
				spam_count = gui.invalid_input(start, spam_count, 10, filename)
	return spam_count, filename_path

def write_to_file(filename_path, allocated, dict_of_cca_choices, merit_allocated):
	'''
	Concise Explanation

	Detailed Explanation

	Parameters:
		NIL

	Returns:
		NIL
	'''
	
	workbook = openpyxl.load_workbook(filename_path)
	sheet = workbook.get_sheet_by_name(classlist)
	classlist_2D = sheet_to_2D_list("classlist")
	number_of_students = len(sheet_to_2D_list("classlist"))
	allocated_student_names = []
	
	for i in range(len(allocated)):
		allocated_student_names.append(allocated[i][0]) #makes a list of ppl who have been allocated ccas and their index
	for i in range(1,number_of_students): 
		if classlist_2D[i][0] in allocated_student_names: #if the classlist name is allocated:
			if allocated[allocated_student_names.index(classlist_2D[i][0])][2] != "DSA" and allocated[allocated_student_names.index(classlist_2D[i][0])][2] != "none":
				sheet[f"H{i}"] = str(int((allocated[allocated_student_names.index(classlist_2D[i][0])][2]+1)/2)) #puts the choice no. in if not dsa
			else:
				sheet[f"H{i}"] = allocated[allocated_student_names.index(classlist_2D[i][0])][2] #puts in DSA or none
			if allocated[allocated_student_names.index(classlist_2D[i][0])][2] %2 == 1:
				sheet[f"I{i}"] = str(dict_of_cca_choices[allocated[allocated_student_names.index(classlist_2D[i][0])][1]].index(classlist_2D[i][0])) #puts in cca choice no. if applicable 
	sheet["J"] = "MERIT1"
	sheet["K"] = "MERIT2"
	merit_names = []
	for i in range(len(merit_allocated)):
		merit_names.append(merit_allocated[i][0])
	for i in range(1,number_of_students):
		if classlist_2D[i][0] in merit_names: #if the classlist name is allocated:
			if len(merit_allocated[merit_names.index(classlist_2D[i][0])]) == 2:
				sheet[f"J{i}"] = merit_allocated[merit_names.index(classlist_2D[i][0])][1]
			elif len(merit_allocated[merit_names.index(classlist_2D[i][0])]) == 3:
				sheet[f"J{i}"] = merit_allocated[merit_names.index(classlist_2D[i][0])][1]
				sheet[f"K{i}"] = merit_allocated[merit_names.index(classlist_2D[i][0])][2]
	wb.save("allocated.xlsx")
def main():
	'''
	Concise Explanation

	Detailed Explanation

	Parameters:
		NIL

	Returns:
		NIL
	'''
	
	spam_count, configurations = 0, config.extract()
	while True:
		gui.clear()
		print("Welcome to the Automatic CCA Allocator.\n\nWhat would you like to do?\n")
		print("1) Configure Allocation of CCA")
		print("2) View Current Configurations")
		print("3) Start CCA Allocation")
		print("4) Exit")

		start = time.time()
		redirect = input("\n\n")

		if redirect == "1":
			spam_count, configurations = config.main(spam_count, configurations)
		elif redirect == "2":
			config.view(configurations)
		elif redirect == "3":
			spam_count, filename_path = filename_check(spam_count)
			workbook_to_sheets(filename_path) #Get and test filename
			cca_quotas_and_choices(spam_count) #Propting user if there are dicrepancies and/or errors in the file
			exec(configuration_1_list[configurations[0]][1]) #Selecting and running Merit CCA Code
			#core code
			#log code here somewhere
			gui.clear()
			print("This part of the code is still being worked on and isn't fully finished yet. Come again later when it's complete to use it.")
			gui.transition()
		elif redirect == "4":
			gui.clear()
			sys.exit("Thank you for using the Automatic CCA Allocator!\n\n© 2019 Christopher Cheng & Nigel Heng")
		else:
			spam_count = gui.invalid_input(start, spam_count, 1)

#main()
spam_count = 0
workbook_to_sheets("unallocated.xlsx")
#print(dict_of_sheets)
spam_count, cca_quotas_dict, dict_of_cca_choices, name_ccaquota_choices_sheet = cca_quotas_and_choices(spam_count)
#quota_discrepancy(spam_count)

max_student_and_overall_happiness(cca_quotas_dict,dict_of_cca_choices)

#p)
#print(merit_allocation_both())
#print(merit_allocation(["ARCHIVE"]))
#print(time.time() - start)
print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]")
#configurations_1(spam_count, configurations)
#print(merit_allocation_both())
#listOfNRICs = []
#for cellObj in dictOfLists["choices"].columns[2]:
#listOfNRICs.append(cellObj.value)
#This code above should give you a list of all the scrambled NRICs in sheet "choices"
#not sure if i can do the same with sheet.rows. need to test that out, if can will be much easier for us.

#DSA_allocation()

#def test():
#	exec("testing = 1", globals())
#test()
#print(testing)