The Artima Developer Community
Sponsored Link

Computing Thoughts
A Fedex Rate Requester using xmlnode
by Bruce Eckel
June 9, 2006
Summary
As a followup to "Simplifying XML Manipulation", here is the Fedex Rate Requester that motivated me to make xmlnode in the first place. Because xmlnode got the XML-creation code noise out of the way, I was able to bash this together very quickly.

Advertisement

As people pointed out for that post, xmlnode does not handle XML namespaces. However, I have not had to deal with those yet; I've only needed to create XML and throw it at some application. If I run into a namespace problem, I'll either adapt xmlnode or move to some other system that handles them, like py.xml.

One thought occurred to me while I was building the rate requester: it's not uncommon that the documentation for the service you're using will give example XML showing what you should generate. It would be interesting to modify xmlnode so that it would parse existing XML and generate the code necessary to build that XML. This could speed up the development process and possibly eliminate mistakes.

Note that the FedexRateRequest class inherits Node, and inheritance (rather than composition) is useful here. Also, in the getRates() function, the copy.deepcopy() function is used to clone FedexRateRequest objects, eliminating duplicated effort.

Although I was able to put this code together quickly, I did depend on the code and information that I found at http://opensource.pseudocode.net/files/shipping.tgz , which definitely helped me bootstrap the process. Look at that package to find out how to get your own meter number.

import sys, urllib, copy
from xml.dom.minidom import parseString
from xmlnode import Node

class FedexRateRequest(Node):
    login = { "account" : "your account #", "meter" : "your meter #" }
    def __init__(self, service, packaging, weight, orginState, originZip, destState, destZip):
        self.Error = False
        if float(weight) > 1.0 and packaging == 'FEDEXENVELOPE': # Can't be more than 1 lb.
            packaging = 'FEDEXBOX'        
        Node.__init__(self, "FDXRateRequest")
        self.setAttribute("xmlns:api", "http://www.fedex.com/fsmapi")
        self.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
        self.setAttribute("xsi:noNamespaceSchemaLocation", "FDXRateRequest.xsd")
        
        self += Node("RequestHeader") + \
                Node("AccountNumber", FedexRateRequest.login["account"]) + \
                Node("MeterNumber", FedexRateRequest.login["meter"]) + \
                Node("CarrierCode", "FDXE")
        
        self += Node("Service", service)
        self += Node("Packaging", packaging)
        self += Node("WeightUnits", 'LBS')
        self += Node("Weight", weight)
        
        self += Node("OriginAddress") + Node("StateOrProvinceCode", orginState) + \
                Node("PostalCode", originZip) + Node("CountryCode", "US")
        
        self += Node("DestinationAddress") + Node("StateOrProvinceCode", destState) + \
                Node("PostalCode", destZip) + Node("CountryCode", "US")
        
        self += Node("Payment")
        
        self += Node("PackageCount", "1")
        
        self.result = None
    
    def requestRate(self):
        reqxml = '<?xml version="1.0" ?>' + self.rawxml().strip()
        result = urllib.urlopen("https://gateway.fedex.com/GatewayDC", reqxml).read()
        self.result = Node.create(parseString(result))
        if result.find("Error") != -1:
            self.Error = True
            return
        self.base = float(self.result["BaseCharge"].value)
        self.discount = float(self.result["TotalDiscount"].value)
        self.surcharge = float(self.result["TotalSurcharge"].value)
        self.netcharge = float(self.result["NetCharge"].value)
        
    def showResults(self):
        print self["Service"].value, self["Packaging"].value
        if self.Error:
            print "Error:", self.result["Message"].value
            return
        print "base:", self.base
        print "discount:", self.discount
        print "surcharge:", self.surcharge
        print "net charge:", self.netcharge
    
def getRates(packaging, weight, orginState, originZip, destState, destZip, homedelivery):
    priority = FedexRateRequest('PRIORITYOVERNIGHT', 
               packaging, weight, orginState, originZip,
               destState, destZip)
    standard = copy.deepcopy(priority)
    standard["Service"].value = 'STANDARDOVERNIGHT'
    twoday = copy.deepcopy(priority)
    twoday["Service"].value = 'FEDEX2DAY'
    ground = copy.deepcopy(priority)
    ground["CarrierCode"].value = 'FDXG'
    ground["Packaging"].value = 'YOURPACKAGING' # Ground must use YOURPACKAGING
    if homedelivery:
        ground["Service"].value = 'GROUNDHOMEDELIVERY'
    else:
        ground["Service"].value = 'FEDEXGROUND'
    priority.requestRate()
    standard.requestRate()
    twoday.requestRate()
    ground.requestRate()
    return (ground, twoday, standard, priority)

if __name__ == "__main__":
    for rate in getRates('1.6', "CA", "91941", "CO", "81224", homedelivery=False):
        rate.showResults()

Talk Back!

Have an opinion? Be the first to post a comment about this weblog entry.

RSS Feed

If you'd like to be notified whenever Bruce Eckel adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Bruce Eckel (www.BruceEckel.com) provides development assistance in Python with user interfaces in Flex. He is the author of Thinking in Java (Prentice-Hall, 1998, 2nd Edition, 2000, 3rd Edition, 2003, 4th Edition, 2005), the Hands-On Java Seminar CD ROM (available on the Web site), Thinking in C++ (PH 1995; 2nd edition 2000, Volume 2 with Chuck Allison, 2003), C++ Inside & Out (Osborne/McGraw-Hill 1993), among others. He's given hundreds of presentations throughout the world, published over 150 articles in numerous magazines, was a founding member of the ANSI/ISO C++ committee and speaks regularly at conferences.

This weblog entry is Copyright © 2006 Bruce Eckel. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use