mirror of
https://github.com/sabazadam/Se302.git
synced 2025-12-31 12:21:22 +00:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -15,20 +15,33 @@ import org.example.se302.service.DataManager;
|
|||||||
*/
|
*/
|
||||||
public class CoursesController {
|
public class CoursesController {
|
||||||
|
|
||||||
@FXML private TextField searchField;
|
@FXML
|
||||||
@FXML private Label resultCountLabel;
|
private TextField searchField;
|
||||||
@FXML private TableView<Course> coursesTable;
|
@FXML
|
||||||
@FXML private TableColumn<Course, String> courseCodeColumn;
|
private Label resultCountLabel;
|
||||||
@FXML private TableColumn<Course, Number> studentCountColumn;
|
@FXML
|
||||||
@FXML private TableColumn<Course, String> classroomColumn;
|
private TableView<Course> coursesTable;
|
||||||
@FXML private TableColumn<Course, String> examDateColumn;
|
@FXML
|
||||||
@FXML private TableColumn<Course, Void> actionColumn;
|
private TableColumn<Course, String> courseCodeColumn;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Course, Number> studentCountColumn;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Course, String> classroomColumn;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Course, String> examDateColumn;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Course, Void> actionColumn;
|
||||||
|
|
||||||
@FXML private VBox studentListPanel;
|
@FXML
|
||||||
@FXML private Label studentListTitleLabel;
|
private VBox studentListPanel;
|
||||||
@FXML private TableView<String> enrolledStudentsTable;
|
@FXML
|
||||||
@FXML private TableColumn<String, String> enrolledStudentIdColumn;
|
private Label studentListTitleLabel;
|
||||||
@FXML private Label enrolledCountLabel;
|
@FXML
|
||||||
|
private TableView<String> enrolledStudentsTable;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<String, String> enrolledStudentIdColumn;
|
||||||
|
@FXML
|
||||||
|
private Label enrolledCountLabel;
|
||||||
|
|
||||||
private DataManager dataManager;
|
private DataManager dataManager;
|
||||||
private FilteredList<Course> filteredCourses;
|
private FilteredList<Course> filteredCourses;
|
||||||
@@ -38,11 +51,10 @@ public class CoursesController {
|
|||||||
dataManager = DataManager.getInstance();
|
dataManager = DataManager.getInstance();
|
||||||
|
|
||||||
// Set up table columns
|
// Set up table columns
|
||||||
courseCodeColumn.setCellValueFactory(cellData ->
|
courseCodeColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getCourseCode()));
|
||||||
new SimpleStringProperty(cellData.getValue().getCourseCode()));
|
|
||||||
|
|
||||||
studentCountColumn.setCellValueFactory(cellData ->
|
studentCountColumn.setCellValueFactory(
|
||||||
new SimpleIntegerProperty(cellData.getValue().getEnrolledStudentsCount()));
|
cellData -> new SimpleIntegerProperty(cellData.getValue().getEnrolledStudentsCount()));
|
||||||
|
|
||||||
classroomColumn.setCellValueFactory(cellData -> {
|
classroomColumn.setCellValueFactory(cellData -> {
|
||||||
String classroom = cellData.getValue().getAssignedClassroom();
|
String classroom = cellData.getValue().getAssignedClassroom();
|
||||||
@@ -50,8 +62,12 @@ public class CoursesController {
|
|||||||
});
|
});
|
||||||
|
|
||||||
examDateColumn.setCellValueFactory(cellData -> {
|
examDateColumn.setCellValueFactory(cellData -> {
|
||||||
String examDate = cellData.getValue().getExamDateTime();
|
Course course = cellData.getValue();
|
||||||
return new SimpleStringProperty(examDate != null ? examDate : "Not Scheduled");
|
if (course.isScheduled()) {
|
||||||
|
return new SimpleStringProperty("Day " + (course.getExamDay() + 1) +
|
||||||
|
", Slot " + (course.getExamTimeSlot() + 1));
|
||||||
|
}
|
||||||
|
return new SimpleStringProperty("Not Scheduled");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add "View Students" button to action column
|
// Add "View Students" button to action column
|
||||||
@@ -73,8 +89,7 @@ public class CoursesController {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Set up enrolled students table column
|
// Set up enrolled students table column
|
||||||
enrolledStudentIdColumn.setCellValueFactory(cellData ->
|
enrolledStudentIdColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue()));
|
||||||
new SimpleStringProperty(cellData.getValue()));
|
|
||||||
|
|
||||||
// Set up filtered list
|
// Set up filtered list
|
||||||
filteredCourses = new FilteredList<>(dataManager.getCourses(), p -> true);
|
filteredCourses = new FilteredList<>(dataManager.getCourses(), p -> true);
|
||||||
@@ -106,14 +121,13 @@ public class CoursesController {
|
|||||||
filteredCourses.setPredicate(course -> true);
|
filteredCourses.setPredicate(course -> true);
|
||||||
} else {
|
} else {
|
||||||
String lowerCaseFilter = searchText.toLowerCase().trim();
|
String lowerCaseFilter = searchText.toLowerCase().trim();
|
||||||
filteredCourses.setPredicate(course ->
|
filteredCourses.setPredicate(course -> course.getCourseCode().toLowerCase().contains(lowerCaseFilter));
|
||||||
course.getCourseCode().toLowerCase().contains(lowerCaseFilter)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showEnrolledStudents(Course course) {
|
private void showEnrolledStudents(Course course) {
|
||||||
if (course == null) return;
|
if (course == null)
|
||||||
|
return;
|
||||||
|
|
||||||
studentListTitleLabel.setText("Students Enrolled in " + course.getCourseCode());
|
studentListTitleLabel.setText("Students Enrolled in " + course.getCourseCode());
|
||||||
enrolledStudentsTable.setItems(FXCollections.observableArrayList(course.getEnrolledStudents()));
|
enrolledStudentsTable.setItems(FXCollections.observableArrayList(course.getEnrolledStudents()));
|
||||||
|
|||||||
@@ -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;");
|
||||||
|
|||||||
@@ -737,4 +737,68 @@ public class ScheduleCalendarController {
|
|||||||
"Could not update the exam assignment. The exam may be locked.");
|
"Could not update the exam assignment. The exam may be locked.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the schedule as a CSV grid (days × time slots).
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
private void onExportCSV() {
|
||||||
|
if (currentSchedule == null || currentConfig == null) {
|
||||||
|
showAlert(Alert.AlertType.WARNING, "No Schedule",
|
||||||
|
"Please generate a schedule first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
javafx.stage.FileChooser fileChooser = new javafx.stage.FileChooser();
|
||||||
|
fileChooser.setTitle("Export Schedule as CSV");
|
||||||
|
fileChooser.getExtensionFilters().add(
|
||||||
|
new javafx.stage.FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||||
|
fileChooser.setInitialFileName("schedule_calendar.csv");
|
||||||
|
|
||||||
|
java.io.File file = fileChooser.showSaveDialog(scheduleGrid.getScene().getWindow());
|
||||||
|
if (file == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try (java.io.PrintWriter writer = new java.io.PrintWriter(file)) {
|
||||||
|
int numDays = currentConfig.getNumDays();
|
||||||
|
int slotsPerDay = currentConfig.getSlotsPerDay();
|
||||||
|
|
||||||
|
// Header row: Time Slot, Day 1, Day 2, ...
|
||||||
|
StringBuilder header = new StringBuilder("Time Slot");
|
||||||
|
for (int day = 0; day < numDays; day++) {
|
||||||
|
LocalDate date = currentConfig.getStartDate().plusDays(day);
|
||||||
|
header.append(",").append(date.toString());
|
||||||
|
}
|
||||||
|
writer.println(header);
|
||||||
|
|
||||||
|
// Data rows: one per time slot
|
||||||
|
for (int slot = 0; slot < slotsPerDay; slot++) {
|
||||||
|
StringBuilder row = new StringBuilder();
|
||||||
|
TimeSlot timeSlot = currentConfig.getTimeSlot(0, slot);
|
||||||
|
String slotLabel = timeSlot != null
|
||||||
|
? timeSlot.getStartTime() + "-" + timeSlot.getEndTime()
|
||||||
|
: "Slot " + (slot + 1);
|
||||||
|
row.append(slotLabel);
|
||||||
|
|
||||||
|
for (int day = 0; day < numDays; day++) {
|
||||||
|
row.append(",");
|
||||||
|
// Find exams at this day/slot
|
||||||
|
List<String> exams = new ArrayList<>();
|
||||||
|
for (ExamAssignment a : currentSchedule.getAssignments().values()) {
|
||||||
|
if (a.isAssigned() && a.getDay() == day && a.getTimeSlotIndex() == slot) {
|
||||||
|
exams.add(a.getCourseCode() + " (" + a.getClassroomId() + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row.append("\"").append(String.join("; ", exams)).append("\"");
|
||||||
|
}
|
||||||
|
writer.println(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
showAlert(Alert.AlertType.INFORMATION, "Export Complete",
|
||||||
|
"Schedule exported to:\n" + file.getAbsolutePath());
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
showAlert(Alert.AlertType.ERROR, "Export Failed",
|
||||||
|
"Could not write file: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ 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.model.TimeSlot;
|
||||||
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 +47,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 +108,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,69 +148,64 @@ 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 usedSlots = 0;
|
int usedSlots = 0;
|
||||||
int totalStudents = 0;
|
int totalStudents = 0;
|
||||||
|
|
||||||
// If we have assignments, filter by this classroom
|
for (Course course : dataManager.getCourses()) {
|
||||||
if (currentAssignments != null && !currentAssignments.isEmpty()) {
|
if (!course.isScheduled() || !selected.getClassroomId().equals(course.getAssignedClassroom()))
|
||||||
for (ExamAssignment assignment : currentAssignments.values()) {
|
continue;
|
||||||
if (assignment.isAssigned() &&
|
|
||||||
selected.getClassroomId().equals(assignment.getClassroomId())) {
|
|
||||||
|
|
||||||
// Format date
|
int dayIndex = course.getExamDay();
|
||||||
String dateStr;
|
int slotIndex = course.getExamTimeSlot();
|
||||||
if (configuration != null && configuration.getStartDate() != null) {
|
int studentCount = course.getEnrolledStudentsCount();
|
||||||
LocalDate examDate = configuration.getStartDate()
|
int utilization = selected.getCapacity() > 0
|
||||||
.plusDays(assignment.getDay());
|
? (studentCount * 100) / selected.getCapacity()
|
||||||
dateStr = examDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
|
: 0;
|
||||||
} else {
|
|
||||||
dateStr = "Day " + (assignment.getDay() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format time
|
entries.add(new ClassroomSlotEntry(
|
||||||
String timeStr = "Slot " + (assignment.getTimeSlotIndex() + 1);
|
formatDate(config, dayIndex),
|
||||||
|
formatTime(config, dayIndex, slotIndex),
|
||||||
|
course.getCourseCode(), studentCount, utilization + "%",
|
||||||
|
utilization, dayIndex, slotIndex));
|
||||||
|
|
||||||
// Calculate utilization percentage for this slot
|
usedSlots++;
|
||||||
int studentCount = assignment.getStudentCount();
|
totalStudents += studentCount;
|
||||||
int capacity = selected.getCapacity();
|
|
||||||
int utilizationPercent = capacity > 0 ? (studentCount * 100) / capacity : 0;
|
|
||||||
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
|
|
||||||
if (configuration != null) {
|
|
||||||
totalSlots = configuration.getNumDays() * configuration.getSlotsPerDay();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by day then slot
|
|
||||||
entries.sort(Comparator.comparingInt(ClassroomSlotEntry::getDayIndex)
|
entries.sort(Comparator.comparingInt(ClassroomSlotEntry::getDayIndex)
|
||||||
.thenComparingInt(ClassroomSlotEntry::getSlotIndex));
|
.thenComparingInt(ClassroomSlotEntry::getSlotIndex));
|
||||||
|
|
||||||
scheduleTable.setItems(entries);
|
scheduleTable.setItems(entries);
|
||||||
|
|
||||||
// Update overall utilization label
|
int totalSlots = config != null ? config.getNumDays() * config.getSlotsPerDay() : 0;
|
||||||
if (totalSlots > 0) {
|
if (totalSlots > 0) {
|
||||||
int overallUtilization = (usedSlots * 100) / totalSlots;
|
int overallUtil = (usedSlots * 100) / totalSlots;
|
||||||
utilizationLabel.setText(String.format(
|
utilizationLabel.setText(String.format(
|
||||||
"Overall Utilization: %d%% (%d/%d slots used, %d total students)",
|
"Overall Utilization: %d%% (%d/%d slots used, %d total students)",
|
||||||
overallUtilization, usedSlots, totalSlots, totalStudents));
|
overallUtil, usedSlots, totalSlots, totalStudents));
|
||||||
} else {
|
} else {
|
||||||
utilizationLabel.setText("Overall Utilization: 0% (No schedule data available)");
|
utilizationLabel.setText("Overall Utilization: 0% (No schedule data available)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String formatDate(ScheduleConfiguration config, int dayIndex) {
|
||||||
|
if (config != null && config.getStartDate() != null) {
|
||||||
|
LocalDate examDate = config.getStartDate().plusDays(dayIndex);
|
||||||
|
return examDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
|
||||||
|
}
|
||||||
|
return "Day " + (dayIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatTime(ScheduleConfiguration config, int dayIndex, int slotIndex) {
|
||||||
|
if (config != null) {
|
||||||
|
TimeSlot slot = config.getTimeSlot(dayIndex, slotIndex);
|
||||||
|
if (slot != null)
|
||||||
|
return slot.getStartTime() + " - " + slot.getEndTime();
|
||||||
|
}
|
||||||
|
return "Slot " + (slotIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Helper class for table entries
|
// Helper class for table entries
|
||||||
public static class ClassroomSlotEntry {
|
public static class ClassroomSlotEntry {
|
||||||
private final String date;
|
private final String date;
|
||||||
@@ -250,4 +261,62 @@ public class ScheduleClassroomController {
|
|||||||
return slotIndex;
|
return slotIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the selected classroom's schedule as CSV.
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
private void onExportCSV() {
|
||||||
|
Classroom selected = classroomComboBox.getValue();
|
||||||
|
if (selected == null) {
|
||||||
|
showAlert(javafx.scene.control.Alert.AlertType.WARNING, "No Classroom",
|
||||||
|
"Please select a classroom first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scheduleTable.getItems().isEmpty()) {
|
||||||
|
showAlert(javafx.scene.control.Alert.AlertType.WARNING, "No Data",
|
||||||
|
"No schedule data to export.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
javafx.stage.FileChooser fileChooser = new javafx.stage.FileChooser();
|
||||||
|
fileChooser.setTitle("Export Classroom Schedule as CSV");
|
||||||
|
fileChooser.getExtensionFilters().add(
|
||||||
|
new javafx.stage.FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||||
|
fileChooser.setInitialFileName("schedule_classroom_" + selected.getClassroomId() + ".csv");
|
||||||
|
|
||||||
|
java.io.File file = fileChooser.showSaveDialog(scheduleTable.getScene().getWindow());
|
||||||
|
if (file == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try (java.io.PrintWriter writer = new java.io.PrintWriter(file)) {
|
||||||
|
// Header
|
||||||
|
writer.println("Classroom,Date,Time,Course,Students");
|
||||||
|
|
||||||
|
// Data rows
|
||||||
|
for (ClassroomSlotEntry entry : scheduleTable.getItems()) {
|
||||||
|
writer.println(String.format("%s,\"%s\",%s,%s,%d",
|
||||||
|
selected.getClassroomId(),
|
||||||
|
entry.getDate(),
|
||||||
|
entry.getTime(),
|
||||||
|
entry.getCourse(),
|
||||||
|
entry.getStudentCount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
showAlert(javafx.scene.control.Alert.AlertType.INFORMATION, "Export Complete",
|
||||||
|
"Classroom schedule exported to:\n" + file.getAbsolutePath());
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
showAlert(javafx.scene.control.Alert.AlertType.ERROR, "Export Failed",
|
||||||
|
"Could not write file: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAlert(javafx.scene.control.Alert.AlertType type, String title, String message) {
|
||||||
|
javafx.scene.control.Alert alert = new javafx.scene.control.Alert(type);
|
||||||
|
alert.setTitle(title);
|
||||||
|
alert.setHeaderText(null);
|
||||||
|
alert.setContentText(message);
|
||||||
|
alert.showAndWait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,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.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.model.TimeSlot;
|
||||||
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 +42,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,68 +116,83 @@ 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()) {
|
||||||
String courseCode = course.getCourseCode();
|
|
||||||
int enrolled = course.getEnrolledStudentsCount();
|
|
||||||
|
|
||||||
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
|
if (course.isScheduled()) {
|
||||||
if (currentAssignments != null && currentAssignments.containsKey(courseCode)) {
|
dayIndex = course.getExamDay();
|
||||||
ExamAssignment assignment = currentAssignments.get(courseCode);
|
slotIndex = course.getExamTimeSlot();
|
||||||
|
classroomStr = course.getAssignedClassroom();
|
||||||
if (assignment.isAssigned()) {
|
dateStr = formatDate(config, dayIndex);
|
||||||
dayIndex = assignment.getDay();
|
timeStr = formatTime(config, dayIndex, slotIndex);
|
||||||
slotIndex = assignment.getTimeSlotIndex();
|
|
||||||
|
|
||||||
// Format date based on configuration start date + day offset
|
|
||||||
if (configuration != null && configuration.getStartDate() != null) {
|
|
||||||
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()
|
|
||||||
: "-";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.add(new CourseScheduleEntry(courseCode, enrolled, dateStr, timeStr, classroomStr,
|
entries.add(new CourseScheduleEntry(course.getCourseCode(),
|
||||||
|
course.getEnrolledStudentsCount(), dateStr, timeStr, classroomStr,
|
||||||
dayIndex, slotIndex));
|
dayIndex, slotIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by day first, then by slot
|
|
||||||
entries.sort(Comparator.comparingInt(CourseScheduleEntry::getDayIndex)
|
entries.sort(Comparator.comparingInt(CourseScheduleEntry::getDayIndex)
|
||||||
.thenComparingInt(CourseScheduleEntry::getSlotIndex));
|
.thenComparingInt(CourseScheduleEntry::getSlotIndex));
|
||||||
|
|
||||||
courseScheduleTable.setItems(entries);
|
courseScheduleTable.setItems(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String formatDate(ScheduleConfiguration config, int dayIndex) {
|
||||||
|
if (config != null && config.getStartDate() != null) {
|
||||||
|
LocalDate examDate = config.getStartDate().plusDays(dayIndex);
|
||||||
|
return examDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy (EEEE)"));
|
||||||
|
}
|
||||||
|
return "Day " + (dayIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatTime(ScheduleConfiguration config, int dayIndex, int slotIndex) {
|
||||||
|
if (config != null) {
|
||||||
|
TimeSlot slot = config.getTimeSlot(dayIndex, slotIndex);
|
||||||
|
if (slot != null) {
|
||||||
|
return slot.getStartTime() + " - " + slot.getEndTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "Slot " + (slotIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Helper class for table entries
|
// Helper class for table entries
|
||||||
public static class CourseScheduleEntry {
|
public static class CourseScheduleEntry {
|
||||||
private final String courseCode;
|
private final String courseCode;
|
||||||
@@ -233,4 +242,55 @@ public class ScheduleCourseController {
|
|||||||
return slotIndex;
|
return slotIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the course schedule as a CSV file.
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
private void onExportCSV() {
|
||||||
|
if (courseScheduleTable.getItems().isEmpty()) {
|
||||||
|
showAlert(javafx.scene.control.Alert.AlertType.WARNING, "No Data",
|
||||||
|
"No schedule data to export.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
javafx.stage.FileChooser fileChooser = new javafx.stage.FileChooser();
|
||||||
|
fileChooser.setTitle("Export Course Schedule as CSV");
|
||||||
|
fileChooser.getExtensionFilters().add(
|
||||||
|
new javafx.stage.FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||||
|
fileChooser.setInitialFileName("schedule_courses.csv");
|
||||||
|
|
||||||
|
java.io.File file = fileChooser.showSaveDialog(courseScheduleTable.getScene().getWindow());
|
||||||
|
if (file == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try (java.io.PrintWriter writer = new java.io.PrintWriter(file)) {
|
||||||
|
// Header
|
||||||
|
writer.println("Course Code,Date,Time,Classroom,Enrolled Students");
|
||||||
|
|
||||||
|
// Data rows
|
||||||
|
for (CourseScheduleEntry entry : courseScheduleTable.getItems()) {
|
||||||
|
writer.println(String.format("%s,\"%s\",%s,%s,%d",
|
||||||
|
entry.getCourseCode(),
|
||||||
|
entry.getDate(),
|
||||||
|
entry.getTime(),
|
||||||
|
entry.getClassroom(),
|
||||||
|
entry.getEnrolledCount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
showAlert(javafx.scene.control.Alert.AlertType.INFORMATION, "Export Complete",
|
||||||
|
"Course schedule exported to:\n" + file.getAbsolutePath());
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
showAlert(javafx.scene.control.Alert.AlertType.ERROR, "Export Failed",
|
||||||
|
"Could not write file: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAlert(javafx.scene.control.Alert.AlertType type, String title, String message) {
|
||||||
|
javafx.scene.control.Alert alert = new javafx.scene.control.Alert(type);
|
||||||
|
alert.setTitle(title);
|
||||||
|
alert.setHeaderText(null);
|
||||||
|
alert.setContentText(message);
|
||||||
|
alert.showAndWait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,77 +105,58 @@ public class ScheduleStudentController {
|
|||||||
|
|
||||||
for (String courseCode : student.getEnrolledCourses()) {
|
for (String courseCode : student.getEnrolledCourses()) {
|
||||||
Course course = dataManager.getCourse(courseCode);
|
Course course = dataManager.getCourse(courseCode);
|
||||||
if (course != null) {
|
if (course == null)
|
||||||
String dateStr = "Not Scheduled";
|
continue;
|
||||||
String timeStr = "-";
|
|
||||||
String classroom = "-";
|
|
||||||
int dayIndex = -1;
|
|
||||||
int slotIndex = -1;
|
|
||||||
|
|
||||||
if (course.isScheduled()) {
|
String dateStr = "Not Scheduled";
|
||||||
dayIndex = course.getExamDay();
|
String timeStr = "-";
|
||||||
slotIndex = course.getExamTimeSlot();
|
String classroom = "-";
|
||||||
classroom = course.getAssignedClassroom();
|
int dayIndex = -1, slotIndex = -1;
|
||||||
|
|
||||||
if (config != null) {
|
if (course.isScheduled()) {
|
||||||
TimeSlot slot = config.getTimeSlot(dayIndex, slotIndex);
|
dayIndex = course.getExamDay();
|
||||||
if (slot != null) {
|
slotIndex = course.getExamTimeSlot();
|
||||||
dateStr = slot.getDate().toString(); // YYYY-MM-DD
|
classroom = course.getAssignedClassroom();
|
||||||
timeStr = slot.getStartTime().toString() + " - " + slot.getEndTime().toString();
|
|
||||||
} else {
|
TimeSlot slot = config != null ? config.getTimeSlot(dayIndex, slotIndex) : null;
|
||||||
dateStr = "Day " + (dayIndex + 1);
|
if (slot != null) {
|
||||||
timeStr = "Slot " + (slotIndex + 1);
|
dateStr = slot.getDate().toString();
|
||||||
}
|
timeStr = slot.getStartTime() + " - " + slot.getEndTime();
|
||||||
} else {
|
} else {
|
||||||
// Fallback if no config saved
|
dateStr = "Day " + (dayIndex + 1);
|
||||||
dateStr = "Day " + (dayIndex + 1);
|
timeStr = "Slot " + (slotIndex + 1);
|
||||||
timeStr = "Slot " + (slotIndex + 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.add(new CourseScheduleEntry(courseCode, dateStr, timeStr, classroom, dayIndex, slotIndex));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entries.add(new CourseScheduleEntry(courseCode, dateStr, timeStr, classroom, dayIndex, slotIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by day and time
|
|
||||||
entries.sort(Comparator.comparingInt(CourseScheduleEntry::getDayIndex)
|
entries.sort(Comparator.comparingInt(CourseScheduleEntry::getDayIndex)
|
||||||
.thenComparingInt(CourseScheduleEntry::getSlotIndex));
|
.thenComparingInt(CourseScheduleEntry::getSlotIndex));
|
||||||
|
|
||||||
// Analyze for highlights
|
|
||||||
analyzeSchedule(entries);
|
analyzeSchedule(entries);
|
||||||
|
|
||||||
scheduleTable.setItems(FXCollections.observableArrayList(entries));
|
scheduleTable.setItems(FXCollections.observableArrayList(entries));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void analyzeSchedule(List<CourseScheduleEntry> entries) {
|
private void analyzeSchedule(List<CourseScheduleEntry> entries) {
|
||||||
if (entries.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
CourseScheduleEntry current = entries.get(i);
|
CourseScheduleEntry current = entries.get(i);
|
||||||
if (current.getDayIndex() == -1)
|
if (current.getDayIndex() == -1)
|
||||||
continue; // Skip unscheduled
|
continue;
|
||||||
|
|
||||||
// Check for multiple exams on same day
|
// Check for multiple exams and conflicts on same day
|
||||||
int examsOnDay = 0;
|
|
||||||
for (CourseScheduleEntry other : entries) {
|
for (CourseScheduleEntry other : entries) {
|
||||||
if (other.getDayIndex() == current.getDayIndex() && other.getDayIndex() != -1) {
|
if (other.getDayIndex() == current.getDayIndex() && other.getDayIndex() != -1) {
|
||||||
examsOnDay++;
|
current.isMultipleExamsOnDay = true;
|
||||||
if (other.getSlotIndex() == current.getSlotIndex() && other != current) {
|
if (other.getSlotIndex() == current.getSlotIndex() && other != current) {
|
||||||
current.hasConflictWarning = true;
|
current.hasConflictWarning = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (examsOnDay > 1) {
|
|
||||||
current.isMultipleExamsOnDay = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for consecutive days (look at previous scheduled exam)
|
// Check for consecutive days with previous exam
|
||||||
// Since list is sorted, we can look at previous entry if it exists
|
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
CourseScheduleEntry prev = entries.get(i - 1);
|
CourseScheduleEntry prev = entries.get(i - 1);
|
||||||
if (prev.getDayIndex() != -1 &&
|
if (prev.getDayIndex() != -1 && current.getDayIndex() == prev.getDayIndex() + 1) {
|
||||||
current.getDayIndex() == prev.getDayIndex() + 1) {
|
|
||||||
current.isConsecutiveDay = true;
|
current.isConsecutiveDay = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,4 +211,62 @@ public class ScheduleStudentController {
|
|||||||
return slotIndex;
|
return slotIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the selected student's schedule as CSV.
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
private void onExportCSV() {
|
||||||
|
Student selected = studentComboBox.getValue();
|
||||||
|
if (selected == null) {
|
||||||
|
showAlert(Alert.AlertType.WARNING, "No Student",
|
||||||
|
"Please select a student first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scheduleTable.getItems().isEmpty()) {
|
||||||
|
showAlert(Alert.AlertType.WARNING, "No Data",
|
||||||
|
"No schedule data to export.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
javafx.stage.FileChooser fileChooser = new javafx.stage.FileChooser();
|
||||||
|
fileChooser.setTitle("Export Student Schedule as CSV");
|
||||||
|
fileChooser.getExtensionFilters().add(
|
||||||
|
new javafx.stage.FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||||
|
fileChooser.setInitialFileName("schedule_student_" + selected.getStudentId() + ".csv");
|
||||||
|
|
||||||
|
java.io.File file = fileChooser.showSaveDialog(scheduleTable.getScene().getWindow());
|
||||||
|
if (file == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try (java.io.PrintWriter writer = new java.io.PrintWriter(file)) {
|
||||||
|
// Header
|
||||||
|
writer.println("Student ID,Course,Date,Time,Classroom");
|
||||||
|
|
||||||
|
// Data rows
|
||||||
|
for (CourseScheduleEntry entry : scheduleTable.getItems()) {
|
||||||
|
writer.println(String.format("%s,%s,\"%s\",%s,%s",
|
||||||
|
selected.getStudentId(),
|
||||||
|
entry.getCourseCode(),
|
||||||
|
entry.getDateDisplay(),
|
||||||
|
entry.getTimeDisplay(),
|
||||||
|
entry.getClassroom()));
|
||||||
|
}
|
||||||
|
|
||||||
|
showAlert(Alert.AlertType.INFORMATION, "Export Complete",
|
||||||
|
"Student schedule exported to:\n" + file.getAbsolutePath());
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
showAlert(Alert.AlertType.ERROR, "Export Failed",
|
||||||
|
"Could not write file: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAlert(Alert.AlertType type, String title, String message) {
|
||||||
|
Alert alert = new Alert(type);
|
||||||
|
alert.setTitle(title);
|
||||||
|
alert.setHeaderText(null);
|
||||||
|
alert.setContentText(message);
|
||||||
|
alert.showAndWait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,72 +4,40 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a course in the exam scheduling system.
|
* Represents a course with exam scheduling details.
|
||||||
* Contains course information and exam scheduling details.
|
|
||||||
*/
|
*/
|
||||||
public class Course {
|
public class Course {
|
||||||
private String courseCode;
|
private String courseCode;
|
||||||
private List<String> enrolledStudents;
|
private List<String> enrolledStudents;
|
||||||
|
private int examDay = -1;
|
||||||
// Exam schedule fields (index-based)
|
private int examTimeSlot = -1;
|
||||||
private int examDay; // -1 if not scheduled, 0-based day index
|
private String assignedClassroom;
|
||||||
private int examTimeSlot; // -1 if not scheduled, 0-based slot index
|
|
||||||
private String assignedClassroom; // null if not scheduled
|
|
||||||
|
|
||||||
// Legacy field for backward compatibility
|
|
||||||
private String examDateTime; // null if not scheduled (string format)
|
|
||||||
|
|
||||||
public Course(String courseCode) {
|
public Course(String courseCode) {
|
||||||
this.courseCode = courseCode;
|
this.courseCode = courseCode;
|
||||||
this.enrolledStudents = new ArrayList<>();
|
this.enrolledStudents = new ArrayList<>();
|
||||||
this.examDay = -1;
|
|
||||||
this.examTimeSlot = -1;
|
|
||||||
this.assignedClassroom = null;
|
|
||||||
this.examDateTime = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if this course has been scheduled for an exam.
|
|
||||||
*/
|
|
||||||
public boolean isScheduled() {
|
public boolean isScheduled() {
|
||||||
return examDay >= 0 && examTimeSlot >= 0 && assignedClassroom != null;
|
return examDay >= 0 && examTimeSlot >= 0 && assignedClassroom != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the exam schedule for this course.
|
|
||||||
*/
|
|
||||||
public void clearSchedule() {
|
public void clearSchedule() {
|
||||||
this.examDay = -1;
|
this.examDay = -1;
|
||||||
this.examTimeSlot = -1;
|
this.examTimeSlot = -1;
|
||||||
this.assignedClassroom = null;
|
this.assignedClassroom = null;
|
||||||
this.examDateTime = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the complete exam schedule.
|
|
||||||
*
|
|
||||||
* @param day Day index (0-based)
|
|
||||||
* @param timeSlot Time slot index (0-based)
|
|
||||||
* @param classroomId Classroom ID
|
|
||||||
*/
|
|
||||||
public void setExamSchedule(int day, int timeSlot, String classroomId) {
|
public void setExamSchedule(int day, int timeSlot, String classroomId) {
|
||||||
this.examDay = day;
|
this.examDay = day;
|
||||||
this.examTimeSlot = timeSlot;
|
this.examTimeSlot = timeSlot;
|
||||||
this.assignedClassroom = classroomId;
|
this.assignedClassroom = classroomId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a unique key for this course's time slot.
|
|
||||||
*/
|
|
||||||
public String getTimeSlotKey() {
|
public String getTimeSlotKey() {
|
||||||
if (!isScheduled()) {
|
return isScheduled() ? "D" + examDay + "_S" + examTimeSlot : null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return "D" + examDay + "_S" + examTimeSlot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic getters and setters
|
|
||||||
|
|
||||||
public String getCourseCode() {
|
public String getCourseCode() {
|
||||||
return courseCode;
|
return courseCode;
|
||||||
}
|
}
|
||||||
@@ -82,14 +50,13 @@ public class Course {
|
|||||||
return enrolledStudents;
|
return enrolledStudents;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnrolledStudents(List<String> enrolledStudents) {
|
public void setEnrolledStudents(List<String> students) {
|
||||||
this.enrolledStudents = enrolledStudents;
|
this.enrolledStudents = students;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addStudent(String studentId) {
|
public void addStudent(String studentId) {
|
||||||
if (!enrolledStudents.contains(studentId)) {
|
if (!enrolledStudents.contains(studentId))
|
||||||
enrolledStudents.add(studentId);
|
enrolledStudents.add(studentId);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeStudent(String studentId) {
|
public void removeStudent(String studentId) {
|
||||||
@@ -100,8 +67,6 @@ public class Course {
|
|||||||
return enrolledStudents.size();
|
return enrolledStudents.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule field getters and setters
|
|
||||||
|
|
||||||
public int getExamDay() {
|
public int getExamDay() {
|
||||||
return examDay;
|
return examDay;
|
||||||
}
|
}
|
||||||
@@ -114,24 +79,16 @@ public class Course {
|
|||||||
return examTimeSlot;
|
return examTimeSlot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExamTimeSlot(int examTimeSlot) {
|
public void setExamTimeSlot(int slot) {
|
||||||
this.examTimeSlot = examTimeSlot;
|
this.examTimeSlot = slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAssignedClassroom() {
|
public String getAssignedClassroom() {
|
||||||
return assignedClassroom;
|
return assignedClassroom;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAssignedClassroom(String assignedClassroom) {
|
public void setAssignedClassroom(String classroom) {
|
||||||
this.assignedClassroom = assignedClassroom;
|
this.assignedClassroom = classroom;
|
||||||
}
|
|
||||||
|
|
||||||
public String getExamDateTime() {
|
|
||||||
return examDateTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExamDateTime(String examDateTime) {
|
|
||||||
this.examDateTime = examDateTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -123,7 +123,7 @@
|
|||||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||||
<Label text="📆 Generated Schedule" styleClass="subsection-title"/>
|
<Label text="📆 Generated Schedule" styleClass="subsection-title"/>
|
||||||
<Region HBox.hgrow="ALWAYS"/>
|
<Region HBox.hgrow="ALWAYS"/>
|
||||||
<Button text="📥 Export PDF" styleClass="small-button"/>
|
<Button fx:id="exportButton" text="📥 Export CSV" onAction="#onExportCSV" styleClass="small-button"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
<!-- Schedule Grid -->
|
<!-- Schedule Grid -->
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<Label text="Select Classroom:"/>
|
<Label text="Select Classroom:"/>
|
||||||
<ComboBox fx:id="classroomComboBox" promptText="Choose a classroom..." prefWidth="200"/>
|
<ComboBox fx:id="classroomComboBox" promptText="Choose a classroom..." prefWidth="200"/>
|
||||||
<Button text="Show Schedule" onAction="#onShowSchedule" styleClass="primary-button"/>
|
<Button text="Show Schedule" onAction="#onShowSchedule" styleClass="primary-button"/>
|
||||||
|
<Button text="📥 Export CSV" onAction="#onExportCSV"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
</top>
|
</top>
|
||||||
|
|||||||
@@ -14,7 +14,11 @@
|
|||||||
<padding>
|
<padding>
|
||||||
<Insets top="20" right="20" bottom="20" left="20"/>
|
<Insets top="20" right="20" bottom="20" left="20"/>
|
||||||
</padding>
|
</padding>
|
||||||
<Label text="Course Schedule View" styleClass="section-title"/>
|
<HBox spacing="10" alignment="CENTER_LEFT">
|
||||||
|
<Label text="Course Schedule View" styleClass="section-title"/>
|
||||||
|
<Region HBox.hgrow="ALWAYS"/>
|
||||||
|
<Button text="📥 Export CSV" onAction="#onExportCSV"/>
|
||||||
|
</HBox>
|
||||||
<Label text="All courses and their exam assignments" wrapText="true"/>
|
<Label text="All courses and their exam assignments" wrapText="true"/>
|
||||||
<Separator/>
|
<Separator/>
|
||||||
<TableView fx:id="courseScheduleTable" VBox.vgrow="ALWAYS">
|
<TableView fx:id="courseScheduleTable" VBox.vgrow="ALWAYS">
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<Label text="Select Student:"/>
|
<Label text="Select Student:"/>
|
||||||
<ComboBox fx:id="studentComboBox" promptText="Choose a student..." prefWidth="200"/>
|
<ComboBox fx:id="studentComboBox" promptText="Choose a student..." prefWidth="200"/>
|
||||||
<Button text="Show Schedule" onAction="#onShowSchedule" styleClass="primary-button"/>
|
<Button text="Show Schedule" onAction="#onShowSchedule" styleClass="primary-button"/>
|
||||||
|
<Button text="📥 Export CSV" onAction="#onExportCSV"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
</top>
|
</top>
|
||||||
|
|||||||
Reference in New Issue
Block a user