aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAriel Costas Guerrero <ariel@costas.dev>2025-11-06 16:48:43 +0100
committerAriel Costas Guerrero <ariel@costas.dev>2025-11-06 16:48:43 +0100
commit817b29603e5b7f79bfe6489eebf73961e6ca93f2 (patch)
tree09057b38ca6ed23ce7852b62ae1d5c97a91cfe9b /src
parent94658119f2c0b555f83e870fbeb50150f18fc38d (diff)
Move controllers, add config, read stop schedules from local directory
Diffstat (limited to 'src')
-rw-r--r--src/Costasdev.Busurbano.Backend/Configuration/AppConfiguration.cs6
-rw-r--r--src/Costasdev.Busurbano.Backend/Controllers/SantiagoController.cs (renamed from src/Costasdev.Busurbano.Backend/SantiagoController.cs)2
-rw-r--r--src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs (renamed from src/Costasdev.Busurbano.Backend/VigoController.cs)93
-rw-r--r--src/Costasdev.Busurbano.Backend/Program.cs4
-rw-r--r--src/Costasdev.Busurbano.Backend/Types/VigoSchedules.cs43
-rw-r--r--src/gtfs_vigo_stops/pytest.ini4
6 files changed, 63 insertions, 89 deletions
diff --git a/src/Costasdev.Busurbano.Backend/Configuration/AppConfiguration.cs b/src/Costasdev.Busurbano.Backend/Configuration/AppConfiguration.cs
new file mode 100644
index 0000000..97296e5
--- /dev/null
+++ b/src/Costasdev.Busurbano.Backend/Configuration/AppConfiguration.cs
@@ -0,0 +1,6 @@
+namespace Costasdev.Busurbano.Backend.Configuration;
+
+public class AppConfiguration
+{
+ public required string ScheduleBasePath { get; set; }
+}
diff --git a/src/Costasdev.Busurbano.Backend/SantiagoController.cs b/src/Costasdev.Busurbano.Backend/Controllers/SantiagoController.cs
index c86c74b..24ecab9 100644
--- a/src/Costasdev.Busurbano.Backend/SantiagoController.cs
+++ b/src/Costasdev.Busurbano.Backend/Controllers/SantiagoController.cs
@@ -3,7 +3,7 @@ using Costasdev.VigoTransitApi.Types;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
-namespace Costasdev.Busurbano.Backend;
+namespace Costasdev.Busurbano.Backend.Controllers;
[ApiController]
[Route("api/santiago")]
diff --git a/src/Costasdev.Busurbano.Backend/VigoController.cs b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs
index 320d898..e82baed 100644
--- a/src/Costasdev.Busurbano.Backend/VigoController.cs
+++ b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs
@@ -1,27 +1,29 @@
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.Configuration;
using Costasdev.Busurbano.Backend.Types;
-using Costasdev.VigoTransitApi.Types;
+using Costasdev.VigoTransitApi;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.Options;
+using SysFile = System.IO.File;
-namespace Costasdev.Busurbano.Backend;
+namespace Costasdev.Busurbano.Backend.Controllers;
[ApiController]
[Route("api/vigo")]
public class VigoController : ControllerBase
{
+ private readonly ILogger<VigoController> _logger;
private readonly VigoTransitApiClient _api;
- private readonly IMemoryCache _cache;
- private readonly HttpClient _httpClient;
+ private readonly AppConfiguration _configuration;
- public VigoController(HttpClient http, IMemoryCache cache)
+ public VigoController(HttpClient http, IOptions<AppConfiguration> options, ILogger<VigoController> logger)
{
+ _logger = logger;
_api = new VigoTransitApiClient(http);
- _cache = cache;
- _httpClient = http;
+ _configuration = options.Value;
}
[HttpGet("GetStopEstimates")]
@@ -53,44 +55,21 @@ public class VigoController : ControllerBase
return BadRequest("Invalid date format. Please use yyyy-MM-dd format.");
}
- // Create cache key
- 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
{
var timetableData = await LoadTimetable(stopId.ToString(), date);
- // Cache the data for 12 hours
- var cacheOptions = new MemoryCacheEntryOptions
- {
- AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12),
- SlidingExpiration = TimeSpan.FromHours(6), // Refresh cache if accessed within 6 hours of expiry
- Priority = CacheItemPriority.Normal
- };
-
- _cache.Set(cacheKey, timetableData, cacheOptions);
-
- Response.Headers.Append("App-CacheUsage", "MISS");
return new OkObjectResult(timetableData);
}
- catch (HttpRequestException ex)
- {
- return StatusCode((int?)ex.StatusCode ?? 500, $"Error fetching timetable data: {ex.Message}");
- }
- catch (JsonException ex)
+ catch (FileNotFoundException ex)
{
- return StatusCode(500, $"Error parsing timetable data: {ex.Message}");
+ _logger.LogError(ex, "Stop data not found for stop {StopId} on date {Date}", stopId, date);
+ return StatusCode(404, $"Stop data not found for stop {stopId} on date {date}");
}
- catch (Exception ex)
+ catch(Exception ex)
{
- return StatusCode(500, $"Unexpected error: {ex.Message}");
+ _logger.LogError(ex, "Error loading stop data");
+ return StatusCode(500, "Error loading timetable");
}
}
@@ -119,23 +98,19 @@ public class VigoController : ControllerBase
var realtimeTask = _api.GetStopEstimates(stopId);
var timetableTask = LoadTimetable(stopId.ToString(), now.ToString("yyyy-MM-dd"));
- Task.WaitAll(realtimeTask, timetableTask);
+ await Task.WhenAll(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):HH:mm}");
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())
+ .Where(c => c.Line.Trim() == estimate.Line.Trim() && c.Route.Trim() == estimate.Route.Trim())
+ .OrderBy(c => c.CallingDateTime())
.ToArray();
outputBuffer.AppendLine($"Found {possibleCirculations.Length} potential circulations");
@@ -145,7 +120,7 @@ public class VigoController : ControllerBase
foreach (var circulation in possibleCirculations)
{
- var diffBetweenScheduleAndTrip = (int)Math.Round((fullArrivalTime - circulation.DepartureDateTime()).TotalMinutes);
+ var diffBetweenScheduleAndTrip = (int)Math.Round((fullArrivalTime - circulation.CallingDateTime()).TotalMinutes);
var diffBetweenNowAndSchedule = (int)(fullArrivalTime - now).TotalMinutes;
var tolerance = Math.Max(2, diffBetweenNowAndSchedule * 0.15); // Positive amount of minutes
@@ -168,7 +143,7 @@ public class VigoController : ControllerBase
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($"Circulation {circulation.TripId} stopping at {circulation.CallingDateTime()} (diff: {fullArrivalTime - circulation.CallingDateTime():HH:mm})");
}
outputBuffer.AppendLine();
@@ -177,19 +152,19 @@ public class VigoController : ControllerBase
if (closestCirculationTime > 0)
{
- outputBuffer.Append($"Closest circulation is {closestCirculation.Trip.Id} and arriving {closestCirculationTime} minutes LATE");
+ outputBuffer.Append($"Closest circulation is {closestCirculation.TripId} and arriving {closestCirculationTime} minutes LATE");
}
else if (closestCirculationTime == 0)
{
- outputBuffer.Append($"Closest circulation is {closestCirculation.Trip.Id} and arriving ON TIME");
+ outputBuffer.Append($"Closest circulation is {closestCirculation.TripId} and arriving ON TIME");
}
else
{
- outputBuffer.Append($"Closest circulation is {closestCirculation.Trip.Id} and arriving {Math.Abs(closestCirculationTime)} minutes EARLY");
+ outputBuffer.Append($"Closest circulation is {closestCirculation.TripId} and arriving {Math.Abs(closestCirculationTime)} minutes EARLY");
}
outputBuffer.AppendLine(
- $" -- Circulation expected at {closestCirculation.DepartureDateTime():HH:mm)}");
+ $" -- Circulation expected at {closestCirculation.CallingDateTime():HH:mm)}");
outputBuffer.AppendLine();
}
@@ -199,12 +174,12 @@ public class VigoController : ControllerBase
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) ?? [];
+ var file = Path.Combine(_configuration.ScheduleBasePath, dateString, stopId + ".json");
+ if (!SysFile.Exists(file))
+ {
+ throw new FileNotFoundException();
+ }
+ var contents = await SysFile.ReadAllTextAsync(file);
+ return JsonSerializer.Deserialize<List<ScheduledStop>>(contents)!;
}
}
diff --git a/src/Costasdev.Busurbano.Backend/Program.cs b/src/Costasdev.Busurbano.Backend/Program.cs
index 68f84fb..7de4039 100644
--- a/src/Costasdev.Busurbano.Backend/Program.cs
+++ b/src/Costasdev.Busurbano.Backend/Program.cs
@@ -1,5 +1,9 @@
+using Costasdev.Busurbano.Backend.Configuration;
+
var builder = WebApplication.CreateBuilder(args);
+builder.Services.Configure<AppConfiguration>(builder.Configuration.GetSection("App"));
+
builder.Services.AddControllers();
builder.Services.AddHttpClient();
builder.Services.AddMemoryCache();
diff --git a/src/Costasdev.Busurbano.Backend/Types/VigoSchedules.cs b/src/Costasdev.Busurbano.Backend/Types/VigoSchedules.cs
index d6012e5..f8e5634 100644
--- a/src/Costasdev.Busurbano.Backend/Types/VigoSchedules.cs
+++ b/src/Costasdev.Busurbano.Backend/Types/VigoSchedules.cs
@@ -4,35 +4,28 @@ namespace Costasdev.Busurbano.Backend.Types;
public class ScheduledStop
{
- [JsonPropertyName("line")] public required Line Line { get; set; }
- [JsonPropertyName("trip")] public required Trip Trip { get; set; }
- [JsonPropertyName("route_id")] public required string RouteId { get; set; }
- [JsonPropertyName("departure_time")] public required string DepartureTime { get; set; }
-
- public DateTime DepartureDateTime()
- {
- var dt = DateTime.Today + TimeOnly.Parse(DepartureTime).ToTimeSpan();
- return dt.AddSeconds(60 - dt.Second);
- }
-
+ [JsonPropertyName("trip_id")] public required string TripId { get; set; }
+ [JsonPropertyName("service_id")] public required string ServiceId { get; set; }
+ [JsonPropertyName("line")] public required string Line { get; set; }
+ [JsonPropertyName("route")] public required string Route { get; set; }
[JsonPropertyName("stop_sequence")] public required int StopSequence { get; set; }
[JsonPropertyName("shape_dist_traveled")]
- public required float ShapeDistTraveled { get; set; }
+ public required double ShapeDistTraveled { get; set; }
[JsonPropertyName("next_streets")] public required string[] NextStreets { get; set; }
-}
-
-public class Line
-{
- [JsonPropertyName("name")] public required string Name { get; set; }
- [JsonPropertyName("colour")] public required string Colour { get; set; }
-}
+ [JsonPropertyName("starting_code")] public required string StartingCode { get; set; }
+ [JsonPropertyName("starting_name")] public required string StartingName { get; set; }
+ [JsonPropertyName("starting_time")] public required string StartingTime { get; set; }
+ [JsonPropertyName("calling_time")] public required string CallingTime { get; set; }
+ public DateTime CallingDateTime()
+ {
+ var dt = DateTime.Today + TimeOnly.Parse(CallingTime).ToTimeSpan();
+ return dt.AddSeconds(60 - dt.Second);
+ }
-public class Trip
-{
- [JsonPropertyName("id")] public required string Id { get; set; }
- [JsonPropertyName("service_id")] public required string ServiceId { get; set; }
- [JsonPropertyName("headsign")] public required string Headsign { get; set; }
- [JsonPropertyName("direction_id")] public required int DirectionId { get; set; }
+ [JsonPropertyName("calling_ssm")] public required int CallingSsm { get; set; }
+ [JsonPropertyName("terminus_code")] public required string TerminusCode { get; set; }
+ [JsonPropertyName("terminus_name")] public required string TerminusName { get; set; }
+ [JsonPropertyName("terminus_time")] public required string TerminusTime { get; set; }
}
diff --git a/src/gtfs_vigo_stops/pytest.ini b/src/gtfs_vigo_stops/pytest.ini
deleted file mode 100644
index e455bb4..0000000
--- a/src/gtfs_vigo_stops/pytest.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[pytest]
-minversion = 6.0
-testpaths = tests
-python_files = test_*.py