fix contract
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
from app.models.genetic_algorithm import GeneticAlgorithm, ReservationOptimizer, ReservationRequest, Gene, Individual
|
||||
from app.models.classroom import Classroom
|
||||
from app.models.subject import Commission
|
||||
|
||||
|
||||
class TestGeneticAlgorithm(unittest.TestCase):
|
||||
"""Test cases for the Genetic Algorithm implementation"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures"""
|
||||
self.ga = GeneticAlgorithm(population_size=10, generations=5)
|
||||
|
||||
# Mock classrooms
|
||||
self.classrooms = [
|
||||
Classroom(id=1, code="A101", building="A", capacity=30, is_active=True),
|
||||
Classroom(id=2, code="A102", building="A", capacity=50, is_active=True),
|
||||
Classroom(id=3, code="B101", building="B", capacity=25, is_active=True),
|
||||
Classroom(id=4, code="B102", building="B", capacity=40, is_active=True),
|
||||
]
|
||||
|
||||
# Mock reservation requests
|
||||
start_time = datetime(2024, 1, 15, 9, 0, 0)
|
||||
end_time = datetime(2024, 1, 15, 11, 0, 0)
|
||||
|
||||
self.requests = [
|
||||
ReservationRequest(
|
||||
commission_id=1,
|
||||
expected_attendees=25,
|
||||
purpose="Math Class",
|
||||
preferred_start_time=start_time,
|
||||
preferred_end_time=end_time,
|
||||
priority=1,
|
||||
flexibility_hours=2
|
||||
),
|
||||
ReservationRequest(
|
||||
commission_id=2,
|
||||
expected_attendees=35,
|
||||
purpose="Physics Lab",
|
||||
preferred_start_time=start_time,
|
||||
preferred_end_time=end_time,
|
||||
priority=1,
|
||||
flexibility_hours=2
|
||||
),
|
||||
ReservationRequest(
|
||||
commission_id=3,
|
||||
expected_attendees=20,
|
||||
purpose="Chemistry Class",
|
||||
preferred_start_time=start_time,
|
||||
preferred_end_time=end_time,
|
||||
priority=1,
|
||||
flexibility_hours=2
|
||||
),
|
||||
]
|
||||
|
||||
def test_individual_fitness_calculation(self):
|
||||
"""Test fitness calculation for an individual"""
|
||||
# Create an individual with some genes
|
||||
genes = [
|
||||
Gene(
|
||||
commission_id=1,
|
||||
classroom_id=1, # Capacity 30 for 25 students
|
||||
start_time=datetime(2024, 1, 15, 9, 0, 0),
|
||||
end_time=datetime(2024, 1, 15, 11, 0, 0),
|
||||
expected_attendees=25,
|
||||
purpose="Math Class"
|
||||
),
|
||||
Gene(
|
||||
commission_id=2,
|
||||
classroom_id=2, # Capacity 50 for 35 students
|
||||
start_time=datetime(2024, 1, 15, 9, 0, 0),
|
||||
end_time=datetime(2024, 1, 15, 11, 0, 0),
|
||||
expected_attendees=35,
|
||||
purpose="Physics Lab"
|
||||
)
|
||||
]
|
||||
|
||||
individual = Individual(genes)
|
||||
classrooms_dict = {c.id: c for c in self.classrooms}
|
||||
requests_dict = {r.commission_id: r for r in self.requests}
|
||||
|
||||
# Calculate fitness
|
||||
fitness = individual.calculate_fitness(classrooms_dict, requests_dict)
|
||||
|
||||
# Fitness should be positive
|
||||
self.assertGreater(fitness, 0)
|
||||
self.assertEqual(individual.fitness, fitness)
|
||||
|
||||
def test_conflict_detection(self):
|
||||
"""Test time conflict detection"""
|
||||
genes = [
|
||||
Gene(
|
||||
commission_id=1,
|
||||
classroom_id=1,
|
||||
start_time=datetime(2024, 1, 15, 9, 0, 0),
|
||||
end_time=datetime(2024, 1, 15, 11, 0, 0),
|
||||
expected_attendees=25,
|
||||
purpose="Math Class"
|
||||
),
|
||||
Gene(
|
||||
commission_id=2,
|
||||
classroom_id=1, # Same classroom
|
||||
start_time=datetime(2024, 1, 15, 10, 0, 0), # Overlapping time
|
||||
end_time=datetime(2024, 1, 15, 12, 0, 0),
|
||||
expected_attendees=35,
|
||||
purpose="Physics Lab"
|
||||
)
|
||||
]
|
||||
|
||||
individual = Individual(genes)
|
||||
classrooms_dict = {c.id: c for c in self.classrooms}
|
||||
requests_dict = {r.commission_id: r for r in self.requests[:2]}
|
||||
|
||||
# Calculate fitness - should detect conflicts
|
||||
fitness = individual.calculate_fitness(classrooms_dict, requests_dict)
|
||||
|
||||
# Should have conflicts
|
||||
self.assertGreater(len(individual.conflicts), 0)
|
||||
self.assertLess(fitness, 2.0) # Lower fitness due to conflicts
|
||||
|
||||
def test_crossover_operation(self):
|
||||
"""Test crossover operation"""
|
||||
parent1_genes = [
|
||||
Gene(1, 1, datetime.now(), datetime.now() + timedelta(hours=2), 25, "Class 1"),
|
||||
Gene(2, 2, datetime.now(), datetime.now() + timedelta(hours=2), 30, "Class 2")
|
||||
]
|
||||
parent2_genes = [
|
||||
Gene(3, 3, datetime.now(), datetime.now() + timedelta(hours=2), 20, "Class 3"),
|
||||
Gene(4, 4, datetime.now(), datetime.now() + timedelta(hours=2), 35, "Class 4")
|
||||
]
|
||||
|
||||
parent1 = Individual(parent1_genes)
|
||||
parent2 = Individual(parent2_genes)
|
||||
|
||||
child1, child2 = self.ga._crossover(parent1, parent2)
|
||||
|
||||
# Children should have genes from both parents
|
||||
self.assertGreater(len(child1.genes), 0)
|
||||
self.assertGreater(len(child2.genes), 0)
|
||||
|
||||
# Total genes should equal sum of parents
|
||||
total_genes = len(child1.genes) + len(child2.genes)
|
||||
self.assertEqual(total_genes, len(parent1_genes) + len(parent2_genes))
|
||||
|
||||
def test_tournament_selection(self):
|
||||
"""Test tournament selection"""
|
||||
population = []
|
||||
for i in range(5):
|
||||
genes = [
|
||||
Gene(i, 1, datetime.now(), datetime.now() + timedelta(hours=2), 25, f"Class {i}")
|
||||
]
|
||||
individual = Individual(genes)
|
||||
individual.fitness = i * 0.1 # Different fitness values
|
||||
population.append(individual)
|
||||
|
||||
selected = self.ga._tournament_selection(population)
|
||||
|
||||
# Should return an individual from the population
|
||||
self.assertIn(selected, population)
|
||||
|
||||
def test_capacity_score_calculation(self):
|
||||
"""Test capacity score calculation"""
|
||||
# Perfect match
|
||||
individual = Individual([])
|
||||
score = individual._calculate_capacity_score(self.classrooms[0], 30) #capacity 30, students 30
|
||||
self.assertEqual(score, 1.0)
|
||||
|
||||
# Underfilled but efficient (27/30 = 11.1% waste, goes to next category)
|
||||
score = individual._calculate_capacity_score(self.classrooms[0], 27) # capacity 30, students 27
|
||||
self.assertEqual(score, 0.8)
|
||||
|
||||
# Overfilled
|
||||
score = individual._calculate_capacity_score(self.classrooms[0], 35) # capacity 30, students 35
|
||||
self.assertEqual(score, 0.0)
|
||||
|
||||
def test_optimization_process(self):
|
||||
"""Test the complete optimization process"""
|
||||
result = self.ga.optimize_reservations(
|
||||
self.requests,
|
||||
self.classrooms,
|
||||
datetime(2024, 1, 15),
|
||||
datetime(2024, 1, 20)
|
||||
)
|
||||
|
||||
# Should return best individual
|
||||
self.assertIsInstance(result, Individual)
|
||||
self.assertGreaterEqual(result.fitness, 0)
|
||||
self.assertGreater(len(result.genes), 0)
|
||||
|
||||
def test_reservation_optimizer_interface(self):
|
||||
"""Test the high-level optimizer interface"""
|
||||
optimizer = ReservationOptimizer()
|
||||
|
||||
# Test the interface methods exist
|
||||
self.assertTrue(hasattr(optimizer, 'optimize_schedule'))
|
||||
self.assertTrue(hasattr(optimizer, 'apply_optimized_reservations'))
|
||||
self.assertTrue(hasattr(optimizer, '_calculate_preferred_time'))
|
||||
|
||||
def test_reservation_request_validation(self):
|
||||
"""Test reservation request validation"""
|
||||
request = ReservationRequest(
|
||||
commission_id=1,
|
||||
expected_attendees=25,
|
||||
purpose="Test Class",
|
||||
preferred_start_time=datetime.now(),
|
||||
preferred_end_time=datetime.now() + timedelta(hours=2)
|
||||
)
|
||||
|
||||
# Should have required attributes
|
||||
self.assertEqual(request.commission_id, 1)
|
||||
self.assertEqual(request.expected_attendees, 25)
|
||||
self.assertEqual(request.purpose, "Test Class")
|
||||
self.assertEqual(request.priority, 1) # Default value
|
||||
self.assertEqual(request.flexibility_hours, 2) # Default value
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user