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
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 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 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), }
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 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), }
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
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), }
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
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