Major WIP state machine

This commit is contained in:
2026-03-24 11:22:20 -04:00
parent 423023d3d9
commit bb6597ff76
16 changed files with 5781 additions and 234 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ async def create_departure(
current_user: User = Depends(get_current_operator_user)
):
"""Create a new departure record"""
departure = crud_departure.create(db, obj_in=departure_in, created_by=current_user.username)
departure = crud_departure.create(db, obj_in=departure_in, created_by=current_user.username, submitted_via="ADMIN")
# Send real-time update via WebSocket
if hasattr(request.app.state, 'connection_manager'):
+14 -6
View File
@@ -173,10 +173,10 @@ async def get_public_departures(db: Session = Depends(get_db)):
'isDeparture': False
})
# Add departures to other airports with BOOKED_OUT status
# Add departures to other airports with BOOKED_OUT and GROUND status
departures_to_airports = crud_departure.get_multi(
db,
status=DepartureStatus.BOOKED_OUT,
status=None, # Get all statuses
limit=1000
)
@@ -187,17 +187,25 @@ async def get_public_departures(db: Session = Depends(get_db)):
# Convert departures to match the format for display
for dep in departures_to_airports:
# Only include departures booked out today
if not (today_start <= dep.created_dt < today_end):
# Only include departures booked out today and not yet departed
if not (today_start <= dep.created_dt < today_end) or dep.status == DepartureStatus.DEPARTED:
continue
# Map status for display
display_status = 'BOOKED_OUT'
if dep.status == DepartureStatus.GROUND:
display_status = 'CONTACT'
elif dep.status == DepartureStatus.LOCAL:
display_status = 'DEPARTED'
departures_list.append({
'ac_call': dep.callsign or dep.registration,
'ac_reg': dep.registration,
'ac_type': dep.type,
'out_to': dep.out_to,
'etd': dep.etd or dep.created_dt,
'departed_dt': None,
'status': 'BOOKED_OUT',
'departed_dt': dep.departed_dt,
'status': display_status,
'isLocalFlight': False,
'isDeparture': True
})
+3 -4
View File
@@ -18,7 +18,7 @@ from app.crud.crud_circuit import crud_circuit
from app.crud.crud_departure import departure as crud_departure
from app.crud.crud_arrival import arrival as crud_arrival
from app.models.local_flight import SubmissionSource
from app.models.departure import SubmissionSource as DepartureSubmissionSource
from app.models.departure import DepartureStatus
from app.models.arrival import SubmissionSource as ArrivalSubmissionSource
router = APIRouter()
@@ -136,11 +136,10 @@ async def public_book_departure(
notes=departure_in.notes,
)
departure = crud_departure.create(db, obj_in=departure_create, created_by="PUBLIC_PILOT")
departure = crud_departure.create(db, obj_in=departure_create, created_by="PUBLIC_PILOT", submitted_via="PUBLIC")
# Update with submission source and pilot email
# Update with pilot email (submitted_via is already set in create method)
db.query(type(departure)).filter(type(departure).id == departure.id).update({
type(departure).submitted_via: DepartureSubmissionSource.PUBLIC,
type(departure).pilot_email: departure_in.pilot_email,
})
db.commit()
+6 -2
View File
@@ -113,8 +113,12 @@ class CRUDArrival:
old_status = db_obj.status
db_obj.status = status
if status == ArrivalStatus.LANDED and timestamp:
db_obj.landed_dt = timestamp
# Set timestamps based on status
current_time = timestamp if timestamp is not None else datetime.utcnow()
if status == ArrivalStatus.LANDED:
db_obj.landed_dt = current_time
elif status == ArrivalStatus.ARRIVED:
db_obj.arrived_dt = current_time
db.add(db_obj)
db.commit()
+22 -4
View File
@@ -47,11 +47,23 @@ class CRUDDeparture:
)
).order_by(Departure.created_dt).all()
def create(self, db: Session, obj_in: DepartureCreate, created_by: str) -> Departure:
def create(self, db: Session, obj_in: DepartureCreate, created_by: str, submitted_via: str = "ADMIN") -> Departure:
from app.models.departure import SubmissionSource
# Set initial status based on submission source
initial_status = DepartureStatus.BOOKED_OUT
contact_dt = None
if submitted_via == SubmissionSource.ADMIN:
initial_status = DepartureStatus.GROUND
contact_dt = func.now() # Set contact_dt to creation time for admin submissions
db_obj = Departure(
**obj_in.dict(),
created_by=created_by,
status=DepartureStatus.BOOKED_OUT
status=initial_status,
contact_dt=contact_dt,
submitted_via=submitted_via
)
db.add(db_obj)
db.commit()
@@ -113,8 +125,14 @@ class CRUDDeparture:
old_status = db_obj.status
db_obj.status = status
if status == DepartureStatus.DEPARTED and timestamp:
db_obj.departed_dt = timestamp
# Set timestamps based on status
current_time = timestamp if timestamp is not None else datetime.utcnow()
if status == DepartureStatus.GROUND:
db_obj.contact_dt = current_time
elif status == DepartureStatus.DEPARTED:
db_obj.departed_dt = current_time
elif status == DepartureStatus.LOCAL:
db_obj.takeoff_dt = current_time
db.add(db_obj)
db.commit()
+11 -1
View File
@@ -144,10 +144,20 @@ class CRUDLocalFlight:
old_status = db_obj.status
db_obj.status = status
# Update flight_type based on status changes
if status == LocalFlightStatus.LOCAL:
db_obj.flight_type = LocalFlightType.LOCAL
elif status == LocalFlightStatus.CIRCUIT:
db_obj.flight_type = LocalFlightType.CIRCUITS
# Set timestamps based on status
current_time = timestamp if timestamp is not None else datetime.utcnow()
if status == LocalFlightStatus.DEPARTED:
if status == LocalFlightStatus.GROUND:
db_obj.contact_dt = current_time
elif status == LocalFlightStatus.DEPARTED:
db_obj.departed_dt = current_time
elif status == LocalFlightStatus.LOCAL:
db_obj.takeoff_dt = current_time
elif status == LocalFlightStatus.LANDED:
db_obj.landed_dt = current_time
# Count circuits from the circuits table and populate the circuits column
+3
View File
@@ -14,6 +14,8 @@ class SubmissionSource(str, Enum):
class ArrivalStatus(str, Enum):
BOOKED_IN = "BOOKED_IN"
LANDED = "LANDED"
GROUND = "GROUND"
ARRIVED = "ARRIVED"
CANCELLED = "CANCELLED"
@@ -31,6 +33,7 @@ class Arrival(Base):
created_dt = Column(DateTime, server_default=func.now(), nullable=False, index=True)
eta = Column(DateTime, nullable=True, index=True)
landed_dt = Column(DateTime, nullable=True)
arrived_dt = Column(DateTime, nullable=True) # Time when aircraft parks and shuts down
created_by = Column(String(16), nullable=True, index=True)
submitted_via = Column(SQLEnum(SubmissionSource), nullable=False, default=SubmissionSource.ADMIN, index=True)
pilot_email = Column(String(128), nullable=True) # For public submissions
+5 -1
View File
@@ -13,7 +13,9 @@ class SubmissionSource(str, Enum):
class DepartureStatus(str, Enum):
BOOKED_OUT = "BOOKED_OUT"
GROUND = "GROUND"
DEPARTED = "DEPARTED"
LOCAL = "LOCAL"
CANCELLED = "CANCELLED"
@@ -30,7 +32,9 @@ class Departure(Base):
notes = Column(Text, nullable=True)
created_dt = Column(DateTime, server_default=func.now(), nullable=False, index=True)
etd = Column(DateTime, nullable=True, index=True) # Estimated Time of Departure
departed_dt = Column(DateTime, nullable=True) # Actual departure time
contact_dt = Column(DateTime, nullable=True) # Time when contact is established with pilot
departed_dt = Column(DateTime, nullable=True) # Actual departure time (QSY)
takeoff_dt = Column(DateTime, nullable=True) # Time when aircraft becomes airborne
created_by = Column(String(16), nullable=True, index=True)
submitted_via = Column(SQLEnum(SubmissionSource), nullable=False, default=SubmissionSource.ADMIN, index=True)
pilot_email = Column(String(128), nullable=True) # For public submissions
+5
View File
@@ -17,7 +17,10 @@ class LocalFlightType(str, Enum):
class LocalFlightStatus(str, Enum):
BOOKED_OUT = "BOOKED_OUT"
GROUND = "GROUND"
DEPARTED = "DEPARTED"
LOCAL = "LOCAL"
CIRCUIT = "CIRCUIT"
LANDED = "LANDED"
CANCELLED = "CANCELLED"
@@ -37,7 +40,9 @@ class LocalFlight(Base):
notes = Column(Text, nullable=True)
created_dt = Column(DateTime, nullable=False, server_default=func.current_timestamp(), index=True)
etd = Column(DateTime, nullable=True, index=True) # Estimated Time of Departure
contact_dt = Column(DateTime, nullable=True) # Time when contact is established with pilot
departed_dt = Column(DateTime, nullable=True) # Actual takeoff time
takeoff_dt = Column(DateTime, nullable=True) # Time when aircraft becomes airborne
landed_dt = Column(DateTime, nullable=True)
created_by = Column(String(16), nullable=True, index=True)
submitted_via = Column(SQLEnum(SubmissionSource), nullable=False, default=SubmissionSource.ADMIN, index=True)
+7
View File
@@ -7,6 +7,8 @@ from enum import Enum
class ArrivalStatus(str, Enum):
BOOKED_IN = "BOOKED_IN"
LANDED = "LANDED"
GROUND = "GROUND"
ARRIVED = "ARRIVED"
CANCELLED = "CANCELLED"
@@ -52,6 +54,10 @@ class ArrivalUpdate(BaseModel):
callsign: Optional[str] = None
pob: Optional[int] = None
in_from: Optional[str] = None
status: Optional[ArrivalStatus] = None
eta: Optional[datetime] = None
landed_dt: Optional[datetime] = None
arrived_dt: Optional[datetime] = None
notes: Optional[str] = None
@@ -66,6 +72,7 @@ class Arrival(ArrivalBase):
created_dt: datetime
eta: Optional[datetime] = None
landed_dt: Optional[datetime] = None
arrived_dt: Optional[datetime] = None
created_by: Optional[str] = None
updated_at: datetime
submitted_via: Optional[SubmissionSource] = None
+8
View File
@@ -6,7 +6,9 @@ from enum import Enum
class DepartureStatus(str, Enum):
BOOKED_OUT = "BOOKED_OUT"
GROUND = "GROUND"
DEPARTED = "DEPARTED"
LOCAL = "LOCAL"
CANCELLED = "CANCELLED"
@@ -53,7 +55,11 @@ class DepartureUpdate(BaseModel):
callsign: Optional[str] = None
pob: Optional[int] = None
out_to: Optional[str] = None
status: Optional[DepartureStatus] = None
etd: Optional[datetime] = None
contact_dt: Optional[datetime] = None
departed_dt: Optional[datetime] = None
takeoff_dt: Optional[datetime] = None
notes: Optional[str] = None
@@ -67,7 +73,9 @@ class Departure(DepartureBase):
status: DepartureStatus
created_dt: datetime
etd: Optional[datetime] = None
contact_dt: Optional[datetime] = None
departed_dt: Optional[datetime] = None
takeoff_dt: Optional[datetime] = None
updated_at: datetime
submitted_via: Optional[SubmissionSource] = None
pilot_email: Optional[str] = None
+7
View File
@@ -12,7 +12,10 @@ class LocalFlightType(str, Enum):
class LocalFlightStatus(str, Enum):
BOOKED_OUT = "BOOKED_OUT"
GROUND = "GROUND"
DEPARTED = "DEPARTED"
LOCAL = "LOCAL"
CIRCUIT = "CIRCUIT"
LANDED = "LANDED"
CANCELLED = "CANCELLED"
@@ -66,7 +69,9 @@ class LocalFlightUpdate(BaseModel):
duration: Optional[int] = None
status: Optional[LocalFlightStatus] = None
etd: Optional[datetime] = None
contact_dt: Optional[datetime] = None
departed_dt: Optional[datetime] = None
takeoff_dt: Optional[datetime] = None
circuits: Optional[int] = None
notes: Optional[str] = None
@@ -81,7 +86,9 @@ class LocalFlightInDBBase(LocalFlightBase):
status: LocalFlightStatus
created_dt: datetime
etd: Optional[datetime] = None
contact_dt: Optional[datetime] = None
departed_dt: Optional[datetime] = None
takeoff_dt: Optional[datetime] = None
landed_dt: Optional[datetime] = None
circuits: Optional[int] = None
created_by: Optional[str] = None