mirror of
https://github.com/sabazadam/Se302.git
synced 2025-12-31 12:21:22 +00:00
Rescheduling button added the calender is updated with new version
This commit is contained in:
@@ -49,6 +49,8 @@ public class ScheduleCalendarController {
|
|||||||
@FXML
|
@FXML
|
||||||
private Button generateButton;
|
private Button generateButton;
|
||||||
@FXML
|
@FXML
|
||||||
|
private Button regenerateButton;
|
||||||
|
@FXML
|
||||||
private Button cancelButton;
|
private Button cancelButton;
|
||||||
@FXML
|
@FXML
|
||||||
private Label statusLabel;
|
private Label statusLabel;
|
||||||
@@ -251,24 +253,81 @@ public class ScheduleCalendarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startGeneration(ScheduleConfiguration config) {
|
private void startGeneration(ScheduleConfiguration config) {
|
||||||
|
startGeneration(config, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regenerate the schedule with randomization to produce a different result.
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
private void onRegenerateSchedule() {
|
||||||
|
// Validate data
|
||||||
|
if (dataManager.getTotalCourses() == 0) {
|
||||||
|
showAlert(Alert.AlertType.WARNING, "No Data",
|
||||||
|
"Please import course data before generating a schedule.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataManager.getTotalClassrooms() == 0) {
|
||||||
|
showAlert(Alert.AlertType.WARNING, "No Classrooms",
|
||||||
|
"Please import classroom data before generating a schedule.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate configuration
|
||||||
|
if (startDatePicker.getValue() == null) {
|
||||||
|
showAlert(Alert.AlertType.WARNING, "Missing Date",
|
||||||
|
"Please select a start date for the exam period.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build configuration
|
||||||
|
ScheduleConfiguration config = buildConfiguration();
|
||||||
|
|
||||||
|
// Validate total slots
|
||||||
|
int totalSlots = config.getTotalSlots();
|
||||||
|
int totalCourses = dataManager.getTotalCourses();
|
||||||
|
int totalClassrooms = dataManager.getTotalClassrooms();
|
||||||
|
|
||||||
|
if (totalSlots * totalClassrooms < totalCourses) {
|
||||||
|
showAlert(Alert.AlertType.WARNING, "Insufficient Capacity",
|
||||||
|
String.format(
|
||||||
|
"Not enough time slots! You have %d courses but only %d total capacity (%d slots × %d classrooms).\n\nPlease increase days or slots per day.",
|
||||||
|
totalCourses, totalSlots * totalClassrooms, totalSlots, totalClassrooms));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start generation with randomization enabled
|
||||||
|
startGeneration(config, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startGeneration(ScheduleConfiguration config, boolean useRandomization) {
|
||||||
// Update UI for generation
|
// Update UI for generation
|
||||||
generateButton.setDisable(true);
|
generateButton.setDisable(true);
|
||||||
|
if (regenerateButton != null)
|
||||||
|
regenerateButton.setDisable(true);
|
||||||
cancelButton.setDisable(false);
|
cancelButton.setDisable(false);
|
||||||
progressContainer.setVisible(true);
|
progressContainer.setVisible(true);
|
||||||
progressContainer.setManaged(true);
|
progressContainer.setManaged(true);
|
||||||
progressBar.setProgress(0);
|
progressBar.setProgress(0);
|
||||||
if (progressIndicator != null)
|
if (progressIndicator != null)
|
||||||
progressIndicator.setProgress(-1.0); // Indeterminate
|
progressIndicator.setProgress(-1.0); // Indeterminate
|
||||||
progressLabel.setText("Generating Schedule...");
|
progressLabel.setText(useRandomization ? "Regenerating Schedule..." : "Generating Schedule...");
|
||||||
if (progressDetailLabel != null)
|
if (progressDetailLabel != null)
|
||||||
progressDetailLabel.setText("Initializing CSP solver...");
|
progressDetailLabel.setText("Initializing CSP solver...");
|
||||||
statusLabel.setText("⏳ Generating schedule...");
|
statusLabel.setText("⏳ " + (useRandomization ? "Regenerating" : "Generating") + " schedule...");
|
||||||
statusLabel.setStyle("-fx-text-fill: #3498db;");
|
statusLabel.setStyle("-fx-text-fill: #3498db;");
|
||||||
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
// Create generator service
|
// Create generator service
|
||||||
generatorService = new ScheduleGeneratorService();
|
generatorService = new ScheduleGeneratorService();
|
||||||
|
|
||||||
|
// Enable randomization if requested (for regenerate button)
|
||||||
|
if (useRandomization) {
|
||||||
|
generatorService.enableRandomization();
|
||||||
|
}
|
||||||
|
|
||||||
generatorService.setProgressListener((progress, message) -> {
|
generatorService.setProgressListener((progress, message) -> {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
progressBar.setProgress(progress);
|
progressBar.setProgress(progress);
|
||||||
@@ -298,6 +357,8 @@ public class ScheduleCalendarController {
|
|||||||
ScheduleConfiguration config, long durationMs) {
|
ScheduleConfiguration config, long durationMs) {
|
||||||
// Reset UI
|
// Reset UI
|
||||||
generateButton.setDisable(false);
|
generateButton.setDisable(false);
|
||||||
|
if (regenerateButton != null)
|
||||||
|
regenerateButton.setDisable(false);
|
||||||
cancelButton.setDisable(true);
|
cancelButton.setDisable(true);
|
||||||
progressContainer.setVisible(false);
|
progressContainer.setVisible(false);
|
||||||
progressContainer.setManaged(false);
|
progressContainer.setManaged(false);
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ public class ScheduleGeneratorService {
|
|||||||
private final DataManager dataManager;
|
private final DataManager dataManager;
|
||||||
private final AtomicBoolean cancelled;
|
private final AtomicBoolean cancelled;
|
||||||
private ProgressListener progressListener;
|
private ProgressListener progressListener;
|
||||||
|
private Random random;
|
||||||
|
private boolean useRandomization = false;
|
||||||
|
|
||||||
// Timeout in milliseconds (30 seconds)
|
// Timeout in milliseconds (30 seconds)
|
||||||
private static final long TIMEOUT_MS = 30000;
|
private static final long TIMEOUT_MS = 30000;
|
||||||
@@ -38,6 +40,31 @@ public class ScheduleGeneratorService {
|
|||||||
public ScheduleGeneratorService() {
|
public ScheduleGeneratorService() {
|
||||||
this.dataManager = DataManager.getInstance();
|
this.dataManager = DataManager.getInstance();
|
||||||
this.cancelled = new AtomicBoolean(false);
|
this.cancelled = new AtomicBoolean(false);
|
||||||
|
this.random = new Random();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable randomization with a new random seed.
|
||||||
|
* Call this before generateSchedule() to get a different schedule each time.
|
||||||
|
*/
|
||||||
|
public void enableRandomization() {
|
||||||
|
this.useRandomization = true;
|
||||||
|
this.random = new Random(); // New seed each time
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable randomization with a specific seed (useful for reproducibility).
|
||||||
|
*/
|
||||||
|
public void enableRandomization(long seed) {
|
||||||
|
this.useRandomization = true;
|
||||||
|
this.random = new Random(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable randomization (use deterministic algorithm).
|
||||||
|
*/
|
||||||
|
public void disableRandomization() {
|
||||||
|
this.useRandomization = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -415,6 +442,7 @@ public class ScheduleGeneratorService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects the best assignment candidate based on the optimization strategy.
|
* Selects the best assignment candidate based on the optimization strategy.
|
||||||
|
* When randomization is enabled, selects randomly from top-scoring candidates.
|
||||||
*/
|
*/
|
||||||
private AssignmentCandidate selectBestCandidate(
|
private AssignmentCandidate selectBestCandidate(
|
||||||
List<AssignmentCandidate> candidates,
|
List<AssignmentCandidate> candidates,
|
||||||
@@ -423,6 +451,11 @@ public class ScheduleGeneratorService {
|
|||||||
Set<String> enrolledStudents,
|
Set<String> enrolledStudents,
|
||||||
Map<String, Set<String>> slotClassrooms) {
|
Map<String, Set<String>> slotClassrooms) {
|
||||||
|
|
||||||
|
// If randomization is enabled, shuffle candidates first to introduce variety
|
||||||
|
if (useRandomization) {
|
||||||
|
Collections.shuffle(candidates, random);
|
||||||
|
}
|
||||||
|
|
||||||
ScheduleConfiguration.OptimizationStrategy strategy = config.getOptimizationStrategy();
|
ScheduleConfiguration.OptimizationStrategy strategy = config.getOptimizationStrategy();
|
||||||
|
|
||||||
// Handle default/null strategy
|
// Handle default/null strategy
|
||||||
@@ -461,10 +494,74 @@ public class ScheduleGeneratorService {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When randomization is enabled, pick randomly from top candidates with similar
|
||||||
|
// scores
|
||||||
|
if (useRandomization && candidates.size() > 1) {
|
||||||
|
return selectFromTopCandidates(candidates, studentExams, enrolledStudents, strategy, slotClassrooms);
|
||||||
|
}
|
||||||
|
|
||||||
// Return best (first)
|
// Return best (first)
|
||||||
return candidates.get(0);
|
return candidates.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects randomly from candidates with similar (near-optimal) scores.
|
||||||
|
* This introduces variety while still respecting the optimization strategy.
|
||||||
|
*/
|
||||||
|
private AssignmentCandidate selectFromTopCandidates(
|
||||||
|
List<AssignmentCandidate> candidates,
|
||||||
|
Map<String, List<int[]>> studentExams,
|
||||||
|
Set<String> enrolledStudents,
|
||||||
|
ScheduleConfiguration.OptimizationStrategy strategy,
|
||||||
|
Map<String, Set<String>> slotClassrooms) {
|
||||||
|
|
||||||
|
// Calculate the score of the best candidate
|
||||||
|
double bestScore = calculateCandidateScore(candidates.get(0), studentExams, enrolledStudents, strategy,
|
||||||
|
slotClassrooms);
|
||||||
|
|
||||||
|
// Tolerance for considering candidates "equally good" (within 10% of best)
|
||||||
|
double tolerance = Math.abs(bestScore) * 0.1;
|
||||||
|
if (tolerance < 1.0)
|
||||||
|
tolerance = 1.0; // Minimum tolerance
|
||||||
|
|
||||||
|
// Collect all candidates within tolerance of the best score
|
||||||
|
List<AssignmentCandidate> topCandidates = new ArrayList<>();
|
||||||
|
for (AssignmentCandidate candidate : candidates) {
|
||||||
|
double score = calculateCandidateScore(candidate, studentExams, enrolledStudents, strategy, slotClassrooms);
|
||||||
|
if (Math.abs(score - bestScore) <= tolerance) {
|
||||||
|
topCandidates.add(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick randomly from top candidates
|
||||||
|
if (topCandidates.isEmpty()) {
|
||||||
|
return candidates.get(0);
|
||||||
|
}
|
||||||
|
return topCandidates.get(random.nextInt(topCandidates.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate a score for a candidate based on the optimization strategy.
|
||||||
|
*/
|
||||||
|
private double calculateCandidateScore(
|
||||||
|
AssignmentCandidate candidate,
|
||||||
|
Map<String, List<int[]>> studentExams,
|
||||||
|
Set<String> enrolledStudents,
|
||||||
|
ScheduleConfiguration.OptimizationStrategy strategy,
|
||||||
|
Map<String, Set<String>> slotClassrooms) {
|
||||||
|
|
||||||
|
switch (strategy) {
|
||||||
|
case MINIMIZE_DAYS:
|
||||||
|
return candidate.day * 100.0 + candidate.slot;
|
||||||
|
case MINIMIZE_CLASSROOMS:
|
||||||
|
String key = candidate.day + "-" + candidate.slot;
|
||||||
|
return slotClassrooms.getOrDefault(key, Collections.emptySet()).size() * 100.0 + candidate.day;
|
||||||
|
case STUDENT_FRIENDLY:
|
||||||
|
default:
|
||||||
|
return calculateStudentFriendlyPenalty(candidate, studentExams, enrolledStudents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates penalty for student-friendly reference.
|
* Calculates penalty for student-friendly reference.
|
||||||
* Higher penalty = worse for students.
|
* Higher penalty = worse for students.
|
||||||
|
|||||||
@@ -90,6 +90,8 @@
|
|||||||
<HBox spacing="16" alignment="CENTER_LEFT">
|
<HBox spacing="16" alignment="CENTER_LEFT">
|
||||||
<Button fx:id="generateButton" text="🚀 Generate Schedule"
|
<Button fx:id="generateButton" text="🚀 Generate Schedule"
|
||||||
onAction="#onGenerateSchedule" styleClass="primary-button"/>
|
onAction="#onGenerateSchedule" styleClass="primary-button"/>
|
||||||
|
<Button fx:id="regenerateButton" text="🔄 Regenerate"
|
||||||
|
onAction="#onRegenerateSchedule" styleClass="secondary-button"/>
|
||||||
<Button fx:id="cancelButton" text="Cancel" onAction="#onCancelGeneration"
|
<Button fx:id="cancelButton" text="Cancel" onAction="#onCancelGeneration"
|
||||||
disable="true"/>
|
disable="true"/>
|
||||||
<Region HBox.hgrow="ALWAYS"/>
|
<Region HBox.hgrow="ALWAYS"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user