fixed typo

This commit is contained in:
2025-10-01 00:10:21 -05:00
parent 30b2b76290
commit bd247fbec6
5 changed files with 128 additions and 10 deletions

View File

@@ -1,5 +1,7 @@
from django.contrib import admin from django.contrib import admin
from django.contrib import messages
from .models import Carrier, AircraftBase, Equipment, Aerodrome, Flight from .models import Carrier, AircraftBase, Equipment, Aerodrome, Flight
from .services import cancel_flight_cascade
# Register your models here. # Register your models here.
admin.site.register(Carrier) admin.site.register(Carrier)
@@ -19,6 +21,7 @@ class FlightAdmin(admin.ModelAdmin): # type: ignore[type-arg]
"arrival_time_display", "arrival_time_display",
"status_display", "status_display",
) )
actions = ["cancel_selected_flights"]
@admin.display(description="Flight") @admin.display(description="Flight")
def flight_number_display(self, obj): def flight_number_display(self, obj):
@@ -31,3 +34,24 @@ class FlightAdmin(admin.ModelAdmin): # type: ignore[type-arg]
@admin.display(description="Status") @admin.display(description="Status")
def status_display(self, obj): def status_display(self, obj):
return obj.status return obj.status
@admin.action(description="Cancel selected flights (and dependent flights)")
def cancel_selected_flights(self, request, queryset):
total_canceled = []
for flight in queryset:
if not flight.canceled:
canceled_flights = cancel_flight_cascade(flight)
total_canceled.extend(canceled_flights)
if total_canceled:
self.message_user(
request,
f"Canceled {len(total_canceled)} flight(s) including dependent flights.",
messages.SUCCESS,
)
else:
self.message_user(
request,
"No flights were canceled (already canceled).",
messages.WARNING,
)

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.6 on 2025-10-01 04:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('simulator', '0004_route_aerodrome_altitude'),
]
operations = [
migrations.AddField(
model_name='flight',
name='canceled',
field=models.BooleanField(default=False),
),
]

View File

@@ -28,6 +28,7 @@ class Flight(models.Model):
validators=[MinValueValidator(1), MaxValueValidator(9999)] validators=[MinValueValidator(1), MaxValueValidator(9999)]
) )
departure_time = models.DateTimeField() departure_time = models.DateTimeField()
canceled = models.BooleanField(default=False)
class Meta: class Meta:
constraints = [ constraints = [
@@ -59,11 +60,9 @@ class Flight(models.Model):
return self.departure_time + self.duration return self.departure_time + self.duration
def clean(self): def clean(self):
# Validate origin and destination are different
if self.origin == self.destination: if self.origin == self.destination:
raise ValidationError("Origin and destination airports cannot be the same.") raise ValidationError("Origin and destination airports cannot be the same.")
# Validate carrier owns the equipment
if self.equipment.owner != self.carrier: if self.equipment.owner != self.carrier:
raise ValidationError( raise ValidationError(
f"{self.equipment} is owned by {self.equipment.owner}, " f"{self.equipment} is owned by {self.equipment.owner}, "
@@ -76,14 +75,18 @@ class Flight(models.Model):
f"maximum range ({self.equipment.model.range_nm} nm)." f"maximum range ({self.equipment.model.range_nm} nm)."
) )
previous_flight = ( previous_flights = Flight.objects.filter(
Flight.objects.filter( equipment=self.equipment,
equipment=self.equipment, departure_time__lt=self.departure_time departure_time__lt=self.departure_time,
) canceled=False,
.order_by("-departure_time") ).exclude(pk=self.pk)
.exclude(pk=self.pk)
.first() previous_flight = None
) latest_arrival = None
for flight in previous_flights:
if latest_arrival is None or flight.arrival_time > latest_arrival:
latest_arrival = flight.arrival_time
previous_flight = flight
if previous_flight: if previous_flight:
if previous_flight.destination != self.origin: if previous_flight.destination != self.origin:
@@ -112,6 +115,8 @@ class Flight(models.Model):
@property @property
def status(self): def status(self):
if self.canceled:
return "Canceled"
now = timezone.now() now = timezone.now()
if now < self.departure_time: if now < self.departure_time:
return "Scheduled" return "Scheduled"

View File

@@ -0,0 +1,3 @@
from .flight_service import cancel_flight_cascade
__all__ = ["cancel_flight_cascade"]

View File

@@ -0,0 +1,68 @@
"""
Flight service module for handling complex flight operations.
"""
from typing import List
from simulator.models.flight import Flight
def cancel_flight_cascade(flight: Flight) -> List[Flight]:
"""
Cancel a flight and all subsequent flights that depend on it.
Returns a list of all canceled flights (including the original).
"""
canceled_flights = []
def cancel_recursive(current_flight: Flight):
"""Recursively cancel flights that depend on the current flight."""
if current_flight.canceled:
return # Already canceled
# Mark flight as canceled
current_flight.canceled = True
current_flight.save(update_fields=["canceled"])
canceled_flights.append(current_flight)
# Find all flights that depart from this flight's destination
# after this flight's arrival time
dependent_flights = Flight.objects.filter(
equipment=current_flight.equipment,
origin=current_flight.destination,
departure_time__gte=current_flight.arrival_time,
canceled=False,
).order_by("departure_time")
# Check if any of these flights actually depend on this flight
for next_flight in dependent_flights:
# Find what the previous non-canceled flight would be if we cancel current_flight
previous_non_canceled = (
Flight.objects.filter(
equipment=current_flight.equipment,
departure_time__lt=next_flight.departure_time,
canceled=False,
)
.exclude(pk=current_flight.pk)
.exclude(pk=next_flight.pk)
)
# Find the one that arrives last
latest_arrival = None
previous_flight = None
for pf in previous_non_canceled:
if latest_arrival is None or pf.arrival_time > latest_arrival:
latest_arrival = pf.arrival_time
previous_flight = pf
# Determine where the equipment would be
if previous_flight:
expected_location = previous_flight.destination
else:
expected_location = current_flight.equipment.base_location
# If the next flight can't depart from its origin, cancel it
if expected_location != next_flight.origin:
cancel_recursive(next_flight)
cancel_recursive(flight)
return canceled_flights