Refactored controller classes and the Course class for better maintainability

This commit is contained in:
haxala1r
2025-12-18 16:19:32 +03:00
parent 39ad08d836
commit 463d8b39ec
5 changed files with 133 additions and 202 deletions

View File

@@ -15,20 +15,33 @@ import org.example.se302.service.DataManager;
*/
public class CoursesController {
@FXML private TextField searchField;
@FXML private Label resultCountLabel;
@FXML private TableView<Course> coursesTable;
@FXML 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 TextField searchField;
@FXML
private Label resultCountLabel;
@FXML
private TableView<Course> coursesTable;
@FXML
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 private Label studentListTitleLabel;
@FXML private TableView<String> enrolledStudentsTable;
@FXML private TableColumn<String, String> enrolledStudentIdColumn;
@FXML private Label enrolledCountLabel;
@FXML
private VBox studentListPanel;
@FXML
private Label studentListTitleLabel;
@FXML
private TableView<String> enrolledStudentsTable;
@FXML
private TableColumn<String, String> enrolledStudentIdColumn;
@FXML
private Label enrolledCountLabel;
private DataManager dataManager;
private FilteredList<Course> filteredCourses;
@@ -38,11 +51,10 @@ public class CoursesController {
dataManager = DataManager.getInstance();
// Set up table columns
courseCodeColumn.setCellValueFactory(cellData ->
new SimpleStringProperty(cellData.getValue().getCourseCode()));
courseCodeColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getCourseCode()));
studentCountColumn.setCellValueFactory(cellData ->
new SimpleIntegerProperty(cellData.getValue().getEnrolledStudentsCount()));
studentCountColumn.setCellValueFactory(
cellData -> new SimpleIntegerProperty(cellData.getValue().getEnrolledStudentsCount()));
classroomColumn.setCellValueFactory(cellData -> {
String classroom = cellData.getValue().getAssignedClassroom();
@@ -50,8 +62,12 @@ public class CoursesController {
});
examDateColumn.setCellValueFactory(cellData -> {
String examDate = cellData.getValue().getExamDateTime();
return new SimpleStringProperty(examDate != null ? examDate : "Not Scheduled");
Course course = cellData.getValue();
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
@@ -73,8 +89,7 @@ public class CoursesController {
});
// Set up enrolled students table column
enrolledStudentIdColumn.setCellValueFactory(cellData ->
new SimpleStringProperty(cellData.getValue()));
enrolledStudentIdColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue()));
// Set up filtered list
filteredCourses = new FilteredList<>(dataManager.getCourses(), p -> true);
@@ -106,14 +121,13 @@ public class CoursesController {
filteredCourses.setPredicate(course -> true);
} else {
String lowerCaseFilter = searchText.toLowerCase().trim();
filteredCourses.setPredicate(course ->
course.getCourseCode().toLowerCase().contains(lowerCaseFilter)
);
filteredCourses.setPredicate(course -> course.getCourseCode().toLowerCase().contains(lowerCaseFilter));
}
}
private void showEnrolledStudents(Course course) {
if (course == null) return;
if (course == null)
return;
studentListTitleLabel.setText("Students Enrolled in " + course.getCourseCode());
enrolledStudentsTable.setItems(FXCollections.observableArrayList(course.getEnrolledStudents()));

View File

@@ -150,78 +150,62 @@ public class ScheduleClassroomController {
ScheduleConfiguration config = dataManager.getActiveConfiguration();
ObservableList<ClassroomSlotEntry> entries = FXCollections.observableArrayList();
int totalSlots = 0;
int usedSlots = 0;
int totalStudents = 0;
// Find all courses scheduled in this classroom
for (Course course : dataManager.getCourses()) {
if (course.isScheduled() &&
selected.getClassroomId().equals(course.getAssignedClassroom())) {
if (!course.isScheduled() || !selected.getClassroomId().equals(course.getAssignedClassroom()))
continue;
int dayIndex = course.getExamDay();
int slotIndex = course.getExamTimeSlot();
// Format date
String dateStr;
if (config != null && config.getStartDate() != null) {
LocalDate examDate = config.getStartDate().plusDays(dayIndex);
dateStr = examDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
} else {
dateStr = "Day " + (dayIndex + 1);
}
// Format time
String timeStr;
if (config != null) {
TimeSlot timeSlot = config.getTimeSlot(dayIndex, slotIndex);
if (timeSlot != null) {
timeStr = timeSlot.getStartTime() + " - " + timeSlot.getEndTime();
} else {
timeStr = "Slot " + (slotIndex + 1);
}
} else {
timeStr = "Slot " + (slotIndex + 1);
}
// Calculate utilization
int studentCount = course.getEnrolledStudentsCount();
int capacity = selected.getCapacity();
int utilizationPercent = capacity > 0 ? (studentCount * 100) / capacity : 0;
String utilizationStr = utilizationPercent + "%";
int utilization = selected.getCapacity() > 0
? (studentCount * 100) / selected.getCapacity()
: 0;
entries.add(new ClassroomSlotEntry(
dateStr, timeStr, course.getCourseCode(),
studentCount, utilizationStr, utilizationPercent,
dayIndex, slotIndex));
formatDate(config, dayIndex),
formatTime(config, dayIndex, slotIndex),
course.getCourseCode(), studentCount, utilization + "%",
utilization, dayIndex, slotIndex));
usedSlots++;
totalStudents += studentCount;
}
}
// Calculate total possible slots
if (config != null) {
totalSlots = config.getNumDays() * config.getSlotsPerDay();
}
// Sort by day then slot
entries.sort(Comparator.comparingInt(ClassroomSlotEntry::getDayIndex)
.thenComparingInt(ClassroomSlotEntry::getSlotIndex));
scheduleTable.setItems(entries);
// Update overall utilization label
int totalSlots = config != null ? config.getNumDays() * config.getSlotsPerDay() : 0;
if (totalSlots > 0) {
int overallUtilization = (usedSlots * 100) / totalSlots;
int overallUtil = (usedSlots * 100) / totalSlots;
utilizationLabel.setText(String.format(
"Overall Utilization: %d%% (%d/%d slots used, %d total students)",
overallUtilization, usedSlots, totalSlots, totalStudents));
overallUtil, usedSlots, totalSlots, totalStudents));
} else {
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
public static class ClassroomSlotEntry {
private final String date;

View File

@@ -151,53 +151,48 @@ public class ScheduleCourseController {
ObservableList<CourseScheduleEntry> entries = FXCollections.observableArrayList();
for (Course course : dataManager.getCourses()) {
String courseCode = course.getCourseCode();
int enrolled = course.getEnrolledStudentsCount();
String dateStr = "Not Scheduled";
String timeStr = "-";
String classroomStr = "-";
int dayIndex = Integer.MAX_VALUE;
int slotIndex = Integer.MAX_VALUE;
// Check if this course has been scheduled
if (course.isScheduled()) {
dayIndex = course.getExamDay();
slotIndex = course.getExamTimeSlot();
classroomStr = course.getAssignedClassroom();
// Format date using configuration's start date
if (config != null && config.getStartDate() != null) {
LocalDate examDate = config.getStartDate().plusDays(dayIndex);
dateStr = examDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy (EEEE)"));
} else {
dateStr = "Day " + (dayIndex + 1);
dateStr = formatDate(config, dayIndex);
timeStr = formatTime(config, dayIndex, slotIndex);
}
// Format time using configuration's time slots
if (config != null) {
TimeSlot timeSlot = config.getTimeSlot(dayIndex, slotIndex);
if (timeSlot != null) {
timeStr = timeSlot.getStartTime() + " - " + timeSlot.getEndTime();
} else {
timeStr = "Slot " + (slotIndex + 1);
}
} else {
timeStr = "Slot " + (slotIndex + 1);
}
}
entries.add(new CourseScheduleEntry(courseCode, enrolled, dateStr, timeStr, classroomStr,
entries.add(new CourseScheduleEntry(course.getCourseCode(),
course.getEnrolledStudentsCount(), dateStr, timeStr, classroomStr,
dayIndex, slotIndex));
}
// Sort by day first, then by slot
entries.sort(Comparator.comparingInt(CourseScheduleEntry::getDayIndex)
.thenComparingInt(CourseScheduleEntry::getSlotIndex));
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
public static class CourseScheduleEntry {
private final String courseCode;

View File

@@ -105,77 +105,58 @@ public class ScheduleStudentController {
for (String courseCode : student.getEnrolledCourses()) {
Course course = dataManager.getCourse(courseCode);
if (course != null) {
if (course == null)
continue;
String dateStr = "Not Scheduled";
String timeStr = "-";
String classroom = "-";
int dayIndex = -1;
int slotIndex = -1;
int dayIndex = -1, slotIndex = -1;
if (course.isScheduled()) {
dayIndex = course.getExamDay();
slotIndex = course.getExamTimeSlot();
classroom = course.getAssignedClassroom();
if (config != null) {
TimeSlot slot = config.getTimeSlot(dayIndex, slotIndex);
TimeSlot slot = config != null ? config.getTimeSlot(dayIndex, slotIndex) : null;
if (slot != null) {
dateStr = slot.getDate().toString(); // YYYY-MM-DD
timeStr = slot.getStartTime().toString() + " - " + slot.getEndTime().toString();
dateStr = slot.getDate().toString();
timeStr = slot.getStartTime() + " - " + slot.getEndTime();
} else {
dateStr = "Day " + (dayIndex + 1);
timeStr = "Slot " + (slotIndex + 1);
}
} else {
// Fallback if no config saved
dateStr = "Day " + (dayIndex + 1);
timeStr = "Slot " + (slotIndex + 1);
}
}
entries.add(new CourseScheduleEntry(courseCode, dateStr, timeStr, classroom, dayIndex, slotIndex));
}
}
// Sort by day and time
entries.sort(Comparator.comparingInt(CourseScheduleEntry::getDayIndex)
.thenComparingInt(CourseScheduleEntry::getSlotIndex));
// Analyze for highlights
analyzeSchedule(entries);
scheduleTable.setItems(FXCollections.observableArrayList(entries));
}
private void analyzeSchedule(List<CourseScheduleEntry> entries) {
if (entries.isEmpty())
return;
for (int i = 0; i < entries.size(); i++) {
CourseScheduleEntry current = entries.get(i);
if (current.getDayIndex() == -1)
continue; // Skip unscheduled
continue;
// Check for multiple exams on same day
int examsOnDay = 0;
// Check for multiple exams and conflicts on same day
for (CourseScheduleEntry other : entries) {
if (other.getDayIndex() == current.getDayIndex() && other.getDayIndex() != -1) {
examsOnDay++;
current.isMultipleExamsOnDay = true;
if (other.getSlotIndex() == current.getSlotIndex() && other != current) {
current.hasConflictWarning = true;
}
}
}
if (examsOnDay > 1) {
current.isMultipleExamsOnDay = true;
}
// Check for consecutive days (look at previous scheduled exam)
// Since list is sorted, we can look at previous entry if it exists
// Check for consecutive days with previous exam
if (i > 0) {
CourseScheduleEntry prev = entries.get(i - 1);
if (prev.getDayIndex() != -1 &&
current.getDayIndex() == prev.getDayIndex() + 1) {
if (prev.getDayIndex() != -1 && current.getDayIndex() == prev.getDayIndex() + 1) {
current.isConsecutiveDay = true;
}
}

View File

@@ -4,71 +4,39 @@ import java.util.ArrayList;
import java.util.List;
/**
* Represents a course in the exam scheduling system.
* Contains course information and exam scheduling details.
* Represents a course with exam scheduling details.
*/
public class Course {
private String courseCode;
private List<String> enrolledStudents;
// Exam schedule fields (index-based)
private int examDay; // -1 if not scheduled, 0-based day index
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)
private int examDay = -1;
private int examTimeSlot = -1;
private String assignedClassroom;
public Course(String courseCode) {
this.courseCode = courseCode;
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() {
return examDay >= 0 && examTimeSlot >= 0 && assignedClassroom != null;
}
/**
* Clears the exam schedule for this course.
*/
public void clearSchedule() {
this.examDay = -1;
this.examTimeSlot = -1;
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) {
this.examDay = day;
this.examTimeSlot = timeSlot;
this.assignedClassroom = classroomId;
}
/**
* Gets a unique key for this course's time slot.
*/
public String getTimeSlotKey() {
if (!isScheduled()) {
return null;
return isScheduled() ? "D" + examDay + "_S" + examTimeSlot : null;
}
return "D" + examDay + "_S" + examTimeSlot;
}
// Basic getters and setters
public String getCourseCode() {
return courseCode;
@@ -82,15 +50,14 @@ public class Course {
return enrolledStudents;
}
public void setEnrolledStudents(List<String> enrolledStudents) {
this.enrolledStudents = enrolledStudents;
public void setEnrolledStudents(List<String> students) {
this.enrolledStudents = students;
}
public void addStudent(String studentId) {
if (!enrolledStudents.contains(studentId)) {
if (!enrolledStudents.contains(studentId))
enrolledStudents.add(studentId);
}
}
public void removeStudent(String studentId) {
enrolledStudents.remove(studentId);
@@ -100,8 +67,6 @@ public class Course {
return enrolledStudents.size();
}
// Schedule field getters and setters
public int getExamDay() {
return examDay;
}
@@ -114,24 +79,16 @@ public class Course {
return examTimeSlot;
}
public void setExamTimeSlot(int examTimeSlot) {
this.examTimeSlot = examTimeSlot;
public void setExamTimeSlot(int slot) {
this.examTimeSlot = slot;
}
public String getAssignedClassroom() {
return assignedClassroom;
}
public void setAssignedClassroom(String assignedClassroom) {
this.assignedClassroom = assignedClassroom;
}
public String getExamDateTime() {
return examDateTime;
}
public void setExamDateTime(String examDateTime) {
this.examDateTime = examDateTime;
public void setAssignedClassroom(String classroom) {
this.assignedClassroom = classroom;
}
@Override