Skip to content

Commit

Permalink
Merge pull request #3 from smasty/master
Browse files Browse the repository at this point in the history
Refactor the SDK
  • Loading branch information
lukasotocerny committed Oct 3, 2018
2 parents c1478fe + 054efee commit baca280
Show file tree
Hide file tree
Showing 16 changed files with 399 additions and 367 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
env
venv
__pycache__
.pytest_cache
test.py
build
dist
exponea_python_sdk.egg-info
exponea_python_sdk.egg-info
.idea
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# Exponea Python SDK

An official Python client for Exponea Data API

## Installation
```
pip install exponea-python-sdk
```

## Usage
```python
from exponea_python_sdk import Exponea
from exponea_python_sdk import Exponea
exponea = Exponea("project_token", username="basic_auth_username", password="basic_auth_password")
```
You can now fully utilize all four API, which are Analyses, Catalog, Customer and Tracking API described bellow.
Expand Down Expand Up @@ -102,7 +106,7 @@ It returns items of the catalog that match the query and filters specified in pa
"matched": 2,
"limit": 20,
"skip": 0,
"data": [{"item_id": "1", "properties": {"field_one": "foo", "field_two": "baz"}}],
"data": [{"item_id": "1", "properties": {"field_one": "foo", "field_two": "baz"}}],
"matched_limited": False,
"total": 2
}
Expand Down Expand Up @@ -315,7 +319,7 @@ It returns a Dictionary.
"type": "test",
"timestamp": 1533495544.343536,
"properties": {}
}],
}],
"properties": {
"first_name": "Lukas",
"last_nam": "Cerny"
Expand Down Expand Up @@ -400,7 +404,7 @@ It returns an Array.
[
{
"ids": {
"cookie": [],
"cookie": [],
"registered": "test"
},
"properties": {
Expand Down Expand Up @@ -517,4 +521,4 @@ It returns a Dictionary. The elements in `data` represent individual segments.
}
]
}
```
```
3 changes: 2 additions & 1 deletion exponea_python_sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from exponea_python_sdk.client import Exponea
name = "exponea_python_sdk"

name = 'exponea_python_sdk'
47 changes: 30 additions & 17 deletions exponea_python_sdk/analyses.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,44 @@
class AnalysisType:
FUNNEL = 'funnel'
REPORT = 'report'
SEGMENTATION = 'segmentation'


ANALYSIS_TYPES = {
AnalysisType.FUNNEL,
AnalysisType.REPORT,
AnalysisType.SEGMENTATION,
}


class Analyses:
def __init__(self, client):
self.client = client
self.endpoint_base = "/data/v2/projects/{}/analyses".format(client.project_token)

# Generic method for getting any type of Analyses
self.endpoint_base = '/data/v2/projects/{}/analyses'.format(client.project_token)

def get_analysis(self, analysis_type, analysis_id):
# Construct request
path = self.endpoint_base + "/" + analysis_type
payload = { "analysis_id": analysis_id, "format": "table_json" }
response = self.client.request("POST", path, payload)
"""Generic method for getting any type of analyses"""
assert analysis_type in ANALYSIS_TYPES, 'Unknown analysis type "{}"'.format(analysis_type)
path = '{}/{}'.format(self.endpoint_base, analysis_type)
payload = {'analysis_id': analysis_id, 'format': 'table_json'}
response = self.client.post(path, payload)
# In case analysis is not found
if response is None:
return None
headers = response["header"]
result = { "name": response["name"], "data": [] }
for row in response["rows"]:
headers = response['header']
result = {'name': response['name'], 'data': []}
for row in response['rows']:
item = {}
for index, data in enumerate(row):
item[headers[index]] = data
result["data"].append(item)
result['data'].append(item)
return result

def get_funnel(self, funnel_id):
return self.get_analysis("funnel", funnel_id)
return self.get_analysis(AnalysisType.FUNNEL, funnel_id)

def get_report(self, report_id):
return self.get_analysis("report", report_id)
return self.get_analysis(AnalysisType.REPORT, report_id)

def get_segmentation(self, segmentation_id):
return self.get_analysis("segmentation", segmentation_id)
return self.get_analysis(AnalysisType.SEGMENTATION, segmentation_id)
100 changes: 45 additions & 55 deletions exponea_python_sdk/catalog.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,70 @@
from urllib.parse import urlencode


class Catalog:
def __init__(self, client):
self.client = client
self.endpoint_base = "/data/v2/projects/{}/catalogs".format(client.project_token)
self.endpoint_base = '/data/v2/projects/{}/catalogs'.format(client.project_token)

def create_catalog(self, name, fields):
path = self.endpoint_base
payload = { "name": name, "fields": [ {"name": field } for field in fields ] }
response = self.client.request("POST", path, payload)
payload = {'name': name, 'fields': [{'name': field} for field in fields]}
response = self.client.post(path, payload)
if response is None:
return None
return response["id"]
return response['id']

def get_catalog_name(self, catalog_id):
path = self.endpoint_base + "/{}".format(catalog_id)
response = self.client.request("GET", path)
path = '{}/{}'.format(self.endpoint_base, catalog_id)
response = self.client.get(path)
if response is None:
return None
return response["data"]["name"]
return response['data']['name']

def get_catalog_items(self, catalog_id, params=None):
# URL encode any filters and params
if bool(params):
query_string = "?" + urlencode(params)
else:
query_string = ""
path = self.endpoint_base + "/{}/items{}".format(catalog_id, query_string)
response = self.client.request("GET", path)
query_string = '?{}'.format(urlencode(params)) if params else ''
path = '{}/{}/items{}'.format(self.endpoint_base, catalog_id, query_string)
response = self.client.get(path)
if response is None:
return None
# Delete unnecessary attributes
del response["success"]
for item in response["data"]:
del item["catalog_id"]
del response['success']
for item in response['data']:
del item['catalog_id']
return response

def update_catalog_name(self, catalog_id, catalog_name, fields):
path = self.endpoint_base + "/{}".format(catalog_id)
payload = { "name": catalog_name, "fields": [ { "name": field } for field in fields ] }
response = self.client.request("PUT", path, payload)
path = '{}/{}'.format(self.endpoint_base, catalog_id)
payload = {'name': catalog_name, 'fields': [{'name': field} for field in fields]}
response = self.client.put(path, payload)
if response is None:
return None
return response["success"]
return response['success']

def create_catalog_item(self, catalog_id, item_id, properties):
path = self.endpoint_base + "/{}/items/{}".format(catalog_id, item_id)
payload = { "properties": properties }
response = self.client.request("PUT", path, payload)
if response is None:
return None
return response["success"]

path = '{}/{}/items/{}'.format(self.endpoint_base, catalog_id, item_id)
payload = {'properties': properties}
response = self.client.put(path, payload)
return None if response is None else response['success']

def update_catalog_item(self, catalog_id, item_id, properties):
path = self.endpoint_base + "/{}/items/{}/partial-update".format(catalog_id, item_id)
payload = { "properties": properties }
response = self.client.request("POST", path, payload)
if response is None:
return None
return response["success"]

path = '{}/{}/items/{}/partial-update'.format(self.endpoint_base, catalog_id, item_id)
payload = {'properties': properties}
response = self.client.post(path, payload)
return None if response is None else response['success']

def delete_catalog_item(self, catalog_id, item_id):
path = self.endpoint_base + "/{}/items/{}".format(catalog_id, item_id)
response = self.client.request("DELETE", path)
if response is None:
return None
return response["success"]

path = '{}/{}/items/{}'.format(self.endpoint_base, catalog_id, item_id)
response = self.client.delete(path)
return None if response is None else response['success']

def delete_catalog_items(self, catalog_id):
path = self.endpoint_base + "/{}/items".format(catalog_id)
response = self.client.request("DELETE", path)
if response is None:
return None
return response["success"]
path = '{}/{}/items'.format(self.endpoint_base, catalog_id)
response = self.client.delete(path)
return None if response is None else response['success']

def delete_catalog(self, catalog_id):
path = self.endpoint_base + "/{}".format(catalog_id)
response = self.client.request("DELETE", path)
if response is None:
return None
return response["success"]
path = '{}/{}'.format(self.endpoint_base, catalog_id)
response = self.client.delete(path)
return None if response is None else response['success']
61 changes: 37 additions & 24 deletions exponea_python_sdk/client.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import json
import logging

import requests
from requests.auth import HTTPBasicAuth
from exponea_python_sdk.exceptions import APIException

from exponea_python_sdk.analyses import Analyses
from exponea_python_sdk.catalog import Catalog
from exponea_python_sdk.customer import Customer
from exponea_python_sdk.exceptions import APIException
from exponea_python_sdk.tracking import Tracking
from exponea_python_sdk.catalog import Catalog
import requests
import logging
import json

DEFAULT_URL = "https://api.exponea.com"
DEFAULT_URL = 'https://api.exponea.com'

logging.basicConfig()
DEFAULT_LOGGER = logging.getLogger("exponea-python-sdk")
DEFAULT_LOGGER = logging.getLogger('exponea-python-sdk')


class Exponea:
def __init__(self, project_token, username="", password="", url=None):
def __init__(self, project_token, username='', password='', url=None):
self.project_token = project_token
self.username = username
self.password = password
Expand All @@ -24,7 +27,7 @@ def __init__(self, project_token, username="", password="", url=None):
self.catalog = Catalog(self)
self.customer = Customer(self)
self.tracking = Tracking(self)

def configure(self, project_token=None, username=None, password=None, url=None):
if project_token is not None:
self.project_token = project_token
Expand All @@ -34,26 +37,36 @@ def configure(self, project_token=None, username=None, password=None, url=None):
self.password = password
if url is not None:
self.url = url

def _process_response_exceptions(self, response):
pass

def request(self, request_type, path, payload=None):
return self

def request(self, method, path, payload=None):
url = self.url + path
self.logger.debug("Sending {} request to {}".format(request_type, url))
response = requests.request(request_type, url, json=payload, auth=HTTPBasicAuth(self.username, self.password))
self.logger.debug('Sending %s request to %s', method, url)
response = requests.request(method, url, json=payload, auth=HTTPBasicAuth(self.username, self.password))
status = response.status_code
self.logger.debug("Response status code {}".format(status))
self.logger.debug('Response status code: %d', status)
result = json.loads(response.text)
if status == 200 and result["success"]:
if status == 200 and result['success']:
return result
self.logger.error(response.text)
if result.get("error") is not None:
raise APIException(result["error"])
elif result.get("errors"):
errors = result.get("errors")
if result.get('error') is not None:
raise APIException(result['error'])
elif result.get('errors'):
errors = result['errors']
if type(errors) == list:
raise APIException(result["errors"])
raise APIException(result['errors'])
elif type(errors) == dict:
raise APIException(list(result["errors"].values()))
raise APIException(list(result['errors'].values()))
raise APIException(response.text)

def get(self, path):
return self.request('GET', path)

def post(self, path, payload=None):
return self.request('POST', path, payload)

def delete(self, path, payload=None):
return self.request('DELETE', path, payload)

def put(self, path, payload=None):
return self.request('PUT', path, payload)
Loading

0 comments on commit baca280

Please sign in to comment.