Search code examples
python-3.xseleniumtkinterautomationwebautomation

How to iterate through multiple sets of data and input data into a method repeatedly? (tkinter, selenium, python)


I am trying to write an automation program that will take user inputed data from a singular UI and auto-input that data into a single website or multiple websites.

UI

When the "Get Quote" button is pressed, a chrome browser session opens and places all the data from the UI into its corresponding entry fields within the website.

browsersessionexample

What I am trying to figure out now is how to account for situations when there is more than 1 sets of data. For example:

UIexample2

Here is a picture of what I want the program to be able to do. Basically input the second set of data and click "Add line Item" and if there isn't any data left to enter, then the script will automatically click "Generate Smart Quote."

websessionexamplescenario

What is the best approach to being able to input second or third sets of data? The data has to be re-inputted in the same method. Here is some code for context.

from tkinter import *
from tkinter import font
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select 

orders = {}
def order_data():
    # start by adding a dict to the dict with key of length dict,
    # the first key will be 0, then 1, 2 and so on
    orders[len(orders)] = {}

    # next you can start adding data using len - 1 to access the correct key
    # and so on for all your data
    orders[len(orders) - 1]['handling unit'] = e3.get()
    orders[len(orders) - 1]['pieces'] = e4.get()
    orders[len(orders) - 1]['description'] = e5.get()
    orders[len(orders) - 1]['length'] = e6.get()
    orders[len(orders) - 1]['width'] = e7.get()
    orders[len(orders) - 1]['height'] = e8.get()
    orders[len(orders) - 1]['weight'] = e9.get()
    orders[len(orders) - 1]['classification'] = e10.get()    


def GetQuote():

    origin_zip = int(e1.get()) #gets entries stores data here
    destination_zip = int(e2.get())
    handling_unit = [e3.get()]
    pieces = [e4.get()]
    description = [e5.get()]
    length = [e6.get()]
    width = [e7.get()]
    height = [e8.get()]
    weight = [e9.get()]
    classification_list =[e10.get]

    driver = webdriver.Chrome("/home/***/***/chromedriver")

    driver.maximize_window() #driver.set_window_size(10, 10)

    #1st Tab (shipco.com)
    driver.get("https://tms.shipco.com/Main/Home")
    driver.implicitly_wait(1)

    #1st Tab Login Page
    driver.find_element(By.XPATH, "//button[@id='dropdownMenuButton']").click()
    driver.find_element(By.XPATH, "//input[@id='UserName']").send_keys('***')
    driver.find_element(By.XPATH, "//input[@id='Password']").send_keys('***')
    driver.find_element(By.XPATH, "//button[@class='btn btn-primary btn-block']").click()

    #1st Tab 2nd Page
    driver.find_element(By.XPATH, "//input[@id='OriginZip']").send_keys(origin_zip)
    driver.find_element(By.XPATH, "//input[@id='DestinationZip']").send_keys(destination_zip)
    driver.find_element(By.XPATH, "//button[@class='btn btn-secondary form-control']").click()

    #Will have to add a conditional statement here to iterate through instances of multiple items/dimensions/weights

    #1st Tab 3rd Page
    driver.refresh();
    for i in range(len(orders)):
        driver.find_element(By.ID, "handlingunits").send_keys(orders[i]['handling unit'])
        driver.find_element(By.ID, "quantityofgoods").send_keys(orders[i]['pieces'])
        driver.find_element(By.ID, "commoditydescription").send_keys(orders[i]['description'])
        driver.find_element(By.ID, "sizelength").send_keys(orders[i]['length'])
        driver.find_element(By.ID, "sizewidth").send_keys(orders[i]['width'])
        driver.find_element(By.ID, "sizeheight").send_keys(orders[i]['height'])
        driver.find_element(By.ID, "totalweight").send_keys(orders[i]['weight'])

        driver.implicitly_wait(10)
        driver.find_element(By.XPATH, "//a[@class='ng-binding']").click()      #this will click the object that gets class for shipment
        if i < len(orders):
            driver.find_element(By.XPATH, "//button[@class='btn btn-info']").click() 

    driver.find_element(By.XPATH, "//span[contains(text(),'Generate Smart Quote')]").click()     

def insert_into_textbox(): #generates input fields for the next set of items

    order_data()   
    #get the inputs
    handling_unit = e3.get()
    pieces = e4.get()
    description = e5.get()
    length = e6.get()
    width = e7.get()
    height = e8.get()
    weight = e9.get()
    classification = e10.get()

    textbox_display = (handling_unit.ljust(11)+pieces.ljust(13)+description.ljust(11)+length.ljust(10)+width.ljust(11)+height.ljust(11)+weight.ljust(10)+classification+"\n")

    textbox.insert("end",textbox_display)
    e3.delete(0, "end")
    e4.delete(0, "end")
    e5.delete(0, "end")
    e6.delete(0, "end")
    e7.delete(0, "end")
    e8.delete(0, "end")
    e9.delete(0, "end")
    e10.delete(0, "end")


master = Tk() 
master.title("Quote Automator")
master.configure(background="#eef56e")
arial8 = font.Font(family="Arial", size=8, weight=font.BOLD)
algerian8 = font.Font(family="Algerian", size=8, weight=font.BOLD)

e1 = Entry(master, borderwidth=5, width=12) #origin zip
e2 = Entry(master, borderwidth=5, width=12) #destination zip
e3 = Entry(master, borderwidth=5, width=12) #handling unit(s)
e4 = Entry(master, borderwidth=5, width=12) #piece(s)
e5 = Entry(master, borderwidth=5, width=13) #description(s)
e6 = Entry(master, borderwidth=5, width=12) #length(s)
e7 = Entry(master, borderwidth=5, width=12) #width(s)
e8 = Entry(master, borderwidth=5, width=12) #height(s)
e9 = Entry(master, borderwidth=5, width=12) #weight(s)
e10 = Entry(master, borderwidth=5, width=12) #class(s)

# grid method customizes position
e1.grid(row = 0, column = 2, pady = 1, sticky="W")  #origin zip
e2.grid(row = 0, column = 4, pady = 1, sticky="W") #destination zip
e3.grid(row = 3, column = 1, pady = 1, sticky="W") #handling unit(s)
e4.grid(row = 3, column = 2, pady = 1, sticky="W") #piece(s)
e5.grid(row = 3, column = 3, pady = 1, sticky="W")  #description(s)
e6.grid(row = 3, column = 4, pady = 1, sticky="W")  #length(s)
e7.grid(row = 3, column = 5, pady = 1, sticky="W") #width(s)
e8.grid(row = 3, column = 6, pady = 1, sticky="W") #height(s)
e9.grid(row = 3, column = 7, pady = 1, sticky="W") #weight(s)
e10.grid(row = 3, column = 8, pady = 1, sticky="W") #class(s)

textbox=Text(master, width=9, borderwidth=5, height=7)
textbox.grid(row=5, column=1, columnspan=9, sticky="nsew")

scrollbar=Scrollbar(master, orient="vertical", command=textbox.yview)
scrollbar.grid(row=5, column=10, sticky=NS)
textbox.configure(yscrollcommand=scrollbar.set)


b0 = Button(master, text = "Add Set", bg="white", fg="black", font=arial8, command=insert_into_textbox) #Add Next Set Button
b1 = Button(master, text = "Caculate Class", bg="yellow", font=arial8, command=set_class)
b2 = Button(master, text = "Get Quote", bg="#6fff00", fg="#7170ff", font=arial8, width=10, command=GetQuote) 

b0.grid(row = 3, column = 0, sticky = W) # Add Next Set Button Positioning
b1.grid(row = 0, column = 8, sticky = E) # Calculate Class Button Positioning
b2.grid(row = 0, column = 7, sticky = E) # Get Quote Button Positioning

mainloop() 

pic5 enter image description here


Solution

  • You could do something like add each line to a dictionary, with each key being a number. Then each value could be another dictionary or a named tuple. Then you can loop through the number keys in your method to get the values for each line.

    For example using a dict you can make a function to take in the data.

    orders = {}
        def order_data():
            # start by adding a dict to the dict with key of length dict,
            # the first key will be 0, then 1, 2 and so on
            orders[len(orders)] = {}
    
            # next you can start adding data using len - 1 to access the correct key
            orders[len(orders) - 1]['handling unit'] = e3.get()
            # and so on for all your data
    

    Then when you call your method you can use a for loop to input the data for each line.

    for i in range(len(orders)):
        class.method(i)
    

    Then inside your method for each send_keys function you can call the dictionary

    driver.find_element(By.ID, "handlingunits").send_keys(orders[i]['handling unit'])
    

    You could also do the for loop inside the method if you prefer.

    for i in range(len(orders)):
            driver.find_element(By.ID, "handlingunits").send_keys(orders[i]['handling unit'])
            driver.find_element(By.ID, "quantityofgoods").send_keys(orders[i]['pieces'])
            driver.find_element(By.ID, "commoditydescription").send_keys(orders[i]['description'])
            driver.find_element(By.ID, "sizelength").send_keys(orders[i]['length'])
            driver.find_element(By.ID, "sizewidth").send_keys(orders[i]['width'])
            driver.find_element(By.ID, "sizeheight").send_keys(orders[i]['height'])
            driver.find_element(By.ID, "totalweight").send_keys(orders[i]['weight'])
    
            driver.implicitly_wait(10)
            driver.find_element(By.XPATH, "//a[@class='ng-binding']").click()      #this will click the object that gets class for shipment
            if i < len(orders):
                driver.find_element(By.XPATH, "//button[@class='btn btn-info']").click() #add another line of items if avaiable
        driver.find_element(By.XPATH, "//span[contains(text(),'Generate Smart Quote')]").click()
    

    Putting the 'add new line' part inside the loop with logic like this will make the program keep adding until data is expended and then it will click the 'Generate Smart Quote' button.