summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAriel Costas Guerrero <ariel@costas.dev>2026-04-04 18:01:45 +0200
committerAriel Costas Guerrero <ariel@costas.dev>2026-04-04 18:01:45 +0200
commit4ed09e535b28ced439dc994a16e58dc9b56055ff (patch)
tree05c3a2e5319e753687ba4d15df7fb1d539bd60e2
parent673818837bf2ba0a03bb9931dadfbc820e764462 (diff)
Enhance feed ID parsing and agency filtering in TransitController; update routes page with additional agency entries
-rw-r--r--src/Enmarcha.Backend/Controllers/TransitController.cs52
-rw-r--r--src/frontend/app/routes/routes.tsx5
2 files changed, 55 insertions, 2 deletions
diff --git a/src/Enmarcha.Backend/Controllers/TransitController.cs b/src/Enmarcha.Backend/Controllers/TransitController.cs
index ae32517..5db71c0 100644
--- a/src/Enmarcha.Backend/Controllers/TransitController.cs
+++ b/src/Enmarcha.Backend/Controllers/TransitController.cs
@@ -46,8 +46,49 @@ public class TransitController : ControllerBase
}
activity?.SetTag("feeds", string.Join(",", feeds));
+ // Parse requested feeds: entries may be plain "feedId" or "feedId:agencyId".
+ // Build the unique set of feed IDs to pass to OTP, and a per-feed agency
+ // allow-list (null = accept all agencies in that feed).
+ var feedIdsForOtp = new List<string>();
+ var agencyFilters = new Dictionary<string, HashSet<string>?>(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var entry in feeds)
+ {
+ var colonIndex = entry.IndexOf(':');
+ if (colonIndex < 0)
+ {
+ // Plain feed ID — include all agencies in this feed.
+ var feedId = entry;
+ if (!feedIdsForOtp.Contains(feedId, StringComparer.OrdinalIgnoreCase))
+ feedIdsForOtp.Add(feedId);
+ // null sentinel means "no agency filter"
+ agencyFilters[feedId] = null;
+ }
+ else
+ {
+ var feedId = entry[..colonIndex];
+ if (!feedIdsForOtp.Contains(feedId, StringComparer.OrdinalIgnoreCase))
+ feedIdsForOtp.Add(feedId);
+
+ // Only add to the filter set if we haven't already opened this feed
+ // to all agencies (null). The full GTFS agency id is "feedId:agencyId".
+ if (!agencyFilters.TryGetValue(feedId, out var existing))
+ {
+ agencyFilters[feedId] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { entry };
+ }
+ else if (existing != null)
+ {
+ existing.Add(entry);
+ }
+ // else: feed already open to all agencies — leave it as null.
+ }
+ }
+
var serviceDate = DateTime.Now.ToString("yyyy-MM-dd");
- var cacheKey = $"routes_{string.Join("_", feeds)}_{serviceDate}";
+ // Cache key uses the original (sorted) feed entries so "renfe" and
+ // "renfe:cercanias" produce different cache entries.
+ var sortedFeeds = feeds.OrderBy(f => f, StringComparer.OrdinalIgnoreCase);
+ var cacheKey = $"routes_{string.Join("_", sortedFeeds)}_{serviceDate}";
var cacheHit = _cache.TryGetValue(cacheKey, out List<RouteDto>? cachedRoutes);
activity?.SetTag("cache.hit", cacheHit);
@@ -58,7 +99,7 @@ public class TransitController : ControllerBase
try
{
- var query = RoutesListContent.Query(new RoutesListContent.Args(feeds, serviceDate));
+ var query = RoutesListContent.Query(new RoutesListContent.Args([.. feedIdsForOtp], serviceDate));
var response = await SendOtpQueryAsync<RoutesListResponse>(query);
if (response?.Data == null)
@@ -67,6 +108,13 @@ public class TransitController : ControllerBase
}
var routes = response.Data.Routes
+ .Where(r =>
+ {
+ var feedId = r.GtfsId.Split(':')[0];
+ if (!agencyFilters.TryGetValue(feedId, out var filter)) return false;
+ if (filter == null) return true;
+ return r.Agency?.GtfsId != null && filter.Contains(r.Agency.GtfsId);
+ })
.Select(_otpService.MapRoute)
.OrderBy(r => SortingHelper.GetRouteSortKey(r.ShortName, r.Id))
.ToList();
diff --git a/src/frontend/app/routes/routes.tsx b/src/frontend/app/routes/routes.tsx
index 9ec941b..52b67c9 100644
--- a/src/frontend/app/routes/routes.tsx
+++ b/src/frontend/app/routes/routes.tsx
@@ -26,6 +26,9 @@ export default function RoutesPage() {
setExpandedAgencies((prev) => ({ ...prev, [agency]: !prev[agency] }));
};
+ // Each entry is either a plain feed ID ("tussa") — which includes all agencies
+ // in that feed — or a "feedId:agencyId" pair ("renfe:cercanias") to restrict
+ // results to a single agency within a feed.
const orderedAgencies = [
"vitrasa",
"tranvias",
@@ -33,6 +36,8 @@ export default function RoutesPage() {
"ourense",
"lugo",
"shuttle",
+ "renfe:1071VC",
+ "xunta:XG621",
];
const { data: routes, isLoading } = useQuery({