From f454cc6167cd7d91297d6b24187193888ed719ee Mon Sep 17 00:00:00 2001 From: Filipp Lepalaan Date: Sun, 9 Jun 2013 11:38:51 +0300 Subject: Added xmltodict.py --- gsxws/core.py | 86 ++----------------------- gsxws/repairs.py | 9 +-- gsxws/xmltodict.py | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 86 deletions(-) create mode 100644 gsxws/xmltodict.py diff --git a/gsxws/core.py b/gsxws/core.py index a49ea6f..c70cd36 100644 --- a/gsxws/core.py +++ b/gsxws/core.py @@ -31,6 +31,7 @@ import hashlib import logging import httplib import tempfile +import xmltodict from urlparse import urlparse import xml.etree.ElementTree as ET @@ -263,10 +264,10 @@ class GsxRequest(object): logging.debug("Response: %s %s %s" % (res.status, res.reason, xml)) response = response or self._response - - for r in ET.fromstring(xml).findall("*//%s" % response): - o = r if raw else GsxObject.from_xml(r) - self.objects.append(o) + logging.debug("Looking for %s" % response) + root = ET.fromstring(xml).find("*//%s" % response) + data = xmltodict.ConvertXmlToDict(root) + self.objects = data[response] return self.objects @@ -355,81 +356,6 @@ class GsxObject(object): return root - @classmethod - def from_xml(cls, el): - "Constructs a GsxObject from an XML Element" - obj = GsxObject() - - for r in el: - - newitem = cls.from_xml(r) - k, v = r.tag, r.text - - if hasattr(obj, k): - # found duplicate tag %s" % k - attr = obj.__getattr__(k) - if isinstance(attr, list): - # append to existing list - newattr = attr.append(newitem) - setattr(obj, k, newattr) - else: - # convert to list - setattr(obj, k, [v, newitem]) - else: - # unique tag %s -> set the dictionary" % k - setattr(obj, k, newitem) - - if k in ["partsInfo"]: - # found new list item %s" % k - attr = [] - attr.append(GsxObject.from_xml(r)) - - setattr(obj, k, attr) - - if k in ["packingList", "proformaFileData", "returnLabelFileData"]: - v = base64.b64decode(v) - of = tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) - of.write(v) - v = of.name - - if isinstance(v, basestring): - - v = unicode(v) # "must be unicode, not str" - - # convert Y and N to boolean - if re.search(r'^[YN]$', v): - v = (v == "Y") - - # strip currency prefix and munge into float - if re.search(r'Price$', k): - v = float(re.sub(r'[A-Z ,]', '', v)) - - # Convert timestamps to native Python type - # 18-Jan-13 14:38:04 - if re.search(r'TimeStamp$', k): - v = datetime.strptime(v, "%d-%b-%y %H:%M:%S") - - if re.search(r'Date$', k): - # looks like some sort of date, let's try to convert - # @TODO: return actual native dates, not isoformat() - try: - # standard GSX format: "mm/dd/yy" - dt = datetime.strptime(v, "%m/%d/%y") - v = dt.date().isoformat() - except ValueError: - pass - - try: - # some dates are formatted as "yyyy-mm-dd" - dt = datetime.strptime(v, "%Y-%m-%d") - v = dt.date().isoformat() - except (ValueError, TypeError): - pass - - setattr(obj, k, v) - - return obj - class GsxRequestObject(GsxObject): "The GSX-friendly representation of this GsxObject" @@ -472,7 +398,7 @@ class GsxSession(GsxObject): else: self._req = GsxRequest(AuthenticateRequest=self) result = self._req._submit("Authenticate") - self._session_id = result[0].userSessionId + self._session_id = result.userSessionId GSX_SESSION = self.get_session() self._cache.set("session", GSX_SESSION) diff --git a/gsxws/repairs.py b/gsxws/repairs.py index b8445db..3461092 100644 --- a/gsxws/repairs.py +++ b/gsxws/repairs.py @@ -161,21 +161,18 @@ class Repair(GsxObject): """ self._namespace = "core:" details = self._submit("RepairDetailsRequest", "RepairDetails", "lookupResponseData") - #self.parts = [] -""" + # fix tracking URL if available for i, p in enumerate(details.partsInfo): - print p try: url = re.sub('<>', p.deliveryTrackingNumber, p.carrierURL) - #details.partsInfo[i].carrierURL = url - #self.parts.append(p) + details.partsInfo[i].carrierURL = url except AttributeError: pass self.details = details return details -""" + class CannotDuplicateRepair(Repair): """ diff --git a/gsxws/xmltodict.py b/gsxws/xmltodict.py new file mode 100644 index 0000000..c577e55 --- /dev/null +++ b/gsxws/xmltodict.py @@ -0,0 +1,183 @@ +## {{{ http://code.activestate.com/recipes/573463/ (r7) + +import re +import base64 +import tempfile +from datetime import datetime +from xml.etree import ElementTree + + +class XmlDictObject(dict): + """ + Adds object like functionality to the standard dictionary. + """ + def __init__(self, initdict=None): + if initdict is None: + initdict = {} + dict.__init__(self, initdict) + + def __getattr__(self, item): + v = self.__getitem__(item) + + if item in ["packingList", "proformaFileData", "returnLabelFileData"]: + v = base64.b64decode(v) + of = tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) + of.write(v) + return of.name + + try: + if isinstance(v, basestring): + v = unicode(v) # "must be unicode, not str" + + # convert Y and N to boolean + if re.search(r'^[YN]$', v): + v = (v == "Y") + + # strip currency prefix and munge into float + if re.search(r'Price$', item): + v = float(re.sub(r'[A-Z ,]', '', v)) + + # Convert timestamps to native Python type + # 18-Jan-13 14:38:04 + if re.search(r'TimeStamp$', item): + v = datetime.strptime(v, "%d-%b-%y %H:%M:%S") + + if re.search(r'Date$', item): + # looks like some sort of date, let's try to convert + try: + # standard GSX format: "mm/dd/yy" + dt = datetime.strptime(v, "%m/%d/%y") + v = dt.date() + except ValueError: + pass + + try: + # some dates are formatted as "yyyy-mm-dd" + dt = datetime.strptime(v, "%Y-%m-%d") + v = dt.date() + except (ValueError, TypeError): + pass + except TypeError: + pass + + return v + + def __setattr__(self, item, value): + self.__setitem__(item, value) + + def __str__(self): + if self.has_key('_text'): + return self.__getitem__('_text') + else: + return '' + + @staticmethod + def Wrap(x): + """ + Static method to wrap a dictionary recursively as an XmlDictObject + """ + + if isinstance(x, dict): + return XmlDictObject((k, XmlDictObject.Wrap(v)) for (k, v) in x.iteritems()) + elif isinstance(x, list): + return [XmlDictObject.Wrap(v) for v in x] + else: + return x + + @staticmethod + def _UnWrap(x): + if isinstance(x, dict): + return dict((k, XmlDictObject._UnWrap(v)) for (k, v) in x.iteritems()) + elif isinstance(x, list): + return [XmlDictObject._UnWrap(v) for v in x] + else: + return x + + def UnWrap(self): + """ + Recursively converts an XmlDictObject to a standard dictionary and returns the result. + """ + + return XmlDictObject._UnWrap(self) + + +def _ConvertDictToXmlRecurse(parent, dictitem): + assert type(dictitem) is not type([]) + + if isinstance(dictitem, dict): + for (tag, child) in dictitem.iteritems(): + if str(tag) == '_text': + parent.text = str(child) + elif type(child) is type([]): + # iterate through the array and convert + for listchild in child: + elem = ElementTree.Element(tag) + parent.append(elem) + _ConvertDictToXmlRecurse(elem, listchild) + else: + elem = ElementTree.Element(tag) + parent.append(elem) + _ConvertDictToXmlRecurse(elem, child) + else: + parent.text = str(dictitem) + + +def ConvertDictToXml(xmldict): + """ + Converts a dictionary to an XML ElementTree Element + """ + roottag = xmldict.keys()[0] + root = ElementTree.Element(roottag) + _ConvertDictToXmlRecurse(root, xmldict[roottag]) + return root + + +def _ConvertXmlToDictRecurse(node, dictclass): + nodedict = dictclass() + + if len(node.items()) > 0: + # if we have attributes, set them + nodedict.update(dict(node.items())) + + for child in node: + # recursively add the element's children + newitem = _ConvertXmlToDictRecurse(child, dictclass) + if nodedict.has_key(child.tag): + # found duplicate tag, force a list + if type(nodedict[child.tag]) is type([]): + # append to existing list + nodedict[child.tag].append(newitem) + else: + # convert to list + nodedict[child.tag] = [nodedict[child.tag], newitem] + else: + # only one, directly set the dictionary + nodedict[child.tag] = newitem + + if node.text is None: + text = '' + else: + text = node.text.strip() + + if len(nodedict) > 0: + # if we have a dictionary add the text as a dictionary value (if there is any) + if len(text) > 0: + nodedict['_text'] = text + else: + # if we don't have child nodes or attributes, just set the text + nodedict = text + + return nodedict + + +def ConvertXmlToDict(root, dictclass=XmlDictObject): + """ + Converts an XML file or ElementTree Element to a dictionary + """ + # If a string is passed in, try to open it as a file + if type(root) == type(''): + root = ElementTree.parse(root).getroot() + elif not isinstance(root, ElementTree.Element): + raise TypeError("Expected ElementTree.Element or file path string") + + return dictclass({root.tag: _ConvertXmlToDictRecurse(root, dictclass)}) -- cgit v1.2.3