Modify Classroom and Course Schedule views to show generated schedule

This commit is contained in:
haxala1r
2025-12-18 15:30:23 +03:00
parent fcbd05edec
commit e53754d2a8
3 changed files with 107 additions and 89 deletions

View File

@@ -236,13 +236,13 @@ public class ExamEditDialog extends Dialog<ExamEditDialog.EditResult> {
validationPanel.setStyle("-fx-background-color: #e8f5e9; -fx-background-radius: 5;"); validationPanel.setStyle("-fx-background-color: #e8f5e9; -fx-background-radius: 5;");
updateButtonStates(true, false); updateButtonStates(true, false);
} else if (currentValidation.hasHardViolations()) { } else if (currentValidation.hasHardViolations()) {
validationStatusLabel.setText("Constraint violations found"); validationStatusLabel.setText("Constraint violations found!");
validationStatusLabel.setTextFill(Color.web("#e74c3c")); validationStatusLabel.setTextFill(Color.web("#e74c3c"));
violationDetails.setText(currentValidation.getFormattedMessage()); violationDetails.setText(currentValidation.getFormattedMessage());
validationPanel.setStyle("-fx-background-color: #ffebee; -fx-background-radius: 5;"); validationPanel.setStyle("-fx-background-color: #ffebee; -fx-background-radius: 5;");
updateButtonStates(false, true); updateButtonStates(false, true);
} else { } else {
validationStatusLabel.setText("⚠️ Warnings found"); validationStatusLabel.setText("Warnings found!");
validationStatusLabel.setTextFill(Color.web("#f39c12")); validationStatusLabel.setTextFill(Color.web("#f39c12"));
violationDetails.setText(currentValidation.getFormattedMessage()); violationDetails.setText(currentValidation.getFormattedMessage());
validationPanel.setStyle("-fx-background-color: #fff3e0; -fx-background-radius: 5;"); validationPanel.setStyle("-fx-background-color: #fff3e0; -fx-background-radius: 5;");

View File

@@ -11,14 +11,13 @@ import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow; import javafx.scene.control.TableRow;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
import org.example.se302.model.Classroom; import org.example.se302.model.Classroom;
import org.example.se302.model.ExamAssignment; import org.example.se302.model.Course;
import org.example.se302.model.ScheduleConfiguration; import org.example.se302.model.ScheduleConfiguration;
import org.example.se302.service.DataManager; import org.example.se302.service.DataManager;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Comparator; import java.util.Comparator;
import java.util.Map;
/** /**
* Controller for the Classroom Schedule view. * Controller for the Classroom Schedule view.
@@ -47,10 +46,6 @@ public class ScheduleClassroomController {
private DataManager dataManager; private DataManager dataManager;
// Reference to the current schedule state (set by parent controller)
private Map<String, ExamAssignment> currentAssignments;
private ScheduleConfiguration configuration;
@FXML @FXML
public void initialize() { public void initialize() {
dataManager = DataManager.getInstance(); dataManager = DataManager.getInstance();
@@ -112,15 +107,35 @@ public class ScheduleClassroomController {
} }
} }
}); });
}
/** // Refresh when tab is selected - find TabPane once scene is available
* Sets the current schedule assignments. Called by parent controller after scheduleTable.sceneProperty().addListener((obs, oldScene, newScene) -> {
* schedule generation. if (newScene != null) {
*/ // Find parent TabPane and listen for selection changes
public void setScheduleData(Map<String, ExamAssignment> assignments, ScheduleConfiguration config) { javafx.scene.Parent parent = scheduleTable.getParent();
this.currentAssignments = assignments; while (parent != null) {
this.configuration = config; if (parent.getParent() instanceof javafx.scene.control.TabPane) {
javafx.scene.control.TabPane tabPane = (javafx.scene.control.TabPane) parent
.getParent();
// Find which tab contains our content
for (javafx.scene.control.Tab tab : tabPane.getTabs()) {
if (tab.getContent() == parent) {
tab.selectedProperty().addListener(
(o, wasSelected, isSelected) -> {
if (isSelected && classroomComboBox
.getValue() != null) {
onShowSchedule();
}
});
break;
}
}
break;
}
parent = parent.getParent();
}
}
});
} }
@FXML @FXML
@@ -132,52 +147,52 @@ public class ScheduleClassroomController {
selectedClassroomLabel.setText("Schedule for: " + selected.getClassroomId() + selectedClassroomLabel.setText("Schedule for: " + selected.getClassroomId() +
" (Capacity: " + selected.getCapacity() + ")"); " (Capacity: " + selected.getCapacity() + ")");
ScheduleConfiguration config = dataManager.getActiveConfiguration();
ObservableList<ClassroomSlotEntry> entries = FXCollections.observableArrayList(); ObservableList<ClassroomSlotEntry> entries = FXCollections.observableArrayList();
int totalSlots = 0; int totalSlots = 0;
int usedSlots = 0; int usedSlots = 0;
int totalStudents = 0; int totalStudents = 0;
// If we have assignments, filter by this classroom // Find all courses scheduled in this classroom
if (currentAssignments != null && !currentAssignments.isEmpty()) { for (Course course : dataManager.getCourses()) {
for (ExamAssignment assignment : currentAssignments.values()) { if (course.isScheduled() &&
if (assignment.isAssigned() && selected.getClassroomId().equals(course.getAssignedClassroom())) {
selected.getClassroomId().equals(assignment.getClassroomId())) {
// Format date int dayIndex = course.getExamDay();
String dateStr; int slotIndex = course.getExamTimeSlot();
if (configuration != null && configuration.getStartDate() != null) {
LocalDate examDate = configuration.getStartDate()
.plusDays(assignment.getDay());
dateStr = examDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
} else {
dateStr = "Day " + (assignment.getDay() + 1);
}
// Format time // Format date
String timeStr = "Slot " + (assignment.getTimeSlotIndex() + 1); String dateStr;
if (config != null && config.getStartDate() != null) {
// Calculate utilization percentage for this slot LocalDate examDate = config.getStartDate().plusDays(dayIndex);
int studentCount = assignment.getStudentCount(); dateStr = examDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
int capacity = selected.getCapacity(); } else {
int utilizationPercent = capacity > 0 ? (studentCount * 100) / capacity : 0; dateStr = "Day " + (dayIndex + 1);
String utilizationStr = utilizationPercent + "%";
entries.add(new ClassroomSlotEntry(
dateStr, timeStr, assignment.getCourseCode(),
studentCount, utilizationStr, utilizationPercent,
assignment.getDay(), assignment.getTimeSlotIndex()));
usedSlots++;
totalStudents += studentCount;
} }
}
// Calculate total possible slots String timeStr = "Slot " + (slotIndex + 1);
if (configuration != null) {
totalSlots = configuration.getNumDays() * configuration.getSlotsPerDay(); // Calculate utilization
int studentCount = course.getEnrolledStudentsCount();
int capacity = selected.getCapacity();
int utilizationPercent = capacity > 0 ? (studentCount * 100) / capacity : 0;
String utilizationStr = utilizationPercent + "%";
entries.add(new ClassroomSlotEntry(
dateStr, timeStr, course.getCourseCode(),
studentCount, utilizationStr, utilizationPercent,
dayIndex, slotIndex));
usedSlots++;
totalStudents += studentCount;
} }
} }
// Calculate total possible slots
if (config != null) {
totalSlots = config.getNumDays() * config.getSlotsPerDay();
}
// Sort by day then slot // Sort by day then slot
entries.sort(Comparator.comparingInt(ClassroomSlotEntry::getDayIndex) entries.sort(Comparator.comparingInt(ClassroomSlotEntry::getDayIndex)
.thenComparingInt(ClassroomSlotEntry::getSlotIndex)); .thenComparingInt(ClassroomSlotEntry::getSlotIndex));

View File

@@ -9,14 +9,12 @@ import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow; import javafx.scene.control.TableRow;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
import org.example.se302.model.Course; import org.example.se302.model.Course;
import org.example.se302.model.ExamAssignment;
import org.example.se302.model.ScheduleConfiguration; import org.example.se302.model.ScheduleConfiguration;
import org.example.se302.service.DataManager; import org.example.se302.service.DataManager;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Comparator; import java.util.Comparator;
import java.util.Map;
// Color palette for days (pastel colors for readability) // Color palette for days (pastel colors for readability)
// Day 0 = Light Blue, Day 1 = Light Green, Day 2 = Light Yellow, etc. // Day 0 = Light Blue, Day 1 = Light Green, Day 2 = Light Yellow, etc.
@@ -43,11 +41,6 @@ public class ScheduleCourseController {
private DataManager dataManager; private DataManager dataManager;
// Reference to the current schedule state (set by parent controller or loaded
// from DB)
private Map<String, ExamAssignment> currentAssignments;
private ScheduleConfiguration configuration;
@FXML @FXML
public void initialize() { public void initialize() {
dataManager = DataManager.getInstance(); dataManager = DataManager.getInstance();
@@ -122,19 +115,38 @@ public class ScheduleCourseController {
// Listen for data changes // Listen for data changes
dataManager.getCourses().addListener( dataManager.getCourses().addListener(
(javafx.collections.ListChangeListener<Course>) c -> loadScheduleData()); (javafx.collections.ListChangeListener<Course>) c -> loadScheduleData());
}
/** // Refresh when tab is selected - find TabPane once scene is available
* Sets the current schedule assignments. Called by parent controller after courseScheduleTable.sceneProperty().addListener((obs, oldScene, newScene) -> {
* schedule generation. if (newScene != null) {
*/ loadScheduleData();
public void setScheduleData(Map<String, ExamAssignment> assignments, ScheduleConfiguration config) { // Find parent TabPane and listen for selection changes
this.currentAssignments = assignments; javafx.scene.Parent parent = courseScheduleTable.getParent();
this.configuration = config; while (parent != null) {
loadScheduleData(); if (parent.getParent() instanceof javafx.scene.control.TabPane) {
javafx.scene.control.TabPane tabPane = (javafx.scene.control.TabPane) parent
.getParent();
// Find which tab contains our content
for (javafx.scene.control.Tab tab : tabPane.getTabs()) {
if (tab.getContent() == parent) {
tab.selectedProperty().addListener(
(o, wasSelected, isSelected) -> {
if (isSelected)
loadScheduleData();
});
break;
}
}
break;
}
parent = parent.getParent();
}
}
});
} }
private void loadScheduleData() { private void loadScheduleData() {
ScheduleConfiguration config = dataManager.getActiveConfiguration();
ObservableList<CourseScheduleEntry> entries = FXCollections.observableArrayList(); ObservableList<CourseScheduleEntry> entries = FXCollections.observableArrayList();
for (Course course : dataManager.getCourses()) { for (Course course : dataManager.getCourses()) {
@@ -144,33 +156,24 @@ public class ScheduleCourseController {
String dateStr = "Not Scheduled"; String dateStr = "Not Scheduled";
String timeStr = "-"; String timeStr = "-";
String classroomStr = "-"; String classroomStr = "-";
int dayIndex = Integer.MAX_VALUE; // For sorting unscheduled items last int dayIndex = Integer.MAX_VALUE;
int slotIndex = Integer.MAX_VALUE; int slotIndex = Integer.MAX_VALUE;
// Check if we have an assignment for this course // Check if this course has been scheduled
if (currentAssignments != null && currentAssignments.containsKey(courseCode)) { if (course.isScheduled()) {
ExamAssignment assignment = currentAssignments.get(courseCode); dayIndex = course.getExamDay();
slotIndex = course.getExamTimeSlot();
classroomStr = course.getAssignedClassroom();
if (assignment.isAssigned()) { // Format date using configuration's start date
dayIndex = assignment.getDay(); if (config != null && config.getStartDate() != null) {
slotIndex = assignment.getTimeSlotIndex(); LocalDate examDate = config.getStartDate().plusDays(dayIndex);
dateStr = examDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy (EEEE)"));
// Format date based on configuration start date + day offset } else {
if (configuration != null && configuration.getStartDate() != null) { dateStr = "Day " + (dayIndex + 1);
LocalDate examDate = configuration.getStartDate().plusDays(dayIndex);
dateStr = examDate.format(
DateTimeFormatter.ofPattern("dd/MM/yyyy (EEEE)"));
} else {
dateStr = "Day " + (dayIndex + 1);
}
// Format time slot
timeStr = "Slot " + (slotIndex + 1);
// Classroom
classroomStr = assignment.getClassroomId() != null ? assignment.getClassroomId()
: "-";
} }
timeStr = "Slot " + (slotIndex + 1);
} }
entries.add(new CourseScheduleEntry(courseCode, enrolled, dateStr, timeStr, classroomStr, entries.add(new CourseScheduleEntry(courseCode, enrolled, dateStr, timeStr, classroomStr,