Address Book to FedEx

I use FedEx’s web page quite a bit as in my business we are constantly shipping product or samples. It’s not at all unusual to ship hundreds of boxes in a day. Now there are invoice programs and the like which can automate this for you, but they are pretty expensive. Furthermore they typically don’t integrate with the programs you want them to. This is a script I first put up on OSX Hints. I then put up a more revised version here at the blog. I also had a discussion of checking when Safari has finished loading. Those links have good discussions of the script however FedEx has changed it’s webpage a bit. Likewise Snow Leopard changed a few things in Safari that affected this script. Rather than simply write a new post every time I do I’ll just update this page.

For this script I’ve revised things a wee bit. Primarily I’ve broken the script into two files. This is primarily because the main module, paste_to_fedex.py, is used by quite a few different scripts. By putting it in its own module I only have to modify a single page any time Safari or FedEx.com changes.

Right now I don’t check all the passed in fields – although to be more robust I should. I assume there are keys for the main address fields, for instance. Where I do check is for keys of things like weight, setting them to a default if they are missing. This would be trivial to expand to set other fields you notice by looking at the HTML source for the FedEx page.

The main script is in address_to_fedex.py. This is fairly simple. It just iterates through the selected contacts in Address Book, fills out a dictionary, and then passes it to paste_to_fedex.fill_safari().

Be aware that this script assumes that you are already logged into FedEx.com and have an active page open. I could have automated the login but that’s really a security risk if done fully within a script. There may be a way to do this with 1Password though. But I’ll leave that for a future treatment.

paste_to_fedex.py

#!/usr/bin/python
 
## paste_to_fedex.py
##-------------------------------------------------------------------------
## Given a dictionary with address information [see test() for fields] this
## opens up a new FedEx shipping window in Safari and fills in the 
## appropriate fields.  Run by itself this simply fills FedEX with test data.
## This is primarily intended to be a module called by other scripts.
 
import string, sys, time, shutil
from appscript import *
import os
 
 
## This checks to see if Safari is finished loading the FedEx page
 
def check_Safari_done():
 
    while True:
        try: 
            s = app(u'/System/Library/CoreServices/System Events.app').application_processes[u'Safari'].windows[1].groups[1].static_texts[1].name.get(resulttype=k.unicode_text)
            break
        except:
            time.sleep(1)
            continue
 
    while True:
        try:
            s = app(u'/System/Library/CoreServices/System Events.app').application_processes[u'Safari'].windows[1].groups[1].static_texts[1].name.get(resulttype=k.unicode_text)
            if s[0:7] == "Loading":
                continue
            if s[0:10] == "Contacting":
                continue
            break
        except:
            time.sleep(1)
            break
 
    s = app(u'/System/Library/CoreServices/System Events.app').application_processes[u'Safari'].windows[1].groups[1].static_texts[1].name.get(resulttype=k.unicode_text)
    print s
    print "Safari Ready...\n\n"
 
## This does all the work of actually filling in the Safari fields for the FedEx website.
## It is pretty self-explanatory.  We check to see if the page is finished loading
## (see check_Safari_done above) and then simply use Javascript to fill in the fields.
## We selected the fields by simply browsing the source HTML for the FedEx page.
##
## Beyond that we simply do a little error checking on company and phone fields, putting
## in some defaults.
##
## We fill in some defaults that you might wish to change, such as the box weight.
 
def fill_safari( order_data ):
 
    Safari = app(u'/Applications/Safari.app')
    Safari.activate()
 
    ## make a new window and go to the FedEx site.  (Note: you have to have logged in already)
    Safari.make(new=k.document)
    Safari.documents[0].URL.set(u'https://www.fedex.com/shipping/shipAction.do?method=doInitialize&urlparams=us')
 
    ## get the document name we just created -- we use that to make sure we operate on the right windo
 
    check_Safari_done()
 
    time.sleep(1)
 
    FEdoc = Safari.documents[0].get()        
 
    if order_data["telephone"] == None:
        order_data["telephone"] = "801-655-1996"
 
    if order_data["telephone"] == "":
        order_data["telephone"] = "801-655-1996"
 
    if order_data["company"] == None:    
        order_data["company"] = "---"
 
    if order_data["company"] =="":
        order_data["company"] == "---"
 
    if 'weight' not in order_data:
        weight = '1'
    else:
        weight = str(order_data['weight'])
 
    if 'value' not in order_data:
        boxvalue = "0"
    else:
        boxvalue = str(order_data['value'])
 
    if 'shipping' not in order_data:
        shipping = 'FedEx Ground'
    else:
        shipping = order_data['shipping']
 
    # the field for residential has to be "true" or "false"
 
    if 'residential' not in order_data:
        residential = 'false'
    else:
        if order_data['shipping'] == 'True' or order_data['shipping'] == 'true' or str(order_data['shipping']) == "1":
            residential = 'true'
        else:
            residential = 'false'
 
 
    Safari.do_JavaScript("document.forms['shipActionForm']['toData.companyName'].value = \"" + order_data['company'] + "\"", in_=FEdoc)
    Safari.do_JavaScript("document.forms['shipActionForm']['toData.contactName'].value = '" + order_data["first"] + ' ' + order_data["last"] + "'" , in_=FEdoc)
 
    Safari.do_JavaScript("document.forms['shipActionForm']['toData.addressLine1'].value = '" + order_data["address"] + "'", in_=FEdoc)
 
    Safari.do_JavaScript("document.forms['shipActionForm']['toData.city'].value = '" + order_data["city"] + "'",  in_=FEdoc)
 
    Safari.do_JavaScript("document.forms['shipActionForm']['toData.stateProvinceCode'].value = '" + order_data["state"] + "'",  in_=FEdoc)
 
    Safari.do_JavaScript("document.forms['shipActionForm']['toData.zipPostalCode'].value = '" + order_data["zip"] + "'",  in_=FEdoc)
    Safari.do_JavaScript("document.forms['shipActionForm']['toData.phoneNumber'].value = '" + order_data["telephone"] + "'",  in_=FEdoc)
    Safari.do_JavaScript("document.forms['shipActionForm']['toData.residential'].checked = " + residential,  in_=FEdoc)
 
    Safari.do_JavaScript("document.forms['shipActionForm']['psdData.serviceType'].value = '" + shipping + "'" ,  in_=FEdoc)
    Safari.do_JavaScript("document.forms['shipActionForm']['psdData.packageType'].value = 'Your Packaging'",  in_=FEdoc)
    Safari.do_JavaScript("document.forms['shipActionForm']['psd.mps.row.weight.0'].value = '" + weight + "'",  in_=FEdoc)
    Safari.do_JavaScript("document.forms['shipActionForm']['psd.mps.row.dimensions.0'].value = '1006956'",  in_=FEdoc)
    Safari.do_JavaScript("document.forms['shipActionForm']['psd.mps.row.declaredValue.0'].value = '" + boxvalue + "'",  in_=FEdoc)
    Safari.do_JavaScript("recipientAddressChanged()",  in_=FEdoc)
 
    return
 
 
## Module unit test    
 
def test():
    # our unit test
    order_data = {}
    order_data['first'] = "Clark"
    order_data['last'] = "Goble"
    order_data['company'] = "Amano Chocolate"
    order_data['address'] = "Some Street"
    order_data['city'] = "Sometown"
    order_data['state'] = "UT"
    order_data['zip'] = "11111"
    order_data['telephone'] = "801-111-1111"
 
    fill_safari( order_data )
 
 
if __name__ == '__main__':
 
    if len(sys.argv) == 1:
        test( )
 
 
    # change to 0 for success, 1 for (partial) failure
    sys.exit(0)

address_to_fedex.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/usr/bin/python
 
## address_to_fedex.py
##-------------------------------------------------------------------------
## We simply iterate through the selected contacts in Address Book.  For 
## each contact we fill out a dictionary based upon the contact's field
## values.  We then call fill_safari() from the module paste_to_fedex.py
## which must be in the same directory or in the Python path.
 
 
import string, sys, time, shutil
from appscript import *
import os
import paste_to_fedex
 
def process_ab_selection():
 
    AB = app(u'/Applications/Address Book.app')
 
    clients = AB.selection.get()
 
    order_data = {}    
    for client in clients:
        # normally this will only run once but I figured I'd make it so you can do more than one person
 
        order_data['last'] = client.last_name.get()
        if order_data['last'] == k.missing_value:
            order_data['last'] = ""
 
        order_data['first'] = client.first_name.get()
        if order_data['first'] == k.missing_value:
            order_data['first'] = ""
 
        order_data['company'] = client.name.get()
        if order_data['company'] == k.missing_value:
            order_data['company'] = "Unknown"
 
        order_data['address'] = client.addresses.get()[0].street.get()
        if order_data['address'] == k.missing_value:
            order_data['address'] = ""
 
        order_data['city'] = client.addresses.get()[0].city.get()
        if order_data['city'] == k.missing_value:
            order_data['city'] = ""
 
        order_data['state'] = client.addresses.get()[0].state.get()
        if order_data['state'] == k.missing_value:
            order_data['state'] = ""
 
        order_data['zip'] = client.addresses.get()[0].zip.get()
        if order_data['zip'] == k.missing_value:
            order_data['zip'] = ""
 
        order_data['telephone'] = client.phones.items[1].value.get()
        if order_data['telephone'] == k.missing_value:
            order_data['telephone'] = "801-655-1996"
 
        paste_to_fedex.fill_safari( order_data )
 
if __name__ == '__main__':
 
    if len(sys.argv) == 1:
        process_ab_selection()
 
 
    # change to 0 for success, 1 for (partial) failure
    sys.exit(0)

Comments