/// <summary> /// Parses an API response from the STATION API /// </summary> /// <param name="response">API response to parse</param> /// <returns>IEnumerable of ParserStatements</returns> public static IEnumerable <ParserStatement> Parse(APIResponse response) { if (!response.RequestSucceded) { yield return(new ErrorStatement(response, ErrorTypes.RequestUnsuccessful)); yield break; } if (response.Result != null) { HtmlDocument document = new HtmlDocument(); document.LoadHtml(HttpUtility.HtmlDecode(response.Result.ToString())); var table = document.DocumentNode.Descendants("table").Where(d => d.HasClass("af")).FirstOrDefault(); if (table != null) { var stationName = table.Descendants("th").Where(d => d.HasClass("title")).FirstOrDefault().ChildNodes.FirstOrDefault().InnerText; var stationId = new StationIdStatement(response, stationName, null); yield return(stationId); foreach (HtmlNode tr in table.ChildNodes.Where(n => n.Name == "tr" && n.Attributes["onmouseover"] != null && n.Attributes["onmouseout"] != null)) { var tds = tr.ChildNodes.Where(n => n.Name == "td").ToArray(); var arrival = TimeTuple.Parse(tds[0]); var departure = TimeTuple.Parse(tds[1]); var trainReference = tds[tds.Length > 3 ? 3 : 2]; var trainRefStatements = ParseTrainReference(response, trainReference, stationId, arrival?.Scheduled, departure?.Scheduled).ToList(); foreach (var s in trainRefStatements) { yield return(s); } if (!(trainRefStatements.First() is TrainIdStatement trainId)) { yield break; } var trainStationId = new TrainStationStatement(response, trainId, stationId, arrival, departure); yield return(trainStationId); var platform = tds.Length > 3 ? tds[2].InnerText.Trim() : null; if (platform?.Length == 0) { platform = null; } yield return(new TrainStationPlatformStatement(response, trainStationId, platform)); } } else { yield return(new ErrorStatement(response, ErrorTypes.NoTable)); } } else { yield return(new ErrorStatement(response, ErrorTypes.NoResult)); } }
/// <summary> /// Processes the station rows in the HTML response /// </summary> /// <param name="response">API response</param> /// <param name="row">Station row tr</param> /// <param name="number">Number of the train station (ordinal)</param> /// <param name="id">Identifies the train</param> /// <param name="lastStation">Last station (to link sequential stations together)</param> /// <returns>Parser statements</returns> private static List <ParserStatement> ProcessStationTableRow(APIResponse response, HtmlNode row, TrainIdStatement id, ref StationIdStatement?lastStation) { List <ParserStatement> ret = new List <ParserStatement>(); var tds = row.ChildNodes.Where(n => n.Name == "td").ToArray(); var stationLink = tds[1].Descendants("a").FirstOrDefault(); var stationId = StationIdStatement.FromScript(response, stationLink?.Attributes["onclick"]?.Value); if (stationId != null) { ret.Add(stationId); } else { ret.Add(new ErrorStatement(response, ErrorTypes.StationLinkUnparsable)); ret.Add(stationId = new StationIdStatement(response, stationLink?.InnerText.Trim(), null)); } var hit = row.HasClass("row_past_odd") || row.HasClass("row_past_even"); var distance = CSExtensions.ParseInt(tds[0].InnerText); var arrival = TimeTuple.Parse(tds[2]); var departure = TimeTuple.Parse(tds[3]); var platform = (tds.Length > 4) ? tds[4].InnerText.Trim() : null; if (platform?.Length == 0) { platform = null; } var trainStationId = new TrainStationStatement(response, id, stationId, arrival, departure); ret.Add(trainStationId); if (lastStation != null) { ret.Add(new TrainStationLinkStatement(response, id, lastStation, stationId, true)); } lastStation = stationId; ret.Add(new TrainStationDistanceStatement(response, trainStationId, distance)); ret.Add(new TrainStationPlatformStatement(response, trainStationId, platform)); ret.Add(new TrainStationHitStatement(response, trainStationId, hit)); return(ret); }
public static IEnumerable <ParserStatement> ParseTrainReference(APIResponse response, HtmlNode trainReference, StationIdStatement station, TimeSpan?arrival, TimeSpan?departure) { var enumerator = trainReference.ChildNodes.AsEnumerable().GetEnumerator(); TrainIdStatement id; if (enumerator.MoveNext()) { if (enumerator.Current.Name == "a") { var trainNumber = CSExtensions.ParseInt(enumerator.Current.InnerText); var elviraId = ElviraIdFromScript(enumerator.Current.Attributes["onclick"]?.Value); if (trainNumber != null) { yield return(id = new TrainIdStatement(response, trainNumber.Value, elviraId)); } else { yield return(new ErrorStatement(response, ErrorTypes.TrainReferenceUnparsable)); yield break; } } else { yield return(new ErrorStatement(response, ErrorTypes.TrainReferenceUnparsable)); yield break; } } else { yield return(new ErrorStatement(response, ErrorTypes.TrainReferenceUnparsable)); yield break; } if (enumerator.MoveNext()) { var textSplit = enumerator.Current.InnerText.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); string? name = null; TrainType?type = null; foreach (var part in textSplit) { if ((type = TrainParser.DetermineTrainType(part)) != null) { break; } else { if (name == null) { name = part; } else { name += " " + part; } } } if (name != null) { yield return(new TrainNameStatement(response, id, name)); } if (type != null) { yield return(new TrainTypeStatement(response, id, type.Value)); } else { throw new Exception("Type does not map!"); } } else { yield break; } if (!enumerator.MoveNext()) { yield break; } if (enumerator.MoveNext()) { var text = enumerator.Current.InnerText; var parts = text.Split(" -- "); parts[0] = parts[0].Trim(); parts[1] = parts[1].Trim(); if (parts[0] == "" && parts[1] == "") { yield return(new ErrorStatement(response, ErrorTypes.TrainRelationUnparsable)); yield break; } var p0 = parts[0].Split(new char[0], StringSplitOptions.RemoveEmptyEntries); var p1 = parts[1].Split(new char[0], StringSplitOptions.RemoveEmptyEntries); if (parts[0] != "") { departure = null; } if (parts[1] != "") { arrival = null; } if (departure == null && TimeSpan.TryParse(p0[0].Trim(), out TimeSpan fromTime)) { departure = fromTime; } if (arrival == null && TimeSpan.TryParse(p1[1].Trim(), out TimeSpan toTime)) { arrival = toTime; } var from = parts[0] != "" ? new StationIdStatement(response, p0[1].Trim(), null) : station; var to = parts[1] != "" ? new StationIdStatement(response, p1[0].Trim(), null) : station; yield return(new TrainRelationStatement(response, id, from, to, departure, arrival)); } }
/// <summary> /// Processes the train header part of the HTML response /// </summary> /// <param name="response">API response</param> /// <param name="table">Table HtmlNode</param> /// <param name="id">Identifies the train</param> /// <param name="elviraId"></param> /// <returns>List of parser statements</returns> private static List <ParserStatement> ProcessHeader(APIResponse response, HtmlNode table, ref TrainIdStatement?id, string?elviraId) { List <ParserStatement> ret = new List <ParserStatement>(); TrainType?type = null; var headerRow = table.ChildNodes.Where(n => n.Name == "tr").FirstOrDefault(); if (headerRow == null) { ret.Add(new ErrorStatement(response, ErrorTypes.NoTableRows)); return(ret); } var headerEnum = headerRow.ChildNodes.Elements().GetEnumerator(); if (!headerEnum.MoveNext()) { ret.Add(new ErrorStatement(response, ErrorTypes.HeaderEmpty)); return(ret); } var headerTextSplit = headerEnum.Current.InnerText.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); if (id == null) { int?trainNumber = null; if (headerTextSplit.Length > 0) { trainNumber = CSExtensions.ParseInt(headerTextSplit[0]); } if (trainNumber != null) { ret.Add(id = new TrainIdStatement(response, trainNumber.Value, elviraId)); } else { ret.Add(new ErrorStatement(response, ErrorTypes.NoTrainIdentification)); } } if (id == null) { return(ret); } string?name = null; for (int i = 1; i < headerTextSplit.Length; i++) { if ((type = DetermineTrainType(headerTextSplit[i])) != null) { break; } else { if (name == null) { name = headerTextSplit[i]; } else { name += " " + headerTextSplit[i]; } } } ret.Add(new TrainNameStatement(response, id, name)); while (headerEnum.MoveNext()) { TrainType?detType = DetermineTrainType(headerEnum.Current); if (detType != null) { if (type != null) { type = detType; } } else if (headerEnum.Current.Name == "span" && headerEnum.Current.HasClass("viszszam2")) { ret.Add(new TrainViszStatement(response, id, headerEnum.Current.InnerText)); } else if (headerEnum.Current.Name == "font") { var relationText = headerEnum.Current.InnerText.Substr(1, -1).Split(',')[0].Trim(); var stationNames = relationText.Split(" - ").Select(s => s.Trim()).ToArray(); var from = new StationIdStatement(response, stationNames[0], null); var to = new StationIdStatement(response, stationNames[1], null); ret.Add(from); ret.Add(to); ret.Add(new TrainRelationStatement(response, id, from, to)); if (type != null) { ret.Add(new TrainTypeStatement(response, id, type.Value)); } } else if (headerEnum.Current.Name == "ul") { StationIdStatement?firstFrom = null; StationIdStatement?lastTo = null; foreach (HtmlNode li in headerEnum.Current.ChildNodes.Where(n => n.Name == "li")) { var liEnum = li.ChildNodes.AsEnumerable().GetEnumerator(); string? fromStation = null, toStation = null; TrainType?relType = null; if (liEnum.MoveNext()) { var text = liEnum.Current.InnerText.Trim(); var colonSplit = text.Split(':'); var relationSplit = colonSplit[0].Split(" - "); fromStation = relationSplit[0].Trim(); toStation = relationSplit[1].Trim(); relType = DetermineTrainType(colonSplit[1].Trim()); } if (liEnum.MoveNext()) { if (liEnum.Current.Name == "img") { relType = DetermineTrainType(liEnum.Current); } } if (fromStation != null && toStation != null && relType != null) { var from = new StationIdStatement(response, fromStation, null); if (firstFrom != null) { firstFrom = from; } var to = new StationIdStatement(response, toStation, null); lastTo = to; ret.Add(from); ret.Add(to); ret.Add(new TrainSubrelationStatement(response, id, from, to, relType.Value)); } } if (firstFrom != null && lastTo != null) { ret.Add(new TrainRelationStatement(response, id, firstFrom, lastTo)); } } } return(ret); }