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
|
||||
private Button generateButton;
|
||||
@FXML
|
||||
private Button regenerateButton;
|
||||
@FXML
|
||||
private Button cancelButton;
|
||||
@FXML
|
||||
private Label statusLabel;
|
||||
@@ -251,24 +253,81 @@ public class ScheduleCalendarController {
|
||||
}
|
||||
|
||||
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
|
||||
generateButton.setDisable(true);
|
||||
if (regenerateButton != null)
|
||||
regenerateButton.setDisable(true);
|
||||
cancelButton.setDisable(false);
|
||||
progressContainer.setVisible(true);
|
||||
progressContainer.setManaged(true);
|
||||
progressBar.setProgress(0);
|
||||
if (progressIndicator != null)
|
||||
progressIndicator.setProgress(-1.0); // Indeterminate
|
||||
progressLabel.setText("Generating Schedule...");
|
||||
progressLabel.setText(useRandomization ? "Regenerating Schedule..." : "Generating Schedule...");
|
||||
if (progressDetailLabel != null)
|
||||
progressDetailLabel.setText("Initializing CSP solver...");
|
||||
statusLabel.setText("⏳ Generating schedule...");
|
||||
statusLabel.setText("⏳ " + (useRandomization ? "Regenerating" : "Generating") + " schedule...");
|
||||
statusLabel.setStyle("-fx-text-fill: #3498db;");
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// Create generator service
|
||||
generatorService = new ScheduleGeneratorService();
|
||||
|
||||
// Enable randomization if requested (for regenerate button)
|
||||
if (useRandomization) {
|
||||
generatorService.enableRandomization();
|
||||
}
|
||||
|
||||
generatorService.setProgressListener((progress, message) -> {
|
||||
Platform.runLater(() -> {
|
||||
progressBar.setProgress(progress);
|
||||
@@ -298,6 +357,8 @@ public class ScheduleCalendarController {
|
||||
ScheduleConfiguration config, long durationMs) {
|
||||
// Reset UI
|
||||
generateButton.setDisable(false);
|
||||
if (regenerateButton != null)
|
||||
regenerateButton.setDisable(false);
|
||||
cancelButton.setDisable(true);
|
||||
progressContainer.setVisible(false);
|
||||
progressContainer.setManaged(false);
|
||||
|
||||
@@ -25,6 +25,8 @@ public class ScheduleGeneratorService {
|
||||
private final DataManager dataManager;
|
||||
private final AtomicBoolean cancelled;
|
||||
private ProgressListener progressListener;
|
||||
private Random random;
|
||||
private boolean useRandomization = false;
|
||||
|
||||
// Timeout in milliseconds (30 seconds)
|
||||
private static final long TIMEOUT_MS = 30000;
|
||||
@@ -38,6 +40,31 @@ public class ScheduleGeneratorService {
|
||||
public ScheduleGeneratorService() {
|
||||
this.dataManager = DataManager.getInstance();
|
||||
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.
|
||||
* When randomization is enabled, selects randomly from top-scoring candidates.
|
||||
*/
|
||||
private AssignmentCandidate selectBestCandidate(
|
||||
List<AssignmentCandidate> candidates,
|
||||
@@ -423,6 +451,11 @@ public class ScheduleGeneratorService {
|
||||
Set<String> enrolledStudents,
|
||||
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();
|
||||
|
||||
// Handle default/null strategy
|
||||
@@ -461,10 +494,74 @@ public class ScheduleGeneratorService {
|
||||
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 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.
|
||||
* Higher penalty = worse for students.
|
||||
|
||||
@@ -90,6 +90,8 @@
|
||||
<HBox spacing="16" alignment="CENTER_LEFT">
|
||||
<Button fx:id="generateButton" text="🚀 Generate Schedule"
|
||||
onAction="#onGenerateSchedule" styleClass="primary-button"/>
|
||||
<Button fx:id="regenerateButton" text="🔄 Regenerate"
|
||||
onAction="#onRegenerateSchedule" styleClass="secondary-button"/>
|
||||
<Button fx:id="cancelButton" text="Cancel" onAction="#onCancelGeneration"
|
||||
disable="true"/>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
|
||||
Reference in New Issue
Block a user