Spaces:
Sleeping
Sleeping
Upload 31 files
Browse files- __pycache__/ConsoleApp.cpython-39.pyc +0 -0
- __pycache__/HtmlOutput.cpython-39.pyc +0 -0
- algorithm/GeneticAlgorithm.py +190 -0
- algorithm/NsgaII.py +221 -0
- algorithm/__pycache__/APNsgaIII.cpython-39.pyc +0 -0
- algorithm/__pycache__/GeneticAlgorithm.cpython-39.pyc +0 -0
- algorithm/__pycache__/NsgaII.cpython-39.pyc +0 -0
- algorithm/__pycache__/NsgaIII.cpython-39.pyc +0 -0
- model/Configuration.py +236 -0
- model/Constant.py +5 -0
- model/Course.py +11 -0
- model/CourseClass.py +53 -0
- model/Criteria.py +49 -0
- model/Professor.py +25 -0
- model/Reservation.py +57 -0
- model/Room.py +35 -0
- model/Schedule.py +438 -0
- model/StudentsGroup.py +26 -0
- model/__pycache__/Configuration.cpython-39.pyc +0 -0
- model/__pycache__/Constant.cpython-39.pyc +0 -0
- model/__pycache__/Course.cpython-39.pyc +0 -0
- model/__pycache__/CourseClass.cpython-39.pyc +0 -0
- model/__pycache__/Criteria.cpython-39.pyc +0 -0
- model/__pycache__/Professor.cpython-39.pyc +0 -0
- model/__pycache__/Reservation.cpython-39.pyc +0 -0
- model/__pycache__/Room.cpython-39.pyc +0 -0
- model/__pycache__/Schedule.cpython-39.pyc +0 -0
- model/__pycache__/StudentsGroup.cpython-39.pyc +0 -0
- processing data/GaSchedule3.json +1 -0
- processing data/data.json +1 -0
- processing data/reprocessing.ipynb +589 -0
__pycache__/ConsoleApp.cpython-39.pyc
ADDED
|
Binary file (978 Bytes). View file
|
|
|
__pycache__/HtmlOutput.cpython-39.pyc
ADDED
|
Binary file (5.03 kB). View file
|
|
|
algorithm/GeneticAlgorithm.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from model.Schedule import Schedule
|
| 2 |
+
import random
|
| 3 |
+
from random import randrange
|
| 4 |
+
from time import time
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
# Lakshmi, R. et al. “A New Biological Operator in Genetic Algorithm for Class Scheduling Problem.”
|
| 8 |
+
# International Journal of Computer Applications 60 (2012): 6-11.
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
# Genetic algorithm
|
| 12 |
+
class GeneticAlgorithm:
|
| 13 |
+
def initAlgorithm(self, prototype, numberOfChromosomes=100, replaceByGeneration=8, trackBest=5):
|
| 14 |
+
# Number of best chromosomes currently saved in best chromosome group
|
| 15 |
+
self._currentBestSize = 0
|
| 16 |
+
# Prototype of chromosomes in population
|
| 17 |
+
self._prototype = prototype
|
| 18 |
+
|
| 19 |
+
# there should be at least 2 chromosomes in population
|
| 20 |
+
if numberOfChromosomes < 2:
|
| 21 |
+
numberOfChromosomes = 2
|
| 22 |
+
|
| 23 |
+
# and algorithm should track at least on of best chromosomes
|
| 24 |
+
if trackBest < 1:
|
| 25 |
+
trackBest = 1
|
| 26 |
+
|
| 27 |
+
# Population of chromosomes
|
| 28 |
+
self._chromosomes = numberOfChromosomes * [None]
|
| 29 |
+
# Inidicates whether chromosome belongs to best chromosome group
|
| 30 |
+
self._bestFlags = numberOfChromosomes * [False]
|
| 31 |
+
|
| 32 |
+
# Indices of best chromosomes
|
| 33 |
+
self._bestChromosomes = trackBest * [0]
|
| 34 |
+
# Number of chromosomes which are replaced in each generation by offspring
|
| 35 |
+
self.set_replace_by_generation(replaceByGeneration)
|
| 36 |
+
|
| 37 |
+
# Initializes genetic algorithm
|
| 38 |
+
def __init__(self, configuration, numberOfCrossoverPoints=2, mutationSize=2, crossoverProbability=80,
|
| 39 |
+
mutationProbability=3):
|
| 40 |
+
self.initAlgorithm(Schedule(configuration))
|
| 41 |
+
self._mutationSize = mutationSize
|
| 42 |
+
self._numberOfCrossoverPoints = numberOfCrossoverPoints
|
| 43 |
+
self._crossoverProbability = crossoverProbability
|
| 44 |
+
self._mutationProbability = mutationProbability
|
| 45 |
+
|
| 46 |
+
@property
|
| 47 |
+
# Returns pointer to best chromosomes in population
|
| 48 |
+
def result(self):
|
| 49 |
+
return self._chromosomes[self._bestChromosomes[0]]
|
| 50 |
+
|
| 51 |
+
def set_replace_by_generation(self, value):
|
| 52 |
+
numberOfChromosomes = len(self._chromosomes)
|
| 53 |
+
trackBest = len(self._bestChromosomes)
|
| 54 |
+
if (value > numberOfChromosomes - trackBest):
|
| 55 |
+
value = numberOfChromosomes - trackBest
|
| 56 |
+
self._replaceByGeneration = value
|
| 57 |
+
|
| 58 |
+
# Tries to add chromosomes in best chromosome group
|
| 59 |
+
def addToBest(self, chromosomeIndex):
|
| 60 |
+
bestChromosomes = self._bestChromosomes
|
| 61 |
+
length_best = len(bestChromosomes)
|
| 62 |
+
bestFlags = self._bestFlags
|
| 63 |
+
chromosomes = self._chromosomes
|
| 64 |
+
|
| 65 |
+
# don't add if new chromosome hasn't fitness big enough for best chromosome group
|
| 66 |
+
# or it is already in the group?
|
| 67 |
+
if (self._currentBestSize == length_best and chromosomes[bestChromosomes[self._currentBestSize - 1]].fitness >=
|
| 68 |
+
chromosomes[chromosomeIndex].fitness) or bestFlags[chromosomeIndex]:
|
| 69 |
+
return
|
| 70 |
+
|
| 71 |
+
# find place for new chromosome
|
| 72 |
+
j = self._currentBestSize
|
| 73 |
+
for i in range(j, -1, -1):
|
| 74 |
+
j = i
|
| 75 |
+
pos = bestChromosomes[i - 1]
|
| 76 |
+
# group is not full?
|
| 77 |
+
if i < length_best:
|
| 78 |
+
# position of new chromosomes is found?
|
| 79 |
+
if chromosomes[pos].fitness > chromosomes[chromosomeIndex].fitness:
|
| 80 |
+
break
|
| 81 |
+
|
| 82 |
+
# move chromosomes to make room for new
|
| 83 |
+
bestChromosomes[i] = pos
|
| 84 |
+
else:
|
| 85 |
+
# group is full remove worst chromosomes in the group
|
| 86 |
+
bestFlags[pos] = False
|
| 87 |
+
|
| 88 |
+
# store chromosome in best chromosome group
|
| 89 |
+
bestChromosomes[j] = chromosomeIndex
|
| 90 |
+
bestFlags[chromosomeIndex] = True
|
| 91 |
+
|
| 92 |
+
# increase current size if it has not reached the limit yet
|
| 93 |
+
if self._currentBestSize < length_best:
|
| 94 |
+
self._currentBestSize += 1
|
| 95 |
+
|
| 96 |
+
# Returns TRUE if chromosome belongs to best chromosome group
|
| 97 |
+
def isInBest(self, chromosomeIndex) -> bool:
|
| 98 |
+
return self._bestFlags[chromosomeIndex]
|
| 99 |
+
|
| 100 |
+
# Clears best chromosome group
|
| 101 |
+
def clearBest(self):
|
| 102 |
+
self._bestFlags = len(self._bestFlags) * [False]
|
| 103 |
+
self._currentBestSize = 0
|
| 104 |
+
|
| 105 |
+
# initialize new population with chromosomes randomly built using prototype
|
| 106 |
+
def initialize(self, population):
|
| 107 |
+
# addToBest = self.addToBest
|
| 108 |
+
prototype = self._prototype
|
| 109 |
+
length_chromosomes = len(population)
|
| 110 |
+
|
| 111 |
+
for i in range(0, length_chromosomes):
|
| 112 |
+
# add new chromosome to population
|
| 113 |
+
population[i] = prototype.makeNewFromPrototype()
|
| 114 |
+
# addToBest(i)
|
| 115 |
+
|
| 116 |
+
def selection(self, population):
|
| 117 |
+
length_chromosomes = len(population)
|
| 118 |
+
return (population[randrange(32768) % length_chromosomes], population[randrange(32768) % length_chromosomes])
|
| 119 |
+
|
| 120 |
+
def replacement(self, population, replaceByGeneration) -> []:
|
| 121 |
+
mutationSize = self._mutationSize
|
| 122 |
+
numberOfCrossoverPoints = self._numberOfCrossoverPoints
|
| 123 |
+
crossoverProbability = self._crossoverProbability
|
| 124 |
+
mutationProbability = self._mutationProbability
|
| 125 |
+
selection = self.selection
|
| 126 |
+
isInBest = self.isInBest
|
| 127 |
+
length_chromosomes = len(population)
|
| 128 |
+
# produce offspring
|
| 129 |
+
offspring = replaceByGeneration * [None]
|
| 130 |
+
for j in range(replaceByGeneration):
|
| 131 |
+
# selects parent randomly
|
| 132 |
+
parent = selection(population)
|
| 133 |
+
|
| 134 |
+
offspring[j] = parent[0].crossover(parent[1], numberOfCrossoverPoints, crossoverProbability)
|
| 135 |
+
offspring[j].mutation(mutationSize, mutationProbability)
|
| 136 |
+
|
| 137 |
+
# replace chromosomes of current operation with offspring
|
| 138 |
+
# select chromosome for replacement randomly
|
| 139 |
+
ci = randrange(32768) % length_chromosomes
|
| 140 |
+
while isInBest(ci):
|
| 141 |
+
ci = randrange(32768) % length_chromosomes
|
| 142 |
+
|
| 143 |
+
# replace chromosomes
|
| 144 |
+
population[ci] = offspring[j]
|
| 145 |
+
|
| 146 |
+
# try to add new chromosomes in best chromosome group
|
| 147 |
+
self.addToBest(ci)
|
| 148 |
+
return offspring
|
| 149 |
+
|
| 150 |
+
# Starts and executes algorithm
|
| 151 |
+
def run(self, maxRepeat=9999, minFitness=0.999):
|
| 152 |
+
# clear best chromosome group from previous execution
|
| 153 |
+
self.clearBest()
|
| 154 |
+
length_chromosomes = len(self._chromosomes)
|
| 155 |
+
|
| 156 |
+
self.initialize(self._chromosomes)
|
| 157 |
+
random.seed(round(time() * 1000))
|
| 158 |
+
|
| 159 |
+
# Current generation
|
| 160 |
+
currentGeneration = 0
|
| 161 |
+
|
| 162 |
+
repeat = 0
|
| 163 |
+
lastBestFit = 0.0
|
| 164 |
+
|
| 165 |
+
while 1:
|
| 166 |
+
best = self.result
|
| 167 |
+
print("Fitness:", "{:f}\t".format(best.fitness), "Generation:", currentGeneration, end="\r")
|
| 168 |
+
|
| 169 |
+
# algorithm has reached criteria?
|
| 170 |
+
if best.fitness > minFitness:
|
| 171 |
+
break
|
| 172 |
+
|
| 173 |
+
difference = abs(best.fitness - lastBestFit)
|
| 174 |
+
if difference <= 0.0000001:
|
| 175 |
+
repeat += 1
|
| 176 |
+
else:
|
| 177 |
+
repeat = 0
|
| 178 |
+
|
| 179 |
+
if repeat > (maxRepeat / 100):
|
| 180 |
+
random.seed(round(time() * 1000))
|
| 181 |
+
self.set_replace_by_generation(self._replaceByGeneration * 3)
|
| 182 |
+
self._crossoverProbability += 1
|
| 183 |
+
|
| 184 |
+
self.replacement(self._chromosomes, self._replaceByGeneration)
|
| 185 |
+
|
| 186 |
+
lastBestFit = best.fitness
|
| 187 |
+
currentGeneration += 1
|
| 188 |
+
|
| 189 |
+
def __str__(self):
|
| 190 |
+
return "Genetic Algorithm"
|
algorithm/NsgaII.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from model.Schedule import Schedule
|
| 2 |
+
import numpy as np
|
| 3 |
+
import random
|
| 4 |
+
import sys
|
| 5 |
+
from time import time
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
# K.Deb, A.Pratap, S.Agrawal, T.Meyarivan, A fast and elitist multiobjective genetic algorithm:
|
| 9 |
+
# NSGA-II, IEEE Transactions on Evolutionary Computation 6 (2002) 182–197.
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
# NSGA II
|
| 13 |
+
class NsgaII:
|
| 14 |
+
def initAlgorithm(self, prototype, numberOfChromosomes=100):
|
| 15 |
+
# Prototype of chromosomes in population
|
| 16 |
+
self._prototype = prototype
|
| 17 |
+
|
| 18 |
+
# there should be at least 2 chromosomes in population
|
| 19 |
+
if numberOfChromosomes < 2:
|
| 20 |
+
numberOfChromosomes = 2
|
| 21 |
+
|
| 22 |
+
# Population of chromosomes
|
| 23 |
+
self._chromosomes = []
|
| 24 |
+
self._populationSize = numberOfChromosomes
|
| 25 |
+
self._repeatRatio = .0
|
| 26 |
+
|
| 27 |
+
# Initializes genetic algorithm
|
| 28 |
+
def __init__(self, configuration, numberOfCrossoverPoints=2, mutationSize=2, crossoverProbability=80,
|
| 29 |
+
mutationProbability=3):
|
| 30 |
+
self.initAlgorithm(Schedule(configuration))
|
| 31 |
+
self._mutationSize = mutationSize
|
| 32 |
+
self._numberOfCrossoverPoints = numberOfCrossoverPoints
|
| 33 |
+
self._crossoverProbability = crossoverProbability
|
| 34 |
+
self._mutationProbability = mutationProbability
|
| 35 |
+
|
| 36 |
+
@property
|
| 37 |
+
# Returns pointer to best chromosomes in population
|
| 38 |
+
def result(self):
|
| 39 |
+
return self._chromosomes[0]
|
| 40 |
+
|
| 41 |
+
# non-dominated sorting function
|
| 42 |
+
def nonDominatedSorting(self, totalChromosome):
|
| 43 |
+
doublePopulationSize = self._populationSize * 2
|
| 44 |
+
s = doublePopulationSize * [ set() ]
|
| 45 |
+
n = np.zeros(doublePopulationSize, dtype=int)
|
| 46 |
+
front = [ set() ]
|
| 47 |
+
|
| 48 |
+
for p in range(doublePopulationSize):
|
| 49 |
+
for q in range(doublePopulationSize):
|
| 50 |
+
if totalChromosome[p].fitness > totalChromosome[q].fitness:
|
| 51 |
+
s[p].add(q)
|
| 52 |
+
elif totalChromosome[p].fitness < totalChromosome[q].fitness:
|
| 53 |
+
n[p] += 1
|
| 54 |
+
|
| 55 |
+
if n[p] == 0:
|
| 56 |
+
front[0].add(p)
|
| 57 |
+
|
| 58 |
+
i = 0
|
| 59 |
+
while front[i]:
|
| 60 |
+
Q = set()
|
| 61 |
+
for p in front[i]:
|
| 62 |
+
for q in s[p]:
|
| 63 |
+
n[q] -= 1
|
| 64 |
+
if n[q] == 0:
|
| 65 |
+
Q.add(q)
|
| 66 |
+
i += 1
|
| 67 |
+
front.append(Q)
|
| 68 |
+
|
| 69 |
+
front.pop()
|
| 70 |
+
return front
|
| 71 |
+
|
| 72 |
+
# calculate crowding distance function
|
| 73 |
+
def calculateCrowdingDistance(self, front, totalChromosome):
|
| 74 |
+
distance, obj = {}, {}
|
| 75 |
+
for key in front:
|
| 76 |
+
distance[key] = 0
|
| 77 |
+
fitness = totalChromosome[key].fitness
|
| 78 |
+
if fitness not in obj.values():
|
| 79 |
+
obj[key] = fitness
|
| 80 |
+
|
| 81 |
+
sorted_keys = sorted(obj, key=obj.get)
|
| 82 |
+
size = len(obj)
|
| 83 |
+
distance[sorted_keys[0]] = distance[sorted_keys[-1]] = sys.float_info.max
|
| 84 |
+
|
| 85 |
+
if size > 1:
|
| 86 |
+
diff2 = totalChromosome[sorted_keys[-1]].getDifference(totalChromosome[sorted_keys[0]])
|
| 87 |
+
|
| 88 |
+
for i in range(1, size - 1):
|
| 89 |
+
diff = totalChromosome[sorted_keys[i + 1]].getDifference(totalChromosome[sorted_keys[i - 1]]) / diff2
|
| 90 |
+
distance[sorted_keys[i]] += diff
|
| 91 |
+
|
| 92 |
+
return distance
|
| 93 |
+
|
| 94 |
+
def selection(self, front, totalChromosome):
|
| 95 |
+
populationSize = self._populationSize
|
| 96 |
+
calculateCrowdingDistance = self.calculateCrowdingDistance
|
| 97 |
+
N = 0
|
| 98 |
+
newPop = []
|
| 99 |
+
while N < populationSize:
|
| 100 |
+
for row in front:
|
| 101 |
+
N += len(row)
|
| 102 |
+
if N > populationSize:
|
| 103 |
+
distance = calculateCrowdingDistance(row, totalChromosome)
|
| 104 |
+
sortedCdf = sorted(distance, key=distance.get, reverse=True)
|
| 105 |
+
for j in sortedCdf:
|
| 106 |
+
if len(newPop) >= populationSize:
|
| 107 |
+
break
|
| 108 |
+
newPop.append(j)
|
| 109 |
+
break
|
| 110 |
+
newPop.extend(row)
|
| 111 |
+
|
| 112 |
+
return [totalChromosome[n] for n in newPop]
|
| 113 |
+
|
| 114 |
+
def replacement(self, population):
|
| 115 |
+
populationSize = self._populationSize
|
| 116 |
+
numberOfCrossoverPoints = self._numberOfCrossoverPoints
|
| 117 |
+
crossoverProbability = self._crossoverProbability
|
| 118 |
+
offspring = []
|
| 119 |
+
# generate a random sequence to select the parent chromosome to crossover
|
| 120 |
+
S = np.arange(populationSize)
|
| 121 |
+
np.random.shuffle(S)
|
| 122 |
+
|
| 123 |
+
halfPopulationSize = populationSize // 2
|
| 124 |
+
for m in range(halfPopulationSize):
|
| 125 |
+
parent0 = population[S[2 * m]]
|
| 126 |
+
parent1 = population[S[2 * m + 1]]
|
| 127 |
+
child0 = parent0.crossover(parent1, numberOfCrossoverPoints, crossoverProbability)
|
| 128 |
+
child1 = parent1.crossover(parent0, numberOfCrossoverPoints, crossoverProbability)
|
| 129 |
+
|
| 130 |
+
# append child chromosome to offspring list
|
| 131 |
+
offspring.extend((child0, child1))
|
| 132 |
+
|
| 133 |
+
return offspring
|
| 134 |
+
|
| 135 |
+
# initialize new population with chromosomes randomly built using prototype
|
| 136 |
+
def initialize(self, population):
|
| 137 |
+
prototype = self._prototype
|
| 138 |
+
|
| 139 |
+
for i in range(len(population)):
|
| 140 |
+
# add new chromosome to population
|
| 141 |
+
population[i] = prototype.makeNewFromPrototype()
|
| 142 |
+
|
| 143 |
+
def reform(self):
|
| 144 |
+
random.seed(round(time() * 1000))
|
| 145 |
+
np.random.seed(int(time()))
|
| 146 |
+
if self._crossoverProbability < 95:
|
| 147 |
+
self._crossoverProbability += 1.0
|
| 148 |
+
elif self._mutationProbability < 30:
|
| 149 |
+
self._mutationProbability += 1.0
|
| 150 |
+
|
| 151 |
+
# Starts and executes algorithm
|
| 152 |
+
def run(self, maxRepeat=9999, minFitness=0.999):
|
| 153 |
+
mutationSize = self._mutationSize
|
| 154 |
+
mutationProbability = self._mutationProbability
|
| 155 |
+
nonDominatedSorting = self.nonDominatedSorting
|
| 156 |
+
selection = self.selection
|
| 157 |
+
populationSize = self._populationSize
|
| 158 |
+
population = populationSize * [None]
|
| 159 |
+
|
| 160 |
+
self.initialize(population)
|
| 161 |
+
random.seed(round(time() * 1000))
|
| 162 |
+
np.random.seed(int(time()))
|
| 163 |
+
|
| 164 |
+
# Current generation
|
| 165 |
+
currentGeneration = 0
|
| 166 |
+
|
| 167 |
+
repeat = 0
|
| 168 |
+
lastBestFit = 0.0
|
| 169 |
+
|
| 170 |
+
while 1:
|
| 171 |
+
if currentGeneration > 0:
|
| 172 |
+
best = self.result
|
| 173 |
+
print("Fitness:", "{:f}\t".format(best.fitness), "Generation:", currentGeneration, end="\r")
|
| 174 |
+
|
| 175 |
+
# algorithm has reached criteria?
|
| 176 |
+
if best.fitness > minFitness:
|
| 177 |
+
break
|
| 178 |
+
|
| 179 |
+
difference = abs(best.fitness - lastBestFit)
|
| 180 |
+
if difference <= 0.0000001:
|
| 181 |
+
repeat += 1
|
| 182 |
+
else:
|
| 183 |
+
repeat = 0
|
| 184 |
+
|
| 185 |
+
self._repeatRatio = repeat * 100 / maxRepeat
|
| 186 |
+
if repeat > (maxRepeat / 100):
|
| 187 |
+
self.reform()
|
| 188 |
+
|
| 189 |
+
# crossover
|
| 190 |
+
offspring = self.replacement(population)
|
| 191 |
+
|
| 192 |
+
# mutation
|
| 193 |
+
for child in offspring:
|
| 194 |
+
child.mutation(mutationSize, mutationProbability)
|
| 195 |
+
|
| 196 |
+
totalChromosome = population + offspring
|
| 197 |
+
|
| 198 |
+
# non-dominated sorting
|
| 199 |
+
front = nonDominatedSorting(totalChromosome)
|
| 200 |
+
if len(front) == 0:
|
| 201 |
+
break
|
| 202 |
+
|
| 203 |
+
# selection
|
| 204 |
+
population = selection(front, totalChromosome)
|
| 205 |
+
self._populationSize = populationSize = len(population)
|
| 206 |
+
|
| 207 |
+
# comparison
|
| 208 |
+
if currentGeneration == 0:
|
| 209 |
+
self._chromosomes = population
|
| 210 |
+
else:
|
| 211 |
+
totalChromosome = population + self._chromosomes
|
| 212 |
+
newBestFront = nonDominatedSorting(totalChromosome)
|
| 213 |
+
if len(newBestFront) == 0:
|
| 214 |
+
break
|
| 215 |
+
self._chromosomes = selection(newBestFront, totalChromosome)
|
| 216 |
+
lastBestFit = best.fitness
|
| 217 |
+
|
| 218 |
+
currentGeneration += 1
|
| 219 |
+
|
| 220 |
+
def __str__(self):
|
| 221 |
+
return "NSGA II"
|
algorithm/__pycache__/APNsgaIII.cpython-39.pyc
ADDED
|
Binary file (3.25 kB). View file
|
|
|
algorithm/__pycache__/GeneticAlgorithm.cpython-39.pyc
ADDED
|
Binary file (4.34 kB). View file
|
|
|
algorithm/__pycache__/NsgaII.cpython-39.pyc
ADDED
|
Binary file (5.06 kB). View file
|
|
|
algorithm/__pycache__/NsgaIII.cpython-39.pyc
ADDED
|
Binary file (11.7 kB). View file
|
|
|
model/Configuration.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import codecs
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
from .Professor import Professor
|
| 5 |
+
from .StudentsGroup import StudentsGroup
|
| 6 |
+
from .Course import Course
|
| 7 |
+
from .Room import Room
|
| 8 |
+
from .CourseClass import CourseClass
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
# Reads configuration file and stores parsed objects
|
| 12 |
+
class Configuration:
|
| 13 |
+
|
| 14 |
+
# Initialize data
|
| 15 |
+
def __init__(self):
|
| 16 |
+
# Indicate that configuration is not parsed yet
|
| 17 |
+
self._isEmpty = True
|
| 18 |
+
# parsed professors
|
| 19 |
+
self._professors = {}
|
| 20 |
+
# parsed student groups
|
| 21 |
+
self._studentGroups = {}
|
| 22 |
+
# parsed courses
|
| 23 |
+
self._courses = {}
|
| 24 |
+
# parsed rooms
|
| 25 |
+
self._rooms = {}
|
| 26 |
+
# parsed classes
|
| 27 |
+
self._courseClasses = []
|
| 28 |
+
|
| 29 |
+
# Returns professor with specified ID
|
| 30 |
+
# If there is no professor with such ID method returns NULL
|
| 31 |
+
def getProfessorById(self, id) -> Professor:
|
| 32 |
+
if id in self._professors:
|
| 33 |
+
return self._professors[id]
|
| 34 |
+
return None
|
| 35 |
+
|
| 36 |
+
@property
|
| 37 |
+
# Returns number of parsed professors
|
| 38 |
+
def numberOfProfessors(self) -> int:
|
| 39 |
+
return len(self._professors)
|
| 40 |
+
|
| 41 |
+
# Returns student group with specified ID
|
| 42 |
+
# If there is no student group with such ID method returns NULL
|
| 43 |
+
def getStudentsGroupById(self, id) -> StudentsGroup:
|
| 44 |
+
if id in self._studentGroups:
|
| 45 |
+
return self._studentGroups[id]
|
| 46 |
+
return None
|
| 47 |
+
|
| 48 |
+
@property
|
| 49 |
+
# Returns number of parsed student groups
|
| 50 |
+
def numberOfStudentGroups(self) -> int:
|
| 51 |
+
return len(self._studentGroups)
|
| 52 |
+
|
| 53 |
+
# Returns course with specified ID
|
| 54 |
+
# If there is no course with such ID method returns NULL
|
| 55 |
+
def getCourseById(self, id) -> Course:
|
| 56 |
+
if id in self._courses:
|
| 57 |
+
return self._courses[id]
|
| 58 |
+
return None
|
| 59 |
+
|
| 60 |
+
@property
|
| 61 |
+
def numberOfCourses(self) -> int:
|
| 62 |
+
return len(self._courses)
|
| 63 |
+
|
| 64 |
+
# Returns room with specified ID
|
| 65 |
+
# If there is no room with such ID method returns NULL
|
| 66 |
+
def getRoomById(self, id) -> Room:
|
| 67 |
+
if id in self._rooms:
|
| 68 |
+
return self._rooms[id]
|
| 69 |
+
return None
|
| 70 |
+
|
| 71 |
+
@property
|
| 72 |
+
# Returns number of parsed rooms
|
| 73 |
+
def numberOfRooms(self) -> int:
|
| 74 |
+
return len(self._rooms)
|
| 75 |
+
|
| 76 |
+
@property
|
| 77 |
+
# Returns reference to list of parsed classes
|
| 78 |
+
def courseClasses(self) -> []:
|
| 79 |
+
return self._courseClasses
|
| 80 |
+
|
| 81 |
+
@property
|
| 82 |
+
# Returns number of parsed classes
|
| 83 |
+
def numberOfCourseClasses(self) -> int:
|
| 84 |
+
return len(self._courseClasses)
|
| 85 |
+
|
| 86 |
+
@property
|
| 87 |
+
# Returns TRUE if configuration is not parsed yet
|
| 88 |
+
def isEmpty(self) -> bool:
|
| 89 |
+
return self._isEmpty
|
| 90 |
+
|
| 91 |
+
# Reads professor's data from config file, makes object and returns
|
| 92 |
+
# Returns NULL if method cannot parse configuration data
|
| 93 |
+
@staticmethod
|
| 94 |
+
def __parseProfessor(dictConfig):
|
| 95 |
+
id = 0
|
| 96 |
+
name = ''
|
| 97 |
+
|
| 98 |
+
for key in dictConfig:
|
| 99 |
+
if key == 'id':
|
| 100 |
+
id = dictConfig[key]
|
| 101 |
+
elif key == 'name':
|
| 102 |
+
name = dictConfig[key]
|
| 103 |
+
|
| 104 |
+
if id == 0 or name == '':
|
| 105 |
+
return None
|
| 106 |
+
return Professor(id, name)
|
| 107 |
+
|
| 108 |
+
# Reads StudentsGroup's data from config file, makes object and returns
|
| 109 |
+
# Returns None if method cannot parse configuration data
|
| 110 |
+
@staticmethod
|
| 111 |
+
def __parseStudentsGroup(dictConfig):
|
| 112 |
+
id = 0
|
| 113 |
+
# name = ''
|
| 114 |
+
size = 0
|
| 115 |
+
|
| 116 |
+
for key in dictConfig:
|
| 117 |
+
if key == 'id':
|
| 118 |
+
id = dictConfig[key]
|
| 119 |
+
# elif key == 'name':
|
| 120 |
+
# name = dictConfig[key]
|
| 121 |
+
elif key == 'size':
|
| 122 |
+
size = dictConfig[key]
|
| 123 |
+
|
| 124 |
+
if id == 0:
|
| 125 |
+
return None
|
| 126 |
+
return StudentsGroup(id, size)
|
| 127 |
+
|
| 128 |
+
# Reads course's data from config file, makes object and returns
|
| 129 |
+
# Returns None if method dictConfig parse configuration data
|
| 130 |
+
@staticmethod
|
| 131 |
+
def __parseCourse(dictConfig):
|
| 132 |
+
id = 0
|
| 133 |
+
name = ''
|
| 134 |
+
|
| 135 |
+
for key in dictConfig:
|
| 136 |
+
if key == 'id':
|
| 137 |
+
id = dictConfig[key]
|
| 138 |
+
elif key == 'name':
|
| 139 |
+
name = dictConfig[key]
|
| 140 |
+
|
| 141 |
+
if id == 0:
|
| 142 |
+
return None
|
| 143 |
+
return Course(id, name)
|
| 144 |
+
|
| 145 |
+
# Reads rooms's data from config file, makes object and returns
|
| 146 |
+
# Returns None if method cannot parse configuration data
|
| 147 |
+
@staticmethod
|
| 148 |
+
def __parseRoom(dictConfig):
|
| 149 |
+
lab = False
|
| 150 |
+
name = ''
|
| 151 |
+
size = 0
|
| 152 |
+
|
| 153 |
+
for key in dictConfig:
|
| 154 |
+
if key == 'lab':
|
| 155 |
+
lab = dictConfig[key]
|
| 156 |
+
elif key == 'name':
|
| 157 |
+
name = dictConfig[key]
|
| 158 |
+
elif key == 'size':
|
| 159 |
+
size = dictConfig[key]
|
| 160 |
+
|
| 161 |
+
if size == 0 or name == '':
|
| 162 |
+
return None
|
| 163 |
+
return Room(name, lab, size)
|
| 164 |
+
|
| 165 |
+
# Reads class' data from config file, makes object and returns pointer
|
| 166 |
+
# Returns None if method cannot parse configuration data
|
| 167 |
+
def __parseCourseClass(self, dictConfig):
|
| 168 |
+
pid = 0
|
| 169 |
+
cid = 0
|
| 170 |
+
dur = 1
|
| 171 |
+
lab = False
|
| 172 |
+
group_list = []
|
| 173 |
+
|
| 174 |
+
for key in dictConfig:
|
| 175 |
+
if key == 'professor':
|
| 176 |
+
pid = dictConfig[key]
|
| 177 |
+
elif key == 'course':
|
| 178 |
+
cid = dictConfig[key]
|
| 179 |
+
elif key == 'lab':
|
| 180 |
+
lab = dictConfig[key]
|
| 181 |
+
elif key == 'duration':
|
| 182 |
+
dur = dictConfig[key]
|
| 183 |
+
elif key == 'group' or key == 'groups':
|
| 184 |
+
groups = dictConfig[key]
|
| 185 |
+
if isinstance(groups, list):
|
| 186 |
+
for grp in groups:
|
| 187 |
+
g = self.getStudentsGroupById(grp)
|
| 188 |
+
if g:
|
| 189 |
+
group_list.append(g)
|
| 190 |
+
else:
|
| 191 |
+
g = self.getStudentsGroupById(groups)
|
| 192 |
+
if g:
|
| 193 |
+
group_list.append(g)
|
| 194 |
+
|
| 195 |
+
# get professor who teaches class and course to which this class belongs
|
| 196 |
+
p = self.getProfessorById(pid)
|
| 197 |
+
c = self.getCourseById(cid)
|
| 198 |
+
|
| 199 |
+
# does professor and class exists
|
| 200 |
+
if not c or not p:
|
| 201 |
+
return None
|
| 202 |
+
|
| 203 |
+
# make object and return
|
| 204 |
+
return CourseClass(p, c, lab, dur, group_list)
|
| 205 |
+
|
| 206 |
+
# parse file and store parsed object
|
| 207 |
+
def parseFile(self, fileName):
|
| 208 |
+
# clear previously parsed objects
|
| 209 |
+
self._professors = {}
|
| 210 |
+
self._studentGroups = {}
|
| 211 |
+
self._courses = {}
|
| 212 |
+
self._rooms = {}
|
| 213 |
+
self._courseClasses = []
|
| 214 |
+
Room.restartIDs()
|
| 215 |
+
CourseClass.restartIDs()
|
| 216 |
+
with codecs.open(fileName, "r", "utf-8") as f:
|
| 217 |
+
# read file into a string and deserialize JSON to a type
|
| 218 |
+
data = json.load(f)
|
| 219 |
+
for dictConfig in data:
|
| 220 |
+
for key in dictConfig:
|
| 221 |
+
if key == 'prof':
|
| 222 |
+
prof = self.__parseProfessor(dictConfig[key])
|
| 223 |
+
self._professors[prof.Id] = prof
|
| 224 |
+
elif key == 'course':
|
| 225 |
+
course = self.__parseCourse(dictConfig[key])
|
| 226 |
+
self._courses[course.Id] = course
|
| 227 |
+
elif key == 'room':
|
| 228 |
+
room = self.__parseRoom(dictConfig[key])
|
| 229 |
+
self._rooms[room.Id] = room
|
| 230 |
+
elif key == 'group':
|
| 231 |
+
group = self.__parseStudentsGroup(dictConfig[key])
|
| 232 |
+
self._studentGroups[group.Id] = group
|
| 233 |
+
elif key == 'class':
|
| 234 |
+
courseClass = self.__parseCourseClass(dictConfig[key])
|
| 235 |
+
self._courseClasses.append(courseClass)
|
| 236 |
+
self._isEmpty = False
|
model/Constant.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class Constant:
|
| 2 |
+
# Number of days in week
|
| 3 |
+
DAYS_NUM = 6
|
| 4 |
+
# Number of working hours per day
|
| 5 |
+
DAY_HOURS = 11
|
model/Course.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Stores data about course
|
| 2 |
+
class Course:
|
| 3 |
+
# Initializes course
|
| 4 |
+
def __init__(self, id, name):
|
| 5 |
+
# Returns course ID
|
| 6 |
+
self.Id = id
|
| 7 |
+
# Returns course name
|
| 8 |
+
self.Name = name
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
model/CourseClass.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CourseClass:
|
| 2 |
+
# ID counter used to assign IDs automatically
|
| 3 |
+
_next_class_id = 0
|
| 4 |
+
|
| 5 |
+
# Initializes class object
|
| 6 |
+
def __init__(self, professor, course, requires_lab, duration, groups):
|
| 7 |
+
self.Id = CourseClass._next_class_id
|
| 8 |
+
CourseClass._next_class_id += 1
|
| 9 |
+
# Return pointer to professor who teaches
|
| 10 |
+
self.Professor = professor
|
| 11 |
+
# Return pointer to course to which class belongs
|
| 12 |
+
self.Course = course
|
| 13 |
+
# Returns number of seats (students) required in room
|
| 14 |
+
self.NumberOfSeats = 0
|
| 15 |
+
# Returns TRUE if class requires computers in room.
|
| 16 |
+
self.LabRequired = requires_lab
|
| 17 |
+
# Returns duration of class in hours
|
| 18 |
+
self.Duration = duration
|
| 19 |
+
# Returns reference to list of student groups who attend class
|
| 20 |
+
self.Groups = set(groups)
|
| 21 |
+
# bind professor to class
|
| 22 |
+
self.Professor.addCourseClass(self)
|
| 23 |
+
|
| 24 |
+
# bind student groups to class
|
| 25 |
+
for grp in self.Groups: # self.groups:
|
| 26 |
+
grp.addClass(self)
|
| 27 |
+
self.NumberOfSeats += grp.NumberOfStudents
|
| 28 |
+
|
| 29 |
+
# Returns TRUE if another class has one or overlapping student groups.
|
| 30 |
+
def groupsOverlap(self, c):
|
| 31 |
+
return len(self.Groups & c.Groups) > 0
|
| 32 |
+
|
| 33 |
+
# Returns TRUE if another class has same professor.
|
| 34 |
+
def professorOverlaps(self, c):
|
| 35 |
+
return self.Professor == c.Professor
|
| 36 |
+
|
| 37 |
+
def __hash__(self):
|
| 38 |
+
return hash(self.Id)
|
| 39 |
+
|
| 40 |
+
def __eq__(self, other):
|
| 41 |
+
if not isinstance(other, self.__class__):
|
| 42 |
+
return False
|
| 43 |
+
return hash(self) == hash(other)
|
| 44 |
+
|
| 45 |
+
def __ne__(self, other):
|
| 46 |
+
# Not strictly necessary, but to avoid having both x==y and x!=y
|
| 47 |
+
# True at the same time
|
| 48 |
+
return not (self == other)
|
| 49 |
+
|
| 50 |
+
# Restarts ID assigments
|
| 51 |
+
@staticmethod
|
| 52 |
+
def restartIDs() -> None:
|
| 53 |
+
CourseClass._next_class_id = 0
|
model/Criteria.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .Constant import Constant
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
# Reads configuration file and stores parsed objects
|
| 5 |
+
class Criteria:
|
| 6 |
+
weights = [0, 0.5, 0.5, 0, 0]
|
| 7 |
+
|
| 8 |
+
# check for room overlapping of classes
|
| 9 |
+
@staticmethod
|
| 10 |
+
def isRoomOverlapped(slots, reservation, dur):
|
| 11 |
+
reservation_index = hash(reservation)
|
| 12 |
+
cls = slots[reservation_index: reservation_index + dur]
|
| 13 |
+
return any(True for slot in cls if len(slot) > 1)
|
| 14 |
+
|
| 15 |
+
# does current room have enough seats
|
| 16 |
+
@staticmethod
|
| 17 |
+
def isSeatEnough(r, cc):
|
| 18 |
+
return r.NumberOfSeats >= cc.NumberOfSeats
|
| 19 |
+
|
| 20 |
+
# does current room have computers if they are required
|
| 21 |
+
@staticmethod
|
| 22 |
+
def isComputerEnough(r, cc):
|
| 23 |
+
# return (not cc.LabRequired) or (cc.LabRequired and r.Lab)
|
| 24 |
+
return cc.LabRequired == r.Lab
|
| 25 |
+
|
| 26 |
+
# check overlapping of classes for professors and student groups (size of course)
|
| 27 |
+
@staticmethod
|
| 28 |
+
def isOverlappedProfStudentGrp(slots, cc, numberOfRooms, timeId):
|
| 29 |
+
po = go = False
|
| 30 |
+
|
| 31 |
+
dur = cc.Duration
|
| 32 |
+
for i in range(numberOfRooms, 0, -1):
|
| 33 |
+
# for each hour of class
|
| 34 |
+
for j in range(timeId, timeId + dur):
|
| 35 |
+
cl = slots[j]
|
| 36 |
+
for cc1 in cl:
|
| 37 |
+
if cc != cc1:
|
| 38 |
+
# professor overlaps?
|
| 39 |
+
if not po and cc.professorOverlaps(cc1):
|
| 40 |
+
po = True
|
| 41 |
+
# student group enough?
|
| 42 |
+
if not go and cc.groupsOverlap(cc1):
|
| 43 |
+
go = True
|
| 44 |
+
# both type of overlapping? no need to check more
|
| 45 |
+
if po and go:
|
| 46 |
+
return po, go
|
| 47 |
+
|
| 48 |
+
timeId += Constant.DAY_HOURS
|
| 49 |
+
return po, go
|
model/Professor.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Stores data about professor
|
| 2 |
+
class Professor:
|
| 3 |
+
# Initializes professor data
|
| 4 |
+
def __init__(self, id, name):
|
| 5 |
+
self.Id = id
|
| 6 |
+
self.Name = name
|
| 7 |
+
self.CourseClasses = []
|
| 8 |
+
|
| 9 |
+
# Bind professor to course
|
| 10 |
+
def addCourseClass(self, courseClass):
|
| 11 |
+
self.CourseClasses.append(courseClass)
|
| 12 |
+
|
| 13 |
+
def __hash__(self):
|
| 14 |
+
return hash(self.Id)
|
| 15 |
+
|
| 16 |
+
# Compares ID's of two objects which represent professors
|
| 17 |
+
def __eq__(self, other):
|
| 18 |
+
if not isinstance(other, self.__class__):
|
| 19 |
+
return False
|
| 20 |
+
return hash(self) == hash(other)
|
| 21 |
+
|
| 22 |
+
def __ne__(self, other):
|
| 23 |
+
# Not strictly necessary, but to avoid having both x==y and x!=y
|
| 24 |
+
# True at the same time
|
| 25 |
+
return not (self == other)
|
model/Reservation.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .Constant import Constant
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class Reservation:
|
| 5 |
+
NR = -1
|
| 6 |
+
_reservationPool = {}
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def __init__(self, day: int, time: int, room: int):
|
| 10 |
+
self.Day = day
|
| 11 |
+
self.Time = time
|
| 12 |
+
self.Room = room
|
| 13 |
+
|
| 14 |
+
@staticmethod
|
| 15 |
+
def parse(hashCode):
|
| 16 |
+
reservation = Reservation._reservationPool.get(hashCode)
|
| 17 |
+
if reservation is None:
|
| 18 |
+
day = hashCode // (Constant.DAY_HOURS * Reservation.NR)
|
| 19 |
+
hashCode2 = hashCode - (day * Constant.DAY_HOURS * Reservation.NR)
|
| 20 |
+
room = hashCode2 // Constant.DAY_HOURS
|
| 21 |
+
time = hashCode2 % Constant.DAY_HOURS
|
| 22 |
+
reservation = Reservation(day, time, room)
|
| 23 |
+
Reservation._reservationPool[hashCode] = reservation
|
| 24 |
+
return reservation
|
| 25 |
+
|
| 26 |
+
@staticmethod
|
| 27 |
+
def getHashCode(day: int, time: int, room: int) -> int:
|
| 28 |
+
return day * Reservation.NR * Constant.DAY_HOURS + room * Constant.DAY_HOURS + time
|
| 29 |
+
|
| 30 |
+
@staticmethod
|
| 31 |
+
def getReservation(nr: int, day: int, time: int, room: int):
|
| 32 |
+
if nr != Reservation.NR and nr > 0:
|
| 33 |
+
Reservation.NR = nr
|
| 34 |
+
Reservation._reservationPool.clear()
|
| 35 |
+
|
| 36 |
+
hashCode = Reservation.getHashCode(day, time, room)
|
| 37 |
+
reservation = Reservation.parse(hashCode)
|
| 38 |
+
|
| 39 |
+
if reservation is None:
|
| 40 |
+
reservation = Reservation(day, time, room)
|
| 41 |
+
Reservation._reservationPool[hashCode] = reservation
|
| 42 |
+
return reservation
|
| 43 |
+
|
| 44 |
+
def __hash__(self) -> int:
|
| 45 |
+
return Reservation.getHashCode(self.Day, self.Time, self.Room)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def __eq__(self, other):
|
| 49 |
+
if not isinstance(other, self.__class__):
|
| 50 |
+
return False
|
| 51 |
+
return hash(self) == hash(other)
|
| 52 |
+
|
| 53 |
+
def __ne__(self, other):
|
| 54 |
+
return not self.__eq__(other)
|
| 55 |
+
|
| 56 |
+
def __str__(self):
|
| 57 |
+
return "Day: " + str(self.Day) + ", " + "Room: " + str(self.Room) + ", Time: " + str(self.Time)
|
model/Room.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Stores data about classroom
|
| 2 |
+
class Room:
|
| 3 |
+
# ID counter used to assign IDs automatically
|
| 4 |
+
_next_room_id = 0
|
| 5 |
+
|
| 6 |
+
# Initializes room data and assign ID to room
|
| 7 |
+
def __init__(self, name, lab, number_of_seats):
|
| 8 |
+
# Returns room ID - automatically assigned
|
| 9 |
+
self.Id = Room._next_room_id
|
| 10 |
+
Room._next_room_id += 1
|
| 11 |
+
# Returns name
|
| 12 |
+
self.Name = name
|
| 13 |
+
# Returns TRUE if room has computers otherwise it returns FALSE
|
| 14 |
+
self.Lab = lab
|
| 15 |
+
# Returns number of seats in room
|
| 16 |
+
self.NumberOfSeats = number_of_seats
|
| 17 |
+
|
| 18 |
+
def __hash__(self):
|
| 19 |
+
return hash(self.Id)
|
| 20 |
+
|
| 21 |
+
# Compares ID's of two objects which represent rooms
|
| 22 |
+
def __eq__(self, other):
|
| 23 |
+
if not isinstance(other, self.__class__):
|
| 24 |
+
return False
|
| 25 |
+
return hash(self) == hash(other)
|
| 26 |
+
|
| 27 |
+
def __ne__(self, other):
|
| 28 |
+
# Not strictly necessary, but to avoid having both x==y and x!=y
|
| 29 |
+
# True at the same time
|
| 30 |
+
return not (self == other)
|
| 31 |
+
|
| 32 |
+
# Restarts ID assigments
|
| 33 |
+
@staticmethod
|
| 34 |
+
def restartIDs() -> None:
|
| 35 |
+
Room._next_room_id = 0
|
model/Schedule.py
ADDED
|
@@ -0,0 +1,438 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .Constant import Constant
|
| 2 |
+
from .CourseClass import CourseClass
|
| 3 |
+
from .Reservation import Reservation
|
| 4 |
+
from .Criteria import Criteria
|
| 5 |
+
from collections import deque
|
| 6 |
+
from random import randrange
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
|
| 10 |
+
# Schedule chromosome
|
| 11 |
+
class Schedule:
|
| 12 |
+
# Initializes chromosomes with configuration block (setup of chromosome)
|
| 13 |
+
def __init__(self, configuration):
|
| 14 |
+
self._configuration = configuration
|
| 15 |
+
# Fitness value of chromosome
|
| 16 |
+
self._fitness = 0
|
| 17 |
+
|
| 18 |
+
# Time-space slots, one entry represent one hour in one classroom
|
| 19 |
+
slots_length = Constant.DAYS_NUM * Constant.DAY_HOURS * self._configuration.numberOfRooms
|
| 20 |
+
self._slots = [[] for _ in range(slots_length)]
|
| 21 |
+
|
| 22 |
+
# Class table for chromosome
|
| 23 |
+
# Used to determine first time-space slot used by class
|
| 24 |
+
self._classes = {}
|
| 25 |
+
|
| 26 |
+
# Flags of class requirements satisfaction
|
| 27 |
+
self._criteria = np.zeros(self._configuration.numberOfCourseClasses * len(Criteria.weights), dtype=bool)
|
| 28 |
+
|
| 29 |
+
self._diversity = 0.0
|
| 30 |
+
self._rank = 0
|
| 31 |
+
|
| 32 |
+
self._convertedObjectives = []
|
| 33 |
+
self._objectives = []
|
| 34 |
+
|
| 35 |
+
def copy(self, c, setup_only):
|
| 36 |
+
if not setup_only:
|
| 37 |
+
self._configuration = c.configuration
|
| 38 |
+
# copy code
|
| 39 |
+
self._slots, self._classes = [row[:] for row in c.slots], {key: value for key, value in c.classes.items()}
|
| 40 |
+
|
| 41 |
+
# copy flags of class requirements
|
| 42 |
+
self._criteria = c.criteria[:]
|
| 43 |
+
|
| 44 |
+
# copy fitness
|
| 45 |
+
self._fitness = c.fitness
|
| 46 |
+
return self
|
| 47 |
+
|
| 48 |
+
return Schedule(c.configuration)
|
| 49 |
+
|
| 50 |
+
# Makes new chromosome with same setup but with randomly chosen code
|
| 51 |
+
def makeNewFromPrototype(self, positions = None):
|
| 52 |
+
# make new chromosome, copy chromosome setup
|
| 53 |
+
new_chromosome = self.copy(self, True)
|
| 54 |
+
new_chromosome_slots, new_chromosome_classes = new_chromosome._slots, new_chromosome._classes
|
| 55 |
+
|
| 56 |
+
# place classes at random position
|
| 57 |
+
classes = self._configuration.courseClasses
|
| 58 |
+
nr = self._configuration.numberOfRooms
|
| 59 |
+
DAYS_NUM, DAY_HOURS = Constant.DAYS_NUM, Constant.DAY_HOURS
|
| 60 |
+
for c in classes:
|
| 61 |
+
# determine random position of class
|
| 62 |
+
dur = c.Duration
|
| 63 |
+
|
| 64 |
+
day = randrange(DAYS_NUM)
|
| 65 |
+
room = randrange(nr)
|
| 66 |
+
time = randrange(DAY_HOURS - dur)
|
| 67 |
+
reservation = Reservation.getReservation(nr, day, time, room)
|
| 68 |
+
|
| 69 |
+
if positions is not None:
|
| 70 |
+
positions.append(day)
|
| 71 |
+
positions.append(room)
|
| 72 |
+
positions.append(time)
|
| 73 |
+
reservation_index = hash(reservation)
|
| 74 |
+
|
| 75 |
+
# fill time-space slots, for each hour of class
|
| 76 |
+
for i in range(dur - 1, -1, -1):
|
| 77 |
+
new_chromosome_slots[reservation_index + i].append(c)
|
| 78 |
+
|
| 79 |
+
# insert in class table of chromosome
|
| 80 |
+
new_chromosome_classes[c] = reservation_index
|
| 81 |
+
|
| 82 |
+
new_chromosome.calculateFitness()
|
| 83 |
+
return new_chromosome
|
| 84 |
+
|
| 85 |
+
# Performs crossover operation using to chromosomes and returns pointer to offspring
|
| 86 |
+
def crossover(self, parent, numberOfCrossoverPoints, crossoverProbability):
|
| 87 |
+
# check probability of crossover operation
|
| 88 |
+
if randrange(100) > crossoverProbability:
|
| 89 |
+
# no crossover, just copy first parent
|
| 90 |
+
return self.copy(self, False)
|
| 91 |
+
|
| 92 |
+
# new chromosome object, copy chromosome setup
|
| 93 |
+
n = self.copy(self, True)
|
| 94 |
+
n_classes, n_slots = n._classes, n._slots
|
| 95 |
+
|
| 96 |
+
classes = self._classes
|
| 97 |
+
course_classes = tuple(classes.keys())
|
| 98 |
+
parent_classes = parent.classes
|
| 99 |
+
parent_course_classes = tuple(parent.classes.keys())
|
| 100 |
+
|
| 101 |
+
# number of classes
|
| 102 |
+
size = len(classes)
|
| 103 |
+
|
| 104 |
+
cp = size * [False]
|
| 105 |
+
|
| 106 |
+
# determine crossover point (randomly)
|
| 107 |
+
for i in range(numberOfCrossoverPoints, 0, -1):
|
| 108 |
+
check_point = False
|
| 109 |
+
while not check_point:
|
| 110 |
+
p = randrange(size)
|
| 111 |
+
if not cp[p]:
|
| 112 |
+
cp[p] = check_point = True
|
| 113 |
+
|
| 114 |
+
# make new code by combining parent codes
|
| 115 |
+
first = randrange(2) == 0
|
| 116 |
+
|
| 117 |
+
for i in range(size):
|
| 118 |
+
if first:
|
| 119 |
+
course_class = course_classes[i]
|
| 120 |
+
dur = course_class.Duration
|
| 121 |
+
reservation_index = classes[course_class]
|
| 122 |
+
# insert class from first parent into new chromosome's class table
|
| 123 |
+
n_classes[course_class] = reservation_index
|
| 124 |
+
# all time-space slots of class are copied
|
| 125 |
+
for j in range(dur - 1, -1, -1):
|
| 126 |
+
n_slots[reservation_index + j].append(course_class)
|
| 127 |
+
else:
|
| 128 |
+
course_class = parent_course_classes[i]
|
| 129 |
+
dur = course_class.Duration
|
| 130 |
+
reservation_index = parent_classes[course_class]
|
| 131 |
+
# insert class from second parent into new chromosome's class table
|
| 132 |
+
n_classes[course_class] = reservation_index
|
| 133 |
+
# all time-space slots of class are copied
|
| 134 |
+
for j in range(dur - 1, -1, -1):
|
| 135 |
+
n_slots[reservation_index + j].append(course_class)
|
| 136 |
+
|
| 137 |
+
# crossover point
|
| 138 |
+
if cp[i]:
|
| 139 |
+
# change source chromosome
|
| 140 |
+
first = not first
|
| 141 |
+
|
| 142 |
+
n.calculateFitness()
|
| 143 |
+
|
| 144 |
+
# return smart pointer to offspring
|
| 145 |
+
return n
|
| 146 |
+
|
| 147 |
+
# Performs crossover operation using to chromosomes and returns pointer to offspring
|
| 148 |
+
def crossovers(self, parent, r1, r2, r3, etaCross, crossoverProbability):
|
| 149 |
+
# number of classes
|
| 150 |
+
size = len(self._classes)
|
| 151 |
+
jrand = randrange(size)
|
| 152 |
+
|
| 153 |
+
nr = self._configuration.numberOfRooms
|
| 154 |
+
DAY_HOURS, DAYS_NUM = Constant.DAY_HOURS, Constant.DAYS_NUM
|
| 155 |
+
|
| 156 |
+
# make new chromosome, copy chromosome setup
|
| 157 |
+
new_chromosome = self.copy(self, True)
|
| 158 |
+
new_chromosome_slots, new_chromosome_classes = new_chromosome._slots, new_chromosome._classes
|
| 159 |
+
classes = self._classes
|
| 160 |
+
course_classes = tuple(classes.keys())
|
| 161 |
+
parent_classes = parent.classes
|
| 162 |
+
parent_course_classes = tuple(parent.classes.keys())
|
| 163 |
+
for i in range(size):
|
| 164 |
+
if randrange(100) > crossoverProbability or i == jrand:
|
| 165 |
+
course_class = course_classes[i]
|
| 166 |
+
reservation1, reservation2 = Reservation.parse(r1.classes[course_class]), Reservation.parse(r2.classes[course_class])
|
| 167 |
+
reservation3 = Reservation.parse(r3.classes[course_class])
|
| 168 |
+
|
| 169 |
+
dur = course_class.Duration
|
| 170 |
+
day = int(reservation3.Day + etaCross * (reservation1.Day - reservation2.Day))
|
| 171 |
+
if day < 0:
|
| 172 |
+
day = 0
|
| 173 |
+
elif day >= DAYS_NUM:
|
| 174 |
+
day = DAYS_NUM - 1
|
| 175 |
+
|
| 176 |
+
room = int(reservation3.Room + etaCross * (reservation1.Room - reservation2.Room))
|
| 177 |
+
if room < 0:
|
| 178 |
+
room = 0
|
| 179 |
+
elif room >= nr:
|
| 180 |
+
room = nr - 1
|
| 181 |
+
|
| 182 |
+
time = int(reservation3.Time + etaCross * (reservation1.Time - reservation2.Time))
|
| 183 |
+
if time < 0:
|
| 184 |
+
time = 0
|
| 185 |
+
elif time >= (DAY_HOURS - dur):
|
| 186 |
+
time = DAY_HOURS - 1 - dur
|
| 187 |
+
|
| 188 |
+
reservation = Reservation.getReservation(nr, day, time, room)
|
| 189 |
+
reservation_index = hash(reservation)
|
| 190 |
+
|
| 191 |
+
# fill time-space slots, for each hour of class
|
| 192 |
+
for j in range(dur - 1, -1, -1):
|
| 193 |
+
new_chromosome_slots[reservation_index + j].append(course_class)
|
| 194 |
+
|
| 195 |
+
# insert in class table of chromosome
|
| 196 |
+
new_chromosome_classes[course_class] = reservation_index
|
| 197 |
+
else:
|
| 198 |
+
course_class = parent_course_classes[i]
|
| 199 |
+
dur = course_class.Duration
|
| 200 |
+
reservation = parent_classes[course_class]
|
| 201 |
+
reservation_index = hash(reservation)
|
| 202 |
+
|
| 203 |
+
# all time-space slots of class are copied
|
| 204 |
+
for j in range(dur - 1, -1, -1):
|
| 205 |
+
new_chromosome_slots[reservation_index + j].append(course_class)
|
| 206 |
+
|
| 207 |
+
# insert class from second parent into new chromosome's class table
|
| 208 |
+
new_chromosome_classes[course_class] = reservation_index
|
| 209 |
+
|
| 210 |
+
new_chromosome.calculateFitness()
|
| 211 |
+
|
| 212 |
+
# return smart pointer to offspring
|
| 213 |
+
return new_chromosome
|
| 214 |
+
|
| 215 |
+
def repair(self, cc1: CourseClass, reservation1_index: int, reservation2: Reservation):
|
| 216 |
+
nr = self._configuration.numberOfRooms
|
| 217 |
+
DAY_HOURS, DAYS_NUM = Constant.DAY_HOURS, Constant.DAYS_NUM
|
| 218 |
+
slots = self._slots
|
| 219 |
+
dur = cc1.Duration
|
| 220 |
+
|
| 221 |
+
for j in range(dur):
|
| 222 |
+
# remove class hour from current time-space slot
|
| 223 |
+
cl = slots[reservation1_index + j]
|
| 224 |
+
while cc1 in cl:
|
| 225 |
+
cl.remove(cc1)
|
| 226 |
+
|
| 227 |
+
# determine position of class randomly
|
| 228 |
+
if reservation2 is None:
|
| 229 |
+
day = randrange(DAYS_NUM)
|
| 230 |
+
room = randrange(nr)
|
| 231 |
+
time = randrange(DAY_HOURS - dur)
|
| 232 |
+
reservation2 = Reservation.getReservation(nr, day, time, room)
|
| 233 |
+
|
| 234 |
+
reservation2_index = hash(reservation2)
|
| 235 |
+
for j in range(dur):
|
| 236 |
+
# move class hour to new time-space slot
|
| 237 |
+
slots[reservation2_index + j].append(cc1)
|
| 238 |
+
|
| 239 |
+
# change entry of class table to point to new time-space slots
|
| 240 |
+
self._classes[cc1] = reservation2_index
|
| 241 |
+
|
| 242 |
+
# Performs mutation on chromosome
|
| 243 |
+
def mutation(self, mutationSize, mutationProbability):
|
| 244 |
+
# check probability of mutation operation
|
| 245 |
+
if randrange(100) > mutationProbability:
|
| 246 |
+
return
|
| 247 |
+
|
| 248 |
+
classes = self._classes
|
| 249 |
+
# number of classes
|
| 250 |
+
numberOfClasses = len(classes)
|
| 251 |
+
course_classes = tuple(classes.keys())
|
| 252 |
+
configuration = self._configuration
|
| 253 |
+
nr = configuration.numberOfRooms
|
| 254 |
+
|
| 255 |
+
# move selected number of classes at random position
|
| 256 |
+
for i in range(mutationSize, 0, -1):
|
| 257 |
+
# select ranom chromosome for movement
|
| 258 |
+
mpos = randrange(numberOfClasses)
|
| 259 |
+
|
| 260 |
+
# current time-space slot used by class
|
| 261 |
+
cc1 = course_classes[mpos]
|
| 262 |
+
reservation1_index = classes[cc1]
|
| 263 |
+
|
| 264 |
+
self.repair(cc1, reservation1_index, None)
|
| 265 |
+
|
| 266 |
+
self.calculateFitness()
|
| 267 |
+
|
| 268 |
+
# Calculates fitness value of chromosome
|
| 269 |
+
def calculateFitness(self):
|
| 270 |
+
|
| 271 |
+
# increment value when criteria violation occurs
|
| 272 |
+
self._objectives = np.zeros(len(Criteria.weights))
|
| 273 |
+
|
| 274 |
+
# chromosome's score
|
| 275 |
+
score = 0
|
| 276 |
+
|
| 277 |
+
criteria, configuration = self._criteria, self._configuration
|
| 278 |
+
items, slots = self._classes.items(), self._slots
|
| 279 |
+
numberOfRooms = configuration.numberOfRooms
|
| 280 |
+
DAY_HOURS, DAYS_NUM = Constant.DAY_HOURS, Constant.DAYS_NUM
|
| 281 |
+
daySize = DAY_HOURS * numberOfRooms
|
| 282 |
+
|
| 283 |
+
ci = 0
|
| 284 |
+
getRoomById = configuration.getRoomById
|
| 285 |
+
|
| 286 |
+
# check criteria and calculate scores for each class in schedule
|
| 287 |
+
for cc, reservation_index in items:
|
| 288 |
+
reservation = Reservation.parse(reservation_index)
|
| 289 |
+
|
| 290 |
+
# coordinate of time-space slot
|
| 291 |
+
day, time, room = reservation.Day, reservation.Time, reservation.Room
|
| 292 |
+
|
| 293 |
+
dur = cc.Duration
|
| 294 |
+
|
| 295 |
+
ro = Criteria.isRoomOverlapped(slots, reservation, dur)
|
| 296 |
+
|
| 297 |
+
# on room overlapping
|
| 298 |
+
criteria[ci + 0] = not ro
|
| 299 |
+
|
| 300 |
+
r = getRoomById(room)
|
| 301 |
+
|
| 302 |
+
# does current room have enough seats
|
| 303 |
+
criteria[ci + 1] = Criteria.isSeatEnough(r, cc)
|
| 304 |
+
|
| 305 |
+
# does current room have computers if they are required
|
| 306 |
+
criteria[ci + 2] = Criteria.isComputerEnough(r, cc)
|
| 307 |
+
|
| 308 |
+
# check overlapping of classes for professors
|
| 309 |
+
timeId = day * daySize + time
|
| 310 |
+
po, go = Criteria.isOverlappedProfStudentGrp(slots, cc, numberOfRooms, timeId)
|
| 311 |
+
|
| 312 |
+
# professors have no overlapping classes?
|
| 313 |
+
criteria[ci + 3] = not po
|
| 314 |
+
|
| 315 |
+
# student groups has no overlapping classes?
|
| 316 |
+
criteria[ci + 4] = not go
|
| 317 |
+
|
| 318 |
+
for i in range(len(self._objectives)):
|
| 319 |
+
if criteria[ci + i]:
|
| 320 |
+
score += 1
|
| 321 |
+
else:
|
| 322 |
+
score += Criteria.weights[i]
|
| 323 |
+
self._objectives[i] += 1 if Criteria.weights[i] > 0 else 2
|
| 324 |
+
|
| 325 |
+
ci += len(Criteria.weights)
|
| 326 |
+
|
| 327 |
+
# calculate fitness value based on score
|
| 328 |
+
self._fitness = score / len(criteria)
|
| 329 |
+
|
| 330 |
+
def getDifference(self, other):
|
| 331 |
+
return (self._criteria ^ other.criteria).sum()
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
def extractPositions(self, positions):
|
| 335 |
+
i = 0
|
| 336 |
+
items = self._classes.items()
|
| 337 |
+
for cc, reservation_index in items:
|
| 338 |
+
reservation = Reservation.parse(reservation_index)
|
| 339 |
+
|
| 340 |
+
positions[i] = reservation.Day
|
| 341 |
+
i += 1
|
| 342 |
+
positions[i] = reservation.Room
|
| 343 |
+
i += 1
|
| 344 |
+
positions[i] = reservation.Time
|
| 345 |
+
i += 1
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
def updatePositions(self, positions):
|
| 349 |
+
DAYS_NUM, DAY_HOURS = Constant.DAYS_NUM, Constant.DAY_HOURS
|
| 350 |
+
nr = self._configuration.numberOfRooms
|
| 351 |
+
i = 0
|
| 352 |
+
items = self._classes.items()
|
| 353 |
+
for cc, reservation1_index in items:
|
| 354 |
+
dur = cc.Duration
|
| 355 |
+
day = abs(int(positions[i]) % DAYS_NUM)
|
| 356 |
+
room = abs(int(positions[i + 1]) % nr)
|
| 357 |
+
time = abs(int(positions[i + 2]) % (DAY_HOURS - dur))
|
| 358 |
+
|
| 359 |
+
reservation2 = Reservation.getReservation(nr, day, time, room)
|
| 360 |
+
self.repair(cc, reservation1_index, reservation2)
|
| 361 |
+
|
| 362 |
+
positions[i] = reservation2.Day
|
| 363 |
+
i += 1
|
| 364 |
+
|
| 365 |
+
positions[i] = reservation2.Room
|
| 366 |
+
i += 1
|
| 367 |
+
|
| 368 |
+
positions[i] = reservation2.Time
|
| 369 |
+
i += 1
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
self.calculateFitness()
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
# Returns fitness value of chromosome
|
| 376 |
+
@property
|
| 377 |
+
def fitness(self):
|
| 378 |
+
return self._fitness
|
| 379 |
+
|
| 380 |
+
@property
|
| 381 |
+
def configuration(self):
|
| 382 |
+
return self._configuration
|
| 383 |
+
|
| 384 |
+
@property
|
| 385 |
+
# Returns reference to table of classes
|
| 386 |
+
def classes(self):
|
| 387 |
+
return self._classes
|
| 388 |
+
|
| 389 |
+
@property
|
| 390 |
+
# Returns array of flags of class requirements satisfaction
|
| 391 |
+
def criteria(self):
|
| 392 |
+
return self._criteria
|
| 393 |
+
|
| 394 |
+
@property
|
| 395 |
+
# Return reference to array of time-space slots
|
| 396 |
+
def slots(self):
|
| 397 |
+
return self._slots
|
| 398 |
+
|
| 399 |
+
@property
|
| 400 |
+
def diversity(self):
|
| 401 |
+
return self._diversity
|
| 402 |
+
|
| 403 |
+
@diversity.setter
|
| 404 |
+
def diversity(self, new_diversity):
|
| 405 |
+
self._diversity = new_diversity
|
| 406 |
+
|
| 407 |
+
@property
|
| 408 |
+
def rank(self):
|
| 409 |
+
return self._rank
|
| 410 |
+
|
| 411 |
+
@rank.setter
|
| 412 |
+
def rank(self, new_rank):
|
| 413 |
+
self._rank = new_rank
|
| 414 |
+
|
| 415 |
+
@property
|
| 416 |
+
def convertedObjectives(self):
|
| 417 |
+
return self._convertedObjectives
|
| 418 |
+
|
| 419 |
+
@property
|
| 420 |
+
def objectives(self):
|
| 421 |
+
return self._objectives
|
| 422 |
+
|
| 423 |
+
def resizeConvertedObjectives(self, numObj):
|
| 424 |
+
self._convertedObjectives = numObj * [0]
|
| 425 |
+
|
| 426 |
+
def clone(self):
|
| 427 |
+
return self.copy(self, False)
|
| 428 |
+
|
| 429 |
+
def dominates(self, other):
|
| 430 |
+
better = False
|
| 431 |
+
for f, obj in enumerate(self.objectives):
|
| 432 |
+
if obj > other.objectives[f]:
|
| 433 |
+
return False
|
| 434 |
+
|
| 435 |
+
if obj < other.objectives[f]:
|
| 436 |
+
better = True
|
| 437 |
+
|
| 438 |
+
return better
|
model/StudentsGroup.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Stores data about student group
|
| 2 |
+
class StudentsGroup:
|
| 3 |
+
# Initializes student group data
|
| 4 |
+
def __init__(self, id, numberOfStudents):
|
| 5 |
+
self.Id = id
|
| 6 |
+
# self.Name = name
|
| 7 |
+
self.NumberOfStudents = numberOfStudents
|
| 8 |
+
self.CourseClasses = []
|
| 9 |
+
|
| 10 |
+
# Bind group to class
|
| 11 |
+
def addClass(self, course_class):
|
| 12 |
+
self.CourseClasses.append(course_class)
|
| 13 |
+
|
| 14 |
+
def __hash__(self):
|
| 15 |
+
return hash(self.Id)
|
| 16 |
+
|
| 17 |
+
# Compares ID's of two objects which represent student groups
|
| 18 |
+
def __eq__(self, other):
|
| 19 |
+
if not isinstance(other, self.__class__):
|
| 20 |
+
return False
|
| 21 |
+
return hash(self) == hash(other)
|
| 22 |
+
|
| 23 |
+
def __ne__(self, other):
|
| 24 |
+
# Not strictly necessary, but to avoid having both x==y and x!=y
|
| 25 |
+
# True at the same time
|
| 26 |
+
return not (self == other)
|
model/__pycache__/Configuration.cpython-39.pyc
ADDED
|
Binary file (5.18 kB). View file
|
|
|
model/__pycache__/Constant.cpython-39.pyc
ADDED
|
Binary file (373 Bytes). View file
|
|
|
model/__pycache__/Course.cpython-39.pyc
ADDED
|
Binary file (475 Bytes). View file
|
|
|
model/__pycache__/CourseClass.cpython-39.pyc
ADDED
|
Binary file (1.74 kB). View file
|
|
|
model/__pycache__/Criteria.cpython-39.pyc
ADDED
|
Binary file (1.63 kB). View file
|
|
|
model/__pycache__/Professor.cpython-39.pyc
ADDED
|
Binary file (1.12 kB). View file
|
|
|
model/__pycache__/Reservation.cpython-39.pyc
ADDED
|
Binary file (2.19 kB). View file
|
|
|
model/__pycache__/Room.cpython-39.pyc
ADDED
|
Binary file (1.18 kB). View file
|
|
|
model/__pycache__/Schedule.cpython-39.pyc
ADDED
|
Binary file (9.32 kB). View file
|
|
|
model/__pycache__/StudentsGroup.cpython-39.pyc
ADDED
|
Binary file (1.16 kB). View file
|
|
|
processing data/GaSchedule3.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
[{"prof": {"id": 174, "name": "Dao Tran Hoang Chau"}}, {"course": {"id": 1, "name": "Functional Programming"}}, {"group": {"id": 1, "size": 10}}, {"class": {"professor": 174, "course": 1, "duration": 3, "group": 1, "lab": false}}, {"prof": {"id": 119, "name": "Tran Manh Ha"}}, {"course": {"id": 2, "name": "Operating Systems"}}, {"group": {"id": 2, "size": 90}}, {"class": {"professor": 119, "course": 2, "duration": 3, "group": 2, "lab": false}}, {"group": {"id": 3, "size": 30}}, {"class": {"professor": 119, "course": 2, "duration": 4, "group": 3, "lab": true}}, {"prof": {"id": 213, "name": "Nguyen Tien Dung"}}, {"group": {"id": 4, "size": 30}}, {"class": {"professor": 213, "course": 2, "duration": 4, "group": 4, "lab": true}}, {"group": {"id": 5, "size": 30}}, {"class": {"professor": 213, "course": 2, "duration": 4, "group": 5, "lab": true}}, {"prof": {"id": 74, "name": "Nguyen Van Sinh"}}, {"course": {"id": 3, "name": "IT Project Management"}}, {"group": {"id": 6, "size": 35}}, {"class": {"professor": 74, "course": 3, "duration": 3, "group": 6, "lab": false}}, {"group": {"id": 7, "size": 35}}, {"class": {"professor": 74, "course": 3, "duration": 4, "group": 7, "lab": true}}, {"prof": {"id": 84, "name": "Le Hai Duong"}}, {"course": {"id": 4, "name": "Introduction to Computing"}}, {"group": {"id": 8, "size": 35}}, {"class": {"professor": 84, "course": 4, "duration": 3, "group": 8, "lab": false}}, {"prof": {"id": 19, "name": "Huynh Kha Tu"}}, {"course": {"id": 5, "name": "Digital Logic Design"}}, {"group": {"id": 9, "size": 90}}, {"class": {"professor": 19, "course": 5, "duration": 3, "group": 9, "lab": false}}, {"prof": {"id": 182, "name": "Tran Thanh Tung"}}, {"course": {"id": 6, "name": "Object-Oriented Programming"}}, {"group": {"id": 10, "size": 90}}, {"class": {"professor": 182, "course": 6, "duration": 3, "group": 10, "lab": false}}, {"group": {"id": 11, "size": 30}}, {"class": {"professor": 213, "course": 6, "duration": 4, "group": 11, "lab": true}}, {"group": {"id": 12, "size": 30}}, {"class": {"professor": 213, "course": 6, "duration": 4, "group": 12, "lab": true}}, {"group": {"id": 13, "size": 30}}, {"class": {"professor": 213, "course": 6, "duration": 4, "group": 13, "lab": true}}, {"prof": {"id": 349, "name": "Nguyen Thi Thanh Sang"}}, {"course": {"id": 7, "name": "Software Engineering"}}, {"group": {"id": 14, "size": 70}}, {"class": {"professor": 349, "course": 7, "duration": 3, "group": 14, "lab": false}}, {"group": {"id": 15, "size": 35}}, {"class": {"professor": 349, "course": 7, "duration": 4, "group": 15, "lab": true}}, {"group": {"id": 16, "size": 35}}, {"class": {"professor": 349, "course": 7, "duration": 4, "group": 16, "lab": true}}, {"prof": {"id": 62, "name": "Vo Thi Luu Phuong"}}, {"course": {"id": 8, "name": "Computer Networks"}}, {"group": {"id": 17, "size": 70}}, {"class": {"professor": 62, "course": 8, "duration": 3, "group": 17, "lab": false}}, {"group": {"id": 18, "size": 35}}, {"class": {"professor": 62, "course": 8, "duration": 4, "group": 18, "lab": true}}, {"group": {"id": 19, "size": 35}}, {"class": {"professor": 62, "course": 8, "duration": 4, "group": 19, "lab": true}}, {"prof": {"id": 60, "name": "Ly Tu Nga"}}, {"group": {"id": 20, "size": 20}}, {"class": {"professor": 60, "course": 8, "duration": 4, "group": 20, "lab": true}}, {"course": {"id": 9, "name": "Web Application Development"}}, {"group": {"id": 21, "size": 15}}, {"class": {"professor": 74, "course": 9, "duration": 3, "group": 21, "lab": false}}, {"group": {"id": 22, "size": 15}}, {"class": {"professor": 74, "course": 9, "duration": 4, "group": 22, "lab": true}}, {"group": {"id": 23, "size": 20}}, {"class": {"professor": 213, "course": 9, "duration": 4, "group": 23, "lab": true}}, {"prof": {"id": 36, "name": "Le Thanh Son"}}, {"course": {"id": 10, "name": "Net-Centric Programming"}}, {"group": {"id": 24, "size": 17}}, {"class": {"professor": 36, "course": 10, "duration": 3, "group": 24, "lab": false}}, {"group": {"id": 25, "size": 17}}, {"class": {"professor": 36, "course": 10, "duration": 4, "group": 25, "lab": true}}, {"course": {"id": 11, "name": "Digital Logic Design Laboratory"}}, {"group": {"id": 26, "size": 15}}, {"class": {"professor": 60, "course": 11, "duration": 4, "group": 26, "lab": false}}, {"group": {"id": 27, "size": 15}}, {"class": {"professor": 60, "course": 11, "duration": 4, "group": 27, "lab": false}}, {"group": {"id": 28, "size": 15}}, {"class": {"professor": 60, "course": 11, "duration": 4, "group": 28, "lab": false}}, {"group": {"id": 29, "size": 15}}, {"class": {"professor": 60, "course": 11, "duration": 4, "group": 29, "lab": false}}, {"group": {"id": 30, "size": 15}}, {"class": {"professor": 60, "course": 11, "duration": 4, "group": 30, "lab": false}}, {"course": {"id": 12, "name": "Entrepreneurship"}}, {"group": {"id": 31, "size": 80}}, {"class": {"professor": 174, "course": 12, "duration": 3, "group": 31, "lab": false}}, {"course": {"id": 13, "name": "System & Network Administration"}}, {"group": {"id": 32, "size": 35}}, {"class": {"professor": 36, "course": 13, "duration": 3, "group": 32, "lab": false}}, {"group": {"id": 33, "size": 35}}, {"class": {"professor": 36, "course": 13, "duration": 4, "group": 33, "lab": true}}, {"prof": {"id": 184, "name": "Ha Viet Uyen Synh"}}, {"course": {"id": 14, "name": "Theoretical Models in Computing"}}, {"group": {"id": 34, "size": 80}}, {"class": {"professor": 184, "course": 14, "duration": 3, "group": 34, "lab": false}}, {"group": {"id": 35, "size": 26}}, {"class": {"professor": 184, "course": 14, "duration": 4, "group": 35, "lab": true}}, {"group": {"id": 36, "size": 28}}, {"class": {"professor": 184, "course": 14, "duration": 4, "group": 36, "lab": true}}, {"course": {"id": 15, "name": "Discrete Mathematics"}}, {"group": {"id": 37, "size": 80}}, {"class": {"professor": 74, "course": 15, "duration": 3, "group": 37, "lab": false}}, {"room": {"name": "A1.309", "lab": false, "size": 90}}, {"room": {"name": "L107", "lab": false, "size": 40}}, {"room": {"name": "LA1.605", "lab": true, "size": 35}}, {"room": {"name": "LA1.607", "lab": true, "size": 35}}]
|
processing data/data.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{'class': {'course': 15, 'duration': 3, 'professor': 1}}{'room': {'lab': False, 'name': 'A1', 'size': 60}}{'prof': {'id': 1, 'name': 'Tran Tien Khoa'}}{'course': {'id': 26, 'name': 'Financial Accounting (2)'}}{'course': {'id': 15, 'name': 'Principles of Marketing (1)'}}{'class': {'course': 15, 'duration': 4, 'professor': 1}}{'class': {'course': 26, 'duration': 4, 'professor': 2}}{'class': {'course': 26, 'duration': 3, 'professor': 2}}{'room': {'lab': True, 'name': 'A2', 'size': 24}}{'prof': {'id': 2, 'name': 'Le Ngoc Anh Khoa'}}
|
processing data/reprocessing.ipynb
ADDED
|
@@ -0,0 +1,589 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": 1,
|
| 6 |
+
"id": "3745035b",
|
| 7 |
+
"metadata": {},
|
| 8 |
+
"outputs": [
|
| 9 |
+
{
|
| 10 |
+
"data": {
|
| 11 |
+
"text/html": [
|
| 12 |
+
"<div>\n",
|
| 13 |
+
"<style scoped>\n",
|
| 14 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 15 |
+
" vertical-align: middle;\n",
|
| 16 |
+
" }\n",
|
| 17 |
+
"\n",
|
| 18 |
+
" .dataframe tbody tr th {\n",
|
| 19 |
+
" vertical-align: top;\n",
|
| 20 |
+
" }\n",
|
| 21 |
+
"\n",
|
| 22 |
+
" .dataframe thead th {\n",
|
| 23 |
+
" text-align: right;\n",
|
| 24 |
+
" }\n",
|
| 25 |
+
"</style>\n",
|
| 26 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 27 |
+
" <thead>\n",
|
| 28 |
+
" <tr style=\"text-align: right;\">\n",
|
| 29 |
+
" <th></th>\n",
|
| 30 |
+
" <th>MaMH</th>\n",
|
| 31 |
+
" <th>TenMH</th>\n",
|
| 32 |
+
" <th>NhomTo</th>\n",
|
| 33 |
+
" <th>ToTH</th>\n",
|
| 34 |
+
" <th>TenLop</th>\n",
|
| 35 |
+
" <th>TongSoSV</th>\n",
|
| 36 |
+
" <th>ThuKieuSo</th>\n",
|
| 37 |
+
" <th>TietBD</th>\n",
|
| 38 |
+
" <th>SoTiet</th>\n",
|
| 39 |
+
" <th>MaPH</th>\n",
|
| 40 |
+
" <th>DSTuanHoc</th>\n",
|
| 41 |
+
" <th>MaNV</th>\n",
|
| 42 |
+
" <th>TenDayDuNV</th>\n",
|
| 43 |
+
" <th>IsTKBDaXep</th>\n",
|
| 44 |
+
" <th>MaDV</th>\n",
|
| 45 |
+
" <th>TenDV</th>\n",
|
| 46 |
+
" <th>TenDVEg</th>\n",
|
| 47 |
+
" <th>NgayBD</th>\n",
|
| 48 |
+
" <th>NgayKT</th>\n",
|
| 49 |
+
" </tr>\n",
|
| 50 |
+
" </thead>\n",
|
| 51 |
+
" <tbody>\n",
|
| 52 |
+
" <tr>\n",
|
| 53 |
+
" <th>0</th>\n",
|
| 54 |
+
" <td>1</td>\n",
|
| 55 |
+
" <td>Functional Programming</td>\n",
|
| 56 |
+
" <td>1</td>\n",
|
| 57 |
+
" <td>NaN</td>\n",
|
| 58 |
+
" <td>ITIT16UN11, ITIT16UN21</td>\n",
|
| 59 |
+
" <td>10</td>\n",
|
| 60 |
+
" <td>2</td>\n",
|
| 61 |
+
" <td>1</td>\n",
|
| 62 |
+
" <td>3</td>\n",
|
| 63 |
+
" <td>LA1.605</td>\n",
|
| 64 |
+
" <td>-2345678901234567---</td>\n",
|
| 65 |
+
" <td>174</td>\n",
|
| 66 |
+
" <td>Dao Tran Hoang Chau</td>\n",
|
| 67 |
+
" <td>0</td>\n",
|
| 68 |
+
" <td>IT IT</td>\n",
|
| 69 |
+
" <td>Computer Science & Engineering</td>\n",
|
| 70 |
+
" <td>Computer Science & Engineering</td>\n",
|
| 71 |
+
" <td>42989</td>\n",
|
| 72 |
+
" <td>43094</td>\n",
|
| 73 |
+
" </tr>\n",
|
| 74 |
+
" <tr>\n",
|
| 75 |
+
" <th>1</th>\n",
|
| 76 |
+
" <td>2</td>\n",
|
| 77 |
+
" <td>Operating Systems</td>\n",
|
| 78 |
+
" <td>1</td>\n",
|
| 79 |
+
" <td>NaN</td>\n",
|
| 80 |
+
" <td>ITIT15CS1, ITIT15IU11, ITIT15IU21, ITIT15IU31</td>\n",
|
| 81 |
+
" <td>90</td>\n",
|
| 82 |
+
" <td>6</td>\n",
|
| 83 |
+
" <td>6</td>\n",
|
| 84 |
+
" <td>3</td>\n",
|
| 85 |
+
" <td>A1.309</td>\n",
|
| 86 |
+
" <td>-2345678901234567---</td>\n",
|
| 87 |
+
" <td>119</td>\n",
|
| 88 |
+
" <td>Tran Manh Ha</td>\n",
|
| 89 |
+
" <td>0</td>\n",
|
| 90 |
+
" <td>IT IT</td>\n",
|
| 91 |
+
" <td>Computer Science & Engineering</td>\n",
|
| 92 |
+
" <td>Computer Science & Engineering</td>\n",
|
| 93 |
+
" <td>42993</td>\n",
|
| 94 |
+
" <td>43098</td>\n",
|
| 95 |
+
" </tr>\n",
|
| 96 |
+
" <tr>\n",
|
| 97 |
+
" <th>2</th>\n",
|
| 98 |
+
" <td>2</td>\n",
|
| 99 |
+
" <td>Operating Systems</td>\n",
|
| 100 |
+
" <td>1</td>\n",
|
| 101 |
+
" <td>1.0</td>\n",
|
| 102 |
+
" <td>ITIT15CS1, ITIT15IU11, ITIT15IU21, ITIT15IU31</td>\n",
|
| 103 |
+
" <td>30</td>\n",
|
| 104 |
+
" <td>2</td>\n",
|
| 105 |
+
" <td>6</td>\n",
|
| 106 |
+
" <td>4</td>\n",
|
| 107 |
+
" <td>LA1.605</td>\n",
|
| 108 |
+
" <td>-----678901234------</td>\n",
|
| 109 |
+
" <td>119</td>\n",
|
| 110 |
+
" <td>Tran Manh Ha</td>\n",
|
| 111 |
+
" <td>0</td>\n",
|
| 112 |
+
" <td>IT IT</td>\n",
|
| 113 |
+
" <td>Computer Science & Engineering</td>\n",
|
| 114 |
+
" <td>Computer Science & Engineering</td>\n",
|
| 115 |
+
" <td>43017</td>\n",
|
| 116 |
+
" <td>43073</td>\n",
|
| 117 |
+
" </tr>\n",
|
| 118 |
+
" <tr>\n",
|
| 119 |
+
" <th>3</th>\n",
|
| 120 |
+
" <td>2</td>\n",
|
| 121 |
+
" <td>Operating Systems</td>\n",
|
| 122 |
+
" <td>1</td>\n",
|
| 123 |
+
" <td>2.0</td>\n",
|
| 124 |
+
" <td>ITIT15CS1, ITIT15IU11, ITIT15IU21, ITIT15IU31</td>\n",
|
| 125 |
+
" <td>30</td>\n",
|
| 126 |
+
" <td>2</td>\n",
|
| 127 |
+
" <td>1</td>\n",
|
| 128 |
+
" <td>4</td>\n",
|
| 129 |
+
" <td>LA1.608</td>\n",
|
| 130 |
+
" <td>-----678901234------</td>\n",
|
| 131 |
+
" <td>213</td>\n",
|
| 132 |
+
" <td>Nguyen Tien Dung</td>\n",
|
| 133 |
+
" <td>0</td>\n",
|
| 134 |
+
" <td>IT IT</td>\n",
|
| 135 |
+
" <td>Computer Science & Engineering</td>\n",
|
| 136 |
+
" <td>Computer Science & Engineering</td>\n",
|
| 137 |
+
" <td>43017</td>\n",
|
| 138 |
+
" <td>43073</td>\n",
|
| 139 |
+
" </tr>\n",
|
| 140 |
+
" <tr>\n",
|
| 141 |
+
" <th>4</th>\n",
|
| 142 |
+
" <td>2</td>\n",
|
| 143 |
+
" <td>Operating Systems</td>\n",
|
| 144 |
+
" <td>1</td>\n",
|
| 145 |
+
" <td>3.0</td>\n",
|
| 146 |
+
" <td>ITIT15CS1, ITIT15IU11, ITIT15IU21, ITIT15IU31</td>\n",
|
| 147 |
+
" <td>30</td>\n",
|
| 148 |
+
" <td>2</td>\n",
|
| 149 |
+
" <td>6</td>\n",
|
| 150 |
+
" <td>4</td>\n",
|
| 151 |
+
" <td>LA1.607</td>\n",
|
| 152 |
+
" <td>-----678901234------</td>\n",
|
| 153 |
+
" <td>213</td>\n",
|
| 154 |
+
" <td>Nguyen Tien Dung</td>\n",
|
| 155 |
+
" <td>0</td>\n",
|
| 156 |
+
" <td>IT IT</td>\n",
|
| 157 |
+
" <td>Computer Science & Engineering</td>\n",
|
| 158 |
+
" <td>Computer Science & Engineering</td>\n",
|
| 159 |
+
" <td>43017</td>\n",
|
| 160 |
+
" <td>43073</td>\n",
|
| 161 |
+
" </tr>\n",
|
| 162 |
+
" </tbody>\n",
|
| 163 |
+
"</table>\n",
|
| 164 |
+
"</div>"
|
| 165 |
+
],
|
| 166 |
+
"text/plain": [
|
| 167 |
+
" MaMH TenMH NhomTo ToTH \\\n",
|
| 168 |
+
"0 1 Functional Programming 1 NaN \n",
|
| 169 |
+
"1 2 Operating Systems 1 NaN \n",
|
| 170 |
+
"2 2 Operating Systems 1 1.0 \n",
|
| 171 |
+
"3 2 Operating Systems 1 2.0 \n",
|
| 172 |
+
"4 2 Operating Systems 1 3.0 \n",
|
| 173 |
+
"\n",
|
| 174 |
+
" TenLop TongSoSV ThuKieuSo TietBD \\\n",
|
| 175 |
+
"0 ITIT16UN11, ITIT16UN21 10 2 1 \n",
|
| 176 |
+
"1 ITIT15CS1, ITIT15IU11, ITIT15IU21, ITIT15IU31 90 6 6 \n",
|
| 177 |
+
"2 ITIT15CS1, ITIT15IU11, ITIT15IU21, ITIT15IU31 30 2 6 \n",
|
| 178 |
+
"3 ITIT15CS1, ITIT15IU11, ITIT15IU21, ITIT15IU31 30 2 1 \n",
|
| 179 |
+
"4 ITIT15CS1, ITIT15IU11, ITIT15IU21, ITIT15IU31 30 2 6 \n",
|
| 180 |
+
"\n",
|
| 181 |
+
" SoTiet MaPH DSTuanHoc MaNV TenDayDuNV \\\n",
|
| 182 |
+
"0 3 LA1.605 -2345678901234567--- 174 Dao Tran Hoang Chau \n",
|
| 183 |
+
"1 3 A1.309 -2345678901234567--- 119 Tran Manh Ha \n",
|
| 184 |
+
"2 4 LA1.605 -----678901234------ 119 Tran Manh Ha \n",
|
| 185 |
+
"3 4 LA1.608 -----678901234------ 213 Nguyen Tien Dung \n",
|
| 186 |
+
"4 4 LA1.607 -----678901234------ 213 Nguyen Tien Dung \n",
|
| 187 |
+
"\n",
|
| 188 |
+
" IsTKBDaXep MaDV TenDV \\\n",
|
| 189 |
+
"0 0 IT IT Computer Science & Engineering \n",
|
| 190 |
+
"1 0 IT IT Computer Science & Engineering \n",
|
| 191 |
+
"2 0 IT IT Computer Science & Engineering \n",
|
| 192 |
+
"3 0 IT IT Computer Science & Engineering \n",
|
| 193 |
+
"4 0 IT IT Computer Science & Engineering \n",
|
| 194 |
+
"\n",
|
| 195 |
+
" TenDVEg NgayBD NgayKT \n",
|
| 196 |
+
"0 Computer Science & Engineering 42989 43094 \n",
|
| 197 |
+
"1 Computer Science & Engineering 42993 43098 \n",
|
| 198 |
+
"2 Computer Science & Engineering 43017 43073 \n",
|
| 199 |
+
"3 Computer Science & Engineering 43017 43073 \n",
|
| 200 |
+
"4 Computer Science & Engineering 43017 43073 "
|
| 201 |
+
]
|
| 202 |
+
},
|
| 203 |
+
"execution_count": 1,
|
| 204 |
+
"metadata": {},
|
| 205 |
+
"output_type": "execute_result"
|
| 206 |
+
}
|
| 207 |
+
],
|
| 208 |
+
"source": [
|
| 209 |
+
"import pandas as pd\n",
|
| 210 |
+
"import numpy as np\n",
|
| 211 |
+
"df = pd.read_csv(\"TKB HKI 2017-2018.csv\")\n",
|
| 212 |
+
"df.head()"
|
| 213 |
+
]
|
| 214 |
+
},
|
| 215 |
+
{
|
| 216 |
+
"cell_type": "code",
|
| 217 |
+
"execution_count": 2,
|
| 218 |
+
"id": "a605911f",
|
| 219 |
+
"metadata": {},
|
| 220 |
+
"outputs": [
|
| 221 |
+
{
|
| 222 |
+
"data": {
|
| 223 |
+
"text/html": [
|
| 224 |
+
"<div>\n",
|
| 225 |
+
"<style scoped>\n",
|
| 226 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 227 |
+
" vertical-align: middle;\n",
|
| 228 |
+
" }\n",
|
| 229 |
+
"\n",
|
| 230 |
+
" .dataframe tbody tr th {\n",
|
| 231 |
+
" vertical-align: top;\n",
|
| 232 |
+
" }\n",
|
| 233 |
+
"\n",
|
| 234 |
+
" .dataframe thead th {\n",
|
| 235 |
+
" text-align: right;\n",
|
| 236 |
+
" }\n",
|
| 237 |
+
"</style>\n",
|
| 238 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 239 |
+
" <thead>\n",
|
| 240 |
+
" <tr style=\"text-align: right;\">\n",
|
| 241 |
+
" <th></th>\n",
|
| 242 |
+
" <th>group_id</th>\n",
|
| 243 |
+
" <th>course_id</th>\n",
|
| 244 |
+
" <th>course_name</th>\n",
|
| 245 |
+
" <th>Lab</th>\n",
|
| 246 |
+
" <th>size</th>\n",
|
| 247 |
+
" <th>duration</th>\n",
|
| 248 |
+
" <th>room</th>\n",
|
| 249 |
+
" <th>prof_id</th>\n",
|
| 250 |
+
" <th>prof_name</th>\n",
|
| 251 |
+
" </tr>\n",
|
| 252 |
+
" </thead>\n",
|
| 253 |
+
" <tbody>\n",
|
| 254 |
+
" <tr>\n",
|
| 255 |
+
" <th>0</th>\n",
|
| 256 |
+
" <td>1</td>\n",
|
| 257 |
+
" <td>1</td>\n",
|
| 258 |
+
" <td>Functional Programming</td>\n",
|
| 259 |
+
" <td>False</td>\n",
|
| 260 |
+
" <td>10</td>\n",
|
| 261 |
+
" <td>3</td>\n",
|
| 262 |
+
" <td>LA1.605</td>\n",
|
| 263 |
+
" <td>174</td>\n",
|
| 264 |
+
" <td>Dao Tran Hoang Chau</td>\n",
|
| 265 |
+
" </tr>\n",
|
| 266 |
+
" <tr>\n",
|
| 267 |
+
" <th>1</th>\n",
|
| 268 |
+
" <td>2</td>\n",
|
| 269 |
+
" <td>2</td>\n",
|
| 270 |
+
" <td>Operating Systems</td>\n",
|
| 271 |
+
" <td>False</td>\n",
|
| 272 |
+
" <td>90</td>\n",
|
| 273 |
+
" <td>3</td>\n",
|
| 274 |
+
" <td>A1.309</td>\n",
|
| 275 |
+
" <td>119</td>\n",
|
| 276 |
+
" <td>Tran Manh Ha</td>\n",
|
| 277 |
+
" </tr>\n",
|
| 278 |
+
" <tr>\n",
|
| 279 |
+
" <th>2</th>\n",
|
| 280 |
+
" <td>3</td>\n",
|
| 281 |
+
" <td>2</td>\n",
|
| 282 |
+
" <td>Operating Systems</td>\n",
|
| 283 |
+
" <td>True</td>\n",
|
| 284 |
+
" <td>30</td>\n",
|
| 285 |
+
" <td>4</td>\n",
|
| 286 |
+
" <td>LA1.605</td>\n",
|
| 287 |
+
" <td>119</td>\n",
|
| 288 |
+
" <td>Tran Manh Ha</td>\n",
|
| 289 |
+
" </tr>\n",
|
| 290 |
+
" <tr>\n",
|
| 291 |
+
" <th>3</th>\n",
|
| 292 |
+
" <td>4</td>\n",
|
| 293 |
+
" <td>2</td>\n",
|
| 294 |
+
" <td>Operating Systems</td>\n",
|
| 295 |
+
" <td>True</td>\n",
|
| 296 |
+
" <td>30</td>\n",
|
| 297 |
+
" <td>4</td>\n",
|
| 298 |
+
" <td>LA1.608</td>\n",
|
| 299 |
+
" <td>213</td>\n",
|
| 300 |
+
" <td>Nguyen Tien Dung</td>\n",
|
| 301 |
+
" </tr>\n",
|
| 302 |
+
" <tr>\n",
|
| 303 |
+
" <th>4</th>\n",
|
| 304 |
+
" <td>5</td>\n",
|
| 305 |
+
" <td>2</td>\n",
|
| 306 |
+
" <td>Operating Systems</td>\n",
|
| 307 |
+
" <td>True</td>\n",
|
| 308 |
+
" <td>30</td>\n",
|
| 309 |
+
" <td>4</td>\n",
|
| 310 |
+
" <td>LA1.607</td>\n",
|
| 311 |
+
" <td>213</td>\n",
|
| 312 |
+
" <td>Nguyen Tien Dung</td>\n",
|
| 313 |
+
" </tr>\n",
|
| 314 |
+
" </tbody>\n",
|
| 315 |
+
"</table>\n",
|
| 316 |
+
"</div>"
|
| 317 |
+
],
|
| 318 |
+
"text/plain": [
|
| 319 |
+
" group_id course_id course_name Lab size duration \\\n",
|
| 320 |
+
"0 1 1 Functional Programming False 10 3 \n",
|
| 321 |
+
"1 2 2 Operating Systems False 90 3 \n",
|
| 322 |
+
"2 3 2 Operating Systems True 30 4 \n",
|
| 323 |
+
"3 4 2 Operating Systems True 30 4 \n",
|
| 324 |
+
"4 5 2 Operating Systems True 30 4 \n",
|
| 325 |
+
"\n",
|
| 326 |
+
" room prof_id prof_name \n",
|
| 327 |
+
"0 LA1.605 174 Dao Tran Hoang Chau \n",
|
| 328 |
+
"1 A1.309 119 Tran Manh Ha \n",
|
| 329 |
+
"2 LA1.605 119 Tran Manh Ha \n",
|
| 330 |
+
"3 LA1.608 213 Nguyen Tien Dung \n",
|
| 331 |
+
"4 LA1.607 213 Nguyen Tien Dung "
|
| 332 |
+
]
|
| 333 |
+
},
|
| 334 |
+
"execution_count": 2,
|
| 335 |
+
"metadata": {},
|
| 336 |
+
"output_type": "execute_result"
|
| 337 |
+
}
|
| 338 |
+
],
|
| 339 |
+
"source": [
|
| 340 |
+
"df = pd.DataFrame(df)\n",
|
| 341 |
+
"df1 = df[['MaMH', 'TenMH', 'ToTH', 'TongSoSV', 'SoTiet', 'MaPH','MaNV', 'TenDayDuNV']]\n",
|
| 342 |
+
"df1 = df1.rename(columns={'MaMH': 'course_id', 'TenMH': 'course_name','ToTH': 'Lab', 'TongSoSV': 'size', 'SoTiet': 'duration', 'MaPH': 'room', 'MaNV': 'prof_id', 'TenDayDuNV': 'prof_name' })\n",
|
| 343 |
+
"df1['Lab'] = df1['Lab'].fillna(0)\n",
|
| 344 |
+
"df1['Lab'] = df1['Lab'].astype(str)\n",
|
| 345 |
+
"df1['prof_id'] = df1['prof_id'].astype(int)\n",
|
| 346 |
+
"df1['course_id'] = df1['course_id'].astype(int)\n",
|
| 347 |
+
"\n",
|
| 348 |
+
"for index, row in df1.iterrows():\n",
|
| 349 |
+
" if row['Lab'] == '1.0' or row['Lab'] == '2.0' or row['Lab'] == '3.0' or row['Lab'] == '4.0':\n",
|
| 350 |
+
" df1.at[index, 'Lab'] = 'True'\n",
|
| 351 |
+
" else:\n",
|
| 352 |
+
" df1.at[index, 'Lab'] = ''\n",
|
| 353 |
+
" \n",
|
| 354 |
+
"df1['Lab'] = df1['Lab'].astype(bool)\n",
|
| 355 |
+
"df1.reset_index(inplace=True)\n",
|
| 356 |
+
"df1 = df1.rename(columns={'index': 'group_id'})\n",
|
| 357 |
+
"df1['group_id'] = np.arange(1, len(df) + 1)\n",
|
| 358 |
+
"df1.head()"
|
| 359 |
+
]
|
| 360 |
+
},
|
| 361 |
+
{
|
| 362 |
+
"cell_type": "code",
|
| 363 |
+
"execution_count": 3,
|
| 364 |
+
"id": "80f6ef63",
|
| 365 |
+
"metadata": {},
|
| 366 |
+
"outputs": [
|
| 367 |
+
{
|
| 368 |
+
"data": {
|
| 369 |
+
"text/html": [
|
| 370 |
+
"<div>\n",
|
| 371 |
+
"<style scoped>\n",
|
| 372 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 373 |
+
" vertical-align: middle;\n",
|
| 374 |
+
" }\n",
|
| 375 |
+
"\n",
|
| 376 |
+
" .dataframe tbody tr th {\n",
|
| 377 |
+
" vertical-align: top;\n",
|
| 378 |
+
" }\n",
|
| 379 |
+
"\n",
|
| 380 |
+
" .dataframe thead th {\n",
|
| 381 |
+
" text-align: right;\n",
|
| 382 |
+
" }\n",
|
| 383 |
+
"</style>\n",
|
| 384 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 385 |
+
" <thead>\n",
|
| 386 |
+
" <tr style=\"text-align: right;\">\n",
|
| 387 |
+
" <th></th>\n",
|
| 388 |
+
" <th>room</th>\n",
|
| 389 |
+
" <th>size</th>\n",
|
| 390 |
+
" <th>Lab</th>\n",
|
| 391 |
+
" </tr>\n",
|
| 392 |
+
" </thead>\n",
|
| 393 |
+
" <tbody>\n",
|
| 394 |
+
" <tr>\n",
|
| 395 |
+
" <th>0</th>\n",
|
| 396 |
+
" <td>A1.309</td>\n",
|
| 397 |
+
" <td>90</td>\n",
|
| 398 |
+
" <td>False</td>\n",
|
| 399 |
+
" </tr>\n",
|
| 400 |
+
" <tr>\n",
|
| 401 |
+
" <th>1</th>\n",
|
| 402 |
+
" <td>L107</td>\n",
|
| 403 |
+
" <td>40</td>\n",
|
| 404 |
+
" <td>False</td>\n",
|
| 405 |
+
" </tr>\n",
|
| 406 |
+
" <tr>\n",
|
| 407 |
+
" <th>2</th>\n",
|
| 408 |
+
" <td>LA1.605</td>\n",
|
| 409 |
+
" <td>35</td>\n",
|
| 410 |
+
" <td>True</td>\n",
|
| 411 |
+
" </tr>\n",
|
| 412 |
+
" <tr>\n",
|
| 413 |
+
" <th>3</th>\n",
|
| 414 |
+
" <td>LA1.607</td>\n",
|
| 415 |
+
" <td>35</td>\n",
|
| 416 |
+
" <td>True</td>\n",
|
| 417 |
+
" </tr>\n",
|
| 418 |
+
" </tbody>\n",
|
| 419 |
+
"</table>\n",
|
| 420 |
+
"</div>"
|
| 421 |
+
],
|
| 422 |
+
"text/plain": [
|
| 423 |
+
" room size Lab\n",
|
| 424 |
+
"0 A1.309 90 False\n",
|
| 425 |
+
"1 L107 40 False\n",
|
| 426 |
+
"2 LA1.605 35 True\n",
|
| 427 |
+
"3 LA1.607 35 True"
|
| 428 |
+
]
|
| 429 |
+
},
|
| 430 |
+
"execution_count": 3,
|
| 431 |
+
"metadata": {},
|
| 432 |
+
"output_type": "execute_result"
|
| 433 |
+
}
|
| 434 |
+
],
|
| 435 |
+
"source": [
|
| 436 |
+
"room_default = [['A1.309', 90, 0],\n",
|
| 437 |
+
" ['L107', 40, 0],\n",
|
| 438 |
+
" ['LA1.605', 35, 1],\n",
|
| 439 |
+
" ['LA1.607', 35, 1],\n",
|
| 440 |
+
"]\n",
|
| 441 |
+
"room_columns = ['room', 'size', 'Lab']\n",
|
| 442 |
+
"df_room = pd.DataFrame(room_default, columns=room_columns)\n",
|
| 443 |
+
"df_room['Lab'] = df_room['Lab'].astype(str)\n",
|
| 444 |
+
"for index, row in df_room.iterrows():\n",
|
| 445 |
+
" if row['Lab'] == '1':\n",
|
| 446 |
+
" df_room.at[index, 'Lab'] = 'True'\n",
|
| 447 |
+
" else:\n",
|
| 448 |
+
" df_room.at[index, 'Lab'] = ''\n",
|
| 449 |
+
"df_room['Lab'] = df_room['Lab'].astype(bool)\n",
|
| 450 |
+
"df_room"
|
| 451 |
+
]
|
| 452 |
+
},
|
| 453 |
+
{
|
| 454 |
+
"cell_type": "code",
|
| 455 |
+
"execution_count": 4,
|
| 456 |
+
"id": "d6b5b6a2",
|
| 457 |
+
"metadata": {},
|
| 458 |
+
"outputs": [],
|
| 459 |
+
"source": [
|
| 460 |
+
"import json\n",
|
| 461 |
+
"\n",
|
| 462 |
+
"# create list of dictionaries representing each object in the JSON file\n",
|
| 463 |
+
"objects = []\n",
|
| 464 |
+
"\n",
|
| 465 |
+
"for index, row in df1.iterrows():\n",
|
| 466 |
+
" if row['prof_id'] != '':\n",
|
| 467 |
+
" # create professor object\n",
|
| 468 |
+
" prof = {\n",
|
| 469 |
+
" \"prof\": {\n",
|
| 470 |
+
" \"id\": row['prof_id'],\n",
|
| 471 |
+
" \"name\": row['prof_name']\n",
|
| 472 |
+
" }\n",
|
| 473 |
+
" }\n",
|
| 474 |
+
" if prof not in objects:\n",
|
| 475 |
+
" objects.append(prof)\n",
|
| 476 |
+
"\n",
|
| 477 |
+
" if row['course_id'] != '':\n",
|
| 478 |
+
" # create course object\n",
|
| 479 |
+
" course = {\n",
|
| 480 |
+
" \"course\": {\n",
|
| 481 |
+
" \"id\": row['course_id'],\n",
|
| 482 |
+
" \"name\": row['course_name']\n",
|
| 483 |
+
" }\n",
|
| 484 |
+
" }\n",
|
| 485 |
+
" if course not in objects:\n",
|
| 486 |
+
" objects.append(course)\n",
|
| 487 |
+
"\n",
|
| 488 |
+
" if row['group_id'] != '':\n",
|
| 489 |
+
" # create room object\n",
|
| 490 |
+
" group = {\n",
|
| 491 |
+
" \"group\": {\n",
|
| 492 |
+
" \"id\": row['group_id'],\n",
|
| 493 |
+
" \"size\": row['size']\n",
|
| 494 |
+
" }\n",
|
| 495 |
+
" }\n",
|
| 496 |
+
" if group not in objects:\n",
|
| 497 |
+
" objects.append(group)\n",
|
| 498 |
+
" \n",
|
| 499 |
+
" if row['prof_id'] != '' and row['course_id'] != '':\n",
|
| 500 |
+
" # create class object\n",
|
| 501 |
+
" class_ = {\n",
|
| 502 |
+
" \"class\": {\n",
|
| 503 |
+
" \"professor\": row['prof_id'],\n",
|
| 504 |
+
" \"course\": row['course_id'],\n",
|
| 505 |
+
" \"duration\": row['duration'],\n",
|
| 506 |
+
" \"group\": row['group_id'],\n",
|
| 507 |
+
" \"lab\": row['Lab']\n",
|
| 508 |
+
" }\n",
|
| 509 |
+
" }\n",
|
| 510 |
+
" if class_ not in objects:\n",
|
| 511 |
+
" objects.append(class_)\n",
|
| 512 |
+
" \n",
|
| 513 |
+
"for index, row in df_room.iterrows():\n",
|
| 514 |
+
" if row['room'] != '':\n",
|
| 515 |
+
" # create room object\n",
|
| 516 |
+
" room = {\n",
|
| 517 |
+
" \"room\": {\n",
|
| 518 |
+
" \"name\": row['room'],\n",
|
| 519 |
+
" \"lab\": row['Lab'],\n",
|
| 520 |
+
" \"size\": row['size']\n",
|
| 521 |
+
" }\n",
|
| 522 |
+
" }\n",
|
| 523 |
+
" objects.append(room) \n",
|
| 524 |
+
" \n",
|
| 525 |
+
"# create JSON object with list of objects\n",
|
| 526 |
+
"json_data = json.dumps(objects, sort_keys=False)\n",
|
| 527 |
+
"\n",
|
| 528 |
+
"# write JSON object to file\n",
|
| 529 |
+
"with open('GaSchedule3.json', 'w') as f:\n",
|
| 530 |
+
" f.write(json_data)"
|
| 531 |
+
]
|
| 532 |
+
},
|
| 533 |
+
{
|
| 534 |
+
"cell_type": "code",
|
| 535 |
+
"execution_count": 5,
|
| 536 |
+
"id": "f7a33b15",
|
| 537 |
+
"metadata": {},
|
| 538 |
+
"outputs": [
|
| 539 |
+
{
|
| 540 |
+
"data": {
|
| 541 |
+
"text/plain": [
|
| 542 |
+
"'[{\"prof\": {\"id\": 174, \"name\": \"Dao Tran Hoang Chau\"}}, {\"course\": {\"id\": 1, \"name\": \"Functional Programming\"}}, {\"group\": {\"id\": 1, \"size\": 10}}, {\"class\": {\"professor\": 174, \"course\": 1, \"duration\": 3, \"group\": 1, \"lab\": false}}, {\"prof\": {\"id\": 119, \"name\": \"Tran Manh Ha\"}}, {\"course\": {\"id\": 2, \"name\": \"Operating Systems\"}}, {\"group\": {\"id\": 2, \"size\": 90}}, {\"class\": {\"professor\": 119, \"course\": 2, \"duration\": 3, \"group\": 2, \"lab\": false}}, {\"group\": {\"id\": 3, \"size\": 30}}, {\"class\": {\"professor\": 119, \"course\": 2, \"duration\": 4, \"group\": 3, \"lab\": true}}, {\"prof\": {\"id\": 213, \"name\": \"Nguyen Tien Dung\"}}, {\"group\": {\"id\": 4, \"size\": 30}}, {\"class\": {\"professor\": 213, \"course\": 2, \"duration\": 4, \"group\": 4, \"lab\": true}}, {\"group\": {\"id\": 5, \"size\": 30}}, {\"class\": {\"professor\": 213, \"course\": 2, \"duration\": 4, \"group\": 5, \"lab\": true}}, {\"prof\": {\"id\": 74, \"name\": \"Nguyen Van Sinh\"}}, {\"course\": {\"id\": 3, \"name\": \"IT Project Management\"}}, {\"group\": {\"id\": 6, \"size\": 35}}, {\"class\": {\"professor\": 74, \"course\": 3, \"duration\": 3, \"group\": 6, \"lab\": false}}, {\"group\": {\"id\": 7, \"size\": 35}}, {\"class\": {\"professor\": 74, \"course\": 3, \"duration\": 4, \"group\": 7, \"lab\": true}}, {\"prof\": {\"id\": 84, \"name\": \"Le Hai Duong\"}}, {\"course\": {\"id\": 4, \"name\": \"Introduction to Computing\"}}, {\"group\": {\"id\": 8, \"size\": 35}}, {\"class\": {\"professor\": 84, \"course\": 4, \"duration\": 3, \"group\": 8, \"lab\": false}}, {\"prof\": {\"id\": 19, \"name\": \"Huynh Kha Tu\"}}, {\"course\": {\"id\": 5, \"name\": \"Digital Logic Design\"}}, {\"group\": {\"id\": 9, \"size\": 90}}, {\"class\": {\"professor\": 19, \"course\": 5, \"duration\": 3, \"group\": 9, \"lab\": false}}, {\"prof\": {\"id\": 182, \"name\": \"Tran Thanh Tung\"}}, {\"course\": {\"id\": 6, \"name\": \"Object-Oriented Programming\"}}, {\"group\": {\"id\": 10, \"size\": 90}}, {\"class\": {\"professor\": 182, \"course\": 6, \"duration\": 3, \"group\": 10, \"lab\": false}}, {\"group\": {\"id\": 11, \"size\": 30}}, {\"class\": {\"professor\": 213, \"course\": 6, \"duration\": 4, \"group\": 11, \"lab\": true}}, {\"group\": {\"id\": 12, \"size\": 30}}, {\"class\": {\"professor\": 213, \"course\": 6, \"duration\": 4, \"group\": 12, \"lab\": true}}, {\"group\": {\"id\": 13, \"size\": 30}}, {\"class\": {\"professor\": 213, \"course\": 6, \"duration\": 4, \"group\": 13, \"lab\": true}}, {\"prof\": {\"id\": 349, \"name\": \"Nguyen Thi Thanh Sang\"}}, {\"course\": {\"id\": 7, \"name\": \"Software Engineering\"}}, {\"group\": {\"id\": 14, \"size\": 70}}, {\"class\": {\"professor\": 349, \"course\": 7, \"duration\": 3, \"group\": 14, \"lab\": false}}, {\"group\": {\"id\": 15, \"size\": 35}}, {\"class\": {\"professor\": 349, \"course\": 7, \"duration\": 4, \"group\": 15, \"lab\": true}}, {\"group\": {\"id\": 16, \"size\": 35}}, {\"class\": {\"professor\": 349, \"course\": 7, \"duration\": 4, \"group\": 16, \"lab\": true}}, {\"prof\": {\"id\": 62, \"name\": \"Vo Thi Luu Phuong\"}}, {\"course\": {\"id\": 8, \"name\": \"Computer Networks\"}}, {\"group\": {\"id\": 17, \"size\": 70}}, {\"class\": {\"professor\": 62, \"course\": 8, \"duration\": 3, \"group\": 17, \"lab\": false}}, {\"group\": {\"id\": 18, \"size\": 35}}, {\"class\": {\"professor\": 62, \"course\": 8, \"duration\": 4, \"group\": 18, \"lab\": true}}, {\"group\": {\"id\": 19, \"size\": 35}}, {\"class\": {\"professor\": 62, \"course\": 8, \"duration\": 4, \"group\": 19, \"lab\": true}}, {\"prof\": {\"id\": 60, \"name\": \"Ly Tu Nga\"}}, {\"group\": {\"id\": 20, \"size\": 20}}, {\"class\": {\"professor\": 60, \"course\": 8, \"duration\": 4, \"group\": 20, \"lab\": true}}, {\"course\": {\"id\": 9, \"name\": \"Web Application Development\"}}, {\"group\": {\"id\": 21, \"size\": 15}}, {\"class\": {\"professor\": 74, \"course\": 9, \"duration\": 3, \"group\": 21, \"lab\": false}}, {\"group\": {\"id\": 22, \"size\": 15}}, {\"class\": {\"professor\": 74, \"course\": 9, \"duration\": 4, \"group\": 22, \"lab\": true}}, {\"group\": {\"id\": 23, \"size\": 20}}, {\"class\": {\"professor\": 213, \"course\": 9, \"duration\": 4, \"group\": 23, \"lab\": true}}, {\"prof\": {\"id\": 36, \"name\": \"Le Thanh Son\"}}, {\"course\": {\"id\": 10, \"name\": \"Net-Centric Programming\"}}, {\"group\": {\"id\": 24, \"size\": 17}}, {\"class\": {\"professor\": 36, \"course\": 10, \"duration\": 3, \"group\": 24, \"lab\": false}}, {\"group\": {\"id\": 25, \"size\": 17}}, {\"class\": {\"professor\": 36, \"course\": 10, \"duration\": 4, \"group\": 25, \"lab\": true}}, {\"course\": {\"id\": 11, \"name\": \"Digital Logic Design Laboratory\"}}, {\"group\": {\"id\": 26, \"size\": 15}}, {\"class\": {\"professor\": 60, \"course\": 11, \"duration\": 4, \"group\": 26, \"lab\": false}}, {\"group\": {\"id\": 27, \"size\": 15}}, {\"class\": {\"professor\": 60, \"course\": 11, \"duration\": 4, \"group\": 27, \"lab\": false}}, {\"group\": {\"id\": 28, \"size\": 15}}, {\"class\": {\"professor\": 60, \"course\": 11, \"duration\": 4, \"group\": 28, \"lab\": false}}, {\"group\": {\"id\": 29, \"size\": 15}}, {\"class\": {\"professor\": 60, \"course\": 11, \"duration\": 4, \"group\": 29, \"lab\": false}}, {\"group\": {\"id\": 30, \"size\": 15}}, {\"class\": {\"professor\": 60, \"course\": 11, \"duration\": 4, \"group\": 30, \"lab\": false}}, {\"course\": {\"id\": 12, \"name\": \"Entrepreneurship\"}}, {\"group\": {\"id\": 31, \"size\": 80}}, {\"class\": {\"professor\": 174, \"course\": 12, \"duration\": 3, \"group\": 31, \"lab\": false}}, {\"course\": {\"id\": 13, \"name\": \"System & Network Administration\"}}, {\"group\": {\"id\": 32, \"size\": 35}}, {\"class\": {\"professor\": 36, \"course\": 13, \"duration\": 3, \"group\": 32, \"lab\": false}}, {\"group\": {\"id\": 33, \"size\": 35}}, {\"class\": {\"professor\": 36, \"course\": 13, \"duration\": 4, \"group\": 33, \"lab\": true}}, {\"prof\": {\"id\": 184, \"name\": \"Ha Viet Uyen Synh\"}}, {\"course\": {\"id\": 14, \"name\": \"Theoretical Models in Computing\"}}, {\"group\": {\"id\": 34, \"size\": 80}}, {\"class\": {\"professor\": 184, \"course\": 14, \"duration\": 3, \"group\": 34, \"lab\": false}}, {\"group\": {\"id\": 35, \"size\": 26}}, {\"class\": {\"professor\": 184, \"course\": 14, \"duration\": 4, \"group\": 35, \"lab\": true}}, {\"group\": {\"id\": 36, \"size\": 28}}, {\"class\": {\"professor\": 184, \"course\": 14, \"duration\": 4, \"group\": 36, \"lab\": true}}, {\"course\": {\"id\": 15, \"name\": \"Discrete Mathematics\"}}, {\"group\": {\"id\": 37, \"size\": 80}}, {\"class\": {\"professor\": 74, \"course\": 15, \"duration\": 3, \"group\": 37, \"lab\": false}}, {\"room\": {\"name\": \"A1.309\", \"lab\": false, \"size\": 90}}, {\"room\": {\"name\": \"L107\", \"lab\": false, \"size\": 40}}, {\"room\": {\"name\": \"LA1.605\", \"lab\": true, \"size\": 35}}, {\"room\": {\"name\": \"LA1.607\", \"lab\": true, \"size\": 35}}]'"
|
| 543 |
+
]
|
| 544 |
+
},
|
| 545 |
+
"execution_count": 5,
|
| 546 |
+
"metadata": {},
|
| 547 |
+
"output_type": "execute_result"
|
| 548 |
+
}
|
| 549 |
+
],
|
| 550 |
+
"source": [
|
| 551 |
+
"json_data"
|
| 552 |
+
]
|
| 553 |
+
},
|
| 554 |
+
{
|
| 555 |
+
"cell_type": "code",
|
| 556 |
+
"execution_count": null,
|
| 557 |
+
"id": "ac354530",
|
| 558 |
+
"metadata": {},
|
| 559 |
+
"outputs": [],
|
| 560 |
+
"source": []
|
| 561 |
+
}
|
| 562 |
+
],
|
| 563 |
+
"metadata": {
|
| 564 |
+
"kernelspec": {
|
| 565 |
+
"display_name": "Python 3",
|
| 566 |
+
"language": "python",
|
| 567 |
+
"name": "python3"
|
| 568 |
+
},
|
| 569 |
+
"language_info": {
|
| 570 |
+
"codemirror_mode": {
|
| 571 |
+
"name": "ipython",
|
| 572 |
+
"version": 3
|
| 573 |
+
},
|
| 574 |
+
"file_extension": ".py",
|
| 575 |
+
"mimetype": "text/x-python",
|
| 576 |
+
"name": "python",
|
| 577 |
+
"nbconvert_exporter": "python",
|
| 578 |
+
"pygments_lexer": "ipython3",
|
| 579 |
+
"version": "3.9.7"
|
| 580 |
+
},
|
| 581 |
+
"vscode": {
|
| 582 |
+
"interpreter": {
|
| 583 |
+
"hash": "9510137371fb582fb03f4a48cfea1312dabd94192ffed7cc50b24473a7d892dd"
|
| 584 |
+
}
|
| 585 |
+
}
|
| 586 |
+
},
|
| 587 |
+
"nbformat": 4,
|
| 588 |
+
"nbformat_minor": 5
|
| 589 |
+
}
|