示例#1
0
 /// <summary>
 /// Whenever we browse the addresses and the stores available at that address, insert the address and the stores in the DB
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e">The object containing the address and available stores</param>
 private void FormMain_ReportAvailableStoresEvent(object sender, Shipt_Available_Stores e)
 {
     if (e.StoreNames.Count > 0)
     {
         DB_Manager.InsertAddress(e.Delivery_Address);
         DB_Manager.InsertStores(e);
     }
 }
示例#2
0
        /// <summary>
        /// The thread that controls the browser, and everything relevant to it
        /// </summary>
        private void t_Crawl()
        {
            try
            {
                browserRunning = true;

                ChromeOptions                chromeOptions = new ChromeOptions();
                ChromeDriverService          driverService = ChromeDriverService.CreateDefaultService();
                HtmlAgilityPack.HtmlDocument FullWebPage;

                //chromeOptions.AddArguments("headless");
                chromeOptions.PageLoadStrategy        = PageLoadStrategy.Normal;
                driverService.HideCommandPromptWindow = true;

                // Open Browser
                using (var browser = new ChromeDriver(driverService, chromeOptions))
                {
                    browser.Navigate().GoToUrl(URL_Login);

                    // Wait for Url to change from URL_Login to URL_MainHomePage
                    while (browser.Url != URL_MainHomePage)
                    {
                        if (!requestBrowserAbort)
                        {
                            Thread.Sleep(100);
                        }

                        else
                        {
                            return;
                        }
                    }

                    // Let's check if we're logged in by looking for an address in the header of the page
                    bool   isLoggedIn         = false;
                    string hasAddressInHeader = browser.FindElements(By.XPath("//a[@class=\"pointer link darkness\"][@href=\"/account/addresses\"]//span")).First().Text;
                    if (!string.IsNullOrEmpty(hasAddressInHeader))
                    {
                        isLoggedIn = true;
                    }

                    if (isLoggedIn)
                    {
                        WebDriverWait wait = new WebDriverWait(browser, TimeSpan.FromSeconds(5));
                        wait.PollingInterval = TimeSpan.FromMilliseconds(50);

                        // Get the addresses on the account, and the available stores at each address
                        {
                            wait.Until(ExpectedConditions.ElementExists(By.XPath("//button[@data-test=\"ShoppingStoreSelect-storeView\"]"))).Click();                             // Trigger event to bring up the Select Store screen

                            wait.Until(ExpectedConditions.ElementExists(By.XPath("//button[@id=\"SelectAddress-select\"][@data-test=\"Select-button\"]"))).Click();               // Trigger event to drop down list of addresses

                            wait.Until(ExpectedConditions.ElementExists(By.XPath("//li[@data-test=\"Dropdown-option\"]")));
                            int numOfAddresses = browser.FindElements(By.XPath("//li[@data-test=\"Dropdown-option\"]")).Count;                             // Get count of addresses

                            for (int i = 0; i < numOfAddresses; i++)
                            {
                                browser.FindElement(By.XPath("//button[@id=\"SelectAddress-select\"][@data-test=\"Select-button\"]")).Click();

                                if (i > 0)
                                {
                                    wait.Until(ExpectedConditions.ElementExists(By.XPath("//li[@data-test=\"Dropdown-option\"]")));
                                    browser.FindElements(By.XPath("//li[@data-test=\"Dropdown-option\"]"))[i].Click();
                                }

                                Shipt_Available_Stores avail_stores = new Shipt_Available_Stores();
                                avail_stores.Delivery_Address = browser.FindElement(By.XPath("//div[@id=\"SelectAddress-select-selected-option-label\"]")).Text;

                                wait.Until(ExpectedConditions.ElementExists(By.XPath("//div[@class=\"cf\"]")));
                                var Stores = browser.FindElements(By.XPath("//div[@class=\"cf\"]//div//div//p"));

                                foreach (var store in Stores)
                                {
                                    avail_stores.AddStore(store.Text);
                                }

                                // TODO: This event might be causing a huge delay in the program
                                ReportAvailableStoresEvent?.Invoke(this, avail_stores);
                            }

                            browser.FindElement(By.XPath("//button[@id=\"SelectAddress-select\"][@data-test=\"Select-button\"]")).Click();
                            browser.FindElements(By.XPath("//li[@data-test=\"Dropdown-option\"]"))[0].Click();
                            browser.FindElement(By.XPath("//button[@data-test=\"Choose Store-modal-close\"]")).Click();
                        }

                        string currURL = browser.Url;
                        while (!requestBrowserAbort)
                        {
                            if (currURL != browser.Url)                             // Check if the URL has changed
                            {
                                // If the user navigated to a product page
                                if (browser.Url.Contains(@"shop.shipt.com/products/"))
                                {
                                    Shipt_Product product = new Shipt_Product();

                                    /*
                                     * // I thought this was a good idea in case I change the type of Product_ID, I knew I'd forget to update this
                                     * // but it just creates more work than it's saving.
                                     * Type type = product.Product_ID.GetType();
                                     * type.GetMethod("Parse").Invoke(null, new object[] { browser.Url.Substring(browser.Url.LastIndexOf("/")) } );
                                     */

                                    product.Address = browser.FindElements(By.XPath("//a[@class=\"pointer link darkness\"][@href=\"/account/addresses\"]//span")).First().Text;

                                    wait.Until(ExpectedConditions.ElementExists(By.XPath("//div[@data-test=\"ProductDetail-product-name\"]")));

                                    // Product_ID
                                    product.Product_ID = uint.Parse(browser.Url.Substring(browser.Url.LastIndexOf("/") + 1));

                                    // Store
                                    if (browser.FindElements(By.XPath("//span[@data-test=\"ShoppingStoreSelect-storeView-storeName\"]")).Count > 0)
                                    {
                                        product.Store = browser.FindElement(By.XPath("//span[@data-test=\"ShoppingStoreSelect-storeView-storeName\"]")).Text;
                                    }

                                    // Brand_Name if it exists
                                    if (browser.FindElements(By.XPath("//span[@data-test=\"ProductDetail-brand-name\"]")).Count > 0)
                                    {
                                        product.Brand_Name = browser.FindElement(By.XPath("//span[@data-test=\"ProductDetail-brand-name\"]")).Text;
                                    }

                                    // Product_Name
                                    if (browser.FindElements(By.XPath("//div[@data-test=\"ProductDetail-product-name\"]")).Count > 0)
                                    {
                                        product.Product_Name = browser.FindElement(By.XPath("//div[@data-test=\"ProductDetail-product-name\"]")).Text;
                                    }

                                    // Sale_Price and Regular_Price if product is on sale
                                    if (browser.FindElements(By.XPath("//div[@data-test=\"ProductDetail-product-sale-price\"]")).Count > 0)
                                    {
                                        /*
                                         * TIL the two FindElements inline are redundant
                                         *
                                         * string temp_Sale_Price = browser.FindElement(By.XPath("//div[@data-test=\"ProductDetail-product-sale-price\"]")).FindElement(By.XPath("//span[@class=\"mr1 title-2 tomato\"]"))
                                         *                                                              .Text
                                         *                                                              .Substring(1);
                                         *
                                         * string temp_Regular_Price = browser.FindElement(By.XPath("//div[@data-test=\"ProductDetail-product-sale-price\"]")).FindElement(By.XPath("//span[@class=\"mr1 body-2 gray strike\"]"))
                                         *                                                              .Text
                                         *                                                              .Substring(1);
                                         */
                                        ReadOnlyCollection <IWebElement> Prices = browser.FindElements(By.XPath("//div[@data-test=\"ProductDetail-product-sale-price\"]//span"));

                                        if (Prices.Count >= 2)
                                        {
                                            string temp_Sale_Price    = Prices[0].Text.Substring(1);
                                            string temp_Regular_Price = Prices[1].Text.Substring(1);

                                            try
                                            {
                                                product.Sale_Price    = Convert.ToDecimal(temp_Sale_Price);
                                                product.Regular_Price = Convert.ToDecimal(temp_Regular_Price);
                                            }

                                            catch (Exception)
                                            {
                                                throw;
                                            }
                                        }

                                        else
                                        {
                                            //TODO: What happens if/when we don't have two elements for discount and regular price?
                                        }
                                    }

                                    // If product not on sale, it should have a regular price
                                    else if (browser.FindElements(By.XPath("//div[@data-test=\"ProductDetail-product-price\"]")).Count > 0)
                                    {
                                        string temp_Regular_Price = browser.FindElement(By.XPath("//div[@data-test=\"ProductDetail-product-price\"]")).Text.Substring(1);

                                        try
                                        {
                                            product.Regular_Price = Convert.ToDecimal(temp_Regular_Price);
                                        }

                                        catch (Exception)
                                        {
                                            throw;
                                        }
                                    }

                                    // This shouldn't be reached, but this should be handled
                                    else
                                    {
                                        //TODO: If product doesn't have a sale price or a regular price, what do we do?
                                    }

                                    // Subtext for Units and Unit_Type
                                    if (browser.FindElements(By.XPath("//div[@data-test=\"ProductDetail-subtext\"]")).Count > 0)
                                    {
                                        //TODO: Sometimes, subtext looks like "12 ct; 12 fl oz". Do we want to handle this a little differently?
                                        //TODO: "1/2 gal" won't parse to decimal.
                                        string temp_Subtext = browser.FindElement(By.XPath("//div[@data-test=\"ProductDetail-subtext\"]")).Text;

                                        if (char.IsDigit(temp_Subtext[0]))                                        // TODO: Probably not a good idea to hard-code this
                                        {
                                            string temp_Units    = temp_Subtext.Substring(0, (temp_Subtext.IndexOf(' '))).Replace("$", "");
                                            string temp_UnitType = temp_Subtext.Substring(temp_Subtext.IndexOf(' ') + 1);

                                            try
                                            {
                                                product.Units = Convert.ToDecimal(temp_Units);
                                            }

                                            catch (Exception)
                                            {
                                                throw;
                                            }

                                            product.Unit_Type = temp_UnitType;
                                        }

                                        else
                                        {
                                            product.Units     = 1;
                                            product.Unit_Type = temp_Subtext;
                                        }
                                    }

                                    // If product is BOGO
                                    if (browser.FindElements(By.XPath("//div[@data-test=\"ProductDetail-bogo-text\"]")).Count > 0)
                                    {
                                        product.AddPromotion("Buy 1, Get 1 Free");
                                    }

                                    // If item has a promotion
                                    if (browser.FindElements(By.XPath("//button[@data-test=\"ProductDetail-feature-promotion\"]")).Count > 0)
                                    {
                                        string temp_Promotion = browser.FindElement(By.XPath("//button[@data-test=\"ProductDetail-feature-promotion\"]//div//div")).Text;
                                        product.AddPromotion(temp_Promotion);
                                    }

                                    LandedOnProductPageEvent?.Invoke(this, product);
                                }

                                // Check if the user left a product page for anything other than a product page
                                else if (currURL.Contains(URL_Prefix_Product) &&
                                         !browser.Url.Contains(URL_Prefix_Product))
                                {
                                    LeftProductPageEvent?.Invoke(this, new EventArgs());
                                }

                                currURL = browser.Url;
                            }
                        }
                    }
                }

                requestBrowserAbort = false;
                browserRunning      = false;

                if (!mainFormIsClosing)
                {
                    //Set btnCrawl text to "Start"
                    if (btnCrawl.InvokeRequired)
                    {
                        btnCrawl.Invoke(new MethodInvoker(() => btnCrawl.Text = "Start Browser"));
                    }

                    else
                    {
                        btnCrawl.Text = "Start Browser";
                    }
                }
            }

            catch (ThreadAbortException)
            {
                requestBrowserAbort = false;
                browserRunning      = false;

                if (!mainFormIsClosing)
                {
                    //Set btnCrawl text to "Start"
                    if (btnCrawl.InvokeRequired)
                    {
                        // This seems to lock the application and keep it alive, even though btnCrawl.IsDisposed and btnCrawl.Disposing are false
                        btnCrawl.Invoke(new MethodInvoker(() => btnCrawl.Text = "Start Browser"));
                    }

                    else
                    {
                        btnCrawl.Text = "Start Browser";
                    }
                }
            }
        }
示例#3
0
        /// <summary>
        /// Add new stores to the Stores table.<para/>
        /// Query's the Addresses table for a match for use at Stores.Address_id.<para/>
        /// Query's the Stores table for existing Stores.id and Stores.Address_id.
        /// </summary>
        /// <param name="stores">The address and stores to be added.</param>
        /// <returns>True if successfully added.</returns>
        public static bool InsertStores(Shipt_Available_Stores stores)
        {
            if (!DatabaseExists())
            {
                CreateDatabase();
            }

            using (SQLiteConnection connection = new SQLiteConnection(LoadConnectionString()))
            {
                int Address_ID;

                SQLiteCommand QueryStores = new SQLiteCommand("SELECT Stores.Store FROM Stores WHERE (Address_id = ?)", connection);

                SQLiteCommand command = new SQLiteCommand("INSERT INTO Stores (Address_id, Store, Active) VALUES (?, ?, ?)", connection);
                command.Parameters.Add("Address_id", DbType.Int32);
                command.Parameters.Add("Store", DbType.String);
                command.Parameters.Add("Active", DbType.String);

                try
                {
                    connection.Open();

                    Address_ID = FindAddress_id(connection, stores.Delivery_Address);

                    if (Address_ID > 0)
                    {
                        command.Parameters["Address_id"].Value = Address_ID;

                        List <string> modifiedStores = stores.StoreNames;

                        QueryStores.Parameters.AddWithValue("Address_id", Address_ID);
                        SQLiteDataReader reader = QueryStores.ExecuteReader();

                        // Take out any existing stores from modifiedStores so we don't spend time adding rows that already exist
                        while (reader.Read())
                        {
                            if (modifiedStores.Contains(reader[0].ToString()))
                            {
                                modifiedStores.Remove(reader[0].ToString());
                            }
                        }

                        foreach (string store in modifiedStores)
                        {
                            command.Parameters["Store"].Value  = store;
                            command.Parameters["Active"].Value = "True";
                            command.ExecuteNonQuery();
                        }
                    }

                    return(true);
                }

                catch (Exception ex)
                {
                    return(false);
                }

                finally
                {
                    connection.Close();
                    command.Dispose();
                }
            }
        }