-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 90acb02
Showing
14 changed files
with
522 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
name: "Release on GitHub" | ||
|
||
on: | ||
push: | ||
branches: | ||
- 'main' | ||
workflow_dispatch: | ||
|
||
jobs: | ||
pre-release: | ||
name: "Build and release on GitHub" | ||
runs-on: "ubuntu-latest" | ||
steps: | ||
- uses: actions/checkout@master | ||
- name: Set up Python 3.10 | ||
uses: actions/setup-python@v3 | ||
with: | ||
python-version: "3.10" | ||
- name: Install package. | ||
run: | | ||
python setup.py sdist --formats=gztar,zip | ||
- uses: "marvinpinto/action-automatic-releases@latest" | ||
with: | ||
repo_token: "${{ secrets.GITHUB_TOKEN }}" | ||
automatic_release_tag: "latest" | ||
prerelease: true | ||
title: "Development Build" | ||
files: | | ||
dist/*.tar.gz | ||
dist/*.zip |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
name: Publish Package 📦 to PyPI | ||
|
||
on: | ||
push: | ||
branches: | ||
- 'main' | ||
workflow_dispatch: | ||
|
||
jobs: | ||
build-n-publish: | ||
name: Build and publish package 📦 to PyPI. | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@master | ||
- name: Set up Python 3.10 | ||
uses: actions/setup-python@v3 | ||
with: | ||
python-version: "3.10" | ||
- name: Install pypa/build | ||
run: >- | ||
python -m | ||
pip install | ||
build | ||
--user | ||
- name: Build a binary wheel and a source tarball | ||
run: >- | ||
python -m | ||
build | ||
--sdist | ||
--wheel | ||
--outdir dist/ | ||
. | ||
- name: Publish package 📦 to PyPI | ||
uses: pypa/gh-action-pypi-publish@release/v1 | ||
with: | ||
password: ${{ secrets.PYPI_API_TOKEN }} | ||
skip_existing: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
name: Test Package | ||
|
||
on: [workflow_dispatch, pull_request] | ||
|
||
jobs: | ||
build: | ||
runs-on: ${{ matrix.os }} | ||
strategy: | ||
matrix: | ||
os: [macos-latest, windows-latest, ubuntu-latest] | ||
python-version: ["2.7", "3.7", "3.8", "3.9", "3.10"] | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Set up Python ${{ matrix.python-version }} | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
- name: Install dependencies for python2 | ||
if: ${{ matrix.python-version < 3.0 }} | ||
run: | | ||
pip install flake8==2.6.2 | ||
pip install setuptools==2.0 | ||
pip install requests==2.25.1 | ||
- name: Install dependencies for python3 | ||
if: ${{ matrix.python-version >= 3.0 }} | ||
run: pip install flake8 | ||
- name: Install PyWindsorAI | ||
run: | | ||
python setup.py install | ||
echo "$(pip --version)" | ||
echo "$(python --version)" | ||
- name: Lint with flake8 | ||
run: | | ||
# stop the build if there are Python syntax errors or undefined names | ||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics | ||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide | ||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics | ||
- name: Unit Tests | ||
run: python -m unittest discover test | ||
env: | ||
WINDSOR_TOKEN: ${{ secrets.WINDSOR_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
ENV | ||
__pycache__ | ||
.pyc | ||
build | ||
dist | ||
.eggs | ||
*.egg-info | ||
*.local | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
include VERSION |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
# SnapchatAdsWindsorApi | ||
|
||
`SnapchatAdsWindsorApi` is a python package makes it easy to get marketing data from Snapchat into python. | ||
|
||
[Windsor.ai](https://windsor.ai/) allows to get marketing data from any platform. It simplifies the complexity of dealing with multiple platforms, unlocking unified, valuable information in a format that matters to you. For more details checkout [onboard.windsor.ai](https://onboard.windsor.ai/). | ||
|
||
## Features | ||
|
||
✅ Easy access to marketing data via windsor.ai APIs | ||
|
||
✅ Lightweight (single dependency - [requests](https://pypi.org/project/requests/)) | ||
|
||
✅ Supports both python 2.7+ and 3 | ||
|
||
## Other Supported marketing and platforms | ||
|
||
✅ Google Analytics | ||
|
||
✅ Google Ads | ||
|
||
✅ Facebook Ads | ||
|
||
✅ Facebook organic | ||
|
||
✅ Bing Ads | ||
|
||
✅ Linkedin Ads | ||
|
||
✅ Hubspot | ||
|
||
✅ Salesforce | ||
|
||
✅ Google search console | ||
|
||
✅ Criteo | ||
|
||
✅ Snapchat | ||
|
||
✅ Tiktok | ||
|
||
✅ Appnexus | ||
|
||
✅ Campaign Manager | ||
|
||
|
||
✅ Awin | ||
|
||
✅ Adroll | ||
|
||
✅ Shopify | ||
|
||
✅ Klaviyo | ||
|
||
✅ Airtable | ||
|
||
✅ Intercom | ||
|
||
✅ Zoho | ||
|
||
✅ Idealo | ||
|
||
|
||
✅ Appsflyer | ||
|
||
✅ Adobe | ||
|
||
|
||
|
||
|
||
|
||
## Usage | ||
|
||
### Installation | ||
|
||
```sh | ||
pip install SnapchatAdsWindsorApi | ||
``` | ||
|
||
### Registration | ||
|
||
You need to get a free API key to access windsor.ai's APIs. Register your account first and add a datasource like facebook ads and then get the API key. For more details check out our official [API documentation](https://windsor.ai/api-documentation/) and [this article](https://windsor.ai/api-fields/). Get the API key at https://onboard.windsor.ai | ||
|
||
### Minimal Example | ||
|
||
```python | ||
from SnapchatAdsWindsorApi.client import Client | ||
from SnapchatAdsWindsorApi.enums import LAST_7D | ||
from SnapchatAdsWindsorApi.enums import FIELD_SOURCE, FIELD_CAMPAIGN, FIELD_CLICKS | ||
|
||
api_key = 'xxx' # Get it from your windsor.ai account. It's recommended to store and get this securely, for example an env variable. | ||
|
||
# Setup a client object with the API key | ||
client = Client(api_key) | ||
|
||
# Call the /connectors API. | ||
campaign_clicks = client.connectors(date_preset=LAST_7D, fields=[FIELD_SOURCE, FIELD_CAMPAIGN, FIELD_CLICKS]) | ||
|
||
# can also be run like: | ||
campaign_clicks = client.connectors(date_preset='last_7d', fields=['date','clicks','spend']) | ||
|
||
# Response will be a python dict (parsed from the json response recieved). | ||
print(campaign_clicks) | ||
|
||
[ | ||
{'date': '2021-04-15', 'clicks': 3, 'spend': 8.139999999999999}, | ||
{'date': '2021-04-15', 'clicks': 2, 'spend': 6.51}, | ||
{'date': '2021-04-15', 'clicks': 1, 'spend': 3.88}, | ||
{'date': '2021-04-15', 'clicks': 4, 'spend': 3.275311}, | ||
{'date': '2021-04-15', 'clicks': 6, 'spend': 1.408321} | ||
], | ||
|
||
# Get Google Ads data only | ||
campaign_clicks = client.connectors( | ||
connector="google_ads", | ||
date_preset=LAST_7D, | ||
fields=["account_name", "campaign", "clicks", "datasource", "source", "spend"] | ||
) | ||
|
||
# Get Facebook Ads data only | ||
campaign_clicks = client.connectors( | ||
connector="facebook", | ||
date_preset=LAST_7D, | ||
fields=["account_name", "campaign", "clicks", "datasource", "source", "spend"] | ||
) | ||
|
||
# Get list of all possible connectors (i.e: Google Ads, Facebook Ads, Twitter, Tik Tok etc.) | ||
list_connectors = client.list_connectors | ||
print(list_connectors) | ||
|
||
['adform', 'adobe', 'adroll', 'all', 'amazon_ads', 'amazon_s3', 'amazon_sp', 'apple_search_ads', 'appnexus', 'appsflyer', 'awin', 'bing', 'cm360', 'criteo' 'currency_conversion', 'daisycon', 'dv360', 'facebook', 'facebook_leads', 'facebook_organic', 'gmailcsv', 'google_ad_manager', 'google_ads', 'google_pagespeed', 'googleanalytics', 'googleanalytics4', 'googlesheets', 'hubspot', 'idealo', 'instagram', 'klaviyo', 'linkedin', 'linkedin_organic', 'mailchimp', 'outbrain', 'pinterest', 'quora', 'reddit', 'rtbhouse', 'salesforce', 'searchconsole', 'sftp', 'shopify', 'snapchat', 'stripe', 'taboola', 'tiktok', 'twitter', 'twitter_organic', 'vertaa', 'zoho'] | ||
|
||
# Sample with date specific ranges. | ||
dataset_with_ranges = client.connectors( | ||
date_from="2022-10-18", | ||
date_to="2022-10-20", | ||
fields=["account_name", "campaign", "clicks", "datasource", "source", "spend", "date"] | ||
) | ||
``` | ||
|
||
### List of fields | ||
The full list of fields that the package accepts is given in https://windsor.ai/connector/all/. Fields can be common to all the connectors or specific for each company. | ||
|
||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import requests | ||
import time | ||
|
||
|
||
class Client(object): | ||
""" | ||
Represents an API Client object for windsor.ai's APIs. | ||
""" | ||
|
||
API_URL = 'https://connectors.windsor.ai' | ||
|
||
def __init__(self, api_key): | ||
|
||
self.API_KEY = api_key | ||
self.session = self._init_session() | ||
self.status_code = None | ||
|
||
def _init_session(self): | ||
|
||
session = requests.session() | ||
session.headers.update({ | ||
'Accept': 'application/json', | ||
'User-Agent': 'windsorai/python' | ||
}) | ||
return session | ||
|
||
def _close_session(self): | ||
self.session.close() | ||
|
||
def _create_api_uri(self, path): | ||
return self.API_URL + '/' + path | ||
|
||
def _request(self, method, path, signed, **kwargs): | ||
uri = self._create_api_uri(path) | ||
|
||
kwargs['params']['api_key'] = self.API_KEY | ||
|
||
response = getattr(self.session, method)(uri, **kwargs) | ||
return self._handle_response(response) | ||
|
||
def _handle_response(self, response): | ||
""" | ||
Internal helper for handling API responses from the windsor.ai server. | ||
Raises the appropriate exceptions when necessary; otherwise, returns the | ||
response. | ||
Parameters | ||
---------- | ||
response : requests.Response Object | ||
Returns | ||
------- | ||
dictionary | ||
Dictionary of the response object. | ||
""" | ||
|
||
# Any windsor.ai specific error handling can be done here | ||
# Nothing as such for now | ||
self.status_code = response.status_code | ||
self._close_session() | ||
return response.json() | ||
|
||
def _get(self, path, signed=False, **kwargs): | ||
return self._request('get', path, signed, **kwargs) | ||
|
||
def _post(self, path, signed=False, **kwargs): | ||
return self._request('post', path, signed, **kwargs) | ||
|
||
def _put(self, path, signed=False, **kwargs): | ||
return self._request('put', path, signed, **kwargs) | ||
|
||
def _delete(self, path, signed=False, **kwargs): | ||
return self._request('delete', path, signed, **kwargs) | ||
|
||
# General Endpoints | ||
|
||
@property | ||
def list_connectors(self): | ||
""" | ||
Fetch list of all possible connectors. | ||
This function returns all the available connectors from Windsor.ai | ||
platform. The list can be found in the following website: | ||
https://windsor.ai/connector/all/ | ||
Parameters | ||
---------- | ||
Returns | ||
------- | ||
list | ||
List of strings for all available connectors. | ||
""" | ||
return self._get(path="list_connectors", params={}) | ||
|
||
def connectors(self, connector="snapchat", **params): | ||
""" | ||
Fetch data from Windsor.ai | ||
Get data from one of the available windsor connectors. | ||
https://connectors.windsor.ai/#operations-default-get_connector | ||
Parameters | ||
---------- | ||
connector : str | ||
A string with the preferred connector. Default - "all" | ||
date_preset : str | ||
It's recommended that you use provided enums, instead of a | ||
hardcoded string. Example: LAST_7D | ||
fields : list | ||
List of fields. It's recommended that you use FIELD_* enums, | ||
instead of hardcoded strings. | ||
Example: [FIELD_SOURCE, FIELD_CAMPAIGN, FIELD_CLICKS]. | ||
Returns | ||
------- | ||
dictionary | ||
Dictionary with the available data. In case of error, it returns | ||
the error message. | ||
Examples | ||
-------- | ||
>>> from SnapchatAdsWindsorApi.enums import LAST_7D | ||
>>> from SnapchatAdsWindsorApi.fields import FIELD_SOURCE, FIELD_CAMPAIGN, FIELD_CLICKS | ||
>>> client = Client(api_key) | ||
>>> orders = client.connectors(date_preset=LAST_7D, fields=[FIELD_SOURCE, FIELD_CAMPAIGN, FIELD_CLICKS]) | ||
""" | ||
# Python interface is a list. Whereas the API expects a comma seperated string. | ||
params['fields'] = ','.join(params['fields']) | ||
|
||
return self._get(connector, params=params) |
Oops, something went wrong.