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:
|
||||
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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user