This commit is contained in:
SowinskiBraeden committed 2022-11-07 08:56:13 -08:00
1 parent 37b961745d
commit 086b1db518
6 files changed
+150 -36

No files matched your search

+2 -1
View File
@@ -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()
+7
View File
@@ -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
}
+25 -11
View File
@@ -1,13 +1,15 @@
#!/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')
@eel.expose @eel.expose
def start( def start(
raw_file_data: str, raw_file_data: str,
@@ -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
View File
@@ -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>
+42 -5
View File
@@ -1,10 +1,15 @@
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;
let classCap = document.getElementById("class_cap").value; let classCap = document.getElementById("class_cap").value;
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
View File
@@ -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;
} }