/// <summary> /// Initializes a new instance of the <see cref="Flight"/> class. /// </summary> /// <param name="baseData"> /// The base data. /// </param> /// <param name="flightOperator"> /// The flight operator. /// </param> /// <param name="price"> /// The price. /// </param> /// <param name="travelAgency"> /// The travel agency. /// </param> /// <param name="outboundLeg"> /// The outbound leg. /// </param> /// <param name="inboundLeg"> /// The inbound leg. /// </param> public Flight( JourneyData baseData, string flightOperator, FlightLeg outboundLeg, FlightLeg inboundLeg) { this.JourneyData = baseData; this.Operator = flightOperator; this.OutboundLeg = outboundLeg; this.InboundLeg = inboundLeg; this.Fares = new List<Fare>(); }
/// <summary> /// The parse web archive. /// </summary> /// <param name="webDocument"> /// The web document. /// </param> /// <returns> /// The <see cref="RouteDataResult"/>. /// </returns> internal RouteDataResult ParseWebArchive(HtmlDocument webDocument) { HtmlNode originInput = webDocument.GetElementbyId("flight_places"); if (originInput == null) { // Data is not ready yet return new RouteDataResult(DataResult.NotReady, null); } var resultDivs = webDocument.DocumentNode.SelectNodes("//div[@id='results_list']//div[@class='flights_a']"); if (resultDivs == null || resultDivs.Count < 1) { // Data is not ready yet return new RouteDataResult(DataResult.NotReady, null); } var route = new TravelRoute(0, this.Departure, this.Destination); foreach (var resultSection in resultDivs) { // Each result set DateTime dataDate = DateTime.Now; var operatorData = new Dictionary<string, List<Flight>>(); var newData = new JourneyData(0, "EUR", dataDate); FlightLeg outboundLeg = null, inboundLeg = null; string flightOperator = null; TravelAgency travelAgency = null; double price = 0.0; List<Fare> fares = new List<Fare>(); var priceContainers = resultSection.SelectNodes(".//div[@class='f_price_container']//div[@class='row']"); if (priceContainers == null || priceContainers.Count < 1) { continue; } foreach (var priceContainer in priceContainers) { string onClickStr = priceContainer.GetAttributeValue("onclick", string.Empty); travelAgency = this.TryGetTravelAgency(onClickStr, false); var priceNode = priceContainer.SelectSingleNode(".//div[@class='f_price']"); if (priceNode == null) { continue; } string priceStr = Regex.Match(priceNode.InnerText.Replace(",", "."), @"\d+(.\d+)?").Value; double.TryParse(priceStr, NumberStyles.Any, NamingRule.NumberCulture, out price); fares.Add(new Fare { TravelAgency = travelAgency, Price = price }); } // Loop through each column in table var flightNode = resultSection.SelectSingleNode(".//div[@class='f_normal_info']"); if (flightNode == null) { continue; } foreach (HtmlNode flightDetailNode in flightNode.ChildNodes) { // Fetch the flight detail from the row if (flightDetailNode.Name != "div") { continue; } string className = flightDetailNode.GetAttributeValue("class", string.Empty); switch (className) { case "f_outbound": case "f_outbound_only": case "f_return": var divNodes = flightDetailNode.Descendants("div"); if (divNodes != null) { string depDatePartStr = null, depTimePartStr = null, stopStr = null, arrDatePartStr = null, arrTimePartStr = null; TimeSpan duration = TimeSpan.Zero; foreach (var dataNode in divNodes) { // Each flight string dataClass = dataNode.GetAttributeValue("class", string.Empty); switch (dataClass) { case "f_dep_date": depDatePartStr = dataNode.InnerText; break; case "f_departure": depTimePartStr = dataNode.InnerText; break; case "f_arr_date": arrDatePartStr = dataNode.InnerText; break; case "f_arrival": arrTimePartStr = dataNode.InnerText; break; case "f_duration": duration = this.TryGetDuration(dataNode.InnerText); break; case "f_stops": stopStr = TryGetNumberString(dataNode.InnerText); break; } } // Validate that we got all required data string depDateStr = string.Format(CultureInfo.InvariantCulture, "{0} {1}", depDatePartStr, depTimePartStr), arrDateStr = string.Format(CultureInfo.InvariantCulture, "{0} {1}", arrDatePartStr, arrTimePartStr); DateTime deptDate, arrDate; if (DateTime.TryParseExact( depDateStr, "dd.MM.yy HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out deptDate) && deptDate.IsDefined() && DateTime.TryParseExact( arrDateStr, "dd.MM.yy HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out arrDate) && arrDate.IsDefined()) { if (duration > TimeSpan.Zero) { int transit; int.TryParse(stopStr, out transit); // This might fail for straight flight: Just ignore it var flightLeg = new FlightLeg(deptDate, arrDate, duration, transit); if (className == "f_return") { inboundLeg = flightLeg; } else { outboundLeg = flightLeg; } } } } break; case "f_company": flightOperator = flightDetailNode.InnerText.Replace("mm. ", string.Empty); break; } } if (outboundLeg != null) { bool shouldAdd; if (shouldAdd = !operatorData.ContainsKey(flightOperator)) { // Flight will always be added if it is the first from this operator if (operatorData.Keys.Count == this.MaxAirlines) { // If we reached the limit for number of flight operators continue; } operatorData.Add(flightOperator, new List<Flight>(this.MaxFlightsPerAirline)); } var opearatorFlights = operatorData[flightOperator]; if (!shouldAdd) { // This is not the first fly from this operator TimeSpan totalDuration = (outboundLeg == null ? TimeSpan.Zero : outboundLeg.Duration) + (inboundLeg == null ? TimeSpan.Zero : inboundLeg.Duration); var lastFlight = opearatorFlights[opearatorFlights.Count - 1]; if ((price - lastFlight.Price) > this.MinPriceMargin) { // If the price differs enough, add new flight if we still have space if (opearatorFlights.Count < this.MaxFlightsPerAirline) { shouldAdd = true; } } else { // The new price does not differ enough from last flight: Add or replace existing flight if the duration is shorter for (int i = opearatorFlights.Count - 1; i >= 0; i--) { var f = opearatorFlights[i]; if ((price - f.Price) <= this.MinPriceMargin && totalDuration < f.Duration) { opearatorFlights.RemoveAt(i); shouldAdd = true; break; } } } } if (shouldAdd && fares.Count > 0) { var newFlight = new Flight(newData, flightOperator, outboundLeg, inboundLeg) { Fares = fares }; newData.AddFlight(newFlight); opearatorFlights.Add(newFlight); } } if (newData.Flights.Count > 0) { Journey existJourney = null; DateTime deptDate = newData.Flights[0].OutboundLeg.Departure.Date; DateTime retDate = DateTime.MinValue; if (newData.Flights[0].InboundLeg != null) { retDate = newData.Flights[0].InboundLeg.Departure.Date; } foreach (var j in route.Journeys) { if (j.DepartureDate == deptDate && j.ReturnDate == retDate) { existJourney = j; break; } } if (existJourney == null) { existJourney = new Journey(); route.AddJourney(existJourney); } existJourney.AddData(newData); existJourney.DepartureDate = deptDate; existJourney.ReturnDate = retDate; } } return new RouteDataResult(DataResult.Ready, route); }
/// <summary> /// Returns true if the flight leg has the same departure time, arrival time, total duration and number of transits /// </summary> /// <param name="otherLeg"> /// The other Leg. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> public bool IsSame(FlightLeg otherLeg) { bool result; if (result = otherLeg != null) { if (result = this.Departure == otherLeg.Departure) { if (result = this.Arrival == otherLeg.Arrival) { if (result = this.Duration == otherLeg.Duration) { result = this.Transit == otherLeg.Transit; } } } } return result; }