Rescheduling button added the calender is updated with new version

This commit is contained in:
sabazadam
2025-12-21 01:03:40 +03:00
parent ba0fdccda1
commit 01068b120a
3 changed files with 162 additions and 2 deletions

View File

@@ -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);

View File

@@ -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.

View File

@@ -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"/>