Boone Putney bio photo

Boone Putney

Software Development
Random Musings
Austin, Texas

HumanPlanet Soleer

Email LinkedIn Github

As previously mentioned, I’ve been consluting with a client on integrating and setting up some of the functionality they require with their new NetSuite deployment. As with almost all SAAS solutions, NetSuite excels within it’s core competancy, but can cause issues when trying to achieve functionality outside of this.

This is how we were able to implement browser automation to perform the automated setup of thousands of customer centers with which would otherwise have taken weeks to do by hand and would have likely resulted in a higher rate of implementation errors.

Background

The goal was to allow users to access account-specific information on my clients’s website using one login managed through NetSuite. The best solution ended up using the built-in NetSuite outbound SSO to generate a unique link within NetSuite, and use that link to authenticate against in our WordPress installation.

We setup a custom portlet within NetSuite that handled all of the link generation and display, and the logic within wordpress to accept the verification data, and perform the handshake with NetSuite to pull the customer information.

Unfortunately, once this was done, we found out the only method to publish this portlet to the thousands of customer centers that the users access, was to manually do it within a browser.

Below is the python script we wrote using the selenium library to automate a browser instance and do this for us.

Code

  1 #!/usr/bin/env python
  2 # -*- coding: utf-8 -*
  3 
  4 from selenium import webdriver
  5 from selenium.webdriver.common.keys import Keys
  6 from selenium.webdriver.support.ui import WebDriverWait
  7 from selenium.webdriver.support import expected_conditions as ec
  8 from selenium.webdriver.common.by import By
  9 from selenium.common.exceptions import NoSuchElementException
 10 import time
 11 import csv
 12 import logging
 13 import datetime
 14 
 15 #setup log file for review later
 16 logger = logging.getLogger('setup-portlet')
 17 hdlr = logging.FileHandler('log'+datetime.datetime.now().strftime("%Y%m%d%H%M%S")+'.log')
 18 formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
 19 hdlr.setFormatter(formatter)
 20 logger.addHandler(hdlr)
 21 logger.setLevel(logging.INFO)
 22 
 23 #open browser instance
 24 browser = webdriver.Firefox()
 25 browser.implicitly_wait(1)
 26 
 27 csv_file = 'access-info.csv'
 28 
 29 #function that takes browser instance along with email and password
 30 #adds portlet to the customer center or checks for existing porlet
 31 def update_user(browser, email, password):
 32 
 33     #open login page and fill out form
 34     browser.get('http://system.netsuite.com')
 35 
 36     elem = browser.find_element_by_name('email')
 37     elem.send_keys(email)
 38 
 39     elem = browser.find_element_by_name('password')
 40     elem.send_keys(password + Keys.RETURN)
 41 
 42     #navigate to correct tab
 43     elem = browser.find_element_by_link_text('Screen Calculator')
 44     elem.click()
 45 
 46     #open setup dialog
 47     try:
 48         elem = browser.find_elements_by_xpath("html/body/div[1]/div[2]/div/div[2]/div/div[5]/div[1]/div/div[2]/div/div[1]/p/span")[0]
 49         elem.click()
 50     except Exception:
 51         #try to find portlet link, if found exit function, otherwise raise error
 52         try:
 53             elem = browser.find_element_by_link_text('Access Screen Calculator')
 54         except Exception:
 55             raise
 56         else:
 57             print "  portlet already setup"
 58             logger.info("portlet already setup")
 59             pass
 60             return
 61         raise
 62 
 63     #switch to iframe
 64     WebDriverWait(browser, 10).until(
 65         ec.frame_to_be_available_and_switch_to_it("scriptportlet_setupDialog_frame")
 66     )
 67 
 68     WebDriverWait(browser, 10).until(
 69         ec.presence_of_element_located((By.CLASS_NAME, "ext-gecko"))
 70     )
 71 
 72     #wait for javascript to load
 73     time.sleep(1) #seconds
 74 
 75     #select dropdown
 76     elem = browser.find_element_by_id("inpt_scriptsource1")
 77     elem.click()
 78 
 79     #select Screen Calculator option
 80     elem = browser.find_elements_by_xpath("html/body/div[2]/div/div/div[2]")[0]
 81     elem.click()
 82 
 83     #save form
 84     elem = browser.find_element_by_id("submitter")
 85     elem.click()
 86 
 87     print "  portlet successfully setup!"
 88     logger.info("portlet successfully setup!")
 89 
 90     #switch back to main window
 91     #browser.switch_to.default_content
 92 
 93     #sign out
 94     #elem = browser.find_elements_by_xpath("html/body/div[1]/div[1]/div[1]/div[8]/div/ul/li[1]/a")[0]
 95     #elem.click()
 96 
 97 with open(csv_file, 'rb') as csvfile:
 98     #open csv file with access information
 99     access_file = csv.reader(csvfile, delimiter=',')
100     next(access_file)   # skip the first line
101     row_counter = 1
102     for row in access_file:
103         email = row[3] #pull access infomration
104         password = row[8]
105         internalid = row[0]
106         access = row[7]
107         if access == "No" or not password or not email:  # if information is missing, skip
108             print "Skipping: "+email+","+password+" information missing / access disabled"
109             continue  # skip if no access
110         print str(row_counter)+") Attempting: "+email+","+password
111         logger.info(str(row_counter)+") Attempting: "+email+","+password)
112         row_counter += 1
113 
114         attempts = 0
115         while attempts < 5:  # on error, attempt 5 times, then fail out
116             try:
117                 update_user(browser, email, password)
118                 break
119             except Exception:
120                 attempts += 1
121                 if attempts is 5:
122                     print "  permanent fail"
123                     logger.error('permanent fail')
124                 else:
125                     print "  unsuccessful attempt"
126                     logger.error('unsuccessful attempt')