Warning

The XML-RPC API is no longer under active development. While it is still supported, no further improvements or changes will be made to it.

We strongly recommend that you use the REST API instead.

Importing User Data

ActionKit provides a high-level method for importing user data. This is useful for loading data about things that have happened already, where strict validation and sending email confirmations would be inappropriate.

Setup

Step one for using the start_upload() function is to create an ImportPage. You can do that by uploading a file through ActionKit's Import Users application (under the Users tab) or using the API. Here's an example of doing it through the API:

import xmlrpclib
import pprint

actionkit = xmlrpclib.Server('https://%s:%s@%s/api/' % (user,password,host))

page = actionkit.ImportPage.create(
        {'name': 'test_api_import_page'})

if 'id' in page:
    print "Success, your page details:"
    print pprint.pprint(page)
else:
    print "Failed to create an import page!"

Hint

See Setup for the hostname, user and password.

Hint

Note that you have to pick a unique name for your page - if you pick one that already exists you'll get an exception when you call create().

And here's the output:

Success, your page details:
{'english_version': None,
 'goal': None,
 'goal_type': 'actions',
 'hosted_with': 1,
 'id': 12,
 'lang': None,
 'list': 1,
 'name': 'test_api_import_page',
 'page_ptr': 12,
 'required_fields': [],
 'status': 'active',
 'tags': [],
 'title': '',
 'type': 'Import',
 'url': ''}

start_upload()

Here's the documentation for the start_upload() method. **kwargs is the Python syntax for variable length list of keyword arguments. When you call start_upload() using XML-RPC clients, send the arguments as a struct.

Examples

Let's import some data! Here's an example of importing a single donation:

import xmlrpclib

actionkit = xmlrpclib.Server('https://%s:%s@%s/api/' % (user,password,host))

# the data to import - keys and data formats are the same as
# header names in the Import Users system
row = dict(email           = "test-upload-row@example.com",
           first_name      = "Testy",
           last_name       = "Testerson",
           address1        = '1 Test Lane',
           city            = 'Testville',
           state           = 'NY',
           zip             = '10001',
           donation_amount = '10.00',
           created_at      = '12/7/09 8:38')

# lookup the page we created earlier - could use create() here instead
page = actionkit.ImportPage.get({'name': 'test_api_import_page'})

# run the import, requesting immediate results by setting immediate to True
result = actionkit.start_upload({'row': row,
                                 'page_id': page['id'],
                                 'immediate': True})

if result['status'] == 'complete':
   print "Import succeeded: %s" % result['message']
else:
   print "Import failed."

If you have more than a few entries to import doing them one at a time like this will be very slow. To load more than one entry at once, format your data as a TSV or CSV. Here's an example of loading data in TSV format:

import xmlrpclib

actionkit = xmlrpclib.Server('https://%s:%s@%s/api/' % (user,password,host))

# the data to import - keys and data formats are the same as
# header names in the Import Users system
data = "email\tfirst_name\tlast_name\tzip\n" + \
       "test1@example.com\tTestee\tTesterson\t10001\n" + \
       "test2@example.com\tTestee\tTesterson\t10001\n" + \
       "test3@example.com\tTestee\tTesterson\t10001\n" + \
       "test4@example.com\tTestee\tTesterson\t10001\n" + \
       "test5@example.com\tTestee\tTesterson\t10001\n"

# lookup the page we created earlier - could use create() here instead
page = actionkit.ImportPage.get({'name': 'test_api_import_page'})

# run the import, requesting immediate results by setting immediate to True
result = actionkit.start_upload({'data': data,
                                 'data_format': 'tsv',
                                 'page_id': page['id'],
                                 'immediate': True})

if result['status'] == 'completed':
   print "Import succeeded: %d rows loaded." % result['ok_rows']
else:
   print "Import failed.  Errors found: %r" % result['errors']

Note that this still isn't quite as efficient as uploading a file via either the REST API interface or via the ActionKit admin interface. If you have thousands of records to load you won't be able to load them quickly through XMLRPC.

Products And Bundling

You can also import product orders and bundling donations using the API. For example, here's code that sets up three new products and imports orders for them:

import xmlrpclib

actionkit = xmlrpclib.Server('https://%s:%s@%s/api/' % (user,password,host))

# the data to import - keys and data formats are the same as
# header names in the Import Users system
row = dict(email           = "test-product-row@example.com",
           zip             = '10001',
           donation_amount = '34.50',
           created_at      = '12/7/09 8:38')

# create a new Product - this could be a lookup with get() if the
# product already exists
red   = actionkit.Product.create({ 'name':  'Red T-Shirt',
                                   'price': '10.00' })
white = actionkit.Product.create({ 'name':  'White T-Shirt',
                                   'price': '10.00' })
blue  = actionkit.Product.create({ 'name':  'Blue T-Shirt',
                                   'price': '10.00' })

# add orders for the product using the product ID in the key for -
# note that we're using the compound form to include an extra
# $1.50 shipping and handling per T-shirt.
row["donation_product_%d" % red['id']]   = "1|11.50"
row["donation_product_%d" % blue['id']]  = "2|23.00"
row["donation_product_%d" % white['id']] = "0"

# lookup the page we created earlier - could use create() here instead
page = actionkit.ImportPage.get({'name': 'test_api_import_page'})

# run the import, requesting immediate results by setting immediate to True
result = actionkit.start_upload({'row': row,
                                 'page_id': page['id'],
                                 'immediate': True}

if result['status'] == 'completed':
   print "Import succeeded: %d rows loaded." % result['ok_count']
else:
   print "Import failed."

Bundling imports are very similar. The only difference is that you reference Candidate objects instead of Products and the values are always amounts:

import xmlrpclib

actionkit = xmlrpclib.Server('https://%s:%s@%s/api/' % (user,password,host))

# the data to import - keys and data formats are the same as
# header names in the Import Users system
row = dict(email           = "test-candidate-row@example.com",
           zip             = '10001',
           donation_amount = '10.00',
           created_at      = '12/7/09 8:38')

# create new Candidates - this could be a lookup with get() if they
# already exist
george = actionkit.Candidate.create({ 'name':  'George Washington' })
abe    = actionkit.Candidate.create({ 'name':  'Abraham Lincoln' })

# add orders for the candidates using the ID in the key
row["donation_candidate_%d" % george['id']] = "5.00"
row["donation_candidate_%d" % abe['id']]    = "2.50"

# lookup the page we created earlier - could use create() here instead
page = actionkit.ImportPage.get({'name': 'test_api_import_page'})

# run the import, requesting immediate results by setting immediate to True
result = actionkit.start_upload({'row': row,
                                 'page_id': page['id'],
                                 'immediate': True}

if result['status'] == 'completed':
   print "Import succeeded: %d row loaded." % result['ok_count']
else:
   print "Import failed."

start_import()

This method is deprecated and support will be removed in a future release. You should use start_upload() instead. Existing code should be migrated to start_upload() as soon as possible.