22
33from datetime import datetime
44import 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 ,
2121
2222
2323class 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 )
0 commit comments