diff --git a/mailchimp/chimp.py b/mailchimp/chimp.py index 3109839..ded73c3 100644 --- a/mailchimp/chimp.py +++ b/mailchimp/chimp.py @@ -29,7 +29,7 @@ class SegmentCondition(object): 'starts': lambda a,b: str(a).startswith(str(b)), 'ends': lambda a,b: str(a).endswith(str(b)) } - + def __init__(self, condition_type, field, op, value): self.condition_type = condition_type self.field = field @@ -39,10 +39,10 @@ def __init__(self, condition_type, field, op, value): if not hasattr(self, check_function_name): check_function_name = 'merge_check' self.checker = getattr(self, check_function_name) - + def check(self, member): return self.checker(member) - + def check_interests(self, member): interests = self.value.split(',') if self.op == 'all': @@ -60,7 +60,7 @@ def check_interests(self, member): if interest in member.interests: return False return True - + def merge_check(self, member): return self.OPERATORS[self.op](member.merges[self.field.upper()], self.value) @@ -68,10 +68,10 @@ def merge_check(self, member): class BaseChimpObject(object): _attrs = () _methods = () - + verbose_attr = 'id' cache_key = 'id' - + def __init__(self, master, info): self.master = master @@ -84,10 +84,10 @@ def __init__(self, master, info): base = self.__class__.__name__.lower() self.cache = master.cache.get_child_cache(getattr(self, self.cache_key)) self.con = master.con - + for method in self._methods: setattr(self, method, wrap(base, self.master.con, method, self.id)) - + def __repr__(self): verbose = getattr(self, self.verbose_attr).encode('utf-8') return '<%s object: %s>' % (self.__class__.__name__, verbose) @@ -101,10 +101,10 @@ class Campaign(BaseChimpObject): 'settings.from_name', 'id', 'settings.inline_css', 'recipients.list_id', 'send_time', 'status', 'settings.subject_line', 'settings.title', 'settings.to_name', 'type') - + _methods = ('delete', 'pause', 'replicate', 'resume', 'schedule', 'send_now', 'send_test', 'unschedule') - + verbose_attr = 'subject_line' def __init__(self, master, info): @@ -115,7 +115,7 @@ def __init__(self, master, info): self.list = None self._content = None self.frozen_info = info - + def __unicode__(self): return self.subject @@ -127,22 +127,22 @@ def get_content(self): if self._content is None: self._content = self.con.campaign_content(self.id) return self._content - + def send_now_async(self): return self.send_now() def delete(self): return self.con.campaign_delete(self.id) - + def pause(self): return self.con.campaign_pause(self.id) - + def update(self): status = [] for key, value in self._get_diff(): status.append(self.con.campaign_update(self.id, key, value)) return all(status) - + def _get_diff(self): diff = [] new_frozen = {} @@ -153,30 +153,30 @@ def _get_diff(self): new_frozen[key] = current self.frozen_info = new_frozen return diff - + @property def is_sent(self): return self.status == 'sent' - - + + class Member(BaseChimpObject): _attrs = ('email', 'timestamp') - + _extended_attrs = ('id', 'ip_opt', 'ip_signup', 'merge_fields', 'status', 'interests') verbose_attr = 'email' cache_key = 'email' - + def __init__(self, master, info): super(Member, self).__init__(master, info) - + def __unicode__(self): return self.email def __getattr__(self, attr): if attr in self._extended_attrs: return self.info[attr] - raise AttributeError, attr + raise AttributeError(attr) @property def merges(self): @@ -185,45 +185,45 @@ def merges(self): @property def info(self): return self.get_info() - + def get_info(self): return self.cache.get('list_member_info', self.con.list_member_info, self.master.id, self.email) - + def update(self): return self.con.list_update_member(self.master.id, self.email, self.merges, interests=self.interests) - - + + class LazyMemberDict(dict): def __init__(self, master): super(LazyMemberDict, self).__init__() self._list = master - + def __getitem__(self, key): if key in self: return super(LazyMemberDict, self).__getitem__(key) value = self._list.get_member(key) self[key] = value return value - - + + class List(BaseChimpObject): ''' This represents a mailing list. Most of the methods (defined in _methods) are wrappers of the flat API found in chimpy.chimpy. As such, signatures are the same. ''' - _methods = ('batch_subscribe', - 'batch_unsubscribe', + _methods = ('batch_subscribe', + 'batch_unsubscribe', 'subscribe', 'unsubscribe') - + _attrs = ('id', 'date_created', 'name', 'stats') verbose_attr = 'name' - + def __init__(self, *args, **kwargs): super(List, self).__init__(*args, **kwargs) self.members = LazyMemberDict(self) - + def segment_test(self, match, conditions): return self.master.con.campaign_segment_test(self.id, {'match': match, 'conditions': conditions}) @@ -263,28 +263,28 @@ def _default_grouping(self): @property def webhooks(self): return self.get_webhooks() - + def get_webhooks(self): return self.cache.get('webhooks', self.master.con.list_webhooks, self.id) - + def add_webhook(self, url, actions, sources): return self.master.con.list_webhook_add(self.id, url, actions, sources) - + def remove_webhook(self, url): return self.master.con.list_webhook_del(self.id, url) - + def add_webhook_if_not_exists(self, url, actions, sources): for webhook in self.webhooks: if webhook['url'] == url: return True return self.add_webhook(url, actions, sources) - + def install_webhook(self): domain = Site.objects.get_current().domain if not (domain.startswith('http://') or domain.startswith('https://')): domain = 'http://%s' % domain if domain.endswith('/'): - domain = domain[:-1] + domain = domain[:-1] url = domain + reverse('mailchimp_webhook', kwargs={'key': WEBHOOK_KEY}) actions = {'subscribe': True, 'unsubscribe': True, @@ -302,7 +302,7 @@ def add_merge(self, key, desc, req=None): def remove_merge(self, key): return self.master.con.list_merge_var_del(self.id, key) - + def add_merges_if_not_exists(self, *new_merges): self.cache.flush('merges') merges = [m['tag'].upper() for m in self.merges] @@ -310,14 +310,14 @@ def add_merges_if_not_exists(self, *new_merges): if merge.upper() not in merges: self.add_merge(merge, merge, False) merges.append(merge.upper()) - + @property def merges(self): return self.get_merges() - + def get_merges(self): return self.cache.get('merges', self.master.con.list_merge_vars, self.id) - + def __unicode__(self): return self.name @@ -331,7 +331,7 @@ def get_member(self, email): memberdata['timestamp'] = data['timestamp_signup'] memberdata['email'] = data['email_address'] return Member(self, memberdata) - + def filter_members(self, segment_opts): """ segment_opts = {'match': 'all' if self.segment_options_all else 'any', @@ -390,7 +390,7 @@ class Connection(object): 'lists': MCListDoesNotExist, 'folders': MCFolderDoesNotExist, } - + def __init__(self, api_key=None, secure=False, check=True): self._secure = secure self._check = check @@ -399,7 +399,7 @@ def __init__(self, api_key=None, secure=False, check=True): self.is_connected = False if api_key is not None: self.connect(api_key) - + def connect(self, api_key): self._api_key = api_key self.cache = Cache(api_key) @@ -415,14 +415,14 @@ def ping(self): @property def campaigns(self): return self.get_campaigns() - + def get_campaigns(self): return self.cache.get('campaigns', self._get_campaigns) @property def lists(self): return self.get_lists() - + def get_lists(self): return self.cache.get('lists', self._get_lists) @@ -432,13 +432,13 @@ def templates(self): def get_templates(self): return self.cache.get('templates', self._get_templates) - + def _get_campaigns(self): return build_dict(self, Campaign, self.con.campaigns()) def _get_lists(self): return build_dict(self, List, self.con.lists()) - + def _get_templates(self): templates = self.con.campaign_templates() return build_dict(self, Template, templates) @@ -452,10 +452,10 @@ def get_folders(self): def _get_folders(self): return build_dict(self, Folder, self.con.folders(), key='folder_id') - + def get_list_by_id(self, id): return self._get_by_id('lists', id) - + def get_campaign_by_id(self, id): return build_dict(self, Campaign, [self.con.campaign(id)])[id] @@ -480,13 +480,13 @@ def _get_by_id(self, thing, id): return getattr(self, thing)[id] except KeyError: raise self.DOES_NOT_EXIST[thing](id) - + def _get_by_key(self, thing, name, key): for id, obj in getattr(self, thing).items(): if getattr(obj, name) == key: return obj raise self.DOES_NOT_EXIST[thing]('%s=%s' % (name, key)) - + def create_campaign(self, campaign_type, campaign_list, template, subject, reply_to, from_name, to_name, folder_id=None, tracking=None, title='', authenticate=False, diff --git a/mailchimp/chimpy/__init__.py b/mailchimp/chimpy/__init__.py index ba997a6..7a9a94a 100644 --- a/mailchimp/chimpy/__init__.py +++ b/mailchimp/chimpy/__init__.py @@ -1 +1 @@ -from chimpy import Connection +from .chimpy import Connection diff --git a/mailchimp/chimpy/chimpy.py b/mailchimp/chimpy/chimpy.py index 1b9fb9a..1bab03b 100644 --- a/mailchimp/chimpy/chimpy.py +++ b/mailchimp/chimpy/chimpy.py @@ -1,6 +1,14 @@ import json import hashlib -import urllib +import six +try: + from urllib import quote_plus, urlencode +except ImportError: + from urllib.parse import quote_plus, urlencode +try: + from urllib2 import build_opener +except ImportError: + from urllib.request import build_opener from warnings import warn from requests import request @@ -82,7 +90,7 @@ def make_request(self, method="GET", path=None, **kwargs): try: response.raise_for_status() - except HTTPError, e: + except HTTPError as e: message = response.json()['detail'] raise ChimpyException(message) return response.json() @@ -272,19 +280,17 @@ def campaign(self, cid): def campaign_create(self, campaign_type, settings, **kwargs): # enforce the 100 char limit (urlencoded!!!) title = settings.get('title', settings['subject_line']) - - if isinstance(title, unicode): + if isinstance(title, six.text_types): title = title.encode('utf-8') - titlelen = len(urllib.quote_plus(title)) + titlelen = len(quote_plus(title)) if titlelen > 99: title = title[:-(titlelen - 96)] + '...' warn("cropped campaign title to fit the 100 character limit, new title: '%s'" % title, ChimpyWarning) subject = settings['subject_line'] - - if isinstance(subject, unicode): + if isinstance(subject, six.text_types): subject = subject.encode('utf-8') - subjlen = len(urllib.quote_plus(subject)) + subjlen = len(quote_plus(subject)) if subjlen > 99: subject = subject[:-(subjlen - 96)] + '...' @@ -351,7 +357,7 @@ def campaign_send_now(self, cid): def campaign_send_test(self, cid, test_emails, send_type='html'): path = 'campaigns/{}/actions/test'.format(cid) - if isinstance(test_emails, basestring): + if isinstance(test_emails, six.string_types): test_emails = [test_emails] payload = { diff --git a/mailchimp/chimpy/test_chimpy.py b/mailchimp/chimpy/test_chimpy.py index 6cca595..4178c37 100644 --- a/mailchimp/chimpy/test_chimpy.py +++ b/mailchimp/chimpy/test_chimpy.py @@ -4,7 +4,7 @@ You need to activate groups in the Mailchimp web UI before running tests: * Browse to http://admin.mailchimp.com - * List setting -> Groups for segmentation + * List setting -> Groups for segmentation * Check "add groups to my list" """ @@ -13,8 +13,9 @@ import pprint import operator import random -import md5 +import hashlib import datetime +import six import chimpy @@ -34,7 +35,6 @@ def setup_module(): " wget 'http://api.mailchimp.com/1.1/?output=json&method=login" \ "&password=xxxxxx&username=yyyyyyyy' -O apikey" - global chimp chimp = chimpy.Connection(os.environ['MAILCHIMP_APIKEY']) @@ -66,9 +66,9 @@ def test_list_subscribe_and_unsubscribe(): assert result == True members = chimp.list_members(list_id())['data'] - print members + print(members) emails = map(lambda x: x['email'], members) - print members + print(members) assert EMAIL_ADDRESS in emails result = chimp.list_unsubscribe(list_id(), @@ -160,7 +160,7 @@ def test_list_update_member_and_member_info(): def test_create_delete_campaign(): - uid = md5.new(str(random.random())).hexdigest() + uid = hashlib.md5(str(random.random())).hexdigest() subject = 'chimpy campaign test %s' % uid options = {'list_id': list_id(), 'subject': subject, @@ -180,7 +180,7 @@ def test_create_delete_campaign(): content = {'html': html} cid = chimp.campaign_create('regular', options, content, segment_opts=segment_opts) - assert isinstance(cid, basestring) + assert isinstance(cid, six.string_types) # check if the new campaign really is there campaigns = chimp.campaigns(filter_subject=subject) @@ -215,7 +215,7 @@ def test_replicate_update_campaign(): cid = chimp.campaign_create('regular', options, content) newcid = chimp.campaign_replicate(cid=cid) - assert isinstance(newcid, basestring) + assert isinstance(newcid, six.string_types) newsubject = 'Fresh subject ' + uid newtitle = 'Custom title ' + uid @@ -305,5 +305,5 @@ def test_rss_campaign(): setup_module() for f in globals().keys(): if f.startswith('test_') and callable(globals()[f]): - print f + print(f) globals()[f]() diff --git a/mailchimp/chimpy/utils.py b/mailchimp/chimpy/utils.py index 3180ad2..3e7834f 100644 --- a/mailchimp/chimpy/utils.py +++ b/mailchimp/chimpy/utils.py @@ -3,8 +3,9 @@ from datetime import timedelta + def transform_datetime(dt): - """ converts datetime parameter""" + """ converts datetime parameter""" if dt is None: dt = '' @@ -29,7 +30,7 @@ def flatten(params, key=None): flat = {} for name, val in params.items(): if key is not None and not isinstance(key, int): - name = "%s[%s]" % (key, name) + name = "{}[{}]".format(key, name) if isinstance(val, dict): flat.update(flatten(val, name)) elif isinstance(val, list): @@ -37,4 +38,3 @@ def flatten(params, key=None): elif val is not None: flat[name] = val return flat - diff --git a/mailchimp/management/commands/mcdequeue.py b/mailchimp/management/commands/mcdequeue.py index 18c6bb1..0ca4129 100644 --- a/mailchimp/management/commands/mcdequeue.py +++ b/mailchimp/management/commands/mcdequeue.py @@ -4,20 +4,20 @@ class Command(BaseCommand): - + def handle(self, *args, **options): if len(args) and args[0].isdigit(): limit = int(args[0]) else: limit = None - print 'Dequeueing Campaigns' + print('Dequeueing Campaigns') done = False for camp in dequeue(limit): done = True if camp: - print '- Dequeued campaign %s (%s)' % (camp.name, camp.campaign_id) + print('- Dequeued campaign {} ({})'.format(camp.name, camp.campaign_id)) else: - print 'ERROR' + print('ERROR') if not done: - print 'Nothing to dequeue' - print 'Done' \ No newline at end of file + print('Nothing to dequeue') + print('Done') \ No newline at end of file diff --git a/mailchimp/management/commands/mcmakemerge.py b/mailchimp/management/commands/mcmakemerge.py index 06841a9..4ccb4fa 100644 --- a/mailchimp/management/commands/mcmakemerge.py +++ b/mailchimp/management/commands/mcmakemerge.py @@ -6,13 +6,13 @@ class Command(BaseCommand): def handle(self, *args, **options): if len(args) != 1: - print 'You have to specify exactly one argument to this command' + print('You have to specify exactly one argument to this command') return merge = args[0] - print 'Adding the merge var `%s` to all lists' % merge + print('Adding the merge var `{}` to all lists'.format(merge)) c = get_connection() for list in c.lists.values(): - print 'Checking list %s' % list.name + print('Checking list {}'.format(list.name)) list.add_merges_if_not_exists(merge) - print ' ok' - print 'Done' \ No newline at end of file + print(' ok') + print('Done') \ No newline at end of file diff --git a/mailchimp/management/commands/mcsitegroups.py b/mailchimp/management/commands/mcsitegroups.py index ece0728..4acd35a 100644 --- a/mailchimp/management/commands/mcsitegroups.py +++ b/mailchimp/management/commands/mcsitegroups.py @@ -5,13 +5,13 @@ class Command(BaseCommand): def handle(self, *args, **options): - print 'Installing site segment groups for all lists and all sites' + print('Installing site segment groups for all lists and all sites') c = get_connection() interests = [] for site in Site.objects.all(): interests.append(site.domain) for list in c.lists.values(): - print 'Checking list %s' % list.name + print('Checking list {}'.format(list.name)) list.add_interests_if_not_exist(*interests) - print ' ok' - print 'Done' \ No newline at end of file + print(' ok') + print('Done') \ No newline at end of file diff --git a/mailchimp/management/commands/mcwebhooks.py b/mailchimp/management/commands/mcwebhooks.py index 94ec2cc..1f0c821 100644 --- a/mailchimp/management/commands/mcwebhooks.py +++ b/mailchimp/management/commands/mcwebhooks.py @@ -4,13 +4,13 @@ class Command(BaseCommand): def handle(self, *args, **options): - print 'Installing webhooks for all lists' + print('Installing webhooks for all lists') c = get_connection() for list in c.lists.values(): - print 'Checking list %s' % list.name + print('Checking list {}'.format(list.name)) # def add_webhook_if_not_exists(self, url, actions, sources): if list.install_webhook(): - print ' ok' + print(' ok') else: - print ' ERROR!' - print 'Done' \ No newline at end of file + print(' ERROR!') + print('Done') \ No newline at end of file diff --git a/mailchimp/signals.py b/mailchimp/signals.py index fe926cb..2b29d73 100644 --- a/mailchimp/signals.py +++ b/mailchimp/signals.py @@ -11,4 +11,4 @@ def get_signal(name): - return globals()['mc_%s' % name] + return globals()['mc_{}'.format(name)] diff --git a/mailchimp/utils.py b/mailchimp/utils.py index fba6052..886dfd0 100644 --- a/mailchimp/utils.py +++ b/mailchimp/utils.py @@ -67,7 +67,7 @@ def _fake_del(self, key): del self._data[key] def get_child_cache(self, key): - return Cache('%s_%s_' % (self._prefix, key)) + return Cache('{}_{}_'.format(self._prefix, key)) def flush(self, *keys): for key in keys: @@ -86,7 +86,7 @@ def wrap(base, parent, name, *baseargs, **basekwargs): def _wrapped(*args, **kwargs): fullargs = baseargs + args kwargs.update(basekwargs) - return getattr(parent, '%s_%s' % (base, name))(*fullargs, **kwargs) + return getattr(parent, '{}_{}'.format(base, name))(*fullargs, **kwargs) return _wrapped @@ -256,7 +256,7 @@ def logout(self): def get_page_link(self, page): - return '%s?page=%s' % (self.request.path, page) + return '{}?page={}'.format(self.request.path, page) def paginate(self, objects, page): return Paginator(objects, page, self.get_page_link, 20, 5) diff --git a/mailchimp/views.py b/mailchimp/views.py index 144f120..c2f2c90 100644 --- a/mailchimp/views.py +++ b/mailchimp/views.py @@ -70,17 +70,17 @@ def handle_get(self): obj = ct.model_class().objects.get(pk=self.kwargs['pk']) self.connection.warnings.reset() if obj.mailchimp_test(self.connection, self.request): - self.message_success("A Test Campaign has been sent to your email address (%s)." % self.request.user.email) + self.message_success("A Test Campaign has been sent to your email address ({}).".format(self.request.user.email)) for message, category, filename, lineno in self.connection.warnings.get(): - self.message_warning("%s: %s" % (category.__name__, message)) + self.message_warning("{}: {}".format(category.__name__, message)) else: self.message_error("And error has occured while trying to send the test mail to you, please try again later") return self.json(True) - + class TestCampaignForObject(ScheduleCampaignForObject): template = 'mailchimp/send_test.html' - + def handle_get(self): referer = self.request.META.get('HTTP_REFERER') or '/' data = { @@ -104,7 +104,7 @@ def handle_get(self): extra_info = camp.object.mailchimp_get_extra_info() data['extra_info'] = extra_info return self.render_to_response(data) - + class WebHook(MailchimpBaseView): @@ -161,7 +161,7 @@ def handle_post(self): signal.send(sender=self.connection, **kwargs) return self.response("ok") - + class Dequeue(ScheduleCampaignForObject): def handle_get(self): @@ -171,7 +171,7 @@ def handle_get(self): else: self.message_error("An error has occured while trying to dequeue this campaign, please try again later.") return self.back() - + class Cancel(ScheduleCampaignForObject):