update
This commit is contained in:
6 files changed
+146
-32
No files matched your search
@@ -6,7 +6,8 @@ import docx
|
|||||||
def putMasterTimetable(timetable: dict) -> None:
|
def putMasterTimetable(timetable: dict) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def putScheduleToWord(courses: dict, student: dict, output_dir: str='../../output/final/student_schedules') -> None:
|
def putScheduleToWord(courses: dict, student: dict, output_dir: str='./output/final/student_schedules') -> None:
|
||||||
|
|
||||||
# create an instance of a word doc.
|
# create an instance of a word doc.
|
||||||
doc = docx.Document()
|
doc = docx.Document()
|
||||||
|
|
||||||
|
|||||||
@@ -17,3 +17,10 @@ class Error:
|
|||||||
def __init__(self, title: str, description: str):
|
def __init__(self, title: str, description: str):
|
||||||
self.Title = title
|
self.Title = title
|
||||||
self.Description = description
|
self.Description = description
|
||||||
|
|
||||||
|
# return dict version of self
|
||||||
|
def toDict(self) -> dict:
|
||||||
|
return {
|
||||||
|
"Title": self.Title,
|
||||||
|
"Description": self.Description
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env python3.11
|
#!/usr/bin/env python3.11
|
||||||
import eel
|
import eel
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
from app.generator import generateScheduleV3
|
from app.generator import generateScheduleV3
|
||||||
from app.util.globals import Error
|
from app.util.globals import Error
|
||||||
from app.util.getCourses import getCourses
|
from app.util.getCourses import getCourses
|
||||||
from app.util.getStudents import getStudents
|
from app.util.getStudents import getStudents
|
||||||
|
from app.util.convertRawData import putScheduleToWord, putMasterTimetable
|
||||||
|
|
||||||
eel.init('template')
|
eel.init('template')
|
||||||
|
|
||||||
@@ -15,8 +17,9 @@ def start(
|
|||||||
class_cap: int,
|
class_cap: int,
|
||||||
block_class_limit: int,
|
block_class_limit: int,
|
||||||
total_blocks: int,
|
total_blocks: int,
|
||||||
) -> Error:
|
) -> dict:
|
||||||
|
|
||||||
|
eel.post_data('Ensuring Directories Exists...')
|
||||||
# Ensure output paths exists
|
# Ensure output paths exists
|
||||||
if not os.path.exists('output'): os.makedirs('output')
|
if not os.path.exists('output'): os.makedirs('output')
|
||||||
if not os.path.exists('output/temp'): os.makedirs('output/temp')
|
if not os.path.exists('output/temp'): os.makedirs('output/temp')
|
||||||
@@ -26,9 +29,9 @@ def start(
|
|||||||
|
|
||||||
raw_data_dir = './output/temp/course_selection_data.csv'
|
raw_data_dir = './output/temp/course_selection_data.csv'
|
||||||
|
|
||||||
|
eel.post_data('Saving raw data to local file...')
|
||||||
# save raw file data to local file
|
# save raw file data to local file
|
||||||
with open(raw_data_dir, 'w') as raw_file:
|
with open(raw_data_dir, 'w') as raw_file:
|
||||||
raw_file_data = raw_file_data.replace('\n', '')
|
|
||||||
raw_file.write(raw_file_data)
|
raw_file.write(raw_file_data)
|
||||||
|
|
||||||
# Ensure params are of correct type
|
# Ensure params are of correct type
|
||||||
@@ -37,8 +40,7 @@ def start(
|
|||||||
block_class_limit = int(block_class_limit)
|
block_class_limit = int(block_class_limit)
|
||||||
total_blocks = int(total_blocks)
|
total_blocks = int(total_blocks)
|
||||||
|
|
||||||
err = None
|
eel.post_data('Collecting student information...')
|
||||||
|
|
||||||
# call pre-algorithm functions read raw data into a processable format
|
# call pre-algorithm functions read raw data into a processable format
|
||||||
students = getStudents(
|
students = getStudents(
|
||||||
raw_data_dir,
|
raw_data_dir,
|
||||||
@@ -46,14 +48,16 @@ def start(
|
|||||||
totalBlocks=total_blocks,
|
totalBlocks=total_blocks,
|
||||||
log_dir='./output/raw/students.json'
|
log_dir='./output/raw/students.json'
|
||||||
)
|
)
|
||||||
|
eel.post_data('Collecting course information...')
|
||||||
courses = getCourses(
|
courses = getCourses(
|
||||||
raw_data_dir,
|
raw_data_dir,
|
||||||
log=True,
|
log=True,
|
||||||
log_dir='./output/raw/courses.json'
|
log_dir='./output/raw/courses.json'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
eel.post_data('Generating timetables...')
|
||||||
# call algorithm to sort data
|
# call algorithm to sort data
|
||||||
_, err = generateScheduleV3(
|
master_timetable, err = generateScheduleV3(
|
||||||
students,
|
students,
|
||||||
courses,
|
courses,
|
||||||
minReq=min_req,
|
minReq=min_req,
|
||||||
@@ -64,12 +68,22 @@ def start(
|
|||||||
conflictsDir='./output/raw/conflicts.json'
|
conflictsDir='./output/raw/conflicts.json'
|
||||||
)
|
)
|
||||||
|
|
||||||
print(err)
|
|
||||||
|
if err is not None:
|
||||||
|
eel.post_data(f'An error has occured while generating the timetable: {err.Title}')
|
||||||
|
return err.toDict()
|
||||||
|
|
||||||
|
eel.post_data('Gathering latest data...')()
|
||||||
|
# Get updated students
|
||||||
|
with open('./output/raw/students.json', 'r') as studentFile: students = json.load(studentFile)
|
||||||
|
|
||||||
|
eel.post_data('Writing timetables to .docx files...')
|
||||||
|
# call post-algorithm functions to present sorted data
|
||||||
|
for student in students:
|
||||||
|
putScheduleToWord(courses, student, './output/final/student_schedules')
|
||||||
|
|
||||||
# TODO: log master_timetable
|
# TODO: log master_timetable
|
||||||
|
|
||||||
# TODO: call post-algorithm functions to present sorted data
|
return err.toDict() if err is not None else None
|
||||||
|
|
||||||
return err
|
|
||||||
|
|
||||||
eel.start('index.html', size=(800, 1000))
|
eel.start('index.html', size=(800, 1000))
|
||||||
+12
-5
@@ -9,8 +9,8 @@
|
|||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;500;600&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;500;600&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<script type="text/javascript" src="script.js"></script>
|
|
||||||
<script type="text/javascript" src="/eel.js"></script>
|
<script type="text/javascript" src="/eel.js"></script>
|
||||||
|
<script type="text/javascript" src="script.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="background">
|
<div class="background">
|
||||||
@@ -21,15 +21,18 @@
|
|||||||
<h3>Schedule Generator</h3>
|
<h3>Schedule Generator</h3>
|
||||||
<label for="input_file">Input File (.csv)</label>
|
<label for="input_file">Input File (.csv)</label>
|
||||||
<input
|
<input
|
||||||
|
required
|
||||||
aria-describedby="inputGroup-sizing-default"
|
aria-describedby="inputGroup-sizing-default"
|
||||||
aria-label="Default"
|
aria-label="Default"
|
||||||
name="input_file"
|
name="input_file"
|
||||||
id="input_file"
|
id="input_file"
|
||||||
type="file"
|
type="file"
|
||||||
|
accept=".csv"
|
||||||
>
|
>
|
||||||
|
|
||||||
<label for="classrooms">Number of classrooms (20-100)</label>
|
<label for="classrooms">Number of classrooms (20-100)</label>
|
||||||
<input
|
<input
|
||||||
|
required
|
||||||
aria-describedby="inputGroup-sizing-default"
|
aria-describedby="inputGroup-sizing-default"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
aria-label="Default"
|
aria-label="Default"
|
||||||
@@ -43,6 +46,7 @@
|
|||||||
|
|
||||||
<label for="min_req">Minimum students per class</label>
|
<label for="min_req">Minimum students per class</label>
|
||||||
<input
|
<input
|
||||||
|
required
|
||||||
aria-describedby="inputGroup-sizing-default"
|
aria-describedby="inputGroup-sizing-default"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
aria-label="Default"
|
aria-label="Default"
|
||||||
@@ -56,6 +60,7 @@
|
|||||||
|
|
||||||
<label for="class_cap">Maximum students per class</label>
|
<label for="class_cap">Maximum students per class</label>
|
||||||
<input
|
<input
|
||||||
|
required
|
||||||
aria-describedby="inputGroup-sizing-default"
|
aria-describedby="inputGroup-sizing-default"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
aria-label="Default"
|
aria-label="Default"
|
||||||
@@ -69,6 +74,7 @@
|
|||||||
|
|
||||||
<label for="total_blocks">Total blocks (10 or 8)</label>
|
<label for="total_blocks">Total blocks (10 or 8)</label>
|
||||||
<input
|
<input
|
||||||
|
required
|
||||||
aria-describedby="inputGroup-sizing-default"
|
aria-describedby="inputGroup-sizing-default"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
aria-label="Default"
|
aria-label="Default"
|
||||||
@@ -82,13 +88,14 @@
|
|||||||
>
|
>
|
||||||
<!-- step="2" prevents a number like 9 -->
|
<!-- step="2" prevents a number like 9 -->
|
||||||
|
|
||||||
<button id="startBtn" onclick="start()">Generate</button>
|
<button id="startBtn" class="btn" onclick="start()">Generate</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="processing" class="processing">
|
<div id="processing" class="processing">
|
||||||
<div id="myProgress">
|
<h3 id="processing-header">Processing...</h3>
|
||||||
<div id="myBar"></div>
|
<div id="loader" class="loader"></div>
|
||||||
</div>
|
<ol id="progress"></ol>
|
||||||
|
<button id="retryBtn" class="btn" onclick="retry()">Again</button>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+40
-3
@@ -1,3 +1,5 @@
|
|||||||
|
function e(n) {return n != undefined && n != null && n != ""}
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
let file = document.getElementById("input_file").files[0];
|
let file = document.getElementById("input_file").files[0];
|
||||||
let minReq = document.getElementById("min_req").value;
|
let minReq = document.getElementById("min_req").value;
|
||||||
@@ -5,6 +7,9 @@ function start() {
|
|||||||
let blockClassLimit = document.getElementById("classrooms").value;
|
let blockClassLimit = document.getElementById("classrooms").value;
|
||||||
let totalBlocks = document.getElementById("total_blocks").value;
|
let totalBlocks = document.getElementById("total_blocks").value;
|
||||||
|
|
||||||
|
// ensure data is passed before continuing
|
||||||
|
if (!e(file) || !e(minReq) || !e(classCap) || !e(blockClassLimit) || !e(totalBlocks)) return alert('Please ensure all fields aren\'t empty.');
|
||||||
|
|
||||||
// start animation
|
// start animation
|
||||||
document.getElementById('main').style.display = 'none';
|
document.getElementById('main').style.display = 'none';
|
||||||
document.getElementById('processing').style.display = 'block';
|
document.getElementById('processing').style.display = 'block';
|
||||||
@@ -12,7 +17,7 @@ function start() {
|
|||||||
let reader = new FileReader()
|
let reader = new FileReader()
|
||||||
reader.addEventListener("load", () => {
|
reader.addEventListener("load", () => {
|
||||||
let raw = reader.result;
|
let raw = reader.result;
|
||||||
eel.start(raw, minReq, classCap, blockClassLimit, totalBlocks)(start_callback);
|
eel.start(raw, minReq, classCap, blockClassLimit, totalBlocks)(finish_callback);
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
@@ -20,6 +25,38 @@ function start() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function start_callback(error) {
|
eel.expose(post_data);
|
||||||
console.log(error, 'done')
|
function post_data(msg) {
|
||||||
|
let newMessage = document.createElement('li');
|
||||||
|
newMessage.innerText = msg;
|
||||||
|
newMessage.className = 'nostyle'
|
||||||
|
document.getElementById('progress').appendChild(newMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
function finish_callback(error) {
|
||||||
|
if (error) {
|
||||||
|
post_data(`Error: ${error.Title}`);
|
||||||
|
post_data(error.Description);
|
||||||
|
} else {
|
||||||
|
document.getElementById('processing-header').innerText = 'Finished Processing!';
|
||||||
|
let data = document.getElementById('progress');
|
||||||
|
while (data.firstChild) {
|
||||||
|
data.removeChild(data.firstChild);
|
||||||
|
}
|
||||||
|
post_data('Individual student schedules can be found in the \'/output/final/student_schedules/\' folder and the master timetable as well as student with conflicts can be found in the \'/output/final/\' folder.')
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('loader').style.display = 'none';
|
||||||
|
document.getElementById('retryBtn').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function retry() {
|
||||||
|
// start animation
|
||||||
|
document.getElementById('processing').style.display = 'none';
|
||||||
|
document.getElementById('main').style.display = 'block';
|
||||||
|
document.getElementById('loader').style.display = 'block';
|
||||||
|
let data = document.getElementById('progress');
|
||||||
|
while (data.firstChild) {
|
||||||
|
data.removeChild(data.firstChild);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+62
-14
@@ -63,15 +63,15 @@ body{
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
.main h3{
|
.main h3{
|
||||||
font-size: 32px;
|
font-size: 26px;
|
||||||
font-weight: 500;
|
font-weight: 1000;
|
||||||
line-height: 42px;
|
line-height: 42px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
label{
|
label{
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 30px;
|
margin-top: 12px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
@@ -82,16 +82,44 @@ label{
|
|||||||
background-color: rgba(255,255,255,0.07);
|
background-color: rgba(255,255,255,0.07);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
margin-top: 8px;
|
margin-top: 0px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
::placeholder{
|
::placeholder{
|
||||||
color: #e5e5e5;
|
color: #e5e5e5;
|
||||||
}
|
}
|
||||||
.main button{
|
.processing{
|
||||||
|
display: none;
|
||||||
|
height: 600px;
|
||||||
|
width: 400px;
|
||||||
|
background-color: rgba(255,255,255,0.13);
|
||||||
|
position: absolute;
|
||||||
|
transform: translate(-50%,-50%);
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
border-radius: 10px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 2px solid rgba(255,255,255,0.1);
|
||||||
|
box-shadow: 0 0 40px rgba(8,7,16,0.6);
|
||||||
|
padding: 50px 35px;
|
||||||
|
}
|
||||||
|
.processing *{
|
||||||
|
font-family: 'Poppins',sans-serif;
|
||||||
|
color: #ffffff;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.processing h3{
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 42px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
transition: 0.05s;
|
transition: 0.05s;
|
||||||
margin-top: 50px;
|
margin-top: 20px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
color: #080710;
|
color: #080710;
|
||||||
@@ -101,17 +129,37 @@ label{
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.processing {
|
.loader {
|
||||||
|
margin-left: 95px;
|
||||||
|
margin-top: 40px;
|
||||||
|
border: 16px solid #f3f3f3;
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top: 16px solid #3498db;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
-webkit-animation: spin 2s linear infinite; /* Safari */
|
||||||
|
animation: spin 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.nostyle {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#retryBtn {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#myProgress {
|
/* Safari */
|
||||||
width: 100%;
|
@-webkit-keyframes spin {
|
||||||
background-color: #ddd;
|
0% { -webkit-transform: rotate(0deg); }
|
||||||
|
100% { -webkit-transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
#myBar {
|
@keyframes spin {
|
||||||
width: 1%;
|
0% { transform: rotate(0deg); }
|
||||||
height: 30px;
|
100% { transform: rotate(360deg); }
|
||||||
background-color: #04AA6D;
|
}
|
||||||
|
|
||||||
|
#progress {
|
||||||
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user