diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2025-11-06 00:29:25 +0100 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-11-06 00:29:25 +0100 |
| commit | 236d760d20a5ade402f8e4e4da92332a09f0bcde (patch) | |
| tree | 75598c091dc46616bab593a42617fd243cf5fade /src/Costasdev.Busurbano.Backend/VigoController.cs | |
| parent | 234d069f74499872a1d6612dc1c3dff418c52f20 (diff) | |
Begin (poorly) implementing merging scheduled and real-time data
Diffstat (limited to 'src/Costasdev.Busurbano.Backend/VigoController.cs')
| -rw-r--r-- | src/Costasdev.Busurbano.Backend/VigoController.cs | 181 |
1 files changed, 131 insertions, 50 deletions
diff --git a/src/Costasdev.Busurbano.Backend/VigoController.cs b/src/Costasdev.Busurbano.Backend/VigoController.cs index 147c22a..6d321b8 100644 --- a/src/Costasdev.Busurbano.Backend/VigoController.cs +++ b/src/Costasdev.Busurbano.Backend/VigoController.cs @@ -1,7 +1,11 @@ +using System.Globalization; +using System.Text; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using Costasdev.VigoTransitApi; using System.Text.Json; +using Costasdev.Busurbano.Backend.Types; +using Costasdev.VigoTransitApi.Types; namespace Costasdev.Busurbano.Backend; @@ -21,23 +25,13 @@ public class VigoController : ControllerBase } [HttpGet("GetStopEstimates")] - public async Task<IActionResult> Run() + public async Task<IActionResult> Run( + [FromQuery] int stopId + ) { - var argumentAvailable = Request.Query.TryGetValue("id", out var requestedStopIdString); - if (!argumentAvailable) - { - return BadRequest("Please provide a stop id as a query parameter with the name 'id'."); - } - - var argumentNumber = int.TryParse(requestedStopIdString, out var requestedStopId); - if (!argumentNumber) - { - return BadRequest("The provided stop id is not a valid number."); - } - try { - var response = await _api.GetStopEstimates(requestedStopId); + var response = await _api.GetStopEstimates(stopId); // Return only the estimates array, not the stop metadata return new OkObjectResult(response.Estimates); } @@ -48,56 +42,30 @@ public class VigoController : ControllerBase } [HttpGet("GetStopTimetable")] - public async Task<IActionResult> GetStopTimetable() + public async Task<IActionResult> GetStopTimetable( + [FromQuery] int stopId, + [FromQuery] string date + ) { - // Get date parameter (default to today if not provided) - var dateString = Request.Query.TryGetValue("date", out var requestedDate) - ? requestedDate.ToString() - : DateTime.Today.ToString("yyyy-MM-dd"); - // Validate date format - if (!DateTime.TryParseExact(dateString, "yyyy-MM-dd", null, System.Globalization.DateTimeStyles.None, out _)) + if (!DateTime.TryParseExact(date, "yyyy-MM-dd", null, DateTimeStyles.None, out _)) { return BadRequest("Invalid date format. Please use yyyy-MM-dd format."); } - // Get stopId parameter - if (!Request.Query.TryGetValue("stopId", out var requestedStopIdString)) - { - return BadRequest("Please provide a stop id as a query parameter with the name 'stopId'."); - } - - if (!int.TryParse(requestedStopIdString, out var requestedStopId)) - { - return BadRequest("The provided stop id is not a valid number."); - } - // Create cache key - var cacheKey = $"timetable_{dateString}_{requestedStopId}"; + var cacheKey = $"timetable_{date}_{stopId}"; // Try to get from cache first if (_cache.TryGetValue(cacheKey, out var cachedData)) { + Response.Headers.Append("App-CacheUsage", "HIT"); return new OkObjectResult(cachedData); } try { - // Fetch data from external API - var url = $"https://costas.dev/static-storage/vitrasa_svc/stops/{dateString}/{requestedStopId}.json"; - var response = await _httpClient.GetAsync(url); - - if (!response.IsSuccessStatusCode) - { - if (response.StatusCode == System.Net.HttpStatusCode.NotFound) - { - return NotFound($"Timetable data not found for stop {requestedStopId} on {dateString}"); - } - return StatusCode((int)response.StatusCode, "Error fetching timetable data"); - } - - var jsonContent = await response.Content.ReadAsStringAsync(); - var timetableData = JsonSerializer.Deserialize<JsonElement>(jsonContent); + var timetableData = await LoadTimetable(stopId.ToString(), date); // Cache the data for 12 hours var cacheOptions = new MemoryCacheEntryOptions @@ -109,11 +77,12 @@ public class VigoController : ControllerBase _cache.Set(cacheKey, timetableData, cacheOptions); + Response.Headers.Append("App-CacheUsage", "MISS"); return new OkObjectResult(timetableData); } catch (HttpRequestException ex) { - return StatusCode(500, $"Error fetching timetable data: {ex.Message}"); + return StatusCode((int?)ex.StatusCode ?? 500, $"Error fetching timetable data: {ex.Message}"); } catch (JsonException ex) { @@ -124,5 +93,117 @@ public class VigoController : ControllerBase return StatusCode(500, $"Unexpected error: {ex.Message}"); } } -} + /*private StopEstimate[] LoadDebugEstimates() + { + var file = @"C:\Users\ariel\Desktop\GetStopEstimates.json"; + var contents = System.IO.File.ReadAllText(file); + return JsonSerializer.Deserialize<StopEstimate[]>(contents, JsonSerializerOptions.Web)!; + } + + private ScheduledStop[] LoadDebugTimetable() + { + var file = @"C:\Users\ariel\Desktop\GetStopTimetable.json"; + var contents = System.IO.File.ReadAllText(file); + return JsonSerializer.Deserialize<ScheduledStop[]>(contents)!; + }*/ + + [HttpGet("GetStopArrivalsMerged")] + public async Task<IActionResult> GetStopArrivalsMerged( + [FromQuery] int stopId + ) + { + StringBuilder outputBuffer = new(); + + var now = DateTime.Now.AddSeconds(60 - DateTime.Now.Second); + var realtimeTask = _api.GetStopEstimates(stopId); + var timetableTask = LoadTimetable(stopId.ToString(), now.ToString("yyyy-MM-dd")); + + Task.WaitAll(realtimeTask, timetableTask); + + var realTimeEstimates = realtimeTask.Result.Estimates; + var timetable = timetableTask.Result; + + /*var now = DateTime.Today.AddHours(17).AddMinutes(59); + var realTimeEstimates = LoadDebugEstimates(); + var timetable = LoadDebugTimetable();*/ + + foreach (var estimate in realTimeEstimates) + { + outputBuffer.AppendLine($"Parsing estimate with line={estimate.Line}, route={estimate.Route} and minutes={estimate.Minutes} - Arrives at {now.AddMinutes(estimate.Minutes)}"); + var fullArrivalTime = now.AddMinutes(estimate.Minutes); + + var possibleCirculations = timetable + .Where(c => c.Line.Name.Trim() == estimate.Line.Trim() && c.Trip.Headsign.Trim() == estimate.Route.Trim()) + .OrderBy(c => c.DepartureDateTime()) + .ToArray(); + + outputBuffer.AppendLine($"Found {possibleCirculations.Length} potential circulations"); + + ScheduledStop? closestCirculation = null; + int closestCirculationTime = int.MaxValue; + + foreach (var circulation in possibleCirculations) + { + var diffBetweenScheduleAndTrip = (int)Math.Round((fullArrivalTime - circulation.DepartureDateTime()).TotalMinutes); + var diffBetweenNowAndSchedule = (int)(fullArrivalTime - now).TotalMinutes; + + var tolerance = Math.Max(2, diffBetweenNowAndSchedule * 0.15); // Positive amount of minutes + if (diffBetweenScheduleAndTrip <= -tolerance) + { + break; + } + + if (diffBetweenScheduleAndTrip < closestCirculationTime) + { + closestCirculation = circulation; + closestCirculationTime = diffBetweenScheduleAndTrip; + } + + } + + if (closestCirculation == null) + { + outputBuffer.AppendLine("**No circulation matched. List of all of them:**"); + foreach (var circulation in possibleCirculations) + { + // Circulation A 03LP000_008003_16 stopping at 05/11/2025 22:06:00 (diff: -03:29:59.2644092) + outputBuffer.AppendLine($"Circulation {circulation.Trip.Id} stopping at {circulation.DepartureDateTime()} (diff: {fullArrivalTime - circulation.DepartureDateTime():HH:mm})"); + } + outputBuffer.AppendLine(); + + continue; + } + + if (closestCirculationTime > 0) + { + outputBuffer.Append($"Closest circulation is {closestCirculation.Trip.Id} and arriving {closestCirculationTime} minutes LATE"); + } else if (closestCirculationTime == 0) + { + outputBuffer.Append($"Closest circulation is {closestCirculation.Trip.Id} and arriving ON TIME"); + } + else + { + outputBuffer.Append($"Closest circulation is {closestCirculation.Trip.Id} and arriving {Math.Abs(closestCirculationTime)} minutes EARLY"); + } + + outputBuffer.AppendLine( + $" -- Circulation expected at {closestCirculation.DepartureDateTime():HH:mm)}"); + + outputBuffer.AppendLine(); + } + + return Ok(outputBuffer.ToString()); + } + + private async Task<List<ScheduledStop>> LoadTimetable(string stopId, string dateString) + { + var url = $"https://www.costas.dev/static-storage/vitrasa_svc/stops/{dateString}/{stopId}.json"; + var response = await _httpClient.GetAsync(url); + + response.EnsureSuccessStatusCode(); + + var jsonContent = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize<List<ScheduledStop>>(jsonContent) ?? []; + } +} |
