Added local flight duration
This commit is contained in:
@@ -32,6 +32,7 @@ def upgrade() -> None:
|
|||||||
sa.Column('pob', sa.Integer(), nullable=False),
|
sa.Column('pob', sa.Integer(), nullable=False),
|
||||||
sa.Column('flight_type', sa.Enum('LOCAL', 'CIRCUITS', 'DEPARTURE', name='localflighttype'), nullable=False),
|
sa.Column('flight_type', sa.Enum('LOCAL', 'CIRCUITS', 'DEPARTURE', name='localflighttype'), nullable=False),
|
||||||
sa.Column('status', sa.Enum('BOOKED_OUT', 'DEPARTED', 'LANDED', 'CANCELLED', name='localflightstatus'), nullable=False, server_default='BOOKED_OUT'),
|
sa.Column('status', sa.Enum('BOOKED_OUT', 'DEPARTED', 'LANDED', 'CANCELLED', name='localflightstatus'), nullable=False, server_default='BOOKED_OUT'),
|
||||||
|
sa.Column('duration', sa.Integer(), nullable=True, comment='Duration in minutes'),
|
||||||
sa.Column('notes', sa.Text(), nullable=True),
|
sa.Column('notes', sa.Text(), nullable=True),
|
||||||
sa.Column('created_dt', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False),
|
sa.Column('created_dt', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False),
|
||||||
sa.Column('etd', sa.DateTime(), nullable=True),
|
sa.Column('etd', sa.DateTime(), nullable=True),
|
||||||
|
|||||||
@@ -51,12 +51,18 @@ async def get_public_arrivals(db: Session = Depends(get_db)):
|
|||||||
# Only include flights booked out today
|
# Only include flights booked out today
|
||||||
if not (today_start <= flight.created_dt < today_end):
|
if not (today_start <= flight.created_dt < today_end):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Calculate ETA from departed_dt + duration (if both are available)
|
||||||
|
eta = flight.departed_dt
|
||||||
|
if flight.departed_dt and flight.duration:
|
||||||
|
eta = flight.departed_dt + timedelta(minutes=flight.duration)
|
||||||
|
|
||||||
arrivals_list.append({
|
arrivals_list.append({
|
||||||
'ac_call': flight.callsign or flight.registration,
|
'ac_call': flight.callsign or flight.registration,
|
||||||
'ac_reg': flight.registration,
|
'ac_reg': flight.registration,
|
||||||
'ac_type': flight.type,
|
'ac_type': flight.type,
|
||||||
'in_from': None,
|
'in_from': None,
|
||||||
'eta': flight.departed_dt,
|
'eta': eta,
|
||||||
'landed_dt': None,
|
'landed_dt': None,
|
||||||
'status': 'DEPARTED',
|
'status': 'DEPARTED',
|
||||||
'isLocalFlight': True,
|
'isLocalFlight': True,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class LocalFlight(Base):
|
|||||||
pob = Column(Integer, nullable=False) # Persons on board
|
pob = Column(Integer, nullable=False) # Persons on board
|
||||||
flight_type = Column(SQLEnum(LocalFlightType), nullable=False, index=True)
|
flight_type = Column(SQLEnum(LocalFlightType), nullable=False, index=True)
|
||||||
status = Column(SQLEnum(LocalFlightStatus), nullable=False, default=LocalFlightStatus.BOOKED_OUT, index=True)
|
status = Column(SQLEnum(LocalFlightStatus), nullable=False, default=LocalFlightStatus.BOOKED_OUT, index=True)
|
||||||
|
duration = Column(Integer, nullable=True) # Duration in minutes
|
||||||
notes = Column(Text, nullable=True)
|
notes = Column(Text, nullable=True)
|
||||||
created_dt = Column(DateTime, nullable=False, server_default=func.current_timestamp(), index=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
|
etd = Column(DateTime, nullable=True, index=True) # Estimated Time of Departure
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class LocalFlightBase(BaseModel):
|
|||||||
callsign: Optional[str] = None
|
callsign: Optional[str] = None
|
||||||
pob: int
|
pob: int
|
||||||
flight_type: LocalFlightType
|
flight_type: LocalFlightType
|
||||||
|
duration: Optional[int] = 45 # Duration in minutes, default 45
|
||||||
etd: Optional[datetime] = None # Estimated Time of Departure
|
etd: Optional[datetime] = None # Estimated Time of Departure
|
||||||
notes: Optional[str] = None
|
notes: Optional[str] = None
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ class LocalFlightUpdate(BaseModel):
|
|||||||
callsign: Optional[str] = None
|
callsign: Optional[str] = None
|
||||||
pob: Optional[int] = None
|
pob: Optional[int] = None
|
||||||
flight_type: Optional[LocalFlightType] = None
|
flight_type: Optional[LocalFlightType] = None
|
||||||
|
duration: Optional[int] = None
|
||||||
status: Optional[LocalFlightStatus] = None
|
status: Optional[LocalFlightStatus] = None
|
||||||
etd: Optional[datetime] = None
|
etd: Optional[datetime] = None
|
||||||
departed_dt: Optional[datetime] = None
|
departed_dt: Optional[datetime] = None
|
||||||
|
|||||||
@@ -409,6 +409,10 @@
|
|||||||
<option value="DEPARTURE">Departure to Other Airport</option>
|
<option value="DEPARTURE">Departure to Other Airport</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="local_duration">Duration (minutes)</label>
|
||||||
|
<input type="number" id="local_duration" name="duration" min="5" max="480" value="45" placeholder="Duration in minutes" tabindex="7">
|
||||||
|
</div>
|
||||||
<div class="form-group" id="departure-destination-group" style="display: none;">
|
<div class="form-group" id="departure-destination-group" style="display: none;">
|
||||||
<label for="local_out_to" id="departure-destination-label">Destination Airport</label>
|
<label for="local_out_to" id="departure-destination-label">Destination Airport</label>
|
||||||
<input type="text" id="local_out_to" name="out_to" placeholder="ICAO Code or Airport Name" oninput="handleLocalOutToAirportLookup(this.value)" tabindex="3">
|
<input type="text" id="local_out_to" name="out_to" placeholder="ICAO Code or Airport Name" oninput="handleLocalOutToAirportLookup(this.value)" tabindex="3">
|
||||||
@@ -487,6 +491,10 @@
|
|||||||
<option value="DEPARTURE">Departure</option>
|
<option value="DEPARTURE">Departure</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="local_edit_duration">Duration (minutes)</label>
|
||||||
|
<input type="number" id="local_edit_duration" name="duration" min="5" max="480">
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="local_edit_departure_dt">Departure Time</label>
|
<label for="local_edit_departure_dt">Departure Time</label>
|
||||||
<div style="display: flex; gap: 0.5rem;">
|
<div style="display: flex; gap: 0.5rem;">
|
||||||
@@ -1890,7 +1898,16 @@
|
|||||||
acType = flight.type;
|
acType = flight.type;
|
||||||
typeIcon = '';
|
typeIcon = '';
|
||||||
fromDisplay = `<i>${flight.flight_type === 'CIRCUITS' ? 'Circuits' : flight.flight_type === 'LOCAL' ? 'Local Flight' : 'Departure'}</i>`;
|
fromDisplay = `<i>${flight.flight_type === 'CIRCUITS' ? 'Circuits' : flight.flight_type === 'LOCAL' ? 'Local Flight' : 'Departure'}</i>`;
|
||||||
eta = flight.departure_dt ? formatTimeOnly(flight.departure_dt) : '-';
|
|
||||||
|
// Calculate ETA: use departed_dt (actual departure) if available, otherwise etd (planned departure)
|
||||||
|
// Then add duration to get ETA
|
||||||
|
let departureTime = flight.departed_dt || flight.etd;
|
||||||
|
let etaTime = departureTime;
|
||||||
|
if (departureTime && flight.duration) {
|
||||||
|
const departTime = new Date(departureTime);
|
||||||
|
etaTime = new Date(departTime.getTime() + flight.duration * 60000).toISOString(); // duration is in minutes
|
||||||
|
}
|
||||||
|
eta = etaTime ? formatTimeOnly(etaTime) : '-';
|
||||||
pob = flight.pob || '-';
|
pob = flight.pob || '-';
|
||||||
fuel = '-';
|
fuel = '-';
|
||||||
|
|
||||||
@@ -3255,6 +3272,7 @@
|
|||||||
document.getElementById('local_edit_callsign').value = flight.callsign || '';
|
document.getElementById('local_edit_callsign').value = flight.callsign || '';
|
||||||
document.getElementById('local_edit_pob').value = flight.pob;
|
document.getElementById('local_edit_pob').value = flight.pob;
|
||||||
document.getElementById('local_edit_flight_type').value = flight.flight_type;
|
document.getElementById('local_edit_flight_type').value = flight.flight_type;
|
||||||
|
document.getElementById('local_edit_duration').value = flight.duration || 45;
|
||||||
document.getElementById('local_edit_notes').value = flight.notes || '';
|
document.getElementById('local_edit_notes').value = flight.notes || '';
|
||||||
|
|
||||||
// Parse and populate departure time if exists
|
// Parse and populate departure time if exists
|
||||||
@@ -3588,7 +3606,7 @@
|
|||||||
|
|
||||||
// Only include non-empty values
|
// Only include non-empty values
|
||||||
if (typeof value === 'number' || (typeof value === 'string' && value.trim() !== '')) {
|
if (typeof value === 'number' || (typeof value === 'string' && value.trim() !== '')) {
|
||||||
if (key === 'pob') {
|
if (key === 'pob' || key === 'duration') {
|
||||||
updateData[key] = parseInt(value);
|
updateData[key] = parseInt(value);
|
||||||
} else if (value.trim) {
|
} else if (value.trim) {
|
||||||
updateData[key] = value.trim();
|
updateData[key] = value.trim();
|
||||||
@@ -3649,7 +3667,7 @@
|
|||||||
|
|
||||||
// Only include non-empty values
|
// Only include non-empty values
|
||||||
if (typeof value === 'number' || (typeof value === 'string' && value.trim() !== '')) {
|
if (typeof value === 'number' || (typeof value === 'string' && value.trim() !== '')) {
|
||||||
if (key === 'pob') {
|
if (key === 'pob' || key === 'duration') {
|
||||||
flightData[key] = parseInt(value);
|
flightData[key] = parseInt(value);
|
||||||
} else if (value.trim) {
|
} else if (value.trim) {
|
||||||
flightData[key] = value.trim();
|
flightData[key] = value.trim();
|
||||||
|
|||||||
@@ -317,7 +317,7 @@
|
|||||||
const aircraftDisplay = `${escapeHtml(aircraftId)} <span style="font-size: 0.8em; color: #666;">(${escapeHtml(arrival.ac_type || '')})</span>`;
|
const aircraftDisplay = `${escapeHtml(aircraftId)} <span style="font-size: 0.8em; color: #666;">(${escapeHtml(arrival.ac_type || '')})</span>`;
|
||||||
const fromDisplay = `<i>${getFlightTypeDisplay(arrival.flight_type)}</i>`;
|
const fromDisplay = `<i>${getFlightTypeDisplay(arrival.flight_type)}</i>`;
|
||||||
const time = convertToLocalTime(arrival.eta);
|
const time = convertToLocalTime(arrival.eta);
|
||||||
const timeDisplay = `<div style="display: flex; align-items: center; gap: 8px;"><span style="color: #27ae60; font-weight: bold;">${time}</span><span style="font-size: 0.7em; background: #27ae60; color: white; padding: 2px 4px; border-radius: 3px; white-space: nowrap;">IN AIR</span></div>`;
|
const timeDisplay = `<div style="display: flex; align-items: center; gap: 8px;"><span style="color: #3498db; font-weight: bold;">${time}</span><span style="font-size: 0.7em; background: #3498db; color: white; padding: 2px 4px; border-radius: 3px; white-space: nowrap;">IN AIR</span></div>`;
|
||||||
|
|
||||||
html = `<tr><td>${aircraftDisplay}</td><td>${fromDisplay}</td><td>${timeDisplay}</td></tr>`;
|
html = `<tr><td>${aircraftDisplay}</td><td>${fromDisplay}</td><td>${timeDisplay}</td></tr>`;
|
||||||
sortKey = `0-${arrival.eta}`; // Live flights, sort by ETA
|
sortKey = `0-${arrival.eta}`; // Live flights, sort by ETA
|
||||||
|
|||||||
Reference in New Issue
Block a user