/// <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 load data. /// </summary> /// <param name="journeys"> /// The journeys. /// </param> /// <param name="loadHistory"> /// The load history. /// </param> /// <param name="callback"> /// The callback. /// </param> public void LoadData(IList<Journey> journeys, bool loadHistory, IProgressCallback callback) { this.Logger.DebugFormat("Load data for {0} journeys [{1}]", journeys.Count, loadHistory ? "H" : null); foreach (var j in journeys) { j.Data.Clear(); } string condition = GenerateCondition(journeys); using (var connection = new SQLiteConnection(this._connectionString)) { string selectSql = "SELECT LID, LJOURNEYID, SCURRENCY, BFLIGHT, " + (loadHistory ? "TUPDATE" : "MAX(TUPDATE) TUPDATE") + " FROM JOURNEY_DATA " + " WHERE " + condition + (loadHistory ? string.Empty : " GROUP BY (LJOURNEYID)"); using (var getFlightsCmd = new SQLiteCommand(selectSql, connection)) { connection.Open(); using (var reader = getFlightsCmd.ExecuteReader()) { if (reader.HasRows) { if (reader.HasRows) { int iId = reader.GetOrdinal("LID"); int iJourneyId = reader.GetOrdinal("LJOURNEYID"); int iCurrency = reader.GetOrdinal("SCURRENCY"); int iUpdate = reader.GetOrdinal("TUPDATE"); int iFlight = reader.GetOrdinal("BFLIGHT"); while (reader.Read()) { var flights = this._formatter.FromRaw<List<Flight>>((byte[])reader[iFlight]); if (flights != null && flights.Count > 0) { long dataId = reader.GetInt64(iId); long journeyId = reader.GetInt64(iJourneyId); string currency = reader.GetString(iCurrency); string dataDateStr = reader.GetString(iUpdate); DateTime dataDate = DateTime.ParseExact( dataDateStr, DATETIME_FORMAT, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); var newData = new JourneyData(dataId, currency, dataDate); newData.AddFlights(flights); foreach (var j in journeys) { if (j.Id == journeyId) { j.AddData(newData); break; } } } } } } } } } }
/// <summary> /// The load data. /// </summary> /// <param name="route"> /// The route. /// </param> /// <param name="loadJourneyData"> /// The load journey data. /// </param> /// <param name="loadHistory"> /// The load history. /// </param> /// <param name="loadFlights"> /// The load flights. /// </param> /// <param name="callback"> /// The callback. /// </param> /// <exception cref="ArgumentException"> /// </exception> public void LoadData(TravelRoute route, bool loadJourneyData, bool loadHistory, bool loadFlights, IProgressCallback callback) { this.Logger.DebugFormat( "Load data for route [{0}-{1}] [{2}{3}{4}]", route.Departure.IATA, route.Destination.IATA, loadJourneyData ? "D" : null, loadHistory ? "H" : null, loadFlights ? "F" : null); using (var connection = new SQLiteConnection(this._connectionString)) { if (route.Id < 1) { throw new ArgumentException("Invalid Route Id"); } route.Journeys.Clear(); string selectSql = "SELECT J.LID, J.TDEPARTURE, J.TRETURN" + (loadJourneyData ? (", D.LID DATAID, D.SCURRENCY, " + (loadHistory ? "D.TUPDATE" : "MAX(D.TUPDATE) TUPDATE") + (loadFlights ? ", D.BFLIGHT" : string.Empty)) : string.Empty) + " FROM JOURNEY J" + (loadJourneyData ? ", JOURNEY_DATA D " : string.Empty) + " WHERE J.LROUTEID = @lRouteId " + (loadJourneyData ? " AND D.LJOURNEYID = J.LID " + (loadHistory ? string.Empty : " GROUP BY (D.LJOURNEYID) ") : string.Empty); using (var getJourneyCmd = new SQLiteCommand(selectSql, connection)) { getJourneyCmd.Parameters.AddWithValue("@lRouteId", route.Id); connection.Open(); using (var reader = getJourneyCmd.ExecuteReader()) { if (reader.HasRows) { int iId = reader.GetOrdinal("LID"); int iDeparture = reader.GetOrdinal("TDEPARTURE"); int iReturn = reader.GetOrdinal("TRETURN"); int iUpdate = reader.GetOrdinal("TUPDATE"); int iCurrency = loadJourneyData ? reader.GetOrdinal("SCURRENCY") : -1; int iDataId = loadJourneyData ? reader.GetOrdinal("DATAID") : -1; int iFlight = loadJourneyData ? reader.GetOrdinal("BFLIGHT") : -1; while (reader.Read()) { long journeyId = reader.GetInt64(iId); Journey journey = null; var allJourneys = route.Journeys; foreach (var j in allJourneys) { if (j.Id == journeyId) { journey = j; break; } } if (journey == null) { var deptDate = DateTime.ParseExact(reader.GetString(iDeparture), DATE_FORMAT, CultureInfo.InvariantCulture); var retDate = DateTime.ParseExact(reader.GetString(iReturn), DATE_FORMAT, CultureInfo.InvariantCulture); journey = new Journey(journeyId, route, deptDate, retDate); route.AddJourney(journey); } if (loadJourneyData) { var dataDate = DateTime.ParseExact( reader.GetString(iUpdate), DATETIME_FORMAT, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); var newData = new JourneyData(reader.GetInt64(iDataId), reader.GetString(iCurrency), dataDate); if (loadFlights) { var dbFlights = this._formatter.FromRaw<List<Flight>>((byte[])reader[iFlight]); if (dbFlights != null && dbFlights.Count > 0) { newData.AddFlights(dbFlights); } } journey.AddData(newData); } } } } } } }
public void FlightDataListViewTest() { // To generate code for this test, select "Generate Code for Coded UI Test" from the shortcut menu and select one of the menu items. // For more information on generated code, see http://go.microsoft.com/fwlink/?LinkId=179463 using (var frm = new Form()) { using (var lv = new FlightDataListView { Dock = DockStyle.Fill }) { var route = new TravelRoute(1, AirportDataProvider.FromIATA("HEL"), AirportDataProvider.FromIATA("SGN")); var j = new Journey(1, route, DateTime.Now, DateTime.Now.AddDays(7)); var d = new JourneyData(1, "EUR", DateTime.Now); var ds = new List<Flight>(100); /* TODO FIX Unit test for (int i = 0; i < 100; i++) { var f = new Flight( d, Guid.NewGuid().ToString(), i + 10, new TravelAgency(Guid.NewGuid().ToString(), "http://google.com"), new FlightLeg(DateTime.Now, DateTime.Now.AddDays(1), TimeSpan.FromHours(i + 12), 2), new FlightLeg(DateTime.Now.AddDays(7), DateTime.Now.AddDays(8), TimeSpan.FromHours(i + 13), 2)); ds.Add(f); } */ j.AddData(d); d.SetFlightLinks(); lv.SetDataSourceAsync(ds, true); frm.Controls.Add(lv); frm.ShowDialog(); } } }
/// <summary> /// Add fare data for the journey /// </summary> /// <param name="data"> /// The data. /// </param> public void AddData(JourneyData data) { if (data != null) { this.Data.Add(data); data.JourneyInfo = this; } }
/// <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); }