diff --git a/.gitignore b/.gitignore
index 883a83d..fa89355 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
build
dist
prestapyt.egg-info
+*.pyc
+tags
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..adb6786
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,5 @@
+CREDITS
+-------
+Camptocamp SA
+Akretion
+Mark Rainess (mrainess)
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..bb3ec5f
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include README.md
diff --git a/README b/README
deleted file mode 100644
index d75539d..0000000
--- a/README
+++ /dev/null
@@ -1,77 +0,0 @@
-prestapyt is a library for Python to interact with the PrestaShop's Web Service API.
-
-Learn more about the PrestaShop Web Service from the [Official Documentation](http://doc.prestashop.com/display/PS14/Using+the+REST+webservice).
-
-prestapyt is a direct port of the PrestaShop PHP API Client, PSWebServiceLibrary.php
-Similar to PSWebServiceLibrary.php, prestapyt is a thin wrapper around the PrestaShop Web Service: it takes care of making the call to your PrestaShop instance's Web Service, supports the Web Service's HTTP-based CRUD operations (handling any errors) and then returns the XML ready for you to work with in Python (as well as prestasac if you work with scala)
-
-#Installation
-TODO
-
-#Usage
-
- from prestapyt import PrestaShopWebServiceError, PrestaShopWebService
-
- prestashop = PrestaShopWebService('http://localhost:8080/api', 'BVWPFFYBT97WKM959D7AVVD0M4815Y1L') # messages will be as xml
- # or
- prestashop = PrestaShopWebServiceDict('http://localhost:8080/api', 'BVWPFFYBT97WKM959D7AVVD0M4815Y1L') # messages will be as dict
-
- # get all addresses
- prestashop.get('addresses') # returns ElementTree (PrestaShopWebService) or dict (PrestaShopWebServiceDict)
-
- # get all addresses
- prestashop.get('addresses') # returns ElementTree
-
- # get address 1
- prestashop.get('addresses', resource_id=1)
- prestashop.get('addresses/1')
-
- # full url
- prestashop.get('http://localhost:8080/api/addresses/1')
-
- #filters
- prestashop.get('addresses', options={'limit': 10})
-
- # head
- print prestashop.head('addresses')
-
- # delete a resource
- prestashop.delete('addresses', resource_ids=4)
-
- # delete many resources
- prestashop.delete('addresses', resource_ids=[5,6])
-
- # add
- prestashop.add('addresses', xml)
-
- # edit
- prestashop.edit('addresses', 5, xml)
-
- # get a blank xml
- prestashop.get('addresses', options={'schema': 'blank'})
-
-#API Documentation
-Documentation for the PrestaShop Web Service can be found on the PrestaShop wiki:
-[Using the REST webservice](http://doc.prestashop.com/display/PS14/Using+the+REST+webservice)
-
-#Credits:
-Thanks to Prestashop SA for their PHP API Client PSWebServiceLibrary.php
-
-Thanks to Alex Dean for his port of PSWebServiceLibrary.php to the Scala language, prestasac (https://github.com/orderly/prestashop-scala-client) from which I also inspired my library.
-
-#Copyright and License
-
-prestapyt is copyright (c) 2012 Guewen Baconnier
-
-prestapyt is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of
-the License, or (at your option) any later version.
-
-prestapyt is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public
-License along with prestapyt. If not, see [GNU licenses](http://www.gnu.org/licenses/).
diff --git a/README.md b/README.md
index 1ee2f26..5c29871 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,38 @@
+Prestapyt
+=========
+
prestapyt is a library for Python to interact with the PrestaShop's Web Service API.
-Learn more about the PrestaShop Web Service from the [Official Documentation](http://doc.prestashop.com/display/PS14/Using+the+REST+webservice).
+Learn more about the PrestaShop Web Service from the [Official Prestashop Documentation].
prestapyt is a direct port of the PrestaShop PHP API Client, PSWebServiceLibrary.php
-Similar to PSWebServiceLibrary.php, prestapyt is a thin wrapper around the PrestaShop Web Service: it takes care of making the call to your PrestaShop instance's Web Service, supports the Web Service's HTTP-based CRUD operations (handling any errors) and then returns the XML ready for you to work with in Python (as well as prestasac if you work with scala)
-#Installation
+Similar to PSWebServiceLibrary.php, prestapyt is a thin wrapper around the PrestaShop Web Service:
+it takes care of making the call to your PrestaShop instance's Web Service,
+supports the Web Service's HTTP-based CRUD operations (handling any errors)
+and then returns the XML ready for you to work with in Python
+(as well as prestasac if you work with scala).
+
+
+Installation
+============
The easiest way to install prestapyt (needs setuptools):
easy_install prestapyt
-If you do not have setuptools, download prestapyt as a .tar.gz or .zip from here (https://github.com/guewen/prestapyt/downloads), untar it and run:
+Or, better, using pip:
+
+ pip install prestapyt
+
+If you do not have setuptools, download prestapyt as a .tar.gz or .zip from
+[Prestapyt Source Archives], untar it and run:
python setup.py install
-#Usage
+
+Usage
+=====
from prestapyt import PrestaShopWebServiceError, PrestaShopWebService
@@ -59,16 +76,25 @@ If you do not have setuptools, download prestapyt as a .tar.gz or .zip from here
# get a blank xml
prestashop.get('addresses', options={'schema': 'blank'})
-#API Documentation
-Documentation for the PrestaShop Web Service can be found on the PrestaShop wiki:
-[Using the REST webservice](http://doc.prestashop.com/display/PS14/Using+the+REST+webservice)
-#Credits:
+API Documentation
+=================
+
+Documentation for the PrestaShop Web Service can be found on the
+PrestaShop wiki: [Using the REST webservice]
+
+
+Credits:
+========
+
Thanks to Prestashop SA for their PHP API Client PSWebServiceLibrary.php
-Thanks to Alex Dean for his port of PSWebServiceLibrary.php to the Scala language, prestasac (https://github.com/orderly/prestashop-scala-client) from which I also inspired my library.
+Thanks to Alex Dean for his port of PSWebServiceLibrary.php
+to the Scala language, [prestasac] from which I also inspired my library.
-#Copyright and License
+
+Copyright and License
+=====================
prestapyt is copyright (c) 2012 Guewen Baconnier
@@ -84,3 +110,11 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public
License along with prestapyt. If not, see [GNU licenses](http://www.gnu.org/licenses/).
+
+
+
+[Official Prestashop Documentation]: http://doc.prestashop.com/display/PS14/Using+the+REST+webservice
+[Using the REST webservice]: http://doc.prestashop.com/display/PS14/Using+the+REST+webservice
+[Prestapyt Source Archives]: https://github.com/guewen/prestapyt/downloads
+[prestasac]: https://github.com/orderly/prestashop-scala-client
+
diff --git a/prestapyt/prestapyt.py b/prestapyt/prestapyt.py
index e4b10cb..a481b12 100644
--- a/prestapyt/prestapyt.py
+++ b/prestapyt/prestapyt.py
@@ -18,18 +18,23 @@
import urllib
import warnings
-import httplib2
+import requests
import xml2dict
import dict2xml
import unicode_encode
+import base64
+from cStringIO import StringIO
from xml.parsers.expat import ExpatError
+from xml.dom.minidom import parseString
from distutils.version import LooseVersion
try:
from xml.etree import cElementTree as ElementTree
except ImportError, e:
from xml.etree import ElementTree
+requests.defaults.defaults['base_headers']['User-Agent'] = 'Prestapyt: Python Prestashop Library'
+
class PrestaShopWebServiceError(Exception):
"""Generic PrestaShop WebServices error class
@@ -38,12 +43,14 @@ class PrestaShopWebServiceError(Exception):
from prestapyt import PrestaShopWebServiceError
"""
- def __init__(self, msg, error_code=None):
- self.error_code = error_code
+ def __init__(self, msg, error_code=None, ps_error_msg='', ps_error_code=None):
self.msg = msg
+ self.error_code = error_code
+ self.ps_error_msg = ps_error_msg
+ self.ps_error_code = ps_error_code
def __str__(self):
- return repr(self.msg)
+ return repr(self.ps_error_msg)
class PrestaShopAuthenticationError(PrestaShopWebServiceError):
@@ -56,7 +63,7 @@ class PrestaShopWebService(object):
"""
MIN_COMPATIBLE_VERSION = '1.4.0.17'
- MAX_COMPATIBLE_VERSION = '1.5.0.5'
+ MAX_COMPATIBLE_VERSION = '1.5.4.0'
def __init__(self, api_url, api_key, debug=False, headers=None, client_args=None):
"""
@@ -81,7 +88,6 @@ def __init__(self, api_url, api_key, debug=False, headers=None, client_args=None
# required to hit prestashop
self._api_url = api_url
- self._api_key = api_key
# add a trailing slash to the url if there is not one
if not self._api_url.endswith('/'):
@@ -93,14 +99,29 @@ def __init__(self, api_url, api_key, debug=False, headers=None, client_args=None
# optional arguments
self.debug = debug
- self.client_args = client_args
+ client_args.update({'auth' : (api_key, '')})
# use header you coders you want, otherwise, use a default
- self.headers = headers
- if self.headers is None:
- self.headers = {'User-agent': 'Prestapyt: Python Prestashop Library'}
+ self.headers = {} if headers is None else headers
+
+ # init http client in the init for re-use the same connection for all call
+ self.client = requests.session(**client_args)
- def _check_status_code(self, status_code):
+ def _parse_error(self, xml_content):
+ """
+ Take the XML content as string and extracts the PrestaShop error
+ @param xml_content: xml content returned by the PS server as string
+ @return (prestashop_error_code, prestashop_error_message)
+ """
+ error_answer = self._parse(xml_content)
+ ps_error_code = ''
+ ps_error_msg = ''
+ if isinstance(error_answer, dict):
+ error_content = error_answer.get('prestashop', {}).get('errors', {}).get('error', {})
+ return (error_content.get('code'), error_content.get('message'))
+
+
+ def _check_status_code(self, status_code, content):
"""
Take the status code and throw an exception if the server didn't return 200 or 201 code
@param status_code: status code returned by the server
@@ -113,20 +134,20 @@ def _check_status_code(self, status_code):
405: 'Method Not Allowed',
500: 'Internal Server Error',}
- error_label = ('This call to PrestaShop Web Services failed and '
- 'returned an HTTP status of %d. That means: %s.')
if status_code in (200, 201):
return True
elif status_code == 401:
- raise PrestaShopAuthenticationError(error_label
- % (status_code, message_by_code[status_code]), status_code)
+ # the content is empty for auth errors
+ raise PrestaShopAuthenticationError(message_by_code[status_code],
+ status_code)
elif status_code in message_by_code:
- raise PrestaShopWebServiceError(error_label
- % (status_code, message_by_code[status_code]), status_code)
+ ps_error_code, ps_error_msg = self._parse_error(content)
+ raise PrestaShopWebServiceError(message_by_code[status_code],
+ status_code, ps_error_msg, ps_error_code)
else:
- raise PrestaShopWebServiceError(("This call to PrestaShop Web Services returned "
- "an unexpected HTTP status of: %d")
- % (status_code,), status_code)
+ ps_error_code, ps_error_msg = self._parse_error(content)
+ raise PrestaShopWebServiceError('Unknown error', status_code,
+ ps_error_msg, ps_error_code)
def _check_version(self, version):
"""
@@ -137,49 +158,59 @@ def _check_version(self, version):
"""
if version:
if not (LooseVersion(self.MIN_COMPATIBLE_VERSION) <
- LooseVersion(version) <
+ LooseVersion(version) <=
LooseVersion(self.MAX_COMPATIBLE_VERSION)):
warnings.warn(("This library may not be compatible with this version of PrestaShop (%s). "
"Please upgrade/downgrade this library") % (version,))
return True
- def _execute(self, url, method, body=None, add_headers=None):
+ def _execute(self, url, method, data=None, files=None, add_headers=None):
"""
Execute a request on the PrestaShop Webservice
@param url: full url to call
@param method: GET, POST, PUT, DELETE, HEAD
- @param body: for PUT (edit) and POST (add) only, the xml sent to PrestaShop
+ @param data: for PUT (edit) and POST (add) only, the xml sent to PrestaShop
+ @param files: should contain {'image': (img_filename, img_file)}
@param add_headers: additional headers merged on the instance's headers
@return: tuple with (status code, header, content) of the response
"""
if add_headers is None: add_headers = {}
- client = httplib2.Http(**self.client_args)
- # Prestashop use the key as username without password
- client.add_credentials(self._api_key, False)
- client.follow_all_redirects = True
-
- if self.debug:
- print "Execute url: %s / method: %s" % (url, method)
+ # Don't print when method = POST, because it contains an encoded URL
+ # The print for POST is in the method add_with_url()
+ if self.debug and data and method <> 'POST':
+ try:
+ xml = parseString(data)
+ pretty_body = xml.toprettyxml(indent=" ")
+ except:
+ pretty_body = data
+ print "Execute url: %s / method: %s\nbody: %s" % (url, method, pretty_body)
request_headers = self.headers.copy()
request_headers.update(add_headers)
- header, content = client.request(url, method, body=body, headers=request_headers)
- status_code = int(header['status'])
- self._check_status_code(status_code)
- self._check_version(header.get('psws-version'))
+ if not files:
+ r = self.client.request(method, url, data=data, headers=request_headers)
+ else:
+ r = self.client.request(method, url, files=files)
if self.debug: # TODO better debug logs
- print ("Response code: %s\nResponse headers:\n%s\nResponse body:\n%s"
- % (status_code, header, content))
+ print ("Response code: %s\nResponse headers:\n%s\n"
+ % (r.status_code, r.headers))
+ if r.headers.get('content-type') and r.headers.get('content-type').startswith('image'):
+ print "Response body: Image in binary format\n"
+ else:
+ print "Response body:\n%s\n" % r.content
+
+ self._check_status_code(r.status_code, r.content)
+ self._check_version(r.headers.get('psws-version'))
- return status_code, header, content
+ return r
def _parse(self, content):
"""
- Parse the response of the webservice, assumed to be a XML in utf-8
+ Parse the response of the webservice
@param content: response from the webservice
@return: an ElementTree of the content
@@ -188,7 +219,12 @@ def _parse(self, content):
raise PrestaShopWebServiceError('HTTP response is empty')
try:
- parsed_content = ElementTree.fromstring(content)
+ # We have to encode it in utf-8, because content has the XML header
+ # cf http://lxml.de/FAQ.html#why-can-t-lxml-parse-my-xml-from-unicode-strings
+ # WARNING : old versions of 'requests', for instance version 0.8.2
+ # packaged in Ubuntu 12.04, return a unicode... but more recent of
+ # requests, for instance 0.13.5 return a str in utf-8 !
+ parsed_content = ElementTree.fromstring(unicode_encode.unicode2encoding(content))
except ExpatError, err:
raise PrestaShopWebServiceError('HTTP XML response is not parsable : %s' % (err,))
@@ -198,12 +234,22 @@ def _validate(self, options):
"""
Check options against supported options
(reference : http://doc.prestashop.com/display/PS14/Cheat+Sheet_+Concepts+Outlined+in+this+Tutorial)
+
+ This syntax also works for options dict :
+ (reference : http://www.prestashop.com/forums/topic/101502-webservice-api-filter-for-date-ranges/#post_id_708102)
+ {'filter[date_upd]': '>[2012-07-30]',
+ 'date': '1'}
+ will returns :
+ '/?filter[date_upd]=>[2012-07-30]&date=1'
+ you may also define {'filter[date_upd]': '>[2012-07-30 16:00:00]', 'date': '1'}
+ Note : you must consider that '>[2012-07-30]' is interpreted like 'equal or greater than' by web service
+
@param options: dict of options to use for the request
@return: True if valid, else raise an error PrestaShopWebServiceError
"""
if not isinstance(options, dict):
raise PrestaShopWebServiceError('Parameters must be a instance of dict')
- supported = ('filter', 'display', 'sort', 'limit', 'schema')
+ supported = ('filter', 'display', 'sort', 'limit', 'schema', 'date', 'date_filter', 'id_shop')
# filter[firstname] (as e.g.) is allowed, so check only the part before a [
unsupported = set([param.split('[')[0] for param in options]).difference(supported)
if unsupported:
@@ -225,30 +271,66 @@ def _options_to_querystring(self, options):
"""
if self.debug:
options.update({'debug': True})
+ if options.get('date_filter'):
+ options['date'] = 1
+ for field, operator, date in options.pop('date_filter'):
+ options['filter[%s]'%field] = '%s[%s]'%(operator, date.strftime('%Y-%m-%d %H:%M:%S'))
return urllib.urlencode(options)
- def add(self, resource, content):
+ def add(self, resource, content, img_filename=None):
"""
Add (POST) a resource. The content can be a dict of values to create.
@param resource: type of resource to create
@param content: Full XML as string or dict of new resource values.
- If a dict is given, it will be converted to XML with the necessary root tag ie:
+ If a dict is given, it will be converted to XML with the necessary
+ root tag ie:
[[dict converted to xml]]
- @return: an ElementTree of the response from the web service
+ If we add an image, it should contain the binary of the image as string.
+ @param img_filename: Filename of the image with its extension as string,
+ for example 'myproduct.jpg'
+ @return: an ElementTree of the response from the web service if it's an XML
+ or True if the response from the web service is a binary
"""
- return self.add_with_url(self._api_url + resource, content)
+ if img_filename:
+ # Check that we have a valid filename with an extension
+ if isinstance(img_filename, (str, unicode)) and 1<=len(img_filename)<= 255 and "/" not in img_filename and "\000" not in img_filename and '.' in img_filename:
+ if self.debug:
+ print "Filename '%s' considered valid" % img_filename
+ else:
+ raise PrestaShopWebServiceError('Invalid image filename: %s'
+ % img_filename)
+
+ return self.add_with_url(self._api_url + resource, content, img_filename=img_filename)
- def add_with_url(self, url, xml):
+ def add_with_url(self, url, content, img_filename=None):
"""
Add (POST) a resource
@param url: A full URL which for the resource type to create
- @param xml: Full XML as string of new resource.
+ @param content: a string containing the full XML of new resource or an image encoded in base64.
+ @param img_filename: a string containing the filename of the image.
@return: an ElementTree of the response from the web service
"""
- headers = {'Content-Type': 'application/x-www-form-urlencoded'}
- return self._parse(self._execute(url, 'POST', body=urllib.urlencode({'xml': xml}), add_headers=headers)[2])
+ if not img_filename:
+ headers = {'Content-Type': 'application/x-www-form-urlencoded'}
+ if self.debug and content:
+ try:
+ xml = parseString(content)
+ pretty_body = xml.toprettyxml(indent=" ")
+ except:
+ pretty_body = content
+ print "Execute url: %s / method: POST\nbody: %s" % (url, pretty_body)
+
+ r = self._execute(url, 'POST', data=urllib.urlencode({'xml': content.encode('utf-8')}), add_headers=headers)
+ else:
+ img_binary = base64.decodestring(content)
+ img_file = StringIO(img_binary)
+ r = self._execute(url, 'POST', files={'image': (img_filename, img_file)})
+ if r.headers.get('content-type') and r.headers.get('content-type').startswith('image'):
+ return True
+ else:
+ return self._parse(r.content)
def search(self, resource, options=None):
"""
@@ -259,7 +341,8 @@ def search(self, resource, options=None):
it is more clear than "get without id" to search resources
@param resource: string of the resource to search like 'addresses', 'products'
- @param options: Optional dict of parameters to filter the search (one or more of 'filter', 'display', 'sort', 'limit', 'schema')
+ @param options: Optional dict of parameters to filter the search (one or more of
+ 'filter', 'display', 'sort', 'limit', 'schema')
@return: ElementTree of the xml message
"""
return self.get(resource, options=options)
@@ -289,7 +372,11 @@ def get_with_url(self, url):
@param url: An URL which explicitly sets the resource type and ID to retrieve
@return: an ElementTree of the resource
"""
- return self._parse(self._execute(url, 'GET')[2])
+ r = self._execute(url, 'GET')
+ if r.headers.get('content-type') and r.headers.get('content-type').startswith('image'):
+ return r.content
+ else:
+ return self._parse(r.content)
def head(self, resource, resource_id=None, options=None):
"""
@@ -315,9 +402,9 @@ def head_with_url(self, url):
@param url: An URL which explicitly sets the resource type and ID to retrieve
@return: the header of the response as a dict
"""
- return self._execute(url, 'HEAD')[1]
+ return self._execute(url, 'HEAD').headers
- def edit(self, resource, resource_id, content):
+ def edit(self, resource, content):
"""
Edit (PUT) a resource.
@@ -326,7 +413,7 @@ def edit(self, resource, resource_id, content):
@param content: modified XML as string of the resource.
@return: an ElementTree of the Webservice's response
"""
- full_url = "%s%s/%s" % (self._api_url, resource, resource_id)
+ full_url = "%s%s" % (self._api_url, resource)
return self.edit_with_url(full_url, content)
def edit_with_url(self, url, content):
@@ -338,7 +425,8 @@ def edit_with_url(self, url, content):
@return: an ElementTree of the Webservice's response
"""
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
- return self._parse(self._execute(url, 'PUT', body=unicode_encode.encode(content), add_headers=headers)[2])
+ r = self._execute(unicode_encode.encode(url), 'PUT', data=unicode_encode.encode(content), add_headers=headers)
+ return self._parse(r.content)
def delete(self, resource, resource_ids):
"""
@@ -415,6 +503,29 @@ def dive(response, level=1):
ids = [int(elems['attrs']['id'])]
return ids
+ def get(self, resource, resource_id=None, options=None):
+ """
+ Retrieve (GET) a resource
+
+ @param resource: type of resource to retrieve
+ @param resource_id: optional resource id to retrieve
+ @param options: Optional dict of parameters (one or more of
+ 'filter', 'display', 'sort', 'limit', 'schema')
+ @return: a dict of the response
+ """
+ response = super(PrestaShopWebServiceDict, self).get(resource, resource_id=resource_id, options=options)
+ if resource == 'images/products' and resource_id:
+ images = []
+ for image in response['image']['declination']:
+ image_id = image['attrs']['id']
+ image_url = '%s%s/%s/%s'%(self._api_url, resource, resource_id, image_id)
+ images.append({
+ 'id': image_id,
+ 'image': self._execute(image_url, 'get').content
+ })
+ return images
+ return response
+
def get_with_url(self, url):
"""
Retrieve (GET) a resource from a full URL
@@ -423,19 +534,69 @@ def get_with_url(self, url):
@return: a dict of the response. Remove root keys ['prestashop'] from the message
"""
response = super(PrestaShopWebServiceDict, self).get_with_url(url)
- return response['prestashop']
+ if isinstance(response, dict):
+ return response['prestashop']
+ else:
+ return response
+
+ def partial_add(self, resource, fields):
+ """
+ Add (POST) a resource without necessary all the content.
+ Retrieve the full empty envelope
+ and merge the given fields in this envelope.
+
+ @param resource: type of resource to create
+ @param fields: dict of fields of the resource to create
+ @return: response of the server
+ """
+ blank_envelope = self.get(resource, options={'schema': 'blank'})
+ complete_content = dict(blank_envelope, **fields)
+ return self.add(resource, complete_content)
- def add_with_url(self, url, content):
+ def partial_edit(self, resource, resource_id, fields):
+ """
+ Edit (PUT) partially a resource.
+ Standard REST PUT means a full replacement of the resource.
+ Allows to edit only only some fields of the resource with
+ a perf penalty. It will read on prestashop,
+ then modify the keys in content,
+ and write on prestashop.
+
+ @param resource: type of resource to edit
+ @param resource_id: id of the resource to edit
+ @param fields: dict containing the field name as key
+ and the values of the files to modify
+ @return: an ElementTree of the Webservice's response
+ """
+ complete_content = self.get(resource, resource_id)
+ for key in complete_content:
+ if fields.get(key):
+ complete_content[key].update(fields[key])
+ return self.edit(resource, complete_content)
+
+ def add_with_url(self, url, content, img_filename=None):
"""
Add (POST) a resource
@param url: A full URL which for the resource type to create
- @param content: dict of new resource values. it will be converted to XML with the necessary root tag ie:
- [[dict converted to xml]]
- @return: a dict of the response from the web service
- """
- xml_content = dict2xml.dict2xml({'prestashop': content})
- return super(PrestaShopWebServiceDict, self).add_with_url(url, xml_content)
+ @param content: a string containing the full XML of new resource
+ or an image encoded in base64.
+ @param img_filename: a string containing the filename of the image.
+ @return: a dict of the response from the web service or True if the
+ response is a binary.
+ """
+ if isinstance(content, dict):
+ xml_content = dict2xml.dict2xml({'prestashop': content})
+ else:
+ xml_content = content
+ res = super(PrestaShopWebServiceDict, self).add_with_url(url, xml_content, img_filename=img_filename)
+ if isinstance(res, dict) and res.get('prestashop'):
+ res_l2 = res['prestashop'].keys()
+ if 'content' in res['prestashop'].keys():
+ res_l2.remove('content')
+ return res['prestashop'][res_l2[0]]['id']
+ else:
+ return True
def edit_with_url(self, url, content):
"""
@@ -446,7 +607,7 @@ def edit_with_url(self, url, content):
@return: an ElementTree of the Webservice's response
"""
xml_content = dict2xml.dict2xml({'prestashop': content})
- return super(PrestaShopWebServiceDict, self).edit_with_url(url, xml_content)
+ return super(PrestaShopWebServiceDict, self).edit_with_url(url, xml_content)
def _parse(self, content):
"""
diff --git a/setup.py b/setup.py
index a9bb2d1..ac0763c 100644
--- a/setup.py
+++ b/setup.py
@@ -1,40 +1,46 @@
#!/usr/bin/env python
+"""
+ Prestapyt
+
+ :copyright: (c) 2011-2012 Guewen Baconnier
+ :copyright: (c) 2011 Camptocamp SA
+ :license: AGPLv3, see LICENSE for more details
+
+"""
+
import os
from setuptools import setup
-__author__ = 'Guewen Baconnier '
-__version__ = '0.4.0'
-
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
setup(
- # Basic package information.
- name = 'prestapyt',
- version = __version__,
+ # Basic package information.
+ name = 'prestapyt',
+ version = prestapyt.__version__,
- # Packaging options.
- include_package_data = True,
+ # Packaging options.
+ include_package_data = True,
- # Package dependencies.
- install_requires = ['httplib2',],
+ # Package dependencies.
+ install_requires = ['httplib2',],
- # Metadata for PyPI.
- author = 'Guewen Baconnier',
- author_email = 'guewen.baconnier@gmail.com',
- license = 'GNU AGPL-3',
- url = 'http://github.com/guewen/prestapyt',
+ # Metadata for PyPI.
+ author = 'Guewen Baconnier',
+ author_email = 'guewen.baconnier@gmail.com',
+ license = 'GNU AGPL-3',
+ url = 'http://github.com/guewen/prestapyt',
packages=['prestapyt'],
- keywords = 'prestashop api client rest',
- description = 'A library to access Prestashop Web Service from Python.',
- long_description = read('README'),
- classifiers = [
- 'Development Status :: 4 - Beta',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: GNU Affero General Public License v3',
- 'Topic :: Software Development :: Libraries :: Python Modules',
- 'Topic :: Internet :: WWW/HTTP :: Site Management',
- 'Topic :: Internet'
- ]
+ keywords = 'prestashop api client rest',
+ description = 'A library to access Prestashop Web Service from Python.',
+ long_description = read('README.md'),
+ classifiers = [
+ 'Development Status :: 4 - Beta',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: GNU Affero General Public License v3',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ 'Topic :: Internet :: WWW/HTTP :: Site Management',
+ 'Topic :: Internet'
+ ]
)