class GeneticAlgorithmManager { constructor() { this.apiBaseUrl = '/api/genetic'; this.setupEventListeners(); } setupEventListeners() { // Initialize any UI elements if needed document.addEventListener('DOMContentLoaded', () => { this.initializeUI(); }); } initializeUI() { // Set up event listeners for genetic algorithm buttons const optimizeBtn = document.getElementById('optimize-assignments-btn'); const previewBtn = document.getElementById('preview-optimization-btn'); const applyBtn = document.getElementById('apply-optimization-btn'); if (optimizeBtn) { optimizeBtn.addEventListener('click', () => this.handleOptimizeReservations()); } if (previewBtn) { previewBtn.addEventListener('click', () => this.handlePreviewOptimization()); } if (applyBtn) { applyBtn.addEventListener('click', () => this.handleApplyOptimization()); } } async optimizeReservations(commissionIds, startDate = null, endDate = null) { try { const requestBody = { commission_ids: commissionIds }; if (startDate) { requestBody.start_date = startDate; } if (endDate) { requestBody.end_date = endDate; } const response = await fetch(`${this.apiBaseUrl}/optimize`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCSRFToken() }, body: JSON.stringify(requestBody) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Optimization failed'); } return await response.json(); } catch (error) { console.error('Error optimizing reservations:', error); throw error; } } async previewOptimization(commissionIds) { try { const response = await fetch(`${this.apiBaseUrl}/preview-optimization`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCSRFToken() }, body: JSON.stringify({ commission_ids: commissionIds, dry_run: true }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Preview failed'); } return await response.json(); } catch (error) { console.error('Error previewing optimization:', error); throw error; } } async applyOptimization(reservations) { try { const response = await fetch(`${this.apiBaseUrl}/apply-optimization`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCSRFToken() }, body: JSON.stringify({ reservations: reservations }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Failed to apply optimization'); } return await response.json(); } catch (error) { console.error('Error applying optimization:', error); throw error; } } async getAlgorithmStatus() { try { const response = await fetch(`${this.apiBaseUrl}/algorithm-status`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCSRFToken() } }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Failed to get algorithm status'); } return await response.json(); } catch (error) { console.error('Error getting algorithm status:', error); throw error; } } async handleOptimizeReservations() { try { // Get selected commissions from UI const commissionIds = this.getSelectedCommissionIds(); if (commissionIds.length === 0) { this.showAlert('Please select at least one commission to optimize.', 'warning'); return; } this.showLoading(true); const result = await this.optimizeReservations(commissionIds); if (result.success) { this.displayOptimizationResults(result.data); this.showAlert('Optimization completed successfully!', 'success'); } else { this.showAlert('Optimization failed: ' + (result.error || 'Unknown error'), 'danger'); } } catch (error) { this.showAlert('Error during optimization: ' + error.message, 'danger'); } finally { this.showLoading(false); } } async handlePreviewOptimization() { try { const commissionIds = this.getSelectedCommissionIds(); if (commissionIds.length === 0) { this.showAlert('Please select at least one commission to preview.', 'warning'); return; } this.showLoading(true); const result = await this.previewOptimization(commissionIds); if (result.success) { this.displayPreviewResults(result.data); this.showAlert('Preview generated successfully!', 'success'); } else { this.showAlert('Preview failed: ' + (result.error || 'Unknown error'), 'danger'); } } catch (error) { this.showAlert('Error during preview: ' + error.message, 'danger'); } finally { this.showLoading(false); } } async handleApplyOptimization() { try { const pendingReservations = this.getPendingReservations(); if (!pendingReservations || pendingReservations.length === 0) { this.showAlert('No pending reservations to apply.', 'warning'); return; } if (!confirm('Are you sure you want to apply these reservations? This will create actual reservation records.')) { return; } this.showLoading(true); const result = await this.applyOptimization(pendingReservations); if (result.success) { this.showAlert(`Successfully applied ${result.data.applied_count} reservations!`, 'success'); this.clearPendingReservations(); // Redirect to reservations page window.location.href = '/schedule'; } else { this.showAlert('Failed to apply reservations: ' + (result.error || 'Unknown error'), 'danger'); } } catch (error) { this.showAlert('Error applying reservations: ' + error.message, 'danger'); } finally { this.showLoading(false); } } getSelectedCommissionIds() { const checkboxes = document.querySelectorAll('input[name="commission_ids"]:checked'); return Array.from(checkboxes).map(cb => parseInt(cb.value)); } getPendingReservations() { // Try to get from localStorage or a hidden form field const stored = localStorage.getItem('pending_optimizations'); return stored ? JSON.parse(stored) : null; } storePendingReservations(reservations) { localStorage.setItem('pending_optimizations', JSON.stringify(reservations)); } clearPendingReservations() { localStorage.removeItem('pending_optimizations'); } displayOptimizationResults(data) { // Store the results for potential application this.storePendingReservations(data.reservations); // Create results display const resultsContainer = document.getElementById('optimization-results'); if (!resultsContainer) return; const scorePercentage = (data.fitness_score * 100).toFixed(1); resultsContainer.innerHTML = `
Optimization Results

${data.assigned_reservations}

Reservations Assigned

${scorePercentage}%

Fitness Score

${data.total_requests}

Total Requests

${data.conflicts.length > 0 ? `
Warning: ${data.conflicts.length} potential conflicts detected.
` : ''}
`; } displayPreviewResults(data) { const previewContainer = document.getElementById('preview-results'); if (!previewContainer) return; const commissionsHtml = data.commission_details.map(commission => ` ${commission.code} ${commission.name} ${commission.current_students}/${commission.max_students} `).join(''); const optimization = data.optimization_result; previewContainer.innerHTML = `
Optimization Preview
Commissions to Optimize
${commissionsHtml}
Code Name Students
Expected Results
  • Reservations Created: ${optimization.assigned_reservations}
  • Fitness Score: ${(optimization.fitness_score * 100).toFixed(1)}%
  • Conflicts: ${optimization.conflicts.length}
`; } viewReservationDetails() { const reservations = this.getPendingReservations(); if (!reservations || reservations.length === 0) { this.showAlert('No reservation details available.', 'info'); return; } // Create modal or redirect to detailed view const detailsHtml = reservations.map((res, index) => ` ${index + 1} ${res.purpose} Classroom ${res.classroom_id} ${new Date(res.start_time).toLocaleString()} ${new Date(res.end_time).toLocaleString()} ${res.expected_attendees} `).join(''); const modal = document.getElementById('detailsModal'); if (modal) { modal.querySelector('.modal-body').innerHTML = `
${detailsHtml}
# Purpose Classroom Start Time End Time Attendees
`; const bootstrapModal = new bootstrap.Modal(modal); bootstrapModal.show(); } } clearResults() { const resultsContainer = document.getElementById('optimization-results'); if (resultsContainer) { resultsContainer.innerHTML = ''; } this.clearPendingReservations(); } clearPreview() { const previewContainer = document.getElementById('preview-results'); if (previewContainer) { previewContainer.innerHTML = ''; } } showLoading(show) { const loadingSpinner = document.getElementById('loading-spinner'); if (loadingSpinner) { loadingSpinner.style.display = show ? 'block' : 'none'; } } showAlert(message, type = 'info') { // Create and show alert const alertContainer = document.getElementById('alert-container'); if (!alertContainer) return; const alertHtml = ` `; alertContainer.insertAdjacentHTML('beforeend', alertHtml); // Auto-dismiss after 5 seconds setTimeout(() => { const alert = alertContainer.lastElementChild; if (alert) { const bsAlert = new bootstrap.Alert(alert); bsAlert.close(); } }, 5000); } getCSRFToken() { // Get CSRF token from meta tag or cookie const metaTag = document.querySelector('meta[name="csrf-token"]'); if (metaTag) { return metaTag.getAttribute('content'); } // Fallback to cookie const cookies = document.cookie.split(';'); for (let cookie of cookies) { const [name, value] = cookie.trim().split('='); if (name === 'csrf_token') { return decodeURIComponent(value); } } return ''; } } // Initialize the genetic algorithm manager const geneticManager = new GeneticAlgorithmManager(); // Export for global access window.geneticManager = geneticManager;