Package: ganapi
ganapi=======The ganapi library presents a simple and easy to use interface to the Get a Newsletter's REST API.Requirements------------* Python 2.7.6 tested* requests 2.2.1* httmock for testsInstallation------------Via PIP:```bashpip install ganapi```Usage-----Start by creating an instance of the ```Api``` object:```pythonfrom ganapi import Apitoken = '...'gan_api = Api(token)```Here ```token``` variable must contain a valid [API token](http://help.getanewsletter.com/en/support/api-token-2/) string.#### The contact objectThe instances of the Contact class represent the contact entities in the API.They have the following fields:*Required fields** ```email``` - contact's email. It's also a ***_lookup field_*** - required when updating or deleting the contact.*Optional fields** ```attributes``` - list of the contact's [attributes](http://help.getanewsletter.com/en/support/attribute-overview/).* ```first_name```* ```last_name```* ```lists``` - list of the newsletters for which this contact is subscribed to.*Read-only fields** ```url``` - the contact's resource URL.* ```active``` - true if the contact is active and can receive mails, false otherwise.* ```updated``` - the date of the last change.* ```created``` - the date of creation.#### Retreiving a contactYou have to create an instance of the ```ContactManager``` class and then use it's ```get()``` method to retrieve the contact you need.```pythonfrom ganapi import ContactManagercontact_manager = ContactManager(gan_api)contact = contact_manager.get('john.doe@example.com')```The manager methods will throw an ```HTTPError``` in case of HTTP error from the API, so it's a good idea to catch it.```pythontry:contact = contact_manager.get('john.doe@example.com')except HTTPError as e:if e.response.code == 404:print 'Contact not found!'else:print 'API error: ' + e```### Querying for contactsYou have to create an instance of the ```ContactManager``` class and then use it's ```query()``` method to retrieve the contacts you need.```query()``` takes a dict of [url parameters](https://api.getanewsletter.com/v3/docs/contacts/#get-contacts)  and will return a ```PaginatedResultSet``` with the first page of contacts in a list in PaginatedResultSet.entities.```python# Get PaginatedResultSet containing contacts with emails starting with name@.queried_contacts = contact_manager.query({'search_email': 'name@'})# list of contacts in current page(1)queried_contacts.entities# list of contacts in next page(2) if available and update PaginatedResultSetqueried_contacts.next()# list of contacts in previous page(1) if available and update PaginatedResultSetqueried_contacts.prev()```#### Creating a contact```pythoncontact = contact_manager.create()contact.email = 'jane.doe@example.com'contact.first_name = 'Jane'contact.save()```This will create a new contact and save it. Again, it'll be a good idea to catch exceptions when calling the ```save()``` method. The API will respond with an error if the contact already exists.One way to avoid it is to force the creation of the contact, overwriting the existing one:```pythoncontact.overwrite()```Both ```save()``` and ```overwrite()``` will return the same contact object with it's read-only fields updated (e.g. ```created```, ```updated```).```pythoncontact = contact.save()print contact.created```#### Updating an existing contact```python# Get the contact.contact = contact_manager.get('john.doe@example.com')# Change some fields.contact.first_name = 'John'# Save it.contact.save()```You can avoid making two calls to the API by forcing a *partial update*.```pythoncontact = contact_manager.create()contact.set_persisted()contact.email = 'john.doe@example.com'contact.first_name = 'John'contact.save()```Calling ```set_persisted()``` on the contact object marks it like it's already existing and coming from the API. The calls to the ```save()``` method when a contact is maked as existing will do only a *partial update*, i.e. update only the supplied fields and skipping all the ```None``` fields.Do not forget that ```email``` is a ***_lookup field_*** and required when updating or deleting the contact.#### Deleting a contact```pythoncontact.delete()```#### The list objectThe instances of the List class represent the [lists](http://help.getanewsletter.com/en/support/lists-overview/) in the API. They have the following structure:*Required fields** ```email``` - sender's email.* ```name``` - name of the list.* ```sender``` - sender's name.**Optional fields** ```description```*Lookup field** ```hash``` - the list's unique hash.*Read-only fields** ```responders_count```* ```subcribers```* ```created```* ```url```* ```subscribers_count```* ```active_subscribers_count```* ```responders```#### Retreiving, creating, updating and deleting a listThe CRUD operations on lists are no different from the operations on contacts:```pythonlist_manager = ListManager(gan_api)# Retrieve a list.list = list_manager.get('hash')# Update the list.list.name = 'my list'list = list.save()print list.updated# Create new list.new_list = list_manager.create()new_list.email = 'john.doe@example.com' # required fieldsnew_list.name = 'my new list'new_list.sender = 'John Doe'new_list.save()# Partial update.list = list_manager.create()list.hash = 'hash' # lookup fieldlist.name = 'updated list'list.save()# Delete the list.list.delete()```#### Subscribing a contact to a list```pythoncontact.subscribe_to(list)contact.save()```You can also create a new contact automatically subscribed.```pythoncontact = contact_manager.create()contact.email = 'john.doe@example.com'contact.subscribe_to(list)contact.save()```#### Unsubscribing a contact from a list```pythoncontact.unsubscribe_from(list)contact.save()```#### Deleting a contacts subscription from a list```pythoncontact.delete_subscription_from(list)contact.save()```#### The attribute objectThe instances of the Attribute class represent the attribute entities in the API.They have the following fields:*Required fields** ```name``` - attribute name.*Lookup field** ```code``` - the slugified attribute code. required when updating or deleting the attribute. *The name "A new attribute" code will be "a-new-attribute"**Read-only fields** ```url``` - the attribute resource URL.* ```usage_count``` - number of usages.#### Retreiving an attributeYou have to create an instance of the ```AttributeManager``` class and then use it's ```get()``` method to retrieve the attribute you need.```pythonfrom ganapi import AttributeManagerattribute_manager = AttributeManager(gan_api)attribute = attribute_manager.get('code')```The manager methods will raise a ```HTTPError``` in case of HTTP error from the API, so it's a good idea to catch it.```pythontry:attribute = attribute_manager.get('code')except HttpError as e:if e.response.code == 404:print 'Attribute not found!'else:print 'API error: ' + e```#### Creating an attribute```pythonattribute = attribute_manager.create()attribute.name = 'City'attribute.save()```This will create a new attribute and save it. Again, it'll be a good idea to catch exceptions when calling the ```save()``` method. The API will respond with an error if the attribute already exists.One way to avoid it is to force the creation of the attribute, overwriting the existing one:```pythonattribute.overwrite()```Both ```save()``` and ```overwrite()``` will return the same attribute object with it's read-only fields updated (e.g. ```url```, ```usage_count```).```pythonattribute = attribute.save()print attribute.usage_count```#### Updating an existing attribute```python# Get the attribute.attribute = attribute_manager.get('code')# Change name field.attribute.name = 'Changed!'# Save it.attribute.save()```You can avoid making two calls to the API by forcing a *partial update*.```pythonattribute = attribute_manager.create()attribute.set_persisted()attribute.code = 'code'attribute.name = 'Changed!'attribute.save()```Calling ```set_persisted()``` on the attribute object marks it like it's already existing and coming from the API. The calls to the ```save()``` method when a attribute is made as existing will do only a *partial update*, i.e. update only the supplied fields and skipping all the ```None``` fields.Do not forget that ```code``` is a ***_lookup field_*** and required when updating or deleting the attribute.#### Deleting an attribute```pythonattribute.delete()```### Get all entities of typeYou can get all contacts, lists or attributes in a generator using the ```all()``` method on their manager.*Parameters** ```start``` - start index (default: 0)* ```stop``` stop index (default: float('inf'))```python# Print all contacts emailsall_contacts = contact_manager.all()for contact in all_contacts:print contact.email# Get a generator starting on contact 100 stopping on contact 200all_contacts = contact_manager.all(start=100, stop=200)```#### The PaginatedResultSet classThe instance of the PaginatedResultSet class represent the result of get from the API.*Parameters** ```data``` - json of result from the ```query()``` method in the managers.* ```manager``` - the manager using ```query()```.*Properties** ```next_link``` - URL to get the next page of the results.* ```previous_link``` - URL to get the previous page of the results.* ```manager``` - ```EntityManager``` currently used.* ```entities``` - List of ```Entity``` for the current result page.* ```count``` - Total amount of entity results in all pages.*Methods** ```prev()``` - replaces the instance with results from the previous page and returns entities.* ```next()``` - replaces the instance with results from the next page and returns entities.```next()``` and ```prev()``` will raise ```StopIteration``` if the end of the direction has been reached.They will raise ```HTTPError``` in case of HTTP error from the API,