Skip to content
This repository was archived by the owner on Jun 11, 2024. It is now read-only.

Commit 2c8923a

Browse files
committed
add documentation
1 parent 0773818 commit 2c8923a

14 files changed

Lines changed: 1041 additions & 51 deletions

File tree

CHANGES.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
0.1.0
2-
-----
1+
0.1
2+
---
33

4-
- Initial version
4+
- First release

README.rst

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
atomx api README
1+
Python Atomx Api
22
================
33

4-
Simple interface for the atomx rest api.
4+
Interface for the atomx rest api.
5+
`atomx wiki <http://wiki.atomx.com/doku.php?id=api>`_
56

6-
Example Usage:
7-
--------------
7+
8+
Example Usage
9+
-------------
810

911
.. code-block:: python
1012
@@ -60,7 +62,7 @@ Example Usage:
6062
6163
# reporting example
6264
# get a report for a specific publisher
63-
report = atomx.report(type='publisher', sums=['impressions', 'clicks'], groups=['hour'], where=[['publisher_id', '==', 42]], from_='2015-02-08 00:00:00Z', to='2015-02-09 00:00:00Z')
65+
report = atomx.report(type='publisher', sums=['impressions', 'clicks'], groups=['hour'], where=[['publisher_id', '==', 42]], from_='2015-02-08 00:00:00', to='2015-02-09 00:00:00', timezone='America/Los_Angeles')
6466
# check if report is ready
6567
print(report.is_ready)
6668
# if pandas is installed you can get the pandas dataframe with `report.pandas`

atomx/__init__.py

Lines changed: 164 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
from datetime import datetime
44
import requests
5-
from .version import API_VERSION, VERSION
6-
from . import models
7-
from .utils import get_model_name
8-
from .exceptions import (
5+
from atomx.version import API_VERSION, VERSION
6+
from atomx import models
7+
from atomx.utils import get_model_name
8+
from atomx.exceptions import (
99
APIError,
1010
ModelNotFoundError,
1111
InvalidCredentials,
@@ -21,6 +21,17 @@
2121

2222

2323
class Atomx(object):
24+
"""Interface for the api on api.atomx.com.
25+
26+
To learn more about the api visit the
27+
`atomx wiki <http://wiki.atomx.com/doku.php?id=api>`_
28+
29+
:param str email: email address of your atomx user
30+
:param str password: password of your atomx user
31+
:param str api_endpoint: url for connections to the api
32+
(defaults to `https://api.atomx.com/{API_VERSION}`)
33+
:return: :class:`.Atomx` session to interact with the api
34+
"""
2435
def __init__(self, email, password, api_endpoint=API_ENDPOINT):
2536
self.email = email
2637
self.password = password
@@ -29,6 +40,18 @@ def __init__(self, email, password, api_endpoint=API_ENDPOINT):
2940
self.login()
3041

3142
def login(self, email=None, password=None):
43+
"""Gets new authentication token for user ``email``.
44+
45+
This method is automatically called in :meth:`__init__` so
46+
you rarely have to call this method directly.
47+
48+
:param str email: Use this email instead of the one provided at
49+
construction time. (optional)
50+
:param str password: Use this password instead of the one provided at
51+
construction time. (optional)
52+
:return: None
53+
:raises: :class:`.exceptions.InvalidCredentials` if ``email``/``password`` is wrong
54+
"""
3255
if email:
3356
self.email = email
3457
if password:
@@ -43,9 +66,37 @@ def login(self, email=None, password=None):
4366
self.auth_tk = r.json()['auth_tkt']
4467

4568
def logout(self):
69+
"""Removes authentication token from session."""
70+
self.auth_tk = None
4671
self.session.get(self.api_endpoint + 'logout')
4772

4873
def search(self, query):
74+
"""Search for ``query``.
75+
76+
Returns a `dict` with all found results for:
77+
'Advertisers', 'Campaigns', 'Creatives', 'Placements', 'Publishers', 'Sites'.
78+
79+
The resulting :mod:`.models` have only `id` and `name` loaded since that's
80+
what's returned from the api `/search` call, but attributes will be lazy loaded
81+
once you try to accessed them.
82+
Or you can just fetch everything with one api call with :meth:`.AtomxModel.reload`.
83+
84+
Example::
85+
86+
>>> atomx = Atomx('apiuser@example.com', 'password')
87+
>>> search_result = atomx.search('atomx')
88+
>>> assert 'campaigns' in search_result
89+
>>> campaign = search_result['campaigns'][0]
90+
>>> assert isinstance(campaign, models.Campaign)
91+
>>> # campaign has only `id` and `name` loaded but you
92+
>>> # can still access (lazy load) all attributes
93+
>>> assert isinstance(campaign.budget, float)
94+
>>> # or reload all attributes with one api call
95+
>>> campaign.reload()
96+
97+
:param str query: keyword to search for.
98+
:return: dict with list of :mod:`.models` as values
99+
"""
49100
r = self.session.get(self.api_endpoint + 'search', params={'q': query})
50101
if not r.ok:
51102
raise APIError(r.json()['error'])
@@ -61,17 +112,25 @@ def search(self, query):
61112
def report(self, scope, groups, sums, where, from_, to=None, timezone='UTC', fast=True):
62113
"""Create a report.
63114
115+
See the `reporting atomx wiki <http://wiki.atomx.com/doku.php?id=reporting>`_
116+
for details about parameters and available groups, sums.
117+
64118
:param str scope: either 'advertiser' or 'publisher' to select the type of report.
65-
:param list groups: columns to group by (see http://wiki.atomx.com/doku.php?id=reporting#groups)
66-
:param list sums: columns to sum on (see http://wiki.atomx.com/doku.php?id=reporting#sums)
119+
:param list groups: columns to group by.
120+
:param list sums: columns to sum on.
67121
:param list where: is a list of expression lists.
68-
An expression list is in the form of `[column, op, value]`.
69-
`column` can be any of the :param:`groups` or :param:`sums` columns.
70-
`op` can be any of `==`, `!=`, `<=`, `>=`, `<`, `>`, `in` or `not in` as a string.
71-
`value` is either a number or in case of `in` and `not in` a list of numbers.
72-
:param datetime from_: `datetime` where the report should start (inclusive)
73-
:param datetime to: `datetime` where the report should end (exclusive).
74-
(defaults to `datetime.now()` if undefined)
122+
An expression list is in the form of ``[column, op, value]``:
123+
124+
- ``column`` can be any of the ``groups`` or ``sums`` parameter columns.
125+
- ``op`` can be any of ``==``, ``!=``, ``<=``, ``>=``,
126+
``<``, ``>``, ``in`` or ``not in`` as a string.
127+
- ``value`` is either a number or in case of ``in``
128+
and ``not in`` a list of numbers.
129+
130+
:param datetime.datetime from_: :class:`datetime.datetime` where the report
131+
should start (inclusive)
132+
:param datetime.datetime to: :class:`datetime.datetime` where the report
133+
should end (exclusive). (defaults to `datetime.now()` if undefined)
75134
:param str timezone: Timezone used for all times. (defaults to `UTC`)
76135
For a supported list see http://wiki.atomx.com/doku.php?id=timezones
77136
:param bool fast: if `False` the report will always be run against the low level data.
@@ -98,6 +157,15 @@ def report(self, scope, groups, sums, where, from_, to=None, timezone='UTC', fas
98157
return models.Report(self, query=r.json()['query'], **r.json()['report'])
99158

100159
def report_status(self, report):
160+
"""Get the status for a `report`.
161+
162+
This is typically used by calling :meth:`.models.Report.status`.
163+
164+
:param report: Either a :class:`str` that contains the ``id`` of
165+
of the report or an :class:`.models.Report` instance.
166+
:type report: :class:`.models.Report` or :class:`list`
167+
:return: :class:`dict` containing the report status.
168+
"""
101169
if isinstance(report, models.Report):
102170
report_id = report.id
103171
else:
@@ -109,6 +177,16 @@ def report_status(self, report):
109177
return r.json()['report']
110178

111179
def report_get(self, report):
180+
"""Get the content (csv) of a :class:`.models.Report`
181+
182+
Typically used by calling :meth:`.models.Report.content` or
183+
:meth:`.models.Report.pandas`.
184+
185+
:param report: Either a :class:`str` that contains the ``id`` of
186+
of the report or an :class:`.models.Report` instance.
187+
:type report: :class:`.models.Report` or :class:`list`
188+
:return: :class:`str` with the report content.
189+
"""
112190
if isinstance(report, models.Report):
113191
report_id = report.id
114192
else:
@@ -120,6 +198,47 @@ def report_get(self, report):
120198
return r.content.decode()
121199

122200
def get(self, resource, **kwargs):
201+
"""Returns a list of models from :mod:`.models` if you query for
202+
multiple models or a single instance of a model from :mod:`.models`
203+
if you query for a specific `id`
204+
205+
:param str resource: Specify the resource to get from the atomx api.
206+
207+
Examples:
208+
209+
Query all advertisers::
210+
211+
>>> atomx = Atomx('apiuser@example.com', 'password')
212+
>>> advertisers = atomx.get('advertisers')
213+
>>> assert isinstance(advertisers, list)
214+
>>> assert isinstance(advertisers[0], atomx.models.Advertiser)
215+
216+
Get publisher with id 23::
217+
218+
>>> publisher = atomx.get('publisher/23')
219+
>>> assert publisher.id == 23
220+
>>> assert isinstance(publisher, atomx.models.Publisher)
221+
222+
Get all profiles for advertiser 42::
223+
224+
>>> profiles = atomx.get('advertiser/42/profiles')
225+
>>> assert isinstance(profiles, list)
226+
>>> assert isinstance(profiles[0], atomx.models.Profile)
227+
>>> assert profiles[0].advertiser.id == 42
228+
229+
:param kwargs: Any argument is passed as URL parameter to the respective api endpoint.
230+
See `API URL Parameters <http://wiki.atomx.com/doku.php?id=api#url_parameters>`_
231+
in the wiki.
232+
233+
Example:
234+
Get the first 20 domains that contain ``atom``::
235+
236+
>>> atom_domains = atomx.get('domains', hostname='*atom*', limit=20)
237+
>>> assert len(atom_domains) == 20
238+
>>> assert 'atom' in atom_domains[1].hostname
239+
240+
:return: a class from :mod:`.models` or a list of models depending on param `resource`
241+
"""
123242
r = self.session.get(self.api_endpoint + resource.strip('/'), params=kwargs)
124243
if not r.ok:
125244
raise APIError(r.json()['error'])
@@ -134,28 +253,51 @@ def get(self, resource, **kwargs):
134253
return getattr(models, model)(self, **res)
135254
return res
136255

137-
def post(self, model, json, **kwargs):
138-
r = self.session.post(self.api_endpoint + model.strip('/'),
256+
def post(self, resource, json, **kwargs):
257+
"""Send HTTP POST to ``resource`` with ``json`` content.
258+
259+
Used by :meth:`.models.AtomxModel.create`.
260+
261+
:param resource: Name of the resource to `POST` to.
262+
:param json: Content of the `POST` request.
263+
:param kwargs: URL Parameters of the request.
264+
:return: :class:`dict` with the newly created resource.
265+
"""
266+
r = self.session.post(self.api_endpoint + resource.strip('/'),
139267
json=json, params=kwargs)
140268
r_json = r.json()
141269
if not r.ok:
142270
raise APIError(r_json['error'])
143271
return r_json[r_json['resource']]
144272

145-
def put(self, model, id, json, **kwargs):
146-
r = self.session.put(self.api_endpoint + model.strip('/') + '/' + str(id),
273+
def put(self, resource, id, json, **kwargs):
274+
"""Send HTTP PUT to ``resource``/``id`` with ``json`` content.
275+
276+
Used by :meth:`.models.AtomxModel.save`.
277+
278+
:param resource: Name of the resource to `PUT` to.
279+
:param id: Id of the resource you want to modify
280+
:param json: Content of the `PUT` request.
281+
:param kwargs: URL Parameters of the request.
282+
:return: :class:`dict` with the modified resource.
283+
"""
284+
r = self.session.put(self.api_endpoint + resource.strip('/') + '/' + str(id),
147285
json=json, params=kwargs)
148286
r_json = r.json()
149287
if not r.ok:
150288
raise APIError(r_json['error'])
151289
return r_json[r_json['resource']]
152290

153-
def delete(self, model, id, json, **kwargs):
154-
return self.session.put(self.api_endpoint + model.strip('/') + '/' + str(id),
155-
json=json, params=kwargs)
291+
def delete(self, resource, id, json, **kwargs):
292+
"""Delete is currently not supported by the api.
293+
Set the resources `state` to `INACTIVE` to deactivate it.
294+
"""
295+
pass
156296

157297
def save(self, model):
298+
"""Alias for :meth:`.models.AtomxModel.save` with `session` argument."""
158299
return model.save(self)
159300

160-
def update(self, model):
161-
return model.update(self)
301+
def create(self, model):
302+
"""Alias for :meth:`.models.AtomxModel.create` with `session` argument."""
303+
return model.create(self)

atomx/exceptions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
class NoSessionError(Exception):
2+
"""Raised when a model from :mod:`.models` wants to ``create``/``update``
3+
but it doesn't have an :class:`atomx.Atomx` session yet.
4+
"""
25
pass
36

47
class InvalidCredentials(Exception):
8+
"""Raised when trying to login with the wrong e-mail or password."""
59
pass
610

711
class APIError(Exception):
12+
"""Raised when the atomx api returns an error that is not caught otherwise."""
813
pass
914

1015
class ModelNotFoundError(Exception):
16+
"""Raised when trying to (re-)load a model that is not in the api."""
1117
pass
1218

1319
class ReportNotReadyError(Exception):
20+
"""Raised when requesting ``report.content`` but the report is not ready yet."""
1421
pass
1522

1623
class NoPandasInstalledError(Exception):
24+
"""Raised when trying to access ``report.pandas`` without :mod:`pandas` installed."""
1725
pass

0 commit comments

Comments
 (0)