Drone flights and Bulk Logging WIPs

This commit is contained in:
2026-06-19 17:27:33 -04:00
parent 1952b89ecf
commit 78d738b0ee
18 changed files with 2051 additions and 70 deletions
+64 -52
View File
@@ -143,6 +143,24 @@ def _movement_to_dict(movement: MovementModel) -> dict:
}
def _local_flight_to_dict(local: LocalFlight) -> dict:
return {
"id": local.id,
"aircraft_registration": local.registration,
"aircraft_type": local.type,
"callsign": local.callsign,
"pob": local.pob,
"flight_type": local.flight_type.value if local.flight_type else None,
"status": local.status.value if local.status else None,
"etd": local.etd.isoformat() if local.etd else None,
"takeoff_time": local.takeoff_dt.isoformat() if local.takeoff_dt else None,
"departed_time": local.departed_dt.isoformat() if local.departed_dt else None,
"landing_time": local.landed_dt.isoformat() if local.landed_dt else None,
"circuits": local.circuits,
"notes": local.notes,
}
def _build_suggestion(pprs: List[PPRRecord], movements: List[MovementModel], flight_kind: str) -> dict:
if movements:
movement = movements[0]
@@ -234,7 +252,8 @@ async def get_bulk_movement_context(
entity_type_filter = _strip_entity_type(flight_kind)
pprs = []
if clean_lookup:
local_flights = []
if clean_lookup and flight_kind.upper() != "LOCAL":
pprs = db.query(PPRRecord).filter(
_sql_clean_alnum(PPRRecord.ac_reg).like(f"{clean_lookup}%"),
or_(
@@ -244,6 +263,20 @@ async def get_bulk_movement_context(
PPRRecord.status != PPRStatus.DELETED
).order_by(PPRRecord.eta).limit(10).all()
if clean_lookup and flight_kind.upper() == "LOCAL":
local_flights = db.query(LocalFlight).filter(
_sql_clean_alnum(LocalFlight.registration).like(f"{clean_lookup}%"),
or_(
func.date(LocalFlight.takeoff_dt) == target_date,
func.date(LocalFlight.departed_dt) == target_date,
func.date(LocalFlight.landed_dt) == target_date,
func.date(LocalFlight.etd) == target_date,
func.date(LocalFlight.created_dt) == target_date
),
LocalFlight.flight_type.in_([LocalFlightType.LOCAL, LocalFlightType.CIRCUITS]),
LocalFlight.status != LocalFlightStatus.CANCELLED
).order_by(LocalFlight.takeoff_dt, LocalFlight.etd, LocalFlight.created_dt).limit(10).all()
movements = []
if clean_lookup:
movements = db.query(MovementModel).filter(
@@ -257,6 +290,7 @@ async def get_bulk_movement_context(
return BulkMovementContext(
pprs=[_ppr_to_dict(ppr) for ppr in pprs],
local_flights=[_local_flight_to_dict(local) for local in local_flights],
movements=[_movement_to_dict(movement) for movement in movements],
suggested=_build_suggestion(pprs, movements, flight_kind)
)
@@ -286,8 +320,8 @@ async def bulk_log_movement(
else entry.movement_time
) or entry.movement_time
timestamp = _combine_date_time(entry.movement_date, primary_time)
existing_movement = crud_movement.get(db, entry.movement_id) if entry.movement_id else None
if not existing_movement:
existing_movement = crud_movement.get(db, entry.movement_id) if entry.movement_id and flight_kind != "LOCAL" else None
if not existing_movement and flight_kind != "LOCAL":
existing_movement = crud_movement.find_daily_match(
db,
entry.movement_date,
@@ -302,56 +336,34 @@ async def bulk_log_movement(
if flight_kind == "LOCAL":
takeoff_dt = _combine_date_time(entry.movement_date, entry.takeoff_time)
landing_dt = _combine_date_time(entry.movement_date, entry.landing_time)
local_type = LocalFlightType.CIRCUITS if (entry.local_nature or "").upper() == "CIRCUITS" else LocalFlightType.LOCAL
local = None
if existing_movement and existing_movement.entity_type == "LOCAL_FLIGHT":
local = db.query(LocalFlight).filter(LocalFlight.id == existing_movement.entity_id).first()
if not local:
local = db.query(LocalFlight).filter(
_sql_clean_alnum(LocalFlight.registration) == clean_lookup,
func.date(LocalFlight.takeoff_dt) == entry.movement_date
).first()
if not local:
local = LocalFlight(
registration=clean_reg,
type=entry.aircraft_type or "",
callsign=entry.callsign,
pob=entry.pob or 1,
flight_type=local_type,
status=LocalFlightStatus.LANDED,
duration=int((landing_dt - takeoff_dt).total_seconds() / 60) if landing_dt > takeoff_dt else None,
circuits=entry.circuits or 0,
notes=entry.notes,
etd=takeoff_dt,
departed_dt=takeoff_dt,
takeoff_dt=takeoff_dt,
landed_dt=landing_dt,
created_by=username,
submitted_via=LocalSubmissionSource.ADMIN
if landing_dt < takeoff_dt:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="LOCAL landing time cannot be before takeoff time"
)
db.add(local)
db.commit()
db.refresh(local)
action = "created"
crud_journal.log_change(db, EntityType.LOCAL_FLIGHT, local.id, "Local strip created from bulk flight log", username, client_ip)
else:
local.registration = clean_reg
local.type = entry.aircraft_type or local.type
local.callsign = entry.callsign
local.pob = entry.pob or local.pob
local.flight_type = local_type
local.status = LocalFlightStatus.LANDED
local.duration = int((landing_dt - takeoff_dt).total_seconds() / 60) if landing_dt > takeoff_dt else local.duration
local.circuits = entry.circuits or 0
local.notes = entry.notes
local.etd = takeoff_dt
local.departed_dt = takeoff_dt
local.takeoff_dt = takeoff_dt
local.landed_dt = landing_dt
db.add(local)
db.commit()
db.refresh(local)
action = "updated"
local_type = LocalFlightType.CIRCUITS if (entry.local_nature or "").upper() == "CIRCUITS" else LocalFlightType.LOCAL
local = LocalFlight(
registration=clean_reg,
type=entry.aircraft_type or "",
callsign=entry.callsign,
pob=entry.pob,
flight_type=local_type,
status=LocalFlightStatus.LANDED,
duration=int((landing_dt - takeoff_dt).total_seconds() / 60) if landing_dt > takeoff_dt else None,
circuits=entry.circuits or 0,
notes=entry.notes,
etd=takeoff_dt,
departed_dt=takeoff_dt,
takeoff_dt=takeoff_dt,
landed_dt=landing_dt,
created_by=username,
submitted_via=LocalSubmissionSource.ADMIN
)
db.add(local)
db.commit()
db.refresh(local)
action = "created"
crud_journal.log_change(db, EntityType.LOCAL_FLIGHT, local.id, "Local strip created from bulk flight log", username, client_ip)
takeoff_movement = _create_or_update_movement(db, MovementCreate(
movement_type=MovementType.TAKEOFF,