Skip to content

Commit

Permalink
Merge pull request #34 from hotungkhanh/kan-79/save-timetable-mods
Browse files Browse the repository at this point in the history
Kan 79/save timetable mods
  • Loading branch information
NguyenDonLam authored Oct 7, 2024
2 parents c564b81 + a6988c2 commit 16a88ef
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 82 deletions.
2 changes: 1 addition & 1 deletion backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ quarkus.http.port=${PORT:8080}

# The solver runs only for 5 seconds to avoid a HTTP timeout in this simple implementation.
# It's recommended to run for at least 5 minutes ("5m") otherwise.
quarkus.timefold.solver.termination.spent-limit=6s
quarkus.timefold.solver.termination.spent-limit=60s
quarkus.http.cors=true
quarkus.http.cors.origins=*
quarkus.http.cors.methods=GET,POST,PUT,DELETE,OPTIONS
Expand Down
55 changes: 55 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
"@mui/material": "^5.16.7",
"dexie": "^4.0.8",
"dexie-react-hooks": "^1.1.7",
"file-saver": "^2.0.5",
"jspreadsheet-ce": "^4.2.1",
"jszip": "^3.10.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.26.1",
Expand All @@ -29,6 +31,7 @@
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"@types/file-saver": "^2.0.7",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
Expand Down
135 changes: 110 additions & 25 deletions frontend/src/components/GanttChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,23 @@ import "../styles/ganttUnassignable.css";
import {
findCampusSolution,
GanttGroup,
GanttItem,
GanttItems,
getGanttItems,
rawDate,
toRawDate,
} from "../scripts/solutionParsing";
import { TimetableSolution } from "../scripts/api";
import { TimetableSolution, Unit } from "../scripts/api";
import { useParams } from "react-router-dom";
import JSZip from "jszip";
import { saveAs } from "file-saver";

export default memo(function GanttChart() {
const params = useParams();
const timelineRef = useRef<HTMLDivElement | null>(null);
const items = useRef(new DataSet<TimelineItem>());
const items = useRef(new DataSet<GanttItem>());
const groups = useRef(new DataSet<GanttGroup>());
let moddedUnits: Unit[] = [];

let campusSolutions: TimetableSolution[];
let check: string | null = sessionStorage.getItem("campusSolutions");
Expand Down Expand Up @@ -48,10 +54,10 @@ export default memo(function GanttChart() {
let prevSelected: Id | null = null;

const options = {
start: "2000-01-01",
end: "2000-01-06",
min: "2000-01-01",
max: "2000-01-07",
start: "2024-10-14",
end: "2024-10-19",
min: "2024-10-14",
max: "2024-10-19",
editable: true,
};

Expand All @@ -63,9 +69,9 @@ export default memo(function GanttChart() {
options
);

const hasOverlap = (newItem: TimelineItem | null) => {
const hasOverlap = (newItem: GanttItem | null) => {
const existingItems = items.current.get();
return existingItems.some((item: TimelineItem) => {
return existingItems.some((item: GanttItem) => {
if (
newItem == null ||
item.id === newItem.id ||
Expand All @@ -84,7 +90,7 @@ export default memo(function GanttChart() {
});
};

const validGroup = (item: TimelineItem | null) => {
const validGroup = (item: GanttItem | null) => {
if (item == null) return true;
const group: GanttGroup|null = groups.current.get(item.group as Id);
return (group !== null && group.treeLevel === 2);
Expand All @@ -101,6 +107,51 @@ export default memo(function GanttChart() {
if (!inRoom) {
alert("ASSIGN ACTIVITIES TO ROOMS ONLY");
}
if (!overlaps && inRoom) {
let rawStartDate: rawDate = toRawDate(items.current.get(prevSelected)?.start as Date);
let rawEndDate: rawDate = toRawDate(
items.current.get(prevSelected)?.end as Date
);
let unitId:number|undefined = items.current.get(prevSelected)?.UnitId;
let campus: string|undefined = items.current.get(prevSelected)?.campus;
let buildingId: string|undefined = groups.current.get(
groups.current.get(
items.current.get(prevSelected)?.group as Id
)?.parent as Id
)?.originalId
let roomCode: string|undefined = groups.current.get(
items.current.get(prevSelected)?.group as Id
)?.originalId;
if ( unitId !== undefined && campus !== undefined && buildingId !== undefined && roomCode !== undefined) {
const modded: Unit = {
campus: "",
name:"",
course: "",
duration:-1,
students:[],
wantsLab: false,
unitId: unitId,
room: {
campus: campus,
buildingId: buildingId,
roomCode: roomCode,
capacity: -1,
lab: false,
},
dayOfWeek: rawStartDate.dayOfWeek,
startTime: rawStartDate.time,
end: rawEndDate.time,
};
const index = moddedUnits.findIndex(
(item) => item.unitId === modded.unitId
);
if (index !== -1) {
moddedUnits.splice(index, 1);
}
moddedUnits.push(modded);
}
console.log(moddedUnits);
}
}

if (properties.items.length > 0) {
Expand All @@ -116,33 +167,67 @@ export default memo(function GanttChart() {
}
}, []);

const convertToCSV = () => {
let csvContent = "id,content,start,end,group\n";
const convertToCSV = (course:string) => {
let csvContent = "id,name,start,end,room,building\n";
const itemList = items.current.get();
itemList.forEach((item) => {
const start = item.start.toString();
const end = item.end ? item.end.toString() : "";
csvContent += `${item.id},${item.content},${start},${end},${groups.current.get(item.group as Id)?.content}\n`;
if (item.course === course) {
const start = item.start.toString();
const end = item.end ? item.end.toString() : "";
csvContent += `${item.id},${item.content},${start},${end},${
groups.current.get(item.group as Id)?.content
}, ${
groups.current.get(
groups.current.get(item.group as Id)
?.parent as Id
)?.originalId
}\n`;
}
});

return csvContent;
};

const downloadCSV = () => {
const csvData = convertToCSV();
const blob = new Blob([csvData], { type: "text/csv;charset=utf-8;" });
const link = document.createElement("a");
const url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", "timetable.csv");
link.style.visibility = "hidden";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
const zip = new JSZip();
const uniqueNames = new Set(items.current.map((obj) => obj.course));
uniqueNames.forEach((course) => {
if (course) {
const csvData = convertToCSV(course);
zip.file(
(params.location as string).replace(/[^a-zA-Z0-9]/g, "_") +
"-" +
course.replace(/[^a-zA-Z0-9]/g, "_") +
".csv",
csvData
);
}
})

zip.generateAsync({ type: "blob" }).then((content) => {
saveAs(
content,
(params.location as string).replace(/[^a-zA-Z0-9]/g, "_") + ".zip"
);
});
};

const saveData = () => {
const saveData = async () => {
try {
const response = await fetch("http://localhost:8080/timetabling/update", {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(moddedUnits),
});

if (!response.ok) {
throw new Error("Failed to save data, error in GanttChart.tsx");
}
} catch (error) {
console.error("Error saving data:", error);
}
};

return (
Expand Down
17 changes: 7 additions & 10 deletions frontend/src/scripts/handleInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,24 +202,21 @@ export async function getTimetableProblems(roomSpreadsheet: Record<string, CellV
problemsByCampus.push({
campusName: campus,
units: unitsByCampus[campus],
daysOfWeek: [
"MONDAY",
"TUESDAY",
"WEDNESDAY",
"THURSDAY",
"FRIDAY"
],
daysOfWeek: ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"],
startTimes: [
"08:00:00",
"09:00:00",
"10:00:00",
"11:00:00",
"12:00:00",
"13:00:00",
"14:00:00",
"15:00:00",
"16:00:00",
"17:00:00",
],
rooms: roomsByCampus[campus]

})
rooms: roomsByCampus[campus],
});
}
else {
alert("This campus don't have any rooms: " + campus);
Expand Down
Loading

0 comments on commit 16a88ef

Please sign in to comment.