From 3f95a49ef389cdd5dc98c443c999aeb74ce47abe Mon Sep 17 00:00:00 2001 From: Dave Walker Date: Sun, 22 Feb 2026 12:51:30 +0000 Subject: [PATCH 1/2] Order flight drop-down by most recently seen --- .../Controllers/FlightsController.cs | 26 ++++++++++++------- .../FlightRecorder.Api.csproj | 6 ++--- .../Database/SightingManager.cs | 26 +++++++++++++++++++ .../FlightRecorder.BusinessLogic.csproj | 6 ++--- .../ApiClient/FlightClient.cs | 3 ++- .../FlightRecorder.Client.csproj | 6 ++--- .../FlightRecorder.Data.csproj | 6 ++--- .../FlightRecorder.DataExchange.csproj | 6 ++--- src/FlightRecorder.Entities/Db/Flight.cs | 6 ++++- .../FlightRecorder.Entities.csproj | 8 +++--- .../Interfaces/ISightingManager.cs | 1 + .../FlightRecorder.Manager.csproj | 6 ++--- .../FlightRecorder.Mvc.csproj | 6 ++--- .../FlightRecorder.Tests.csproj | 2 +- 14 files changed, 77 insertions(+), 37 deletions(-) diff --git a/src/FlightRecorder.Api/Controllers/FlightsController.cs b/src/FlightRecorder.Api/Controllers/FlightsController.cs index acc6415..16ea4ed 100644 --- a/src/FlightRecorder.Api/Controllers/FlightsController.cs +++ b/src/FlightRecorder.Api/Controllers/FlightsController.cs @@ -24,6 +24,7 @@ public async Task>> GetFlightsByRouteAsync(string emba { LogMessage(Severity.Debug, $"Retrieving list of flights by route {embarkation}-{destination} (page {pageNumber}, page size {pageSize})"); + // Retrieve the matching list of flights string decodedEmbarkation = HttpUtility.UrlDecode(embarkation).ToUpper(); string decodedDestination = HttpUtility.UrlDecode(destination).ToUpper(); List flights = await Factory.Flights @@ -31,15 +32,16 @@ public async Task>> GetFlightsByRouteAsync(string emba (f.Destination == decodedDestination), pageNumber, pageSize) .ToListAsync(); - LogMessage(Severity.Debug, $"Retrieved {flights.Count} flights(s)"); + LogMessage(Severity.Debug, $"Retrieved {flights.Count} flight(s)"); if (!flights.Any()) { return NoContent(); } - // Assign airport details + // Assign airport and sighting details await Factory.Airports.LoadAirportDetails(flights); + await Factory.Sightings.LoadSightingDetails(flights); return flights; } @@ -54,15 +56,16 @@ public async Task>> GetFlightsByAirlineAsync(int airli .ListAsync(f => f.AirlineId == airlineId, pageNumber, pageSize) .ToListAsync(); - LogMessage(Severity.Debug, $"Retrieved {flights.Count} flights(s)"); + LogMessage(Severity.Debug, $"Retrieved {flights.Count} flight(s)"); if (!flights.Any()) { return NoContent(); } - // Assign airport details + // Assign airport and sighting details await Factory.Airports.LoadAirportDetails(flights); + await Factory.Sightings.LoadSightingDetails(flights); return flights; } @@ -78,15 +81,16 @@ public async Task>> GetFlightsByNumberAsync(string num .ListAsync(f => f.Number == decodedNumber, pageNumber, pageSize) .ToListAsync(); - LogMessage(Severity.Debug, $"Retrieved {flights.Count} flights(s)"); + LogMessage(Severity.Debug, $"Retrieved {flights.Count} flight(s)"); if (!flights.Any()) { return NoContent(); } - // Assign airport details + // Assign airport and sighting details await Factory.Airports.LoadAirportDetails(flights); + await Factory.Sightings.LoadSightingDetails(flights); return flights; } @@ -105,8 +109,9 @@ public async Task> GetFlightByIdAsync(int id) return NotFound(); } - // Assign airport details + // Assign airport and sighting details await Factory.Airports.LoadAirportDetails([flight]); + await Factory.Sightings.LoadSightingDetails([flight]); return flight; } @@ -123,8 +128,10 @@ public async Task> AddFlightAsync([FromBody] Flight templat template.AirlineId); LogMessage(Severity.Debug, $"Added flight: {flight}"); - // Assign airport details + // Assign airport and sighting details await Factory.Airports.LoadAirportDetails([flight]); + await Factory.Sightings.LoadSightingDetails([flight]); + return flight; } @@ -141,8 +148,9 @@ public async Task> UpdateFlightAsync([FromBody] Flight temp template.Destination, template.AirlineId); - // Assign airport details + // Assign airport and sighting details await Factory.Airports.LoadAirportDetails([flight]); + await Factory.Sightings.LoadSightingDetails([flight]); LogMessage(Severity.Debug, $"Flight updated: {flight}"); diff --git a/src/FlightRecorder.Api/FlightRecorder.Api.csproj b/src/FlightRecorder.Api/FlightRecorder.Api.csproj index 6272971..5facb11 100644 --- a/src/FlightRecorder.Api/FlightRecorder.Api.csproj +++ b/src/FlightRecorder.Api/FlightRecorder.Api.csproj @@ -2,9 +2,9 @@ net9.0 - 1.23.0.0 - 1.23.0.0 - 1.23.0 + 1.24.0.0 + 1.24.0.0 + 1.24.0 enable false NU1900 diff --git a/src/FlightRecorder.BusinessLogic/Database/SightingManager.cs b/src/FlightRecorder.BusinessLogic/Database/SightingManager.cs index a8576a5..b4aafd9 100644 --- a/src/FlightRecorder.BusinessLogic/Database/SightingManager.cs +++ b/src/FlightRecorder.BusinessLogic/Database/SightingManager.cs @@ -356,5 +356,31 @@ public async Task> ListByLocationAsync(string locatio return sightings; } + + /// + /// Assign the last seen date on a collection of flights + /// + /// + /// + public async Task LoadSightingDetails(IEnumerable flights) + { + // Get a list of flight IDs + var flightIds = flights.Select(x => x.Id).Distinct(); + + // Load matching sighting details + var sightings = await ListAsync(x => flightIds.Contains(x.FlightId), 1, int.MaxValue).ToListAsync(); + + // Map them into the flights + foreach (var flight in flights) + { + // Find the most recent sighting for this flight + var sighting = sightings.Where(s => s.FlightId == flight.Id) + .OrderByDescending(s => s.Date) + .FirstOrDefault(); + + // Set the last seen date on the flight from the sighting + flight.LastSeen = sighting.Date; + } + } } } diff --git a/src/FlightRecorder.BusinessLogic/FlightRecorder.BusinessLogic.csproj b/src/FlightRecorder.BusinessLogic/FlightRecorder.BusinessLogic.csproj index 53af710..3c1afee 100644 --- a/src/FlightRecorder.BusinessLogic/FlightRecorder.BusinessLogic.csproj +++ b/src/FlightRecorder.BusinessLogic/FlightRecorder.BusinessLogic.csproj @@ -3,7 +3,7 @@ net9.0 FlightRecorder.BusinessLogic - 1.23.0.0 + 1.24.0.0 Dave Walker Copyright (c) Dave Walker 2020, 2021, 2022, 2023, 2024, 2025 Dave Walker @@ -12,10 +12,10 @@ Flight Recorder Business Logic FlightRecorder business logic true - https://github.com/davewalker5/FlightRecorderDb + https://github.com/davewalker5/FlightRecorder MIT false - 1.23.0.0 + 1.24.0.0 NU1900 diff --git a/src/FlightRecorder.Client/ApiClient/FlightClient.cs b/src/FlightRecorder.Client/ApiClient/FlightClient.cs index e7b8581..954521d 100644 --- a/src/FlightRecorder.Client/ApiClient/FlightClient.cs +++ b/src/FlightRecorder.Client/ApiClient/FlightClient.cs @@ -99,7 +99,8 @@ public async Task> GetFlightsByNumberAsync(string number) if (!string.IsNullOrEmpty(json)) { flights = Deserialize>(json) - ?.OrderBy(m => m.Airline.Name) + ?.OrderByDescending(m => m.LastSeen) + .ThenBy(m => m.Airline.Name) .ThenBy(m => m.Embarkation) .ThenBy(m => m.Destination) .ToList(); diff --git a/src/FlightRecorder.Client/FlightRecorder.Client.csproj b/src/FlightRecorder.Client/FlightRecorder.Client.csproj index 62737eb..4513db9 100644 --- a/src/FlightRecorder.Client/FlightRecorder.Client.csproj +++ b/src/FlightRecorder.Client/FlightRecorder.Client.csproj @@ -3,7 +3,7 @@ net9.0 FlightRecorder.Client - 1.23.0.0 + 1.24.0.0 Dave Walker Copyright (c) Dave Walker 2020, 2021, 2022, 2023, 2024, 2025 Dave Walker @@ -12,10 +12,10 @@ Flight Recorder Client API FlightRecorder Client API true - https://github.com/davewalker5/FlightRecorderDb + https://github.com/davewalker5/FlightRecorder MIT false - 1.23.0.0 + 1.24.0.0 NU1900 diff --git a/src/FlightRecorder.Data/FlightRecorder.Data.csproj b/src/FlightRecorder.Data/FlightRecorder.Data.csproj index 0fe8734..9926f43 100644 --- a/src/FlightRecorder.Data/FlightRecorder.Data.csproj +++ b/src/FlightRecorder.Data/FlightRecorder.Data.csproj @@ -3,7 +3,7 @@ net9.0 FlightRecorder.Data - 1.23.0.0 + 1.24.0.0 Dave Walker Copyright (c) Dave Walker 2020, 2021, 2022, 2023, 2024, 2025 Dave Walker @@ -12,10 +12,10 @@ Flight Recorder EF Core Database Layer FlightRecorder EF Core Database Layer true - https://github.com/davewalker5/FlightRecorderDb + https://github.com/davewalker5/FlightRecorder MIT false - 1.23.0.0 + 1.24.0.0 NU1900 diff --git a/src/FlightRecorder.DataExchange/FlightRecorder.DataExchange.csproj b/src/FlightRecorder.DataExchange/FlightRecorder.DataExchange.csproj index a701244..8b92001 100644 --- a/src/FlightRecorder.DataExchange/FlightRecorder.DataExchange.csproj +++ b/src/FlightRecorder.DataExchange/FlightRecorder.DataExchange.csproj @@ -3,7 +3,7 @@ net9.0 FlightRecorder.DataExchange - 1.23.0.0 + 1.24.0.0 Dave Walker Copyright (c) Dave Walker 2020, 2021, 2022, 2023, 2024, 2025 Dave Walker @@ -12,10 +12,10 @@ Flight Recorder Data Exchange Tools FlightRecorder Data Exchange Tools true - https://github.com/davewalker5/FlightRecorderDb + https://github.com/davewalker5/FlightRecorder MIT false - 1.23.0.0 + 1.24.0.0 NU1900 diff --git a/src/FlightRecorder.Entities/Db/Flight.cs b/src/FlightRecorder.Entities/Db/Flight.cs index 3755680..8ba81d8 100644 --- a/src/FlightRecorder.Entities/Db/Flight.cs +++ b/src/FlightRecorder.Entities/Db/Flight.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics.CodeAnalysis; @@ -36,6 +37,9 @@ public partial class Flight : FlightRecorderEntityBase [NotMapped] public Airport DestinationAirport { get; set; } + [NotMapped] + public DateTime? LastSeen { get; set; } + public virtual Airline Airline { get; set; } public string Route { get { return $"{Embarkation} - {Destination}"; } } diff --git a/src/FlightRecorder.Entities/FlightRecorder.Entities.csproj b/src/FlightRecorder.Entities/FlightRecorder.Entities.csproj index 3bcfd8d..7d5406b 100644 --- a/src/FlightRecorder.Entities/FlightRecorder.Entities.csproj +++ b/src/FlightRecorder.Entities/FlightRecorder.Entities.csproj @@ -3,7 +3,7 @@ net9.0 FlightRecorder.Entities - 1.23.0.0 + 1.24.0.0 Dave Walker Copyright (c) Dave Walker 2020, 2021, 2022, 2023, 2024, 2025 Dave Walker @@ -12,10 +12,10 @@ Flight Recorder Domain Models FlightRecorder Domain Models true - https://github.com/davewalker5/FlightRecorderDb + https://github.com/davewalker5/FlightRecorder MIT false - 1.23.0.0 + 1.24.0.0 NU1900 @@ -26,6 +26,6 @@ - + diff --git a/src/FlightRecorder.Entities/Interfaces/ISightingManager.cs b/src/FlightRecorder.Entities/Interfaces/ISightingManager.cs index ae84053..5b46562 100644 --- a/src/FlightRecorder.Entities/Interfaces/ISightingManager.cs +++ b/src/FlightRecorder.Entities/Interfaces/ISightingManager.cs @@ -21,5 +21,6 @@ public interface ISightingManager Task> ListByLocationAsync(string locationName, int pageNumber, int pageSize); Task UpdateAsync(long id, long altitude, DateTime date, long locationId, long flightId, long aircraftId, bool isMyFlight); Task DeleteAsync(long id); + Task LoadSightingDetails(IEnumerable flights); } } \ No newline at end of file diff --git a/src/FlightRecorder.Manager/FlightRecorder.Manager.csproj b/src/FlightRecorder.Manager/FlightRecorder.Manager.csproj index a36761a..cfb9c60 100644 --- a/src/FlightRecorder.Manager/FlightRecorder.Manager.csproj +++ b/src/FlightRecorder.Manager/FlightRecorder.Manager.csproj @@ -3,9 +3,9 @@ Exe net9.0 - 1.23.0.0 - 1.23.0.0 - 1.23.0.0 + 1.24.0.0 + 1.24.0.0 + 1.24.0.0 Release;Debug NU1900 diff --git a/src/FlightRecorder.Mvc/FlightRecorder.Mvc.csproj b/src/FlightRecorder.Mvc/FlightRecorder.Mvc.csproj index a043069..f980dcc 100644 --- a/src/FlightRecorder.Mvc/FlightRecorder.Mvc.csproj +++ b/src/FlightRecorder.Mvc/FlightRecorder.Mvc.csproj @@ -2,9 +2,9 @@ net9.0 - 1.23.0.0 - 1.23.0.0 - 1.23.0 + 1.24.0.0 + 1.24.0.0 + 1.24.0 enable false NU1900 diff --git a/src/FlightRecorder.Tests/FlightRecorder.Tests.csproj b/src/FlightRecorder.Tests/FlightRecorder.Tests.csproj index 221a3dc..9e5f505 100644 --- a/src/FlightRecorder.Tests/FlightRecorder.Tests.csproj +++ b/src/FlightRecorder.Tests/FlightRecorder.Tests.csproj @@ -3,7 +3,7 @@ net9.0 false - 1.23.0.0 + 1.24.0.0 NU1900 From 00cdfacd605f11e89d4f69887bfe83f9f26ee1c4 Mon Sep 17 00:00:00 2001 From: Dave Walker Date: Sun, 22 Feb 2026 12:52:30 +0000 Subject: [PATCH 2/2] Update the docker build version --- docker/api/Dockerfile | 2 +- docker/ui/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/api/Dockerfile b/docker/api/Dockerfile index e89b618..85c9a40 100644 --- a/docker/api/Dockerfile +++ b/docker/api/Dockerfile @@ -1,4 +1,4 @@ FROM mcr.microsoft.com/dotnet/core/aspnet:latest -COPY flightrecorder.api-1.21.0.0 /opt/flightrecorder.api +COPY flightrecorder.api-1.24.0.0 /opt/flightrecorder.api WORKDIR /opt/flightrecorder.api/bin ENTRYPOINT [ "./FlightRecorder.Api" ] diff --git a/docker/ui/Dockerfile b/docker/ui/Dockerfile index 1fcae5d..9127549 100644 --- a/docker/ui/Dockerfile +++ b/docker/ui/Dockerfile @@ -1,4 +1,4 @@ FROM mcr.microsoft.com/dotnet/aspnet:latest AS runtime -COPY flightrecorder.mvc-1.21.0.0 /opt/flightrecorder.mvc +COPY flightrecorder.mvc-1.24.0.0 /opt/flightrecorder.mvc WORKDIR /opt/flightrecorder.mvc/bin ENTRYPOINT [ "./FlightRecorder.Mvc" ]