Skip to content

Commit

Permalink
support-offer-tags (#74)
Browse files Browse the repository at this point in the history
* Isort code in tests

* Support offer files

* Isort

* Isort

* Isort code in tests

* Isort!!

* Isort

* Isort!!

* Support new Tag model

* Fix lint errors

* Remove ValuesDict

* Fix typos

* Remove redundant assert

* Add changelog entry

* Isort

* Add extra whitespace

* isort

* Don't comment code, move changes to unreleased

* Remove redundant empty line

* Lint
  • Loading branch information
iamanikeev authored and Stranger6667 committed Jun 15, 2018
1 parent 40f7a15 commit c56c6c3
Show file tree
Hide file tree
Showing 10 changed files with 1,609 additions and 20 deletions.
5 changes: 5 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ Changelog
`Unreleased`_
-------------

Added
~~~~~
- Support of Tag controller. (`iamanikeev`_)


`0.6.7`_ - 2018-05-11
---------------------

Expand Down
11 changes: 8 additions & 3 deletions pyoffers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def is_empty(data):


def is_paginated(data):
return 'pageCount' in data
return any(key in data for key in ('page', 'pageCount')) # Apparently, there are two types of pagination HO


def retry(method):
Expand Down Expand Up @@ -67,12 +67,16 @@ def setup_managers(self):
self._managers = {}
for manager_class in MODEL_MANAGERS:
instance = manager_class(self)
if not isinstance(instance, ApplicationManager) or instance.__class__ is ApplicationManager:
if not instance.forbid_registration \
and not isinstance(instance, ApplicationManager) or instance.__class__ is ApplicationManager:
# Descendants of ``ApplicationManager`` shouldn't be present in API instance. They are controlled by
# Application controller. The manager itself, on the other hand, should.
setattr(self, instance.name, instance)
if instance.model:
self._managers[instance.model.__name__] = instance
if instance.model_aliases:
for alias in instance.model_aliases:
self._managers[alias] = instance

def __str__(self):
return '%s: %s / %s' % (self.__class__.__name__, self.network_token, self.network_id)
Expand Down Expand Up @@ -126,7 +130,8 @@ def handle_response(self, content, target=None, single_result=True, raw=False):
if is_empty(data):
return data
elif is_paginated(data):
if not data['count']:
if 'count' in data and not data['count']:
# Response is paginated, but is empty
return data['data']
data = data['data']

Expand Down
18 changes: 15 additions & 3 deletions pyoffers/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
# coding: utf-8
from .advertiser import Advertiser, AdvertiserManager # noqa
from .affiliate import Affiliate, AffiliateManager, AffiliateUser, AffiliateUserManager # noqa
from .affiliate_offer import AffiliateOffer, AffiliateOfferManager # noqa
from .affiliate import ( # noqa
Affiliate,
AffiliateManager,
AffiliateOffer,
AffiliateOfferManager,
AffiliateUser,
AffiliateUserManager,
)
from .conversion import Conversion, ConversionManager # noqa
from .core import ApplicationManager, ModelManager # noqa
from .country import Country, CountryManager # noqa
from .goal import Goal, GoalManager # noqa
from .offer import Offer, OfferCategory, OfferCategoryManager, OfferManager # noqa
from .offer_file import OfferFile, OfferFileManager # noqa
from .raw_log import RawLogManager # noqa
from .tags import Tag, TagManager, TagRelationManager # noqa


MODEL_MANAGERS = (
Expand All @@ -24,11 +31,16 @@
ApplicationManager,
OfferFileManager,
AffiliateOfferManager,
TagManager,
TagRelationManager,
)

MANAGER_ALIASES = {
# Some API calls return instances of object under different name. F.e. you have to specify ``contain=['Thumbnail']``
# in order to find Offer thumbnail (https://developers.tune.com/network/offer-findall/). But it will in fact return
# ``OfferFile`` instance under ``Thumbnail``. For that we need aliases.
'Thumbnail': 'OfferFile'
'Thumbnail': 'OfferFile',
'OfferTag': 'Tag',
'AdvertiserTag': 'Tag',
'AffiliateTag': 'Tag',
}
12 changes: 11 additions & 1 deletion pyoffers/models/affiliate.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
# coding: utf-8
from .core import Model, ModelManager
from .core import InvisibleModelManager, Model, ModelManager
from .offer_file import OfferFileManager


class AffiliateOffer(Model):
"""
Model that defines relation between Offer and Affiliate
"""


class AffiliateOfferManager(InvisibleModelManager):
model = AffiliateOffer


class AffiliateUser(Model):
generic_methods = ('update', 'delete')

Expand Down
13 changes: 0 additions & 13 deletions pyoffers/models/affiliate_offer.py

This file was deleted.

8 changes: 8 additions & 0 deletions pyoffers/models/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,16 @@ class ModelManager(metaclass=SelectiveInheritanceMeta):
Class properties:
- model: model class
- model_aliases: possible names by which HO API might return instances of the model
- name: a name by which a manager will be referenced in API instance
- generic_methods: what methods to inherit from base ``ModelManager``
- forbid_registration: whether the manager shouldn't be accessible in API instance
"""
model = None
model_aliases = None
name = None
generic_methods = ()
forbid_registration = False

def __init__(self, api):
self.api = api
Expand Down Expand Up @@ -133,6 +137,10 @@ def find_all_ids(self, **kwargs):
return self._call('findAllIds', filters=Filter(**kwargs), single_result=False, raw=True)


class InvisibleModelManager(ModelManager):
forbid_registration = True


class RelatedManager:
related_object_name = None

Expand Down
96 changes: 96 additions & 0 deletions pyoffers/models/tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from .core import InvisibleModelManager, Model, ModelManager


class TagRelation(Model):
"""
This is a common model for (Offer|Affiliate|Advertiser)Tag objects. These objects just represent relation between
tag and corresponding object. They are auxiliary objects, hence - no actions on them could be performed directly.
"""


class TagRelationManager(InvisibleModelManager):
model = TagRelation
model_aliases = (
'OffersTags',
'AffiliatesTags',
'AdvertisersTags',
)


class Tag(Model):
generic_methods = ('update', 'delete')

def add_to_offer(self, offer_id):
return self._manager.add_to_offer(self.id, offer_id=offer_id)

def add_to_affiliate(self, affiliate_id):
return self._manager.add_to_affiliate(self.id, affiliate_id=affiliate_id)

def add_to_advertiser(self, advertiser_id):
return self._manager.add_to_advertiser(self.id, advertiser_id=advertiser_id)

def remove_from_offer(self, offer_id):
return self._manager.remove_from_offer(self.id, offer_id=offer_id)

def remove_from_affiliate(self, affiliate_id):
return self._manager.remove_from_affiliate(self.id, affiliate_id=affiliate_id)

def remove_from_advertiser(self, advertiser_id):
return self._manager.remove_from_advertiser(self.id, advertiser_id=advertiser_id)


class TagManager(ModelManager):
model = Tag
name = 'tags'
generic_methods = (
'find_by_id',
'find_all',
'delete',
'update',
'create',
)

def find_all_offer_tag_relations(self, **kwargs):
return self._call('findAllOfferTagRelations', target_class='TagRelation', single_result=False, **kwargs)

def find_all_affiliate_tag_relations(self, **kwargs):
return self._call('findAllAffiliateTagRelations', target_class='TagRelation', single_result=False, **kwargs)

def find_all_advertiser_tag_relations(self, **kwargs):
return self._call('findAllAdvertiserTagRelations', target_class='TagRelation', single_result=False, **kwargs)

def add_to_offer(self, tag_id, offer_id):
return self._call('addToOffer', target_class='TagRelation', tag_id=tag_id, offer_id=offer_id)

def add_to_affiliate(self, tag_id, affiliate_id):
return self._call('addToAffiliate', target_class='TagRelation', tag_id=tag_id, affiliate_id=affiliate_id)

def add_to_advertiser(self, tag_id, advertiser_id):
return self._call('addToAdvertiser', target_class='TagRelation', tag_id=tag_id, advertiser_id=advertiser_id)

def set_for_offer(self, tag_ids, offer_id):
return self._call('setForOffer', target_class='TagRelation', tag_ids=tag_ids, offer_id=offer_id)

def set_for_affiliate(self, tag_ids, affiliate_id):
return self._call('setForAffiliate', target_class='TagRelation', tag_ids=tag_ids, affiliate_id=affiliate_id)

def set_for_advertiser(self, tag_ids, advertiser_id):
return self._call('setForAdvertiser', target_class='TagRelation', tag_ids=tag_ids, advertiser_id=advertiser_id)

def remove_from_offer(self, tag_id, offer_id):
return self._call('removeFromOffer', tag_id=tag_id, offer_id=offer_id)

def remove_from_affiliate(self, tag_id, affiliate_id):
return self._call('removeFromAffiliate', tag_id=tag_id, affiliate_id=affiliate_id)

def remove_from_advertiser(self, tag_id, advertiser_id):
return self._call('removeFromAdvertiser', tag_id=tag_id, advertiser_id=advertiser_id)

def remove_from_advertiser_by_relational_id(self, id):
return self._call('removeFromAdvertiserByRelationalId', id=id)

def remove_from_offer_by_relational_id(self, id):
return self._call('removeFromOfferByRelationalId', id=id)

def remove_from_affiliate_by_relational_id(self, id):
return self._call('removeFromAffiliateByRelationalId', id=id)

0 comments on commit c56c6c3

Please sign in to comment.