aboutsummaryrefslogtreecommitdiff
path: root/src/Costasdev.Busurbano.Backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/Costasdev.Busurbano.Backend')
-rw-r--r--src/Costasdev.Busurbano.Backend/Controllers/ArrivalsController.cs68
-rw-r--r--src/Costasdev.Busurbano.Backend/GraphClient/App/ArrivalsAtStop.cs110
-rw-r--r--src/Costasdev.Busurbano.Backend/Program.cs9
-rw-r--r--src/Costasdev.Busurbano.Backend/Types/Arrivals/Arrival.cs82
-rw-r--r--src/Costasdev.Busurbano.Backend/Types/Arrivals/StopArrivalsResponse.cs15
-rw-r--r--src/Costasdev.Busurbano.Backend/Types/Otp/OtpModels.cs4
-rw-r--r--src/Costasdev.Busurbano.Backend/Types/Planner/PlannerResponse.cs (renamed from src/Costasdev.Busurbano.Backend/Types/Planner/PlannerModels.cs)0
7 files changed, 245 insertions, 43 deletions
diff --git a/src/Costasdev.Busurbano.Backend/Controllers/ArrivalsController.cs b/src/Costasdev.Busurbano.Backend/Controllers/ArrivalsController.cs
index eb81784..5dee48d 100644
--- a/src/Costasdev.Busurbano.Backend/Controllers/ArrivalsController.cs
+++ b/src/Costasdev.Busurbano.Backend/Controllers/ArrivalsController.cs
@@ -1,13 +1,16 @@
-using Costasdev.Busurbano.Backend.GraphClient;
+using System.Net;
+using Costasdev.Busurbano.Backend.GraphClient;
using Costasdev.Busurbano.Backend.GraphClient.App;
+using Costasdev.Busurbano.Backend.Types;
+using Costasdev.Busurbano.Backend.Types.Arrivals;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace Costasdev.Busurbano.Backend.Controllers;
[ApiController]
-[Route("api")]
-public class ArrivalsController : ControllerBase
+[Route("api/stops")]
+public partial class ArrivalsController : ControllerBase
{
private readonly ILogger<ArrivalsController> _logger;
private readonly IMemoryCache _cache;
@@ -25,9 +28,16 @@ public class ArrivalsController : ControllerBase
}
[HttpGet("arrivals")]
- public async Task<IActionResult> GetArrivals(string id)
+ public async Task<IActionResult> GetArrivals(
+ [FromQuery] string id,
+ [FromQuery] bool reduced
+ )
{
- var requestContent = ArrivalsAtStopContent.Query(id);
+ var tz = TimeZoneInfo.FindSystemTimeZoneById("Europe/Madrid");
+ var nowLocal = TimeZoneInfo.ConvertTime(DateTime.UtcNow, tz);
+ var todayLocal = nowLocal.Date;
+
+ var requestContent = ArrivalsAtStopContent.Query(new(id, reduced ? 4 : 10));
var request = new HttpRequestMessage(HttpMethod.Post, "http://100.67.54.115:3957/otp/gtfs/v1");
request.Content = JsonContent.Create(new GraphClientRequest
{
@@ -37,16 +47,50 @@ public class ArrivalsController : ControllerBase
var response = await _httpClient.SendAsync(request);
var responseBody = await response.Content.ReadFromJsonAsync<GraphClientResponse<ArrivalsAtStopResponse>>();
- if (responseBody is not { IsSuccess: true })
+ if (responseBody is not { IsSuccess: true } || responseBody.Data?.Stop == null)
{
- _logger.LogError(
- "Error fetching stop data, received {StatusCode} {ResponseBody}",
- response.StatusCode,
- await response.Content.ReadAsStringAsync()
- );
+ LogErrorFetchingStopData(response.StatusCode, await response.Content.ReadAsStringAsync());
return StatusCode(500, "Error fetching stop data");
}
- return Ok(responseBody.Data?.Stop);
+ var stop = responseBody.Data.Stop;
+ List<Arrival> arrivals = [];
+ foreach (var item in stop.Arrivals)
+ {
+ var departureTime = todayLocal.AddSeconds(item.ScheduledDepartureSeconds);
+ var minutesToArrive = (int)(departureTime - nowLocal).TotalMinutes;
+ //var isRunning = departureTime < nowLocal;
+
+ Arrival arrival = new()
+ {
+ Route = new RouteInfo
+ {
+ ShortName = item.Trip.RouteShortName,
+ Colour = item.Trip.Route.Color,
+ TextColour = item.Trip.Route.TextColor
+ },
+ Headsign = new HeadsignInfo
+ {
+ Destination = item.Headsign
+ },
+ Estimate = new ArrivalDetails
+ {
+ Minutes = minutesToArrive,
+ Precission = departureTime < nowLocal ? ArrivalPrecission.Past : ArrivalPrecission.Scheduled
+ }
+ };
+
+ arrivals.Add(arrival);
+ }
+
+ return Ok(new StopArrivalsResponse
+ {
+ StopCode = stop.Code,
+ StopName = stop.Name,
+ Arrivals = arrivals
+ });
}
+
+ [LoggerMessage(LogLevel.Error, "Error fetching stop data, received {statusCode} {responseBody}")]
+ partial void LogErrorFetchingStopData(HttpStatusCode statusCode, string responseBody);
}
diff --git a/src/Costasdev.Busurbano.Backend/GraphClient/App/ArrivalsAtStop.cs b/src/Costasdev.Busurbano.Backend/GraphClient/App/ArrivalsAtStop.cs
index dfecdd6..53c1165 100644
--- a/src/Costasdev.Busurbano.Backend/GraphClient/App/ArrivalsAtStop.cs
+++ b/src/Costasdev.Busurbano.Backend/GraphClient/App/ArrivalsAtStop.cs
@@ -3,26 +3,34 @@ using System.Text.Json.Serialization;
namespace Costasdev.Busurbano.Backend.GraphClient.App;
-public class ArrivalsAtStopContent : IGraphRequest<string>
+public class ArrivalsAtStopContent : IGraphRequest<ArrivalsAtStopContent.Args>
{
- public static string Query(string id)
+ public record Args(string Id, int DepartureCount);
+
+ public static string Query(Args args)
{
return string.Create(CultureInfo.InvariantCulture, $@"
query Query {{
- stop(id:""{id}"") {{
+ stop(id:""{args.Id}"") {{
code
name
- arrivals: stoptimesWithoutPatterns(numberOfDepartures:10) {{
+ arrivals: stoptimesWithoutPatterns(numberOfDepartures:{args.DepartureCount}) {{
+ headsign
+ scheduledDeparture
+ pickupType
+
trip {{
gtfsId
+ serviceId
routeShortName
route {{
color
textColor
}}
+ departureStoptime {{
+ scheduledDeparture
+ }}
}}
- headsign
- scheduledDeparture
}}
}}
}}
@@ -32,51 +40,97 @@ public class ArrivalsAtStopContent : IGraphRequest<string>
public class ArrivalsAtStopResponse : AbstractGraphResponse
{
- [JsonPropertyName("stop")]
- public StopItem Stop { get; set; }
+ [JsonPropertyName("stop")] public StopItem Stop { get; set; }
public class StopItem
{
- [JsonPropertyName("code")]
- public required string Code { get; set; }
+ [JsonPropertyName("code")] public required string Code { get; set; }
- [JsonPropertyName("name")]
- public required string Name { get; set; }
+ [JsonPropertyName("name")] public required string Name { get; set; }
- [JsonPropertyName("arrivals")]
- public List<Arrival> Arrivals { get; set; } = [];
+ [JsonPropertyName("arrivals")] public List<Arrival> Arrivals { get; set; } = [];
}
public class Arrival
{
- [JsonPropertyName("headsign")]
- public required string Headsign { get; set; }
+ [JsonPropertyName("headsign")] public required string Headsign { get; set; }
[JsonPropertyName("scheduledDeparture")]
public int ScheduledDepartureSeconds { get; set; }
- [JsonPropertyName("trip")]
- public required TripDetails Trip { get; set; }
+ [JsonPropertyName("pickupType")] public required string PickupTypeOriginal { get; set; }
+
+ public PickupType PickupTypeParsed => PickupTypeParsed.Parse(PickupTypeOriginal);
+
+ [JsonPropertyName("trip")] public required TripDetails Trip { get; set; }
}
public class TripDetails
{
- [JsonPropertyName("gtfsId")]
- public required string GtfsId { get; set; }
+ [JsonPropertyName("gtfsId")] public required string GtfsId { get; set; }
+
+ [JsonPropertyName("serviceId")] public required string ServiceId { get; set; }
+
+ [JsonPropertyName("routeShortName")] public required string RouteShortName { get; set; }
+
+ [JsonPropertyName("departureStoptime")]
+ public required DepartureStoptime DepartureStoptime { get; set; }
- [JsonPropertyName("routeShortName")]
- public required string RouteShortName { get; set; }
+ [JsonPropertyName("route")] public required RouteDetails Route { get; set; }
+ }
- [JsonPropertyName("route")]
- public required RouteDetails Route { get; set; }
+ public class DepartureStoptime
+ {
+ [JsonPropertyName("scheduledDeparture")]
+ public int ScheduledDeparture { get; set; }
}
public class RouteDetails
{
- [JsonPropertyName("color")]
- public required string Color { get; set; }
+ [JsonPropertyName("color")] public required string Color { get; set; }
+
+ [JsonPropertyName("textColor")] public required string TextColor { get; set; }
+ }
+
+ public class PickupType
+ {
+ private readonly string _value;
+
+ private PickupType(string value)
+ {
+ _value = value;
+ }
+
+ public PickupType Parse(string value)
+ {
+ return value switch
+ {
+ "SCHEDULED" => Scheduled,
+ "NONE" => None,
+ "CALL_AGENCY" => CallAgency,
+ "COORDINATE_WITH_DRIVER" => CoordinateWithDriver,
+ _ => throw new ArgumentException("Unsupported pickup type ", value)
+ };
+ }
+
+ public static readonly PickupType Scheduled = new PickupType("SCHEDULED");
+ public static readonly PickupType None = new PickupType("NONE");
+ public static readonly PickupType CallAgency = new PickupType("CALL_AGENCY");
+ public static readonly PickupType CoordinateWithDriver = new PickupType("COORDINATE_WITH_DRIVER");
+
+ public override bool Equals(object? other)
+ {
+ if (other is not PickupType otherPt)
+ {
+ return false;
+ }
+
+ return otherPt._value == _value;
+ }
- [JsonPropertyName("textColor")]
- public required string TextColor { get; set; }
+ public override int GetHashCode()
+ {
+ return _value.GetHashCode();
+ }
}
}
diff --git a/src/Costasdev.Busurbano.Backend/Program.cs b/src/Costasdev.Busurbano.Backend/Program.cs
index 74c6337..70372e8 100644
--- a/src/Costasdev.Busurbano.Backend/Program.cs
+++ b/src/Costasdev.Busurbano.Backend/Program.cs
@@ -1,3 +1,4 @@
+using System.Text.Json.Serialization;
using Costasdev.Busurbano.Backend.Configuration;
using Costasdev.Busurbano.Backend.Services;
using Costasdev.Busurbano.Backend.Services.Providers;
@@ -6,7 +7,13 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<AppConfiguration>(builder.Configuration.GetSection("App"));
-builder.Services.AddControllers();
+builder.Services
+ .AddControllers()
+ .AddJsonOptions(options =>
+ {
+ options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
+ });
+
builder.Services.AddHttpClient();
builder.Services.AddMemoryCache();
builder.Services.AddSingleton<ShapeTraversalService>();
diff --git a/src/Costasdev.Busurbano.Backend/Types/Arrivals/Arrival.cs b/src/Costasdev.Busurbano.Backend/Types/Arrivals/Arrival.cs
new file mode 100644
index 0000000..c813ccf
--- /dev/null
+++ b/src/Costasdev.Busurbano.Backend/Types/Arrivals/Arrival.cs
@@ -0,0 +1,82 @@
+using System.Text.Json.Serialization;
+
+namespace Costasdev.Busurbano.Backend.Types.Arrivals;
+
+public class Arrival
+{
+ [JsonPropertyName("route")]
+ public required RouteInfo Route { get; set; }
+
+ [JsonPropertyName("headsign")]
+ public required HeadsignInfo Headsign { get; set; }
+
+ [JsonPropertyName("estimate")]
+ public required ArrivalDetails Estimate { get; set; }
+
+ [JsonPropertyName("delay")]
+ public DelayBadge? Delay { get; set; }
+
+ [JsonPropertyName("shift")]
+ public ShiftBadge? Shift { get; set; }
+}
+
+public class RouteInfo
+{
+ [JsonPropertyName("shortName")]
+ public required string ShortName { get; set; }
+
+ [JsonPropertyName("colour")]
+ public required string Colour { get; set; }
+
+ [JsonPropertyName("textColour")]
+ public required string TextColour { get; set; }
+}
+
+public class HeadsignInfo
+{
+ [JsonPropertyName("badge")]
+ public string? Badge { get; set; }
+
+ [JsonPropertyName("destination")]
+ public required string Destination { get; set; }
+
+ [JsonPropertyName("marquee")]
+ public string? Marquee { get; set; }
+}
+
+public class ArrivalDetails
+{
+ [JsonPropertyName("minutes")]
+ public required int Minutes { get; set; }
+
+ [JsonPropertyName("precission")]
+ public ArrivalPrecission Precission { get; set; } = ArrivalPrecission.Scheduled;
+}
+
+[JsonConverter(typeof(JsonStringEnumConverter))]
+public enum ArrivalPrecission
+{
+ [JsonStringEnumMemberName("confident")]
+ Confident = 0,
+ [JsonStringEnumMemberName("unsure")]
+ Unsure = 1,
+ [JsonStringEnumMemberName("scheduled")]
+ Scheduled = 2,
+ [JsonStringEnumMemberName("past")]
+ Past = 3
+}
+
+public class DelayBadge
+{
+ [JsonPropertyName("minutes")]
+ public int Minutes { get; set; }
+}
+
+public class ShiftBadge
+{
+ [JsonPropertyName("shiftName")]
+ public string ShiftName { get; set; }
+
+ [JsonPropertyName("shiftTrip")]
+ public string ShiftTrip { get; set; }
+}
diff --git a/src/Costasdev.Busurbano.Backend/Types/Arrivals/StopArrivalsResponse.cs b/src/Costasdev.Busurbano.Backend/Types/Arrivals/StopArrivalsResponse.cs
new file mode 100644
index 0000000..8c5438c
--- /dev/null
+++ b/src/Costasdev.Busurbano.Backend/Types/Arrivals/StopArrivalsResponse.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace Costasdev.Busurbano.Backend.Types.Arrivals;
+
+public class StopArrivalsResponse
+{
+ [JsonPropertyName("stopCode")]
+ public required string StopCode { get; set; }
+
+ [JsonPropertyName("stopName")]
+ public required string StopName { get; set; }
+
+ [JsonPropertyName("arrivals")]
+ public List<Arrival> Arrivals { get; set; } = [];
+}
diff --git a/src/Costasdev.Busurbano.Backend/Types/Otp/OtpModels.cs b/src/Costasdev.Busurbano.Backend/Types/Otp/OtpModels.cs
index 1c47a4a..b67663d 100644
--- a/src/Costasdev.Busurbano.Backend/Types/Otp/OtpModels.cs
+++ b/src/Costasdev.Busurbano.Backend/Types/Otp/OtpModels.cs
@@ -98,7 +98,7 @@ public class OtpLeg
public OtpGeometry? LegGeometry { get; set; }
[JsonPropertyName("steps")]
- public List<OtpWalkStep> Steps { get; set; } = new();
+ public List<OtpWalkStep> Steps { get; set; } = [];
[JsonPropertyName("headsign")]
public string? Headsign { get; set; }
@@ -113,7 +113,7 @@ public class OtpLeg
public string? RouteTextColor { get; set; }
[JsonPropertyName("intermediateStops")]
- public List<OtpPlace> IntermediateStops { get; set; } = new();
+ public List<OtpPlace> IntermediateStops { get; set; } = [];
}
public class OtpPlace
diff --git a/src/Costasdev.Busurbano.Backend/Types/Planner/PlannerModels.cs b/src/Costasdev.Busurbano.Backend/Types/Planner/PlannerResponse.cs
index c31d12a..c31d12a 100644
--- a/src/Costasdev.Busurbano.Backend/Types/Planner/PlannerModels.cs
+++ b/src/Costasdev.Busurbano.Backend/Types/Planner/PlannerResponse.cs