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

No files matched your search

+2 -1
View File
@@ -6,7 +6,8 @@ import docx
def putMasterTimetable(timetable: dict) -> None:
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.
doc = docx.Document()
+7
View File
@@ -17,3 +17,10 @@ class Error:
def __init__(self, title: str, description: str):
self.Title = title
self.Description = description
# return dict version of self
def toDict(self) -> dict:
return {
"Title": self.Title,
"Description": self.Description
}
+23 -9
View File
@@ -1,10 +1,12 @@
#!/usr/bin/env python3.11
import eel
import os
import json
from app.generator import generateScheduleV3
from app.util.globals import Error
from app.util.getCourses import getCourses
from app.util.getStudents import getStudents
from app.util.convertRawData import putScheduleToWord, putMasterTimetable
eel.init('template')
@@ -15,8 +17,9 @@ def start(
class_cap: int,
block_class_limit: int,
total_blocks: int,
) -> Error:
) -> dict:
eel.post_data('Ensuring Directories Exists...')
# Ensure output paths exists
if not os.path.exists('output'): os.makedirs('output')
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'
eel.post_data('Saving raw data to local file...')
# save raw file data to local file
with open(raw_data_dir, 'w') as raw_file:
raw_file_data = raw_file_data.replace('\n', '')
raw_file.write(raw_file_data)
# Ensure params are of correct type
@@ -37,8 +40,7 @@ def start(
block_class_limit = int(block_class_limit)
total_blocks = int(total_blocks)
err = None
eel.post_data('Collecting student information...')
# call pre-algorithm functions read raw data into a processable format
students = getStudents(
raw_data_dir,
@@ -46,14 +48,16 @@ def start(
totalBlocks=total_blocks,
log_dir='./output/raw/students.json'
)
eel.post_data('Collecting course information...')
courses = getCourses(
raw_data_dir,
log=True,
log_dir='./output/raw/courses.json'
)
eel.post_data('Generating timetables...')
# call algorithm to sort data
_, err = generateScheduleV3(
master_timetable, err = generateScheduleV3(
students,
courses,
minReq=min_req,
@@ -64,12 +68,22 @@ def start(
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: call post-algorithm functions to present sorted data
return err
return err.toDict() if err is not None else None
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 href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;500;600&display=swap" rel="stylesheet">
<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="script.js"></script>
</head>
<body>
<div class="background">
@@ -21,15 +21,18 @@
<h3>Schedule Generator</h3>
<label for="input_file">Input File (.csv)</label>
<input
required
aria-describedby="inputGroup-sizing-default"
aria-label="Default"
name="input_file"
id="input_file"
type="file"
accept=".csv"
>
<label for="classrooms">Number of classrooms (20-100)</label>
<input
required
aria-describedby="inputGroup-sizing-default"
class="form-control"
aria-label="Default"
@@ -43,6 +46,7 @@
<label for="min_req">Minimum students per class</label>
<input
required
aria-describedby="inputGroup-sizing-default"
class="form-control"
aria-label="Default"
@@ -56,6 +60,7 @@
<label for="class_cap">Maximum students per class</label>
<input
required
aria-describedby="inputGroup-sizing-default"
class="form-control"
aria-label="Default"
@@ -69,6 +74,7 @@
<label for="total_blocks">Total blocks (10 or 8)</label>
<input
required
aria-describedby="inputGroup-sizing-default"
class="form-control"
aria-label="Default"
@@ -82,13 +88,14 @@
>
<!-- 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 id="processing" class="processing">
<div id="myProgress">
<div id="myBar"></div>
</div>
<h3 id="processing-header">Processing...</h3>
<div id="loader" class="loader"></div>
<ol id="progress"></ol>
<button id="retryBtn" class="btn" onclick="retry()">Again</button>
</div>
</body>
</html>
+40 -3
View File
@@ -1,3 +1,5 @@
function e(n) {return n != undefined && n != null && n != ""}
function start() {
let file = document.getElementById("input_file").files[0];
let minReq = document.getElementById("min_req").value;
@@ -5,6 +7,9 @@ function start() {
let blockClassLimit = document.getElementById("classrooms").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
document.getElementById('main').style.display = 'none';
document.getElementById('processing').style.display = 'block';
@@ -12,7 +17,7 @@ function start() {
let reader = new FileReader()
reader.addEventListener("load", () => {
let raw = reader.result;
eel.start(raw, minReq, classCap, blockClassLimit, totalBlocks)(start_callback);
eel.start(raw, minReq, classCap, blockClassLimit, totalBlocks)(finish_callback);
}, false);
if (file) {
@@ -20,6 +25,38 @@ function start() {
}
}
function start_callback(error) {
console.log(error, 'done')
eel.expose(post_data);
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;
}
.main h3{
font-size: 32px;
font-weight: 500;
font-size: 26px;
font-weight: 1000;
line-height: 42px;
text-align: center;
}
label{
display: block;
margin-top: 30px;
margin-top: 12px;
font-size: 16px;
font-weight: 500;
}
@@ -82,16 +82,44 @@ label{
background-color: rgba(255,255,255,0.07);
border-radius: 3px;
padding: 0 10px;
margin-top: 8px;
margin-top: 0px;
font-size: 14px;
font-weight: 300;
}
::placeholder{
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;
margin-top: 50px;
margin-top: 20px;
width: 100%;
background-color: #ffffff;
color: #080710;
@@ -101,17 +129,37 @@ label{
border-radius: 5px;
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;
}
#myProgress {
width: 100%;
background-color: #ddd;
/* Safari */
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
#myBar {
width: 1%;
height: 30px;
background-color: #04AA6D;
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#progress {
margin-top: 10px;
}