aboutsummaryrefslogtreecommitdiff
path: root/src/Costasdev.Busurbano.Backend/Services/FeedService.cs
diff options
context:
space:
mode:
authorAriel Costas Guerrero <ariel@costas.dev>2025-12-23 12:59:52 +0100
committerAriel Costas Guerrero <ariel@costas.dev>2025-12-23 13:00:16 +0100
commit87417c313b455ba0dee19708528cc8d0b830a276 (patch)
tree34b7a2d6bb97157a1d35f57be85b8ff6532865d2 /src/Costasdev.Busurbano.Backend/Services/FeedService.cs
parentbed48c3d7e49b1736d50ce42d92bb6c18cf02504 (diff)
Reimplement real time for Vitrasa
Diffstat (limited to 'src/Costasdev.Busurbano.Backend/Services/FeedService.cs')
-rw-r--r--src/Costasdev.Busurbano.Backend/Services/FeedService.cs189
1 files changed, 189 insertions, 0 deletions
diff --git a/src/Costasdev.Busurbano.Backend/Services/FeedService.cs b/src/Costasdev.Busurbano.Backend/Services/FeedService.cs
new file mode 100644
index 0000000..48f9338
--- /dev/null
+++ b/src/Costasdev.Busurbano.Backend/Services/FeedService.cs
@@ -0,0 +1,189 @@
+using System.Text.RegularExpressions;
+using Costasdev.Busurbano.Backend.Types.Arrivals;
+
+namespace Costasdev.Busurbano.Backend.Services;
+
+public class FeedService
+{
+ private static readonly Regex RemoveQuotationMarks = new(@"[""”]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+ private static readonly Regex StreetNameRegex = new(@"^(.*?)(?:,|\s\s|\s-\s| \d| S\/N|\s\()", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+
+ private static readonly Dictionary<string, string> NameReplacements = new(StringComparer.OrdinalIgnoreCase)
+ {
+ { "Rúa da Salguera Entrada", "Rúa da Salgueira" },
+ { "Rúa da Salgueira Entrada", "Rúa da Salgueira" },
+ { "Estrada de Miraflores", "Estrada Miraflores" },
+ { "FORA DE SERVIZO.G.B.", "" },
+ { "Praza de Fernando O Católico", "" },
+ { "Rúa da Travesía de Vigo", "Travesía de Vigo" },
+ { "Rúa de ", " " },
+ { "Rúa do ", " " },
+ { "Rúa da ", " " },
+ { "Rúa das ", " " },
+ { "Avda. de ", " " },
+ { "Avda. do ", " " },
+ { "Avda. da ", " " },
+ { "Avda. das ", " " },
+ { "Riós", "Ríos" },
+ { "Avda. Beiramar Porto Pesqueiro Berbés", "Berbés" },
+ { "Conde de Torrecedeira", "Torrecedeira" }
+ };
+
+ public (string Color, string TextColor) GetFallbackColourForFeed(string feed)
+ {
+ return feed switch
+ {
+ "vitrasa" => ("#95D516", "#000000"),
+ "santiago" => ("#508096", "#FFFFFF"),
+ "coruna" => ("#E61C29", "#FFFFFF"),
+ "xunta" => ("#007BC4", "#FFFFFF"),
+ "renfe" => ("#870164", "#FFFFFF"),
+ "feve" => ("#EE3D32", "#FFFFFF"),
+ _ => ("#000000", "#FFFFFF"),
+ };
+ }
+
+ public string NormalizeStopCode(string feedId, string code)
+ {
+ if (feedId == "vitrasa")
+ {
+ var digits = new string(code.Where(char.IsDigit).ToArray());
+ if (int.TryParse(digits, out int numericCode))
+ {
+ return numericCode.ToString();
+ }
+ }
+ return code;
+ }
+
+ public string NormalizeRouteShortName(string feedId, string shortName)
+ {
+ if (feedId == "xunta" && shortName.StartsWith("XG") && shortName.Length >= 8)
+ {
+ // XG817014 -> 817.14
+ var contract = shortName.Substring(2, 3);
+ var lineStr = shortName.Substring(5);
+ if (int.TryParse(lineStr, out int line))
+ {
+ return $"{contract}.{line}";
+ }
+ }
+ return shortName;
+ }
+
+ public string NormalizeStopName(string feedId, string name)
+ {
+ if (feedId == "vitrasa")
+ {
+ return name
+ .Replace("\"", "")
+ .Replace(" ", ", ")
+ .Trim();
+ }
+
+ return name;
+ }
+
+ public string NormalizeRouteNameForMatching(string name)
+ {
+ var normalized = name.Trim().ToLowerInvariant();
+ // Remove diacritics/accents
+ normalized = Regex.Replace(normalized.Normalize(System.Text.NormalizationForm.FormD), @"\p{Mn}", "");
+ // Keep only alphanumeric
+ return Regex.Replace(normalized, @"[^a-z0-9]", "");
+ }
+
+ public string GetStreetName(string originalName)
+ {
+ var name = RemoveQuotationMarks.Replace(originalName, "").Trim();
+ var match = StreetNameRegex.Match(name);
+ var streetName = match.Success ? match.Groups[1].Value : name;
+
+ foreach (var replacement in NameReplacements)
+ {
+ if (streetName.Contains(replacement.Key, StringComparison.OrdinalIgnoreCase))
+ {
+ streetName = streetName.Replace(replacement.Key, replacement.Value, StringComparison.OrdinalIgnoreCase);
+ return streetName.Trim();
+ }
+ }
+
+ return streetName.Trim();
+ }
+
+ public string? GenerateMarquee(string feedId, List<string> nextStops)
+ {
+ if (nextStops.Count == 0) return null;
+
+ if (feedId == "vitrasa")
+ {
+ var streets = nextStops
+ .Select(GetStreetName)
+ .Where(s => !string.IsNullOrWhiteSpace(s))
+ .Distinct()
+ .ToList();
+
+ return string.Join(" - ", streets);
+ }
+
+ return feedId switch
+ {
+ "xunta" => string.Join(" > ", nextStops),
+ _ => string.Join(", ", nextStops.Take(4))
+ };
+ }
+
+ public bool IsStopHidden(string stopId)
+ {
+ return HiddenStops.Contains(stopId);
+ }
+
+ public ShiftBadge? GetShiftBadge(string feedId, string tripId)
+ {
+ if (feedId != "vitrasa") return null;
+
+ // Example: C1 04LN 02_001004_4
+ var parts = tripId.Split('_');
+ if (parts.Length < 2) return null;
+
+ var shiftGroup = parts[parts.Length - 2]; // 001004
+ var tripNumber = parts[parts.Length - 1]; // 4
+
+ if (shiftGroup.Length != 6) return null;
+
+ if (!int.TryParse(shiftGroup.Substring(0, 3), out var routeNum)) return null;
+ if (!int.TryParse(shiftGroup.Substring(3, 3), out var shiftNum)) return null;
+
+ var routeName = routeNum switch
+ {
+ 1 => "C1",
+ 3 => "C3",
+ 30 => "N1",
+ 33 => "N4",
+ 8 => "A",
+ 101 => "H",
+ 201 => "U1",
+ 202 => "U2",
+ 150 => "REF",
+ 500 => "TUR",
+ _ => $"L{routeNum}"
+ };
+
+ return new ShiftBadge
+ {
+ ShiftName = $"{routeName}-{shiftNum}",
+ ShiftTrip = tripNumber
+ };
+ }
+
+ private static readonly string[] HiddenStops =
+ [
+ "vitrasa:20223", // Castrelos (Pavillón - U1)
+ "vitrasa:20146", // García Barbón, 7 (A, 18A)
+ "vitrasa:20220", // COIA-SAMIL (15)
+ "vitrasa:20001", // Samil por Beiramar (15B)
+ "vitrasa:20002", // Samil por Torrecedeira (15C)
+ "vitrasa:20144", // Samil por Coia (C3d, C3i)
+ "vitrasa:20145" // Samil por Bouzs (C3d, C3i)
+ ];
+}