from django.db import models from .carrier import Carrier from .aircraft import Equipment from .aerodrome import Aerodrome from django.core.validators import MinValueValidator, MaxValueValidator from simulator.utils import haversine_nm from datetime import timedelta from django.core.exceptions import ValidationError from django.utils import timezone class Flight(models.Model): carrier = models.ForeignKey( Carrier, on_delete=models.CASCADE, related_name="flights" ) equipment = models.ForeignKey( Equipment, on_delete=models.CASCADE, related_name="flights" ) origin = models.ForeignKey( Aerodrome, on_delete=models.CASCADE, related_name="departures" ) destination = models.ForeignKey( Aerodrome, on_delete=models.CASCADE, related_name="arrivals" ) flight_number = models.PositiveIntegerField( validators=[MinValueValidator(1), MaxValueValidator(9999)] ) departure_time = models.DateTimeField() class Meta: constraints = [ models.UniqueConstraint( fields=["carrier", "flight_number"], name="unique_flight_per_carrier" ) ] def __str__(self): return f"{self.carrier.icao}{self.flight_number} {self.origin.icao} > {self.destination.icao}" @property def distance_nm(self) -> float: return haversine_nm( self.origin.latitude, self.origin.longitude, self.destination.latitude, self.destination.longitude, ) @property def duration(self) -> timedelta: speed = self.equipment.model.cruise_speed_kt # knots = nm/hr hours = self.distance_nm / speed return timedelta(hours=hours) @property def arrival_time(self): return self.departure_time + self.duration def clean(self): overlapping = Flight.objects.filter( equipment=self.equipment, departure_time__lt=self.arrival_time, arrival_time__gt=self.departure_time, ).exclude(pk=self.pk) if overlapping.exists(): raise ValidationError( f"{self.equipment} is already assigned to another flight in this timeframe." ) @property def status(self): now = timezone.now() if now < self.departure_time: return "Scheduled" elif self.departure_time <= now < self.arrival_time: return "In-Flight" else: return "Arrived"