Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/chartbeat-labs/parselmouth

An object-oriented interface for abstracting away the ugly parts of ad server APIs
https://github.com/chartbeat-labs/parselmouth

Last synced: about 2 months ago
JSON representation

An object-oriented interface for abstracting away the ugly parts of ad server APIs

Awesome Lists containing this project

README

        

# Parselmouth

## What is Parselmouth?

Parselmouth serves as a facade/abstraction to adserver interfaces.
As of this writing (2015-11-03) Parselmouth only interfaces with Google's
Doubleclick for Publishers (DFP).

External API services like Google's DFP can be painful to interface with given
the irregularity of the data structures they use to communicate and the
frequency with which the API changes. Parselmouth aims to alleviate the pain of
using these services by standardizing a simple, pythonic object-oriented
interface that abstracts away the frustrating implementation details of an
external API.

For background on how this project began, click [here](http://engineering.chartbeat.com/2015/11/11/parselmouth/)

## How do I get started?
#### Installing the library
Install or update the library from PyPI. If you're using pip, this is as easy
as:

`$ pip install [--upgrade] parselmouth`

If you don't want to install directly from PyPI, you can download the library
as a tarball and then install it manually. The download can be found here:
https://pypi.python.org/pypi/parselmouth
Navigate to the directory that contains your downloaded unzipped client
library and run the "setup.py" script to install the "parselmouth"
module.

`$ python setup.py build install`

#### Intializing Connection

Parselmouth can be initialized in one of the following ways:

##### Manually enumerating credentials:
```python
from parselmouth import Parselmouth
from parselmouth import ParselmouthProviders

client = Parselmouth(
ParselmouthProviders.google_dfp_premium, # Or google_dfp_small_business
client_id='ID',
client_secret='SECRET',
refresh_token='REFRESH_TOKEN',
network_code='NETWORK_CODE',
application_name='APPLICATION_NAME',
)
```

##### Loading credentials from a configuration file:

Credentials may optionally be stored in a YAML file like the
[following](sample_config.yaml):
```yaml
google_dfp_premium:
client_id: 'ID'
client_secret: 'SECRET'
refresh_token: 'REFRESH_TOKEN'
network_code: 'NETWORK_CODE'
application_name: 'APPLICATION_NAME'

google_dfp_small_business:
client_id: 'ID'
client_secret: 'SECRET'
refresh_token: 'REFRESH_TOKEN'
network_code: 'NETWORK_CODE'
application_name: 'APPLICATION_NAME'
```

And then loaded like this:
```python
client = Parselmouth(
provider_name=ParselmouthProviders.google_dfp_premium,
config_path='sample_config.yaml',
)
```

##### Loading credentials from a configuration file:

Parselmouth may additionally be initialized using implementation-specific
configuration classes. These classes can be written to retrieve credentials from
external sources such as a SQL database or key-value store.

```python
from parselmouth import ParselmouthConfig # Or your own custom config object

config = ParselmouthConfig(
ParselmouthProviders.google_dfp_premium,
config_path='sample_config.yaml',
)

client = Parselmouth(config)
```

##Basic Usage
####Campaigns

A primary use-case of Parselmouth is to obtain information
about ad campaigns from an ad service provider.

```python
>>> campaign = client.get_campaign('ORDER_ID')
>>> campaign.id
'ORDER_ID'
>>> campaign.name
'ORDER_NAME'
>>> campaign.start
datetime.datetime(2013, 7, 31, 16, 30, tzinfo=)
>>> campaign.stats
Stats({'video_completions': 0, 'impressions': 572, 'click_through_rate': 0, 'clicks': 0, 'video_starts': 0})
```

####Line Items and Creatives

You can also get line item and creative objects
```python
>>> line_items = client.get_campaign_line_items(campaign)
>>> line_item = line_items[0]
>>> line_item.id
'LINE_ITEM_ID'
>>> creatives = client.get_line_item_creatives(line_item)
>>> creatives[0].id
'CREATIVE_ID'
```

Click [here](docs/delivery.md) for more details on working with delivery objects.

####Line Item Targeting

One of the most convenient features of Parselmouth is it's abstraction of
targeting information. Native API implementations of targeting configurations
are terribly complex, unwieldy, and difficult to manipulate. Parselmouth defines
a simple way of applying arbitrary boolean inclusion/exclusion criterion to a
LineItem.

```python
from parselmouth.targeting import Geography

# Initialize some geography targets
usa = Geography(name='USA')
canada = Geography(name='Canada')
uk = Geography(name='UK')
scotland = Geography(name='Scotland')

# Initialize some regional TargetingCriterion
na_region = TargetingCriterion([usa, canada], TargetingCriterion.OPERATOR.OR)
uk_region = TargetingCriterion([uk, scotland], TargetingCriterion.OPERATOR.OR)

# Build new TargetingCriterion using boolean operators
target_either = na_region | uk_region
target_na_only = na_region & ~uk_region
target_neither = ~(na_region | uk_region)
```

Click [here](docs/targeting.md) for more details on targeting.

#### Trees

DFP also supports ad unit hierarchies governed by complex tree
structures. For example a site might have an ad unit (or zone)
called Sports with child ad units called Sports/Hockey and
Sports/Baseball. Both of these ad units share a parent ad unit
called Sports and are sibblings in this tree structure.
Parselmouth also supports functions which help in working with
these tree structures using objects called NodeTrees.

```python
>>> from parselmouth.constants import ParselmouthTargetTypes
>>> adunit_tree = client.construct_tree(ParselmouthTargetTypes.adunit)
>>> sports_tree = adunit_tree.get_subtree('name', 'Sports')
>>> for sub_unit in sports_tree.flatten():
... print sub_unit.name
...
'Sports'
'Sports/Hockey'
'Sports/Baseball'
```

Click [here](docs/trees.md) for more details on trees.

####Object Serialization

All objects within Parselmouth can also be serialized to a dictionary.

```python
>>> line_item_doc = line_item.to_doc()
>>> print line_item_doc
{'status': 'COMPLETED', 'domain': None, 'targeting': {'video_position': None, 'day_part': None, 'custom': None, u'_metadata': {u'cls': 'TargetingData'}, 'inventory': {u'_metadata': {u'cls': 'TargetingCriterion'}, 'OR': [{'adunits': None, 'external_name': None, 'name': None, u'_metadata': {u'cls': 'Placement'}, 'parent_id': None, 'external_id': None, 'id': '1904883'}]}, 'video_content': None, 'user_domain': None, 'technology': None, 'geography': None}, 'name': 'Flight 1', 'cost_per_unit': {'budget_currency_code': 'USD', 'budget_micro_amount': 0.0, u'_metadata': {u'cls': 'Cost'}}, 'type': 'standard', 'campaign_id': '134419323', 'last_modified_by': 'Goog_DFPUI', 'value_cost_per_unit': {'budget_currency_code': 'USD', 'budget_micro_amount': 0.0, u'_metadata': {u'cls': 'Cost'}}, 'delivery': {'stats': {'video_completions': 0, u'_metadata': {u'cls': 'Stats'}, 'click_through_rate': 0, 'video_starts': 0, 'impressions': 572, 'clicks': 0}, 'pace': 5.72e-06, 'expected_delivery_percent': 100.0, u'_metadata': {u'cls': 'DeliveryMeta'}, 'delivery_rate_type': 'FRONTLOADED', 'actual_delivery_percent': 0.000572}, 'start': datetime.datetime(2013, 7, 31, 16, 30, tzinfo=), 'campaign_name': 'Test', 'cost_type': 'CPM', 'creative_placeholder': [{u'expectedCreativeCount': '1', u'creativeSizeType': 'PIXEL', u'size': {u'width': '300', u'isAspectRatio': False, u'height': '600'}}, {u'expectedCreativeCount': '1', u'creativeSizeType': 'PIXEL', u'size': {u'width': '728', u'isAspectRatio': False, u'height': '90'}}, {u'expectedCreativeCount': '1', u'creativeSizeType': 'PIXEL', u'size': {u'width': '300', u'isAspectRatio': False, u'height': '250'}}], u'_metadata': {u'cls': 'LineItem'}, 'last_modified': datetime.datetime(2013, 11, 20, 13, 34, 52, tzinfo=), 'budget': {'budget_currency_code': 'USD', 'budget_micro_amount': 0.0, u'_metadata': {u'cls': 'Cost'}}, 'primary_goal': {'unit_type': 'IMPRESSIONS', 'units': 100000000, 'goal_type': 'LIFETIME', u'_metadata': {u'cls': 'Goal'}}, 'end': datetime.datetime(2013, 9, 1, 23, 59, tzinfo=), 'target_platform': 'WEB', 'id': '74067003'}
```

And these dictionaries can be re-serialized back into their native type.

```python
>>> from parselmouth.delivery import LineItem
>>> reconstructed_line_item = LineItem.from_doc(line_item_doc)
>>> line_item == reconstructed_line_item
True
```

##Authors:
* Justin Mazur: [email protected]
* Paul Kiernan: [email protected]

## License:

```
Copyright 2015 Chartbeat, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```