def shop_all_items(request):
    ret = {
        'shop_brushes':
        Brush.for_shop(viewer=request.user, request=request),
        'shop_colors':
        Color.for_shop(viewer=request.user),
        'color_packs':
        ColorPack.for_shop(viewer=request.user),
        'coin_products':
        COIN_PRODUCTS,
        'brush_products':
        brush_products(request=request),
        'balance':
        economy.balance(request.user),
        'color_packs_header':
        redis.get('color_packs_header'),
        'colors_header':
        redis.get('colors_header'),
        'tabs': [
            {
                'name': 'colors',
                'default': True
            },
            {
                'name': 'coins'
            },
            {
                'name': 'brushes'
            },
        ],
    }

    return ret
Exemple #2
0
    def handle(self, *args, **options):
        balances = []

        for user in User.objects.filter(
                date_joined__gte=datetime.datetime.now() -
                datetime.timedelta(days=90)):
            if user.comments.count() < 2:
                continue
            balances.append(economy.balance(user))

        figures = {'count': 0}

        def hist(bins):
            figures['count'] += 1
            pyplot.figure(figures['count'])
            pyplot.hist(balances, bins=bins, facecolor='green', alpha=0.75)
            pyplot.xlabel('Coins')
            pyplot.ylabel('User count')
            pyplot.suptitle(r'Coin balances')
            pyplot.grid(True)
            pyplot.savefig('/home/ubuntu/graphs/{}.svg'.format(
                figures['count']),
                           dpi=180)

        hist(range(0, 101, 1))
        hist(range(0, 301, 5))
        hist(range(0, 1001, 10))
        hist(range(1001, 10000, 100))
Exemple #3
0
def post_quest_comment(request, quest_id, content_id, fact_metadata={},
                       facebook_share=False, facebook_access_token=None):
    # Rate-limit?
    if not request.user.is_staff:
        prefix = 'user:{}:post_limit:'.format(request.user.id)
        if not RateLimit(prefix+'h', 60, 60*60).allowed() or not RateLimit(prefix+'d', 100, 8*60*60).allowed():
            raise ServiceError("Attempting to post drawings too quickly.")

    _, parent_comment, content, _, _, _ = validate_and_clean_comment(
        request.user,
        parent_comment=quest_id,
        reply_content=content_id,
    )

    if facebook_share:
        if not facebook_access_token:
            raise ServiceError("Can't share to your timeline if you haven't signed into Facebook yet.")

        associate_facebook_account(request.user, facebook_access_token)

    comment = QuestComment.create_and_post(request, request.user, content, parent_comment,
                                           fact_metadata=fact_metadata)

    if facebook_share:
        complete_quest(request.user, comment, facebook_access_token, request=request)

    return {
        'comment': comment.details(),
        'balance': economy.balance(request.user),
    }
Exemple #4
0
def iap_process_receipt(request, receipt_data):
    """
    Verifies the receipt, and processes the purchase.
    """
    # TODO To be safer against botting, the receipt_data uniqueness constraint
    # needs to be done atomically.
    if IapReceipt.objects.filter(receipt_data=receipt_data).exists():
        # Already processed this receipt, fail silently.
        return {"balance": economy.balance(request.user)}

    receipt = IapReceipt.objects.create(purchaser=request.user, receipt_data=receipt_data, timestamp=Now())

    receipt.verify()

    if receipt.verified:
        deliver_product(request.user, receipt.product_id)

    return {"balance": economy.balance(request.user)}
def purchase_brush(request, brush_canonical_name):
    try:
        economy.purchase_brush(request.user, brush_canonical_name)
    except economy.InvalidPurchase as e:
        raise ServiceError(e.message)

    return {
        'shop_brushes': list(models.Brush.for_shop(viewer=request.user)),
        'user_brushes': list(models.Brush.for_user(request.user)),
        'balance': economy.balance(request.user),
    }
Exemple #6
0
def purchase_color_pack(request, color_pack_id):
    try:
        economy.purchase_color_pack(request.user, color_pack_id)
    except economy.InvalidPurchase as e:
        raise ServiceError(e.message)

    return {
        'shop_colors': list(models.Color.for_shop(viewer=request.user)),
        'user_colors': list(models.Color.for_user(request.user)),
        'color_packs': list(models.ColorPack.for_shop(viewer=request.user)),
        'balance': economy.balance(request.user),
    }
def purchase_color_pack(request, color_pack_id):
    try:
        economy.purchase_color_pack(request.user, color_pack_id)
    except economy.InvalidPurchase as e:
        raise ServiceError(e.message)

    return {
        'shop_colors': list(models.Color.for_shop(viewer=request.user)),
        'user_colors': list(models.Color.for_user(request.user)),
        'color_packs': list(models.ColorPack.for_shop(viewer=request.user)),
        'balance': economy.balance(request.user),
    }
Exemple #8
0
def iap_process_receipt(request, receipt_data):
    """
    Verifies the receipt, and processes the purchase.
    """
    #TODO To be safer against botting, the receipt_data uniqueness constraint
    # needs to be done atomically.
    if IapReceipt.objects.filter(receipt_data=receipt_data).exists():
        # Already processed this receipt, fail silently.
        return {'balance': economy.balance(request.user)}

    receipt = IapReceipt.objects.create(
        purchaser=request.user,
        receipt_data=receipt_data,
        timestamp=Now(),
    )

    receipt.verify()

    if receipt.verified:
        deliver_product(request.user, receipt.product_id)

    return {'balance': economy.balance(request.user)}
def post_quest_comment(request, quest_id, content_id, fact_metadata={},
                       facebook_share=False, facebook_access_token=None,
                       twitter_share=False, twitter_access_token=None, twitter_access_token_secret=None,
                       email_share=False, email_recipients=[],
                       resolve_share_ids=[],
                       uuid=None):
    if not request.user.is_staff and not settings.STAGING:
        prefix = 'user:{}:post_limit:'.format(request.user.id)
        if not RateLimit(prefix+'h', 60, 60*60).allowed() or not RateLimit(prefix+'d', 100, 8*60*60).allowed():
            raise ServiceError("Attempting to post drawings too quickly.")

    _, parent_comment, content, _, _ = validate_and_clean_comment(
        request.user,
        parent_comment=quest_id,
        reply_content=content_id,
    )

    if facebook_share:
        facebook_share = sns_publishing.facebook_share_pre_post(request, facebook_access_token)

    if twitter_share:
        twitter_share = sns_publishing.twitter_share_pre_post(request, twitter_access_token, twitter_access_token_secret)

    comment = QuestComment.create_and_post(request, request.user, content, parent_comment,
                                           uuid=uuid, fact_metadata=fact_metadata, debug_content_id=content_id)

    if facebook_share:
        sns_publishing.facebook_share_post_post(request, facebook_access_token, comment)

    if twitter_share:
        sns_publishing.twitter_share_post_post(request, twitter_access_token, twitter_access_token_secret, comment)

    for share_id in resolve_share_ids:
        share = ShareTrackingUrl.objects.get(id=share_id)

        if share.redirect_url:
            pass #TODO log some error here without failing this request

        share.redirect_url = comment.get_share_page_url()
        share.save()

    if email_share:
        @bgwork.defer
        def defer_email_share():
            sns_publishing.share_comment_by_email(comment, request.user, email_recipients)

    return {
        'comment': comment.details(),
        'balance': economy.balance(request.user),
    }
Exemple #10
0
def heavy_state_sync(request):
    ret = {
        'realtime_sync': models.realtime_sync(request.user),
        'user_palettes': user_palettes(request.user),
        'current_quest': current_quest_details(),
        'onboarding_quest_id': knobs.ONBOARDING_QUEST_ID,
    }

    if request.user.is_authenticated():
        ret.update({
            'user_email': request.user.email,
            'user_profile': models.user_profile(request.user.username),
            'balance': economy.balance(request.user),
            'completed_quest_ids': completed_quest_ids(request.user),
        })

    return ret
Exemple #11
0
def heavy_state_sync(request):
    ret = {
        'realtime_sync': models.realtime_sync(request.user),
        'user_palettes': user_palettes(request.user),
        'current_quest': current_quest_details(),
        'onboarding_quest_id': knobs.ONBOARDING_QUEST_ID,
    }

    if request.user.is_authenticated():
        ret.update({
            'user_email': request.user.email,
            'user_profile': models.user_profile(request.user.username),
            'balance': economy.balance(request.user),
            'completed_quest_ids': completed_quest_ids(request.user),
        })

    return ret
def shop_all_items(request):
    ret = {
        'shop_brushes': Brush.for_shop(viewer=request.user, request=request),
        'shop_colors': Color.for_shop(viewer=request.user),
        'color_packs': ColorPack.for_shop(viewer=request.user),
        'coin_products': COIN_PRODUCTS,
        'brush_products': brush_products(request=request),
        'balance': economy.balance(request.user),
        'color_packs_header': redis.get('color_packs_header'),
        'colors_header': redis.get('colors_header'),
        'tabs': [
            {'name': 'colors', 'default': True},
            {'name': 'coins'},
            {'name': 'brushes'},
        ],
    }

    return ret
Exemple #13
0
def post_quest_comment(request,
                       quest_id,
                       content_id,
                       fact_metadata={},
                       facebook_share=False,
                       facebook_access_token=None):
    # Rate-limit?
    if not request.user.is_staff:
        prefix = 'user:{}:post_limit:'.format(request.user.id)
        if not RateLimit(prefix + 'h', 60, 60 * 60).allowed() or not RateLimit(
                prefix + 'd', 100, 8 * 60 * 60).allowed():
            raise ServiceError("Attempting to post drawings too quickly.")

    _, parent_comment, content, _, _, _ = validate_and_clean_comment(
        request.user,
        parent_comment=quest_id,
        reply_content=content_id,
    )

    if facebook_share:
        if not facebook_access_token:
            raise ServiceError(
                "Can't share to your timeline if you haven't signed into Facebook yet."
            )

        associate_facebook_account(request.user, facebook_access_token)

    comment = QuestComment.create_and_post(request,
                                           request.user,
                                           content,
                                           parent_comment,
                                           fact_metadata=fact_metadata)

    if facebook_share:
        complete_quest(request.user,
                       comment,
                       facebook_access_token,
                       request=request)

    return {
        'comment': comment.details(),
        'balance': economy.balance(request.user),
    }
    def handle(self, *args, **options):
        balances = []

        for user in User.objects.filter(date_joined__gte=datetime.datetime.now() - datetime.timedelta(days=90)):
            if user.comments.count() < 2:
                continue
            balances.append(economy.balance(user))

        figures = {'count': 0}

        def hist(bins):
            figures['count'] += 1
            pyplot.figure(figures['count'])
            pyplot.hist(balances, bins=bins, facecolor='green', alpha=0.75)
            pyplot.xlabel('Coins')
            pyplot.ylabel('User count')
            pyplot.suptitle(r'Coin balances')
            pyplot.grid(True)
            pyplot.savefig('/home/ubuntu/graphs/{}.svg'.format(figures['count']), dpi=180)

        hist(range(0, 101, 1))
        hist(range(0, 301, 5))
        hist(range(0, 1001, 10))
        hist(range(1001, 10000, 100))
 def response():
     return {
         'balance': economy.balance(request.user),
         'shop_brushes': Brush.for_shop(viewer=request.user, request=request),
     }
Exemple #16
0
 def test_self_star_reward_is_nothing(self):
     balance = economy.balance(self.comment.author)
     self._star(user=self.comment.author)
     self.assertEqual(economy.balance(self.comment.author), balance)
def heavy_state_sync(user,
                     app_version=None,
                     app_version_tuple=None,
                     tab_last_seen_timestamps={}):
    from drawquest.apps.brushes.models import Brush
    from drawquest.apps.palettes.models import user_palettes, Color

    twitter_keys = '{}@{}'.format(settings.TWITTER_APP_KEY,
                                  settings.TWITTER_APP_SECRET)
    twitter_keys = twitter_keys[-6:] + twitter_keys[:-6]
    twitter_keys = swapcase(twitter_keys)

    ret = {
        'realtime_sync':
        realtime_sync(user),
        'user_palettes':
        user_palettes(user),
        'current_quest':
        current_quest_details(),
        'onboarding_quest_id':
        knobs.ONBOARDING_QUEST_ID,
        'sync':
        twitter_keys,
        'tumblr_success_regex':
        '''<div style="margin-bottom:10px; font-size:40px; color:#777;">Done!</div>''',
        'rewards': {
            'amounts': knobs.REWARDS,
            'copy': {
                'quest_of_the_day': _("You drew the Quest of the Day"),
                'archived_quest': _("You drew a Quest"),
                'first_quest': _("Woo! Your first Quest ever!"),
                'streak_3': _("Quest Streak: 3"),
                'streak_10': _("Quest Streak: 10"),
                'streak_100': _("Epic! 100 straight Quests"),
            },
            'iphone_copy': {
                'archived_quest': _("You drew a Quest"),
                'first_quest': _("Your first Quest!"),
                'quest_of_the_day': _("Quest of the Day!"),
                'streak_10': _("Bonus Streak"),
                'streak_100': _("Bonus Streak"),
                'streak_3': _("Bonus Streak"),
                'personal_share': _("Shared with Facebook"),
                'personal_twitter_share': _("Shared with Twitter"),
            },
        },
        'features': {
            'invite_from_facebook': True,
            'invite_from_twitter': True,
            'user_search': True,
            'urban_airship_registration_before_auth': True,
            'urban_airship_registration': True,
        },
        'logging': {
            'on': True,
            'authentication-controller': {
                'request-for-me': False,
            },
            'facebook-controller': {
                'open-active-session-with-read-permissions': False,
                'request-new-publish-permissions': False,
                'request-new-publish-permissions-cancelled': False,
                'request-new-read-permissions': False,
                'request-new-read-permissions-cancelled': False,
            },
            'facebook-friends-coordinator': {
                'present-requests-dialog': False,
                'request-my-friends': False,
            },
            'http-request': {
                'error-auth/login_with_facebook': {
                    'mute-error-codes': {
                        '403': True,
                    }
                },
                'error-auth/login_with_twitter': {
                    'mute-error-codes': {
                        '403': True,
                    }
                },
                'error-quests/gallery_for_comment': {
                    'mute-error-codes': {
                        '404': True,
                    }
                }
            },
            'private-api': {
                'failed-activity/iphone_activities': {
                    'mute-error-codes': {
                        '1005': True,
                    }
                }
            },
            'sharing-controller': {
                'present-feed-dialog': False,
                'present-share-dialog-with-link': False,
            },
            'shop-controller': {
                'add-payment': False,
                'brush-products-request': False,
                'coin-products-request': False,
            },
            'twitter-api-manager': {
                'step-1': False,
                'step-2': False,
            },
            'twitter-controller': {
                'request-data-cursored-user-ids': False,
                'request-data-send-dm': False,
                'request-data-unknown': False,
                'request-data-users-for-ids': False,
            },
        },
        #TODO use settings.LOCALES once that's ready
        'supported_languages': [
            'de', 'en', 'es', 'fr', 'ja', 'ko', 'nl', 'pt', 'ru', 'th',
            'zh-Hant', 'zh-Hans'
        ],
        'l10n_files_url':
        None,
        'user_colors':
        list(Color.for_user(user)),
        'user_brushes':
        list(Brush.for_user(user)),
        'global_brushes':
        list(Brush.for_global()),
        'comment_view_logging_interval':
        10,
        'papertrail': {
            'host': 'logs.papertrailapp.com',
            'port': 27889,
            'disabled_logging_points': [],
        },
        'modals': {},
    }

    if app_version_tuple and app_version_tuple >= (3, ):
        ret['appirater_url'] = 'itms-apps://itunes.apple.com/app/idAPP_ID'
    else:
        ret['appirater_url'] = 'itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID'

    try:
        ret['color_alert_version'] = int(redis.get('color_alert_version'))
    except TypeError:
        ret['color_alert_version'] = 0

    if user.is_authenticated():
        user_kv_items = user.kv.hgetall()
        user_kv_items = dict((key, val) for key, val in user_kv_items.items()
                             if key in [
                                 'saw_update_modal_for_version',
                                 'saw_share_web_profile_modal',
                                 'publish_to_facebook',
                                 'publish_to_twitter',
                             ])

        ret.update({
            'user_email': user.email,
            'user_profile': user_profile(user.username),
            'balance': economy.balance(user),
            'completed_quest_ids': completed_quest_ids(user),
            'web_profile_privacy': user.kv.web_profile_privacy.get(),
            'twitter_privacy': user.kv.twitter_privacy.get(),
            'facebook_privacy': user.kv.facebook_privacy.get(),
            'user_kv': user_kv_items,
            'reminders': {
                'invite': 1,
            },
        })

        if (app_version and parse_version(knobs.CURRENT_APP_VERSION) >
                parse_version(app_version)):
            saw_version = user_kv_items.get('saw_update_modal_for_version')
            if (saw_version is None or parse_version(saw_version) <
                    parse_version(knobs.CURRENT_APP_VERSION)):
                ret['modals'][
                    'show_update_modal_for_version'] = knobs.CURRENT_APP_VERSION
                ret['modals']['update_modal_type'] = 'alert'

        if not user_kv_items.get('saw_share_web_profile_modal'):
            ret['modals']['show_share_web_profile_modal'] = (
                user.date_joined <= (datetime.now() - td(days=2))
                or user.comments.count() >= 3)

    ret['tab_badge_type'] = 'flag'
    if tab_last_seen_timestamps:
        ret['tab_badges'] = tab_badges(
            user, last_seen_timestamps=tab_last_seen_timestamps)

    return ret
Exemple #18
0
 def response():
     return {
         'balance': economy.balance(request.user),
         'shop_brushes': Brush.for_shop(viewer=request.user,
                                        request=request),
     }
def coin_balance(request):
    return {'balance': economy.balance(request.user)}
def coin_balance(request):
    return {'balance': economy.balance(request.user)}
def heavy_state_sync(user, app_version=None, app_version_tuple=None, tab_last_seen_timestamps={}):
    from drawquest.apps.brushes.models import Brush
    from drawquest.apps.palettes.models import user_palettes, Color

    twitter_keys = '{}@{}'.format(settings.TWITTER_APP_KEY , settings.TWITTER_APP_SECRET)
    twitter_keys = twitter_keys[-6:] + twitter_keys[:-6]
    twitter_keys = swapcase(twitter_keys)

    ret = {
        'realtime_sync': realtime_sync(user),
        'user_palettes': user_palettes(user),
        'current_quest': current_quest_details(),
        'onboarding_quest_id': knobs.ONBOARDING_QUEST_ID,
        'sync': twitter_keys,
        'tumblr_success_regex': '''<div style="margin-bottom:10px; font-size:40px; color:#777;">Done!</div>''',
        'rewards': {
            'amounts': knobs.REWARDS,
            'copy': {
                'quest_of_the_day': _("You drew the Quest of the Day"),
                'archived_quest':   _("You drew a Quest"),
                'first_quest':      _("Woo! Your first Quest ever!"),
                'streak_3':         _("Quest Streak: 3"),
                'streak_10':        _("Quest Streak: 10"),
                'streak_100':       _("Epic! 100 straight Quests"),
            },
            'iphone_copy': {
                'archived_quest': _("You drew a Quest"),
                'first_quest': _("Your first Quest!"),
                'quest_of_the_day': _("Quest of the Day!"),
                'streak_10': _("Bonus Streak"),
                'streak_100': _("Bonus Streak"),
                'streak_3': _("Bonus Streak"),
                'personal_share': _("Shared with Facebook"),
                'personal_twitter_share': _("Shared with Twitter"),
            },
        },
        'features': {
            'invite_from_facebook': True,
            'invite_from_twitter': True,
            'user_search': True,
            'urban_airship_registration_before_auth': True,
            'urban_airship_registration': True,
        },
        'logging': {
            'on': True,
            'authentication-controller': {
                'request-for-me': False,
            },
            'facebook-controller': {
                'open-active-session-with-read-permissions': False,
                'request-new-publish-permissions': False,
                'request-new-publish-permissions-cancelled': False,
                'request-new-read-permissions': False,
                'request-new-read-permissions-cancelled': False,
            },
            'facebook-friends-coordinator': {
                'present-requests-dialog': False,
                'request-my-friends': False,
            },
            'http-request': {
                'error-auth/login_with_facebook': {
                    'mute-error-codes': {
                        '403': True,
                    }
                },
                'error-auth/login_with_twitter': {
                    'mute-error-codes': {
                        '403': True,
                    }
                },
                'error-quests/gallery_for_comment': {
                    'mute-error-codes': {
                        '404': True,
                    }
                }
            },
            'private-api': {
                'failed-activity/iphone_activities': {
                    'mute-error-codes': {
                        '1005': True,
                    }
                }
            },
            'sharing-controller': {
                'present-feed-dialog': False,
                'present-share-dialog-with-link': False,
            },
            'shop-controller': {
                'add-payment': False,
                'brush-products-request': False,
                'coin-products-request': False,
            },
            'twitter-api-manager': {
                'step-1': False,
                'step-2': False,
            },
            'twitter-controller': {
                'request-data-cursored-user-ids': False,
                'request-data-send-dm': False,
                'request-data-unknown': False,
                'request-data-users-for-ids': False,
            },
        },
        #TODO use settings.LOCALES once that's ready
        'supported_languages': ['de', 'en', 'es', 'fr', 'ja', 'ko', 'nl', 'pt', 'ru', 'th', 'zh-Hant', 'zh-Hans'],
        'l10n_files_url': None,
        'user_colors': list(Color.for_user(user)),
        'user_brushes': list(Brush.for_user(user)),
        'global_brushes': list(Brush.for_global()),
        'comment_view_logging_interval': 10,
        'papertrail': {
            'host': 'logs.papertrailapp.com',
            'port': 27889,
            'disabled_logging_points': [],
        },
        'modals': {},
    }

    if app_version_tuple and app_version_tuple >= (3,):
        ret['appirater_url'] = 'itms-apps://itunes.apple.com/app/idAPP_ID'
    else:
        ret['appirater_url'] = 'itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID'

    try:
        ret['color_alert_version'] = int(redis.get('color_alert_version'))
    except TypeError:
        ret['color_alert_version'] = 0

    if user.is_authenticated():
        user_kv_items = user.kv.hgetall()
        user_kv_items = dict((key, val) for key, val in user_kv_items.items()
                             if key in [
                                 'saw_update_modal_for_version',
                                 'saw_share_web_profile_modal',
                                 'publish_to_facebook',
                                 'publish_to_twitter',
                             ])

        ret.update({
            'user_email': user.email,
            'user_profile': user_profile(user.username),
            'balance': economy.balance(user),
            'completed_quest_ids': completed_quest_ids(user),
            'web_profile_privacy': user.kv.web_profile_privacy.get(),
            'twitter_privacy': user.kv.twitter_privacy.get(),
            'facebook_privacy': user.kv.facebook_privacy.get(),
            'user_kv': user_kv_items,
            'reminders': {
                'invite': 1,
            },
        })

        if (app_version and parse_version(knobs.CURRENT_APP_VERSION) > parse_version(app_version)):
            saw_version = user_kv_items.get('saw_update_modal_for_version')
            if (saw_version is None
                    or parse_version(saw_version) < parse_version(knobs.CURRENT_APP_VERSION)):
                ret['modals']['show_update_modal_for_version'] = knobs.CURRENT_APP_VERSION
                ret['modals']['update_modal_type'] = 'alert'

        if not user_kv_items.get('saw_share_web_profile_modal'):
            ret['modals']['show_share_web_profile_modal'] = (user.date_joined <= (datetime.now() - td(days=2))
                                                             or user.comments.count() >= 3)

    ret['tab_badge_type'] = 'flag'
    if tab_last_seen_timestamps:
        ret['tab_badges'] = tab_badges(user, last_seen_timestamps=tab_last_seen_timestamps)

    return ret
 def test_self_star_reward_is_nothing(self):
     balance = economy.balance(self.comment.author)
     self._star(user=self.comment.author)
     self.assertEqual(economy.balance(self.comment.author), balance)