diff --git a/CHANGES.txt b/CHANGES.txt
deleted file mode 100644
index e776699..0000000
--- a/CHANGES.txt
+++ /dev/null
@@ -1,63 +0,0 @@
-Changelog
-*********
-
-0.7(????-??-??)
----------------
-
-- no changes yet
-
-0.6 (2010-02-25)
-----------------
-
-- api methods update_recipient, opt_in_recipient,
- add_recipient now set a optional node in created xml requests
- **true**
-- extended api_methods update_recipient, opt_in_recipient,
- add_recipient with optional parameter
- optionals to control optional nodes.
- [hplocher]
-
-0.5 (2009-05-18)
-----------------
-
-- Changed log level to DEBUG [hplocher]
-
-0.4 (2009-05-13)
-----------------
-
-- implemented api method:
- update_recipient(api_url, list_id, old_email, columns=[])
- opt_in_recipient(api_url, list_id, email, columns=[])
- [hplocher]
-
-0.3 (2009-05-12)
-----------------
-
-- implemented api methods:
- is_opted_in(api_url, list_id, email)
- select_recipient_data(api_url, list_id, email, column=None)
- [hplocher]
-
-
-0.2 (2009-05-12)
-----------------
-
-- added doctest [hplocher]
-- refactored [hplocher]
-- implemented xml_request(api_url, xml) [hplocher]
-
-0.1 (2009-05-12)
-----------------
-
-- Initial release
-- implemented api methods:
- add_recipient(api_url, list_id, email, columns=[])
- opt_out_recipient(api_url, list_id, email)
- [hplocher]
-- mocked api methods:
- is_opted_in(api_url, list_id, email)
- select_recipient_data(api_url, list_id, email, columns=[])
- xml_request(api_url, xml)
- [hplocher]
-
-- initial package skeleton [hplocher]
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
deleted file mode 100644
index 4f70aa4..0000000
--- a/CONTRIBUTORS.txt
+++ /dev/null
@@ -1 +0,0 @@
-Hans-Peter Locher, Author
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index cf22b3a..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,2 +0,0 @@
-recursive-include silverpop *
-global-exclude *pyc
diff --git a/ProjectHome.md b/ProjectHome.md
new file mode 100644
index 0000000..6a3cdab
--- /dev/null
+++ b/ProjectHome.md
@@ -0,0 +1,3 @@
+Python implementation of the Silverpop API
+
+- Silverpop: http://www.silverpop.com/
\ No newline at end of file
diff --git a/README.txt b/README.txt
deleted file mode 100644
index 96b9155..0000000
--- a/README.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-silverpop
-=========
-
-Python implementation of the Silverpop API
-
-Currently implemented API methods::
-
- def add_recipient(api_url, list_id, email, columns=[], optionals=ADD_RECIPIENT_OPTIONALS):
- """Add recipient to a list (only email key supported)
- api_url, list_id, email are required, optionally
- takes a list of dicts to define additional columns like
- [{'column_name':'State', 'column_value':'Germany'},]
- optionally takes a list of dicts to define optionals,
- defaults to
- [{'optional_name':'UPDATE_IF_FOUND', 'optional_value':'true'},
- {'optional_name':'SEND_AUTOREPLY', 'optional_value':'true'},
- ]
- returns True or False
- """
-
- def update_recipient(api_url, list_id, old_email, columns=[], optionals=UPDATE_RECIPIENT_OPTIONALS):
- """Update recipient of a list,
- if the old_email is not a recipient of the list, a recipient will be added.
- api_url, list_id, old_email are required, optionally
- takes a list of dicts to define additional columns like:
- [{'column_name':'State', 'column_value':'Germany'},]
- Can change the email of a recipient by specifiying a column like:
- {'column_name':'EMAIL', 'column_value':'new@email.com'}
- Can re-opt-in an opted-out recipient by specifying a column like:
- {'column_name':'OPT_OUT', 'column_value':'False'}
- optionally takes a list of dicts to define optionals,
- defaults to
- [{'optional_name':'SEND_AUTOREPLY', 'optional_value':'true'},]
- returns True or False
- """
-
- def opt_in_recipient(api_url, list_id, email, columns=[], optionals=OPT_IN_RECIPIENT_OPTIONALS):
- """opt in a recipient to a list (only email key supported)
- api_url, list_id, email are required, optionally
- takes a list of dicts to define additional columns like
- [{'column_name':'State', 'column_value':'Germany'},]
- returns True or False
- optionally takes a list of dicts to define optionals,
- defaults to
- [{'optional_name':'SEND_AUTOREPLY', 'optional_value':'true'},]
- returns True or False
- """
-
- def is_opted_in(api_url, list_id, email):
- """Is the specified email opted in to the list?
- api_url, list_id, email are required
- returns True or False
- """
-
- def opt_out_recipient(api_url, list_id, email):
- """opt out a recipient from a list
- api_url, list_id, email are required
- returns True or False
- """
-
- def select_recipient_data(api_url, list_id, email, column=None):
- """get the recipients data
- api_url, list_id, email are required
- you may specify a column dict for non email key lists, like
- {'column_name': 'USER_ID', 'column_value': '4711'}
- returns the silverpop response (xml)
- """
-
- def xml_request(api_url, xml):
- """submit a custom xml request
- api_url, xml, are required
- returns the silverpop response (xml)
- """
-
-- Silverpop: http://www.silverpop.com/
diff --git a/docs/LICENSE.GPL b/docs/LICENSE.GPL
deleted file mode 100644
index ba9543b..0000000
--- a/docs/LICENSE.GPL
+++ /dev/null
@@ -1,222 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
diff --git a/docs/LICENSE.txt b/docs/LICENSE.txt
deleted file mode 100644
index c928745..0000000
--- a/docs/LICENSE.txt
+++ /dev/null
@@ -1,16 +0,0 @@
- silverpop is copyright Hans-Peter Locher
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA.
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 01e9edb..0000000
--- a/setup.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from setuptools import setup, find_packages
-import os
-
-
-def read(*rnames):
- return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
-
-version = '0.7dev'
-
-
-long_description = (
- read('README.txt')
- + '\n' +
- read('CHANGES.txt')
- + '\n' +
- read('silverpop', 'README.txt')
- + '\n' +
- 'Contributors\n'
- '************\n'
- + '\n' +
- read('CONTRIBUTORS.txt')
- + '\n' +
- 'Download\n'
- '********\n'
- )
-
-setup(name='silverpop',
- version=version,
- description="API for Silverpop Enterprise Newslettering",
- long_description=long_description,
- # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
- classifiers=[
- 'Intended Audience :: Developers',
- 'Topic :: Software Development :: Libraries :: Python Modules',
- 'License :: OSI Approved :: GNU General Public License (GPL)',
- ],
- keywords='Silverpop API',
- author='Hans-Peter Locher',
- author_email='hans-peter.locher@inquant.de',
- url='http://silverpop.googlecode.com/svn/',
- license='GPL',
- packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
- include_package_data=True,
- zip_safe=False,
- test_suite = 'silverpop.tests.test_docs.test_suite',
- install_requires=[
- # -*- Extra requirements: -*-
- ],
- entry_points="""
- # -*- Entry points: -*-
- """,
- )
diff --git a/silverpop/CHANGELOG b/silverpop/CHANGELOG
deleted file mode 100644
index aec29e0..0000000
--- a/silverpop/CHANGELOG
+++ /dev/null
@@ -1,48 +0,0 @@
-2009-05-18 Hans-Peter Locher
-
- * api.py: change log level to debug
-
-2009-05-13 Hans-Peter Locher
-
- * README.txt: fix header levels
- * api.py (update_recipient):
- implemented
-
-2009-05-12 Hans-Peter Locher
-
- * api.py (xml_request):
- implemented, returns silverpop api response (xml)
- * api.py (submit_to_silverpop):
- refactored , now needs argument api_url,
- uses xml_request now
- * api.py (add_recipient, opt_out_recipient):
- refactored uses submit_to silverpop, which now takes the api_url,
- xml)
- * README.txt: add doctest for api methods
- * api.py (select_recipient_data): implemented method,
- needs seperate test in doctest
- * api.py (is_opted_in): implemented method,
- tested in doctest, need more tests
-
-2009-05-11 Hans-Peter Locher
-
- * api.py (add_recipient):
- implement method (taken from collective.pfg.silverpop 0.5)
- * api.py (opt_out_recipient):
- implement method (taken from collective.pfg.silverpop 0.5)
- * api.py (submit_to_silverpop):
- helper method for sending the request,
- interprets the silverpop response and returns True/False
-
-2009-05-08 Hans-Peter Locher
-
- * api.py (select_recipient_data):
- created, currently returns always True
- * api.py (is_opted_in):
- created, currently returns always True
- * api.py (opt_out_recipient):
- created, currently returns always True
- * api.py (select_recipient_data):
- created, currently returns 'foo'
- * api.py (xml_request):
- created, currently returns 'foo'
diff --git a/silverpop/README.txt b/silverpop/README.txt
deleted file mode 100644
index 774d7a8..0000000
--- a/silverpop/README.txt
+++ /dev/null
@@ -1,821 +0,0 @@
-Detailed documentation
-**********************
-
-Test Setup
-----------
-
-Most API methods will only return True or False,
-to get a more verbose output and prohibit
-making requests to Silverpop
-We monkeypatch urllib to print
-(url, headers, data) instead of doing any requests.
-
-We create a Fake class to be returned by urlib2.urlopen,
-which will always return a successful silverpop response::
-
- >>> class Fake(object):
- ... def read(self): return "true"
-
-In our test method, we print request's url, headers, data (we decode
-the urlencoded data for the test) and
-return a Fake object::
-
- >>> import cgi
- >>> def test_urlopen(req):
- ... print '-'*30 + 'request details' + '-'*30
- ... print req.get_full_url()
- ... print req.headers
- ... xml = dict(cgi.parse_qsl(req.data))['xml']
- ... print xml
- ... print '-'*75
- ... return Fake()
- >>> import urllib2
-
-Finally we patch urllib2.urlopen::
-
- >>> urllib2.urlopen = test_urlopen
-
-We also define a FakeRequest class to define our request
-containing just a form::
-
- >>> class FakeRequest(dict):
- ... def __init__(self, **kwargs):
- ... self.form = kwargs
-
-API Methods
------------
-
-All api methods can be accessed by importing the module::
-
- >>> import silverpop
-
-
-First, we define data needed for the various api_methods.
-
-The URL of the Silverpop Server::
-
- >>> api_url = 'http://api1.silverpop.com/XMLAPI '
-
-A List Id::
-
- >>> list_id = 999
-
-An Email Address (we only support email key lists, so this is
-our key identifying a newsletter subscriber)::
-
- >>> email = 'my@email.com'
-
-add_recipient
-+++++++++++++
-
-
-Let's call the add_recipient api with the required attributes::
-
- >>> silverpop.add_recipient(api_url, list_id, email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- true
- true
-
- EMAIL
- my@email.com
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-If we provide a list of columns, these will be used in the request, leading to columns in silverpop.
-
-For example, we want to use a custom column **gender**::
-
- >>> columns = [{'column_name': 'gender', 'column_value': 'male'}, ]
-
- >>> silverpop.add_recipient(api_url, list_id, email, columns)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- true
- true
-
- EMAIL
- my@email.com
-
-
- gender
- male
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-Note that we have some optional nodes (UPDATE_IF_FOUND, SEND_AUTOREPLY) in the created requests.
-These are default. To change this we can use an additional parameter optionals.
-
-Let's call the add_recipient api with the additional optionals attribute to omit the **SEND_AUTOREPLY**::
-
- >>> custom_optionals = [{'optional_name':'UPDATE_IF_FOUND', 'optional_value':'true'},]
- >>> silverpop.add_recipient(api_url, list_id, email, optionals=custom_optionals)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- true
-
- EMAIL
- my@email.com
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-The **SEND_AUTOREPLY** node is gone.
-
-Of course you can use this together with columns::
-
- >>> columns = [{'column_name': 'gender', 'column_value': 'male'}, ]
-
- >>> silverpop.add_recipient(api_url, list_id, email, columns, optionals=custom_optionals)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- true
-
- EMAIL
- my@email.com
-
-
- gender
- male
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-The **SEND_AUTOREPLY** node is gone.
-
-update_recipient
-++++++++++++++++
-
-**update_recipient** works similar as **add_recipient**,
-but offers to change the recipient's email address, and re-opt-in
-an opted out recipient in addition.
-for our first test, we are not going to do that::
-
- >>> old_email = 'my@email.com'
-
-Let's call the update_recipient api with the required attributes::
-
- >>> silverpop.update_recipient(api_url, list_id, old_email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- my@email.com
- true
-
-
-
- ---------------------------------------------------------------------------
- True
-
-If we provide a list of columns, these will be used in the request, leading to columns in silverpop.
-This is also used to change the recipient's email address.
-
-For example, we want to change the **email address**, therefore we need to specify one column **EMAIL**::
-
- >>> columns = [{'column_name': 'EMAIL', 'column_value': 'new@email.com'}, ]
-
- >>> silverpop.update_recipient(api_url, list_id, old_email, columns)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- my@email.com
- true
-
- EMAIL
- new@email.com
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-Another common use case is to to re-opt-in an opted-out recipient.
-
-Therefore we provide a column **OPT_OUT**, which when set to **False**
-will re-opt-in the recipient::
-
-
- >>> columns = [{'column_name': 'OPT_OUT', 'column_value': 'False'}, ]
-
- >>> silverpop.update_recipient(api_url, list_id, old_email, columns)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- my@email.com
- true
-
- OPT_OUT
- False
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-Note that we have an optional node ( SEND_AUTOREPLY ) in the created requests.
-This is default. To change this we can use an additional parameter optionals.
-
-
- >>> old_email = 'my@email.com'
-
-Let's call the update_recipient api with the additional optionals attribute to omit the **SEND_AUTOREPLY**::
-
- >>> silverpop.update_recipient(api_url, list_id, old_email, optionals=[])
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- my@email.com
-
-
-
- ---------------------------------------------------------------------------
- True
-
-The **SEND_AUTOREPLY** node is gone.
-
-Of course you can use this together with columns::
-
- >>> columns = [{'column_name': 'EMAIL', 'column_value': 'new@email.com'}, ]
-
- >>> silverpop.update_recipient(api_url, list_id, old_email, columns, optionals=[])
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- my@email.com
-
- EMAIL
- new@email.com
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-The **SEND_AUTOREPLY** node is gone.
-
-opt_in_recipient
-++++++++++++++++
-
-This method is a wrapper around update_recipient_data,
-for explicitly opting in a recipient.
-
-Let's call the opt_in_recipient api with the required attributes::
-
- >>> silverpop.opt_in_recipient(api_url, list_id, email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- my@email.com
- true
-
- OPT_OUT
- False
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-If we provide a list of columns, these will be used in the request,
-leading to columns in silverpop.
-This can be also used to change the recipient's email address.
-For example, we want to change the **email address**.
-Therefore we need to specify one column **EMAIL**::
-
- >>> columns = [{'column_name': 'EMAIL', 'column_value': 'new@email.com'}, ]
-
- >>> silverpop.opt_in_recipient(api_url, list_id, email, columns)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- my@email.com
- true
-
- OPT_OUT
- False
-
-
- EMAIL
- new@email.com
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-If the user specifies an **OPT_OUT** column, this will be ignored,
-as we always want to opt in.
-
-Let's define the to be ignored column::
-
- >>> columns = [{'column_name': 'OPT_OUT', 'column_value': 'some value'}, ]
-
-Notice how the value of **OPT_OUT** is still **False**::
-
- >>> silverpop.opt_in_recipient(api_url, list_id, email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- my@email.com
- true
-
- OPT_OUT
- False
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-Note that we have an optional node ( SEND_AUTOREPLY ) in the created requests.
-This is default. To change this we can use an additional parameter optionals.
-
-
- >>> old_email = 'my@email.com'
-
-Let's call the opt_in_recipient api with the additional optionals attribute to omit the **SEND_AUTOREPLY**::
-
- >>> silverpop.opt_in_recipient(api_url, list_id, email, optionals=[])
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- my@email.com
-
- OPT_OUT
- False
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-The **SEND_AUTOREPLY** node is gone.
-
-Of course you can use this together with columns::
-
- >>> columns = [{'column_name': 'EMAIL', 'column_value': 'new@email.com'}, ]
-
- >>> silverpop.opt_in_recipient(api_url, list_id, email, columns, optionals=[])
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- 2
- my@email.com
-
- OPT_OUT
- False
-
-
- EMAIL
- new@email.com
-
-
-
-
- ---------------------------------------------------------------------------
- True
-
-The **SEND_AUTOREPLY** node is gone.
-
-opt_out_recipient
-+++++++++++++++++
-
-Let's call the opt_out_recipient api with the required attributes::
-
- >>> silverpop.opt_out_recipient(api_url, list_id, email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- my@email.com
-
-
-
- ---------------------------------------------------------------------------
- True
-
-is_opted_in
-+++++++++++
-
-This method returns **True** when the specified email is a
-non-opted out recipient of a list.
-If the list doesn't exist, or
-the user isn't a recipient of an existing list, or
-the user is opted out form the specified list,
-it should return **False**.
-
-For this test we need to interpret the various responses,
-which we could get from silverpop, so we are going to change
-in more detail, so we change our Fake class simulating, the different Silverpop
-responses.
-
-First, we assume that we have specified a non existing list_id
-(is_opted_in should return False)::
-
- >>> class Fake(object):
- ... def read(self): return """
- ...
- ...
- ...
- ... false
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- ... 108
- ...
- ... SP.ListManager
- ...
- ...
- ...
- ...
- ...
- ...
- ... """
-
- >>> silverpop.is_opted_in(api_url, list_id, email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- my@email.com
-
-
-
- ---------------------------------------------------------------------------
- False
-
-Now, we specify a list_id which is not valid (it must be an integer)::
-
- >>> class Fake(object):
- ... def read(self): return """
- ...
- ...
- ...
- ... false
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- ... 106
- ...
- ... SP.ListManager
- ...
- ...
- ...
- ...
- ...
- ...
- ... """
-
- >>> silverpop.is_opted_in(api_url, 'NOT AN INTEGER', email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- NOT AN INTEGER
- my@email.com
-
-
-
- ---------------------------------------------------------------------------
- False
-
-Now, we assume the provided email is not a member of the list::
-
- >>> class Fake(object):
- ... def read(self): return """
- ...
- ...
- ...
- ... false
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- ... 128
- ...
- ... SP.ListManager
- ...
- ...
- ...
- ...
- ...
- ...
- ... """
-
- >>> silverpop.is_opted_in(api_url, list_id, email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- my@email.com
-
-
-
- ---------------------------------------------------------------------------
- False
-
-Now, we assume the provided email is a member of the list, but the member is opted out
-(leading to a value in the **** tag)::
-
- >>> class Fake(object):
- ... def read(self): return """
- ...
- ...
- ...
- ... TRUE
- ... my@email.com
- ... my@email.com
- ... 5
- ... 0
- ... 5/11/09 9:43 AM
- ... 2
- ... 3/26/09 10:29 AM
- ... 5/11/09 9:43 AM
- ...
- ...
- ... State
- ... Germany
- ...
- ...
- ...
- ...
- ...
- ... """
-
- >>> silverpop.is_opted_in(api_url, list_id, email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- my@email.com
-
-
-
- ---------------------------------------------------------------------------
- False
-
-
-Finally, we assume the provided email is a member of the list, and the member isn't opted out
-(leading to an empty **** tag)::
-
- >>> class Fake(object):
- ... def read(self): return """
- ...
- ...
- ...
- ... TRUE
- ... my@email.com
- ... my@email.com
- ... 5
- ... 0
- ... 5/11/09 9:43 AM
- ... 2
- ... 3/26/09 10:29 AM
- ...
- ...
- ...
- ... State
- ... Germany
- ...
- ...
- ...
- ...
- ...
- ... """
-
- >>> silverpop.is_opted_in(api_url, list_id, email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- my@email.com
-
-
-
- ---------------------------------------------------------------------------
- True
-
-select_recipient_data
-+++++++++++++++++++++
-
-For this test, we want to have a simple xml as reply from Silverpop,
-as we don't process the response further::
-
- >>> class Fake(object):
- ... def read(self): return "true"
-
-
- >>> silverpop.select_recipient_data(api_url, list_id, email)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- my@email.com
-
-
-
- ---------------------------------------------------------------------------
- 'true'
-
-If we provide a column, this will be used in the request
-(use for non email key lists only).
-
-
-For example, we have a custom key **USER_ID**::
-
- >>> column = {'column_name': 'USER_ID', 'column_value': '4711'}
-
- >>> silverpop.select_recipient_data(api_url, list_id, email, column)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- 999
- my@email.com
-
- USER_ID
- 4711
-
-
-
-
- ---------------------------------------------------------------------------
- 'true'
-
-xml_request
-+++++++++++
-
-The Silverpop XML API offers a quite large variaty of commands,
-of which we only implement a subset.
-If you need to make different requests you can use this method to
-submit custom xml to Silverpop. As result, you will get the Silverpop
-Response, which is also xml.
-
-Imagine, we want to use the **ForwardToFrient** xml command.
-
-Let's define the custom xml::
-
- >>> xml = """
- ...
- ...
- ... bob@bob.com
- ... 5
- ... 10
- ... jane@jane.com
- ... Forwarded: Check this out, I just got that
- ...
- ...
- ... """
-
-The xml is sent to Silverpop and we get the response back, for further processing::
-
- >>> silverpop.xml_request(api_url, xml)
- ------------------------------request details------------------------------
- http://api1.silverpop.com/XMLAPI
- {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
-
-
-
- bob@bob.com
- 5
- 10
- jane@jane.com
- Forwarded: Check this out, I just got that
-
-
-
- ---------------------------------------------------------------------------
- 'true'
diff --git a/silverpop/__init__.py b/silverpop/__init__.py
deleted file mode 100644
index cb26b8c..0000000
--- a/silverpop/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-
-from api import add_recipient
-from api import update_recipient
-from api import opt_in_recipient
-from api import opt_out_recipient
-from api import is_opted_in
-from api import select_recipient_data
-from api import xml_request
diff --git a/silverpop/api.py b/silverpop/api.py
deleted file mode 100644
index c68a08e..0000000
--- a/silverpop/api.py
+++ /dev/null
@@ -1,254 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# File: api.py
-#
-# Copyright (c) InQuant GmbH
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-__author__ = """Hans-Peter Locher """
-__docformat__ = 'plaintext'
-
-import urllib
-import urllib2
-
-import logging
-
-LOGGER="silverpop"
-ADD_RECIPIENT_OPTIONALS = [{'optional_name':'UPDATE_IF_FOUND', 'optional_value':'true'},
- {'optional_name':'SEND_AUTOREPLY', 'optional_value':'true'},]
-
-UPDATE_RECIPIENT_OPTIONALS = [{'optional_name':'SEND_AUTOREPLY', 'optional_value':'true'},]
-
-OPT_IN_RECIPIENT_OPTIONALS = [{'optional_name':'SEND_AUTOREPLY', 'optional_value':'true'},]
-
-
-def log(msg):
- logging.getLogger(LOGGER).debug(msg)
-
-
-def simple_submit(api_url, xml):
- """submit a request to silverpop
- api_url, xml are required
- intended to be used, when the response of
- silverpop doesn't need to be interpreted further
- returns True, if the API request was successful,
- otherwise False
- """
- response = xml_request(api_url, xml)
- result = response.lower()
- if 'true' in result:
- return True
- elif 'success' in result:
- return True
- elif 'false' in result:
- return False
- else:
- return False
-
-
-def add_recipient(api_url, list_id, email, columns=[], optionals=ADD_RECIPIENT_OPTIONALS):
- """Add recipient to a list (only email key supported)
- api_url, list_id, email are required, optionally
- takes a list of dicts to define additional columns like
- [{'column_name':'State', 'column_value':'Germany'},]
- optionally takes a list of dicts to define optionals,
- defaults to
- [{'optional_name':'UPDATE_IF_FOUND', 'optional_value':'true'},
- {'optional_name':'SEND_AUTOREPLY', 'optional_value':'true'},
- ]
- returns True or False
- """
- parts = []
- parts.append("""
-
-
- %s
- 2""" % list_id
- )
-
- for optional in optionals:
- parts.append("""
- <%s>%s%s>""" % (optional['optional_name'], optional['optional_value'], optional['optional_name'])
- )
-
- parts.append("""
-
- EMAIL
- %s
- """ % email
- )
-
- for column in columns:
- parts.append("""
-
- %s
- %s
- """ % (column['column_name'], column['column_value'])
- )
-
- parts.append("""
-
-
-"""
- )
- xml = "".join(parts)
- return simple_submit(api_url, xml)
-
-
-def update_recipient(api_url, list_id, old_email, columns=[], optionals=UPDATE_RECIPIENT_OPTIONALS):
- """Update recipient of a list,
- if the old_email is not a recipient of the list, a recipient will be added.
- api_url, list_id, old_email are required, optionally
- takes a list of dicts to define additional columns like:
- [{'column_name':'State', 'column_value':'Germany'},]
- Can change the email of a recipient by specifiying a column like:
- {'column_name':'EMAIL', 'column_value':'new@email.com'}
- Can re-opt-in an opted-out recipient by specifying a column like:
- {'column_name':'OPT_OUT', 'column_value':'False'}
- optionally takes a list of dicts to define optionals,
- defaults to
- [{'optional_name':'SEND_AUTOREPLY', 'optional_value':'true'},]
- returns True or False
- """
- parts = []
- parts.append("""
-
-
- %s
- 2
- %s""" % (list_id, old_email)
- )
-
- for optional in optionals:
- parts.append("""
- <%s>%s%s>""" % (optional['optional_name'], optional['optional_value'], optional['optional_name'])
- )
-
- for column in columns:
- parts.append("""
-
- %s
- %s
- """ % (column['column_name'], column['column_value'])
- )
-
- parts.append("""
-
-
-"""
- )
- xml = "".join(parts)
- return simple_submit(api_url, xml)
-
-
-
-def is_opted_in(api_url, list_id, email):
- """Is the specified email opted in to the list?
- api_url, list_id, email are required
- returns True or False
- """
- response = select_recipient_data(api_url, list_id, email)
- result = response.lower()
- if 'false' in result:
- return False
- elif '' in result:
- return True
- else:
- return False
-
-def opt_in_recipient(api_url, list_id, email, columns=[], optionals=OPT_IN_RECIPIENT_OPTIONALS):
- """opt in a recipient to a list (only email key supported)
- api_url, list_id, email are required, optionally
- takes a list of dicts to define additional columns like
- [{'column_name':'State', 'column_value':'Germany'},]
- returns True or False
- optionally takes a list of dicts to define optionals,
- defaults to
- [{'optional_name':'SEND_AUTOREPLY', 'optional_value':'true'},]
- returns True or False
- """
- columns = filter(lambda column:column['column_name'] != 'OPT_OUT', columns)
- columns.insert(0, {'column_name': 'OPT_OUT', 'column_value': 'False'})
- return update_recipient(api_url, list_id, email, columns, optionals)
-
-
-def opt_out_recipient(api_url, list_id, email):
- """opt out a recipient from a list
- api_url, list_id, email are required
- returns True or False
- """
- xml = """
-
-
- %s
- %s
-
-
-""" % (list_id, email)
- return simple_submit(api_url, xml)
-
-
-def select_recipient_data(api_url, list_id, email, column=None):
- """get the recipients data
- api_url, list_id, email are required
- you may specify a column dict for non email key lists, like
- {'column_name': 'USER_ID', 'column_value': '4711'}
- returns the silverpop response (xml)
- """
- parts = []
- parts.append("""
-
-
- %s
- %s""" % (list_id, email)
- )
-
- if column:
- parts.append("""
-
- %s
- %s
- """ % (column['column_name'], column['column_value'])
- )
-
- parts.append("""
-
-
-"""
- )
- xml = "".join(parts)
- return xml_request(api_url, xml)
-
-
-def xml_request(api_url, xml):
- """submit a custom xml request
- api_url, xml, are required
- returns the silverpop response (xml)
- """
- log('xml: %s' % xml)
-
- headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}
- xml = urllib.urlencode({'xml': xml})
- request = urllib2.Request(api_url, xml, headers)
-
- handle = urllib2.urlopen(request)
-
- response = handle.read()
- log('Silverpop API response: %s' % response)
- return response
-
-
-# vim: set ft=python ts=4 sw=4 expandtab :
diff --git a/silverpop/tests/__init__.py b/silverpop/tests/__init__.py
deleted file mode 100644
index 5bb534f..0000000
--- a/silverpop/tests/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# package
diff --git a/silverpop/tests/test_docs.py b/silverpop/tests/test_docs.py
deleted file mode 100644
index c0cdd9c..0000000
--- a/silverpop/tests/test_docs.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Doctest runner for 'silverpop'
-"""
-__docformat__ = 'restructuredtext'
-
-import unittest
-import doctest
-
-optionflags = (doctest.ELLIPSIS |
- doctest.NORMALIZE_WHITESPACE |
- doctest.REPORT_ONLY_FIRST_FAILURE)
-
-def setUp(test):
- pass
-
-def test_suite():
- suite = unittest.TestSuite((
- doctest.DocFileSuite(
- '../README.txt',
- setUp=setUp,
- optionflags=optionflags,
- ),
- ))
- return suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')