Example #1
0
    def post(self):
        """
        Get a proposed username and test to see whether it is valid. If so,
        return that it is available, otherwise return that it is not via a
        simple JSON response.
        """
        user = UserPrefs.get()

        if not user:
            self.abort(404)

        username = cgi.escape(self.request.get('username'))
        count = 0

        # Does an existing user have that username?
        if not user.name == username:
            count = UserPrefs.all().filter('name =', username).count()

        # Is the name too short or one of the disallowed names?
        if not username or len(username) < 4 \
                or username in settings.RESERVED_USERNAMES:
            count = 1

        self.render_json({
            'username': username,
            'available': count == 0
        })
Example #2
0
    def process(self, username, action):
        """
        Follow the given user.
        """
        user = UserPrefs.get()
        publicuser = UserPrefs.all().filter('name =', username).get()

        if not user or not publicuser:
            return self.render_json({
                'status': 'error',
                'error': 'User not found'
            })

        if action == 'post':
            if publicuser.user_id in user.following:
                return self.render_json({
                    'status': 'error',
                    'error': 'Already following user'
                })

            user.following.append(publicuser.user_id)

            existing = UserAction.all()\
                                 .filter('owner =', user)\
                                 .filter('type =', UserAction.TYPE_USER_FOLLOWED)\
                                 .filter('object_id =', publicuser.key().id())\
                                 .count()

            if not existing:
                user_action = UserAction()
                user_action.owner = user
                user_action.type = user_action.TYPE_USER_FOLLOWED
                user_action.object_id = publicuser.key().id()
                user_action.put()
        else:
            if publicuser.user_id not in user.following:
                return self.render_json({
                    'status': 'error',
                    'error': 'User not being followed'
                })

            user.following.remove(publicuser.user_id)

            existing = UserAction.all()\
                                 .filter('owner =', user)\
                                 .filter('type =', UserAction.TYPE_USER_FOLLOWED)\
                                 .filter('object_id =', publicuser.key().id())\
                                 .get()

            if existing:
                existing.delete()

        # Save updated following list
        user.put()

        self.render_json({
            'status': 'ok'
        })
Example #3
0
    def process(self, action, username=None, recipe_slug=None):
        """
        Process a request to add or remove a user from the liked list.
        """
        user = UserPrefs.get()
        publicuser = UserPrefs.all()\
                              .filter('name = ', username)\
                              .get()

        if not publicuser:
            self.render_json({'status': 'error', 'error': 'User not found'})
            return

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug =', recipe_slug)\
                       .get()

        if not recipe:
            self.render_json({'status': 'error', 'error': 'Recipe not found'})
            return

        if action == 'post':
            if user.user_id not in recipe.likes:
                recipe.likes.append(user.user_id)
                recipe.put()

                existing = UserAction.all()\
                                     .filter('owner =', user)\
                                     .filter('type =', UserAction.TYPE_RECIPE_LIKED)\
                                     .filter('object_id =', recipe.key().id())\
                                     .count()

                if not existing:
                    user_action = UserAction()
                    user_action.owner = user
                    user_action.type = user_action.TYPE_RECIPE_LIKED
                    user_action.object_id = recipe.key().id()
                    user_action.put()

        elif action == 'delete':
            if user.user_id in recipe.likes:
                recipe.likes.remove(user.user_id)
                recipe.put()

                existing = UserAction.all()\
                                     .filter('owner =', user)\
                                     .filter('type =', UserAction.TYPE_RECIPE_LIKED)\
                                     .filter('object_id =', recipe.key().id())\
                                     .get()

                if existing:
                    existing.delete()

        return self.render_json({'status': 'ok', 'likes': len(recipe.likes)})
Example #4
0
    def get(self, username):
        """
        Render a user page.
        """
        publicuser = UserPrefs.all().filter('name =', username).get()

        if not publicuser:
            self.abort(404)

        recipes = Recipe.all()\
                        .filter('owner =', publicuser)\
                        .order('name')\
                        .run(limit=25)

        actions = UserAction.all()\
                            .filter('owner =', publicuser)\
                            .order('-created')\
                            .fetch(15)

        object_ids = UserAction.gather_object_ids(actions)

        user_map = {publicuser.key().id(): publicuser}

        for user in UserPrefs.get_by_id(object_ids['users']):
            user_map[user.key().id()] = user

        recipes = [r for r in recipes]
        recipe_ids = [recipe.key().id() for recipe in recipes]
        object_ids['recipes'] = [
            id for id in object_ids['recipes'] if id not in recipe_ids
        ]

        recipe_map = {}

        for recipe in recipes:
            recipe.owner = publicuser
            recipe_map[recipe.key().id()] = recipe

        for recipe in Recipe.get_by_id(object_ids['recipes']):
            recipe_map[recipe.key().id()] = recipe

        brew_map = {}

        for brew in Brew.get_by_id(object_ids['brews']):
            brew_map[brew.key().id()] = brew

        self.render(
            'user.html', {
                'publicuser': publicuser,
                'recipes': recipes,
                'actions': actions,
                'user_map': user_map,
                'recipe_map': recipe_map,
                'brew_map': brew_map
            })
Example #5
0
    def post(self, username=None, recipe_slug=None):
        publicuser = UserPrefs.all()\
                              .filter('name = ', username)\
                              .get()

        if not publicuser:
            self.render_json({
                'status': 'error',
                'error': 'User not found'
            })
            return

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug =', recipe_slug)\
                       .get()

        if not recipe:
            self.render_json({
                'status': 'error',
                'error': 'Recipe not found'
            })
            return

        new_recipe = Recipe(**{
            'owner': UserPrefs.get(),
            'cloned_from': recipe,
            'color': recipe.color,
            'ibu': recipe.ibu,
            'alcohol': recipe.alcohol,
            'name': recipe.name,
            'description': recipe.description,
            'type': recipe.type,
            'style': recipe.style,
            'batch_size': recipe.batch_size,
            'boil_size': recipe.boil_size,
            'bottling_temp': recipe.bottling_temp,
            'bottling_pressure': recipe.bottling_pressure,
            '_ingredients': recipe._ingredients
        })

        new_recipe.slug = generate_usable_slug(new_recipe)
        new_recipe.put()

        action = UserAction()
        action.owner = UserPrefs.get()
        action.type = action.TYPE_RECIPE_CLONED
        action.object_id = new_recipe.key().id()
        action.put()

        return self.render_json({
            'status': 'ok',
            'redirect': new_recipe.url
        })
Example #6
0
    def get(self, username):
        """
        Render a user page.
        """
        publicuser = UserPrefs.all().filter('name =', username).get()

        if not publicuser:
            self.abort(404)

        recipes = Recipe.all()\
                        .filter('owner =', publicuser)\
                        .order('name')\
                        .run(limit=25)

        actions = UserAction.all()\
                            .filter('owner =', publicuser)\
                            .order('-created')\
                            .fetch(15)

        object_ids = UserAction.gather_object_ids(actions)

        user_map = {
            publicuser.key().id(): publicuser
        }

        for user in UserPrefs.get_by_id(object_ids['users']):
            user_map[user.key().id()] = user

        recipes = [r for r in recipes]
        recipe_ids = [recipe.key().id() for recipe in recipes]
        object_ids['recipes'] = [id for id in object_ids['recipes'] if id not in recipe_ids]

        recipe_map = {}

        for recipe in recipes:
            recipe.owner = publicuser
            recipe_map[recipe.key().id()] = recipe

        for recipe in Recipe.get_by_id(object_ids['recipes']):
            recipe_map[recipe.key().id()] = recipe

        brew_map = {}

        for brew in Brew.get_by_id(object_ids['brews']):
            brew_map[brew.key().id()] = brew

        self.render('user.html', {
            'publicuser': publicuser,
            'recipes': recipes,
            'actions': actions,
            'user_map': user_map,
            'recipe_map': recipe_map,
            'brew_map': brew_map
        })
Example #7
0
    def get(self, provider=None):
        """
        Handle login initiation via a known OAuth 2 provider.
        """
        if settings.DEBUG and provider == "dummy":
            # Handle test dev login
            auth_id = 'dummy:test'
            UserPrefs.create_or_update(
                auth_id, {
                    'id':
                    '1234',
                    'name':
                    'Test User',
                    'email':
                    '*****@*****.**',
                    'avatar':
                    'http://www.gravatar.com/avatar/55502f40dc8b7c769880b10874abc9d0?s={0}&d=identicon'
                }, {})

            # Update session
            self.session['auth_id'] = auth_id

            return self.redirect(self.session.pop('next', '/'))

        auth_url = AUTH_URLS[provider][0]

        key = getattr(settings, provider.upper() + '_OAUTH_KEY')
        scope = getattr(settings, provider.upper() + '_OAUTH_SCOPE')

        # Generate a random state parameter to prevent CSRF attacks
        # This is both stored in the session and sent to the authorizing
        # server, relayed back and then checked to make sure it matches
        # up. If not, then the request likely did not originate from
        # this site and it can be ignored.
        csrf_state = hashlib.md5(uuid.uuid4().hex).hexdigest()

        self.session['login_csrf'] = csrf_state

        params = {
            'response_type': 'code',
            'client_id': key,
            'redirect_uri': self.callback_url(provider),
            'state': csrf_state
        }

        if scope:
            params.update(scope=scope)

        target_url = auth_url.format(urlencode(params))
        logging.info('Redirecting user to %s', target_url)

        self.redirect(target_url)
Example #8
0
    def get_recipes(self, request):
        """
        Get a list of recipes, optionally filtered by user name.
        """
        offset, limit = get_limits(request)

        if request.user_name:
            publicuser = UserPrefs.all().filter('name =',
                                                request.user_name).get()

            if not publicuser:
                raise endpoints.NotFoundException(USER_NOT_FOUND)

            query = Recipe.all()\
                            .filter('owner =', publicuser)
        else:
            query = Recipe.all()

        if request.order == apimessages.RecipeOrder.NAME:
            query = query.order('name')
        elif request.order == apimessages.RecipeOrder.CREATED:
            query = query.order('-created')
        elif request.order == apimessages.RecipeOrder.EDITED:
            query = query.order('-edited')
        elif request.order == apimessages.RecipeOrder.LIKES:
            query = query.order('-likes_count')

        recipes = query.fetch(limit, offset=offset)

        items = []
        for recipe in recipes:
            items.append(recipe_to_response(recipe))

        return apimessages.RecipeListResponse(**{'items': items})
Example #9
0
    def process(self, username=None, recipe_slug=None, brew_slug=None):
        publicuser = UserPrefs.all()\
                              .filter('name =', username)\
                              .get()

        if not publicuser:
            return [None, None, None]

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug = ', recipe_slug)\
                       .get()

        if not recipe:
            return [publicuser, None, None]

        if brew_slug is not None:
            brew = Brew.all()\
                       .filter('owner =', publicuser)\
                       .filter('recipe =', recipe)\
                       .filter('slug =', brew_slug)\
                       .get()

            if not brew:
                return [publicuser, recipe, None]
        else:
            brew = Brew()
            brew.owner = self.user
            brew.recipe = recipe

        return [publicuser, recipe, brew]
Example #10
0
 def get(self):
     """
     Render the public users list.
     """
     self.render('users.html', {
         'users': UserPrefs.all()
     })
Example #11
0
    def get(self, username=None):
        """
        Render the public recipe list for a user or all users.
        """
        show_owners = False
        if username:
            publicuser = UserPrefs.all().filter('name =', username).get()
            recipes = Recipe.all()\
                            .filter('owner =', publicuser)\
                            .order('-grade')
            def setowner(recipe):
                recipe.owner = publicuser
                return recipe
            recipes = map(setowner, recipes)
        else:
            publicuser = None
            recipes = Recipe.all().order('-grade')
            recipes = [r for r in recipes]
            show_owners = True

        self.render('recipes.html', {
            'publicuser': publicuser,
            'recipes': recipes,
            'show_owners': show_owners
        })
Example #12
0
    def get(self, username=None):
        """
        Render the public recipe list for a user or all users.
        """
        show_owners = False
        if username:
            publicuser = UserPrefs.all().filter('name =', username).get()
            recipes = Recipe.all()\
                            .filter('owner =', publicuser)\
                            .order('-grade')

            def setowner(recipe):
                recipe.owner = publicuser
                return recipe

            recipes = map(setowner, recipes)
        else:
            publicuser = None
            recipes = Recipe.all().order('-grade')
            recipes = [r for r in recipes]
            show_owners = True

        self.render(
            'recipes.html', {
                'publicuser': publicuser,
                'recipes': recipes,
                'show_owners': show_owners
            })
Example #13
0
def handle_error(request, response, exception, code, msg):
    """
    Render an error template with a code and message.
    """
    logging.exception(exception)

    # Try to get the current user
    session_store = sessions.get_store(request=request)
    session = session_store.get_session()
    auth_id = session.get('auth_id')
    user = None
    if auth_id:
        user = UserPrefs.get(auth_id)

    if user:
        logging.error('Currently logged in user is ' + user.name + ' (' +
                      user.email + ')')

    # Render and error template
    template = get_template('error.html')
    response.status = code
    response.out.write(
        template.render({
            'user': user,
            'code': code,
            'message': msg
        }))
Example #14
0
    def get(self, username, recipe_slug):
        publicuser = UserPrefs.all().filter('name = ', username).get()

        if publicuser:
            recipe = Recipe.all()\
                           .filter('owner =', publicuser)\
                           .filter('slug =', recipe_slug)\
                           .get()
        else:
            recipe = None

        width = 260
        try:
            width = int(self.request.get('width'))
        except:
            pass

        if publicuser and recipe:
            self.render('recipe-embed.html', {
                'publicuser': publicuser,
                'recipe': recipe,
                'width': width,
            })
        else:
            self.render('recipe-embed-404.html', {
                'publicuser': publicuser,
                'width': width,
            })
Example #15
0
    def get_recipes(self, request):
        """
        Get a list of recipes, optionally filtered by user name.
        """
        offset, limit = get_limits(request)

        if request.user_name:
            publicuser = UserPrefs.all().filter('name =', request.user_name).get()

            if not publicuser:
                raise endpoints.NotFoundException(USER_NOT_FOUND)

            query = Recipe.all()\
                            .filter('owner =', publicuser)
        else:
            query = Recipe.all()

        if request.order == apimessages.RecipeOrder.NAME:
            query = query.order('name')
        elif request.order == apimessages.RecipeOrder.CREATED:
            query = query.order('-created')
        elif request.order == apimessages.RecipeOrder.EDITED:
            query = query.order('-edited')
        elif request.order == apimessages.RecipeOrder.LIKES:
            query = query.order('-likes_count')

        recipes = query.fetch(limit, offset=offset)

        items = []
        for recipe in recipes:
            items.append(recipe_to_response(recipe))

        return apimessages.RecipeListResponse(**{
            'items': items
        })
Example #16
0
    def process(self, username=None, recipe_slug=None, brew_slug=None):
        publicuser = UserPrefs.all()\
                              .filter('name =', username)\
                              .get()

        if not publicuser:
            return [None, None, None]

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug = ', recipe_slug)\
                       .get()

        if not recipe:
            return [publicuser, None, None]

        if brew_slug is not None:
            brew = Brew.all()\
                       .filter('owner =', publicuser)\
                       .filter('recipe =', recipe)\
                       .filter('slug =', brew_slug)\
                       .get()

            if not brew:
                return [publicuser, recipe, None]
        else:
            brew = Brew()
            brew.owner = self.user
            brew.recipe = recipe

        return [publicuser, recipe, brew]
Example #17
0
    def get(self, username, recipe_slug):
        publicuser = UserPrefs.all().filter('name = ', username).get()

        if publicuser:
            recipe = Recipe.all()\
                           .filter('owner =', publicuser)\
                           .filter('slug =', recipe_slug)\
                           .get()
        else:
            recipe = None

        width = 260
        try:
            width = int(self.request.get('width'))
        except: pass

        if publicuser and recipe:
            self.render('recipe-embed.html', {
                'publicuser': publicuser,
                'recipe': recipe,
                'width': width,
            })
        else:
            self.render('recipe-embed-404.html', {
                'publicuser': publicuser,
                'width': width,
            })
Example #18
0
    def get(self, provider=None):
        """
        Handle login initiation via a known OAuth 2 provider.
        """
        if settings.DEBUG and provider == "dummy":
            # Handle test dev login
            auth_id = 'dummy:test'
            UserPrefs.create_or_update(auth_id, {
                'id': '1234',
                'name': 'Test User',
                'email': '*****@*****.**',
                'avatar': 'http://www.gravatar.com/avatar/55502f40dc8b7c769880b10874abc9d0?s={0}&d=identicon'
            }, {})

            # Update session
            self.session['auth_id'] = auth_id

            return self.redirect(self.session.pop('next', '/'))

        auth_url = AUTH_URLS[provider][0]

        key = getattr(settings, provider.upper() + '_OAUTH_KEY')
        scope = getattr(settings, provider.upper() + '_OAUTH_SCOPE')

        # Generate a random state parameter to prevent CSRF attacks
        # This is both stored in the session and sent to the authorizing
        # server, relayed back and then checked to make sure it matches
        # up. If not, then the request likely did not originate from
        # this site and it can be ignored.
        csrf_state = hashlib.md5(uuid.uuid4().hex).hexdigest()

        self.session['login_csrf'] = csrf_state

        params = {
            'response_type': 'code',
            'client_id': key,
            'redirect_uri': self.callback_url(provider),
            'state': csrf_state
        }

        if scope:
            params.update(scope=scope)

        target_url = auth_url.format(urlencode(params))
        logging.info('Redirecting user to %s', target_url)

        self.redirect(target_url)
Example #19
0
    def post(self, username=None, recipe_slug=None):
        publicuser = UserPrefs.all()\
                              .filter('name = ', username)\
                              .get()

        if not publicuser:
            self.render_json({'status': 'error', 'error': 'User not found'})
            return

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug =', recipe_slug)\
                       .get()

        if not recipe:
            self.render_json({'status': 'error', 'error': 'Recipe not found'})
            return

        new_recipe = Recipe(
            **{
                'owner': UserPrefs.get(),
                'cloned_from': recipe,
                'color': recipe.color,
                'ibu': recipe.ibu,
                'alcohol': recipe.alcohol,
                'name': recipe.name,
                'description': recipe.description,
                'type': recipe.type,
                'style': recipe.style,
                'batch_size': recipe.batch_size,
                'boil_size': recipe.boil_size,
                'bottling_temp': recipe.bottling_temp,
                'bottling_pressure': recipe.bottling_pressure,
                '_ingredients': recipe._ingredients
            })

        new_recipe.slug = generate_usable_slug(new_recipe)
        new_recipe.put()

        action = UserAction()
        action.owner = UserPrefs.get()
        action.type = action.TYPE_RECIPE_CLONED
        action.object_id = new_recipe.key().id()
        action.put()

        return self.render_json({'status': 'ok', 'redirect': new_recipe.url})
Example #20
0
 def object(self):
     if self.type in [self.TYPE_USER_FOLLOWED]:
         return UserPrefs.get_by_id(self.object_id)
     elif self.type in [
             self.TYPE_RECIPE_CREATED, self.TYPE_RECIPE_EDITED,
             self.TYPE_RECIPE_CLONED, self.TYPE_RECIPE_LIKED
     ]:
         from models.recipe import Recipe
         return Recipe.get_by_id(self.object_id)
Example #21
0
 def object(self):
     if self.type in [self.TYPE_USER_FOLLOWED]:
         return UserPrefs.get_by_id(self.object_id)
     elif self.type in [self.TYPE_RECIPE_CREATED,
                        self.TYPE_RECIPE_EDITED,
                        self.TYPE_RECIPE_CLONED,
                        self.TYPE_RECIPE_LIKED]:
         from models.recipe import Recipe
         return Recipe.get_by_id(self.object_id)
Example #22
0
    def get_user(self, request):
        """
        Get a user by name.
        """
        publicuser = UserPrefs.all().filter('name =', request.user_name).get()

        if not publicuser:
            raise endpoints.NotFoundException(USER_NOT_FOUND)

        return user_to_response(publicuser)
Example #23
0
    def get_user(self, request):
        """
        Get a user by name.
        """
        publicuser = UserPrefs.all().filter('name =', request.user_name).get()

        if not publicuser:
            raise endpoints.NotFoundException(USER_NOT_FOUND)

        return user_to_response(publicuser)
Example #24
0
    def get(self, username=None, recipe_slug=None, version=None):
        """
        Render the recipe view. If no slug is given then create a new recipe
        and render it in edit mode.
        """
        # Create a new recipe if we have no slug, otherwise query
        if not recipe_slug:
            publicuser = self.user
            recipe = Recipe()
            recipe.owner = publicuser
            recipe.new = True
        else:
            publicuser = UserPrefs.all().filter('name =', username).get()

            if not publicuser:
                self.abort(404)

            recipe = Recipe.all()\
                           .filter('owner =', publicuser)\
                           .filter('slug =', recipe_slug)\
                           .get()

            if not recipe:
                self.abort(404)

            if version:
                try:
                    version = int(version)
                except:
                    self.abort(404)

                history = RecipeHistory.get_by_id(version, recipe)

                if not history:
                    self.abort(404)

                recipe.old = True
                recipe.oldname = history.name
                recipe.description = history.description
                recipe.type = history.type
                recipe.category = history.category
                recipe.style = history.style
                recipe.batch_size = history.batch_size
                recipe.boil_size = history.boil_size
                recipe.bottling_temp = history.bottling_temp
                recipe.bottling_pressure = history.bottling_pressure
                recipe._ingredients = history._ingredients

        cloned_from = None
        try:
            cloned_from = recipe.cloned_from
        except Exception, e:
            pass
Example #25
0
    def get(self, username=None, recipe_slug=None, version=None):
        """
        Render the recipe view. If no slug is given then create a new recipe
        and render it in edit mode.
        """
        # Create a new recipe if we have no slug, otherwise query
        if not recipe_slug:
            publicuser = self.user
            recipe = Recipe()
            recipe.owner = publicuser
            recipe.new = True
        else:
            publicuser = UserPrefs.all().filter('name =', username).get()

            if not publicuser:
                self.abort(404)

            recipe = Recipe.all()\
                           .filter('owner =', publicuser)\
                           .filter('slug =', recipe_slug)\
                           .get()

            if not recipe:
                self.abort(404)

            if version:
                try:
                    version = int(version)
                except:
                    self.abort(404)

                history = RecipeHistory.get_by_id(version, recipe)

                if not history:
                    self.abort(404)

                recipe.old = True
                recipe.oldname = history.name
                recipe.description = history.description
                recipe.type = history.type
                recipe.category = history.category
                recipe.style = history.style
                recipe.batch_size = history.batch_size
                recipe.boil_size = history.boil_size
                recipe.bottling_temp = history.bottling_temp
                recipe.bottling_pressure = history.bottling_pressure
                recipe._ingredients = history._ingredients

        cloned_from = None
        try:
            cloned_from = recipe.cloned_from
        except Exception, e:
            pass
Example #26
0
    def get(self):
        """
        Render the public users list.
        """
        # Try to get users list from memcache instead of datastore
        users_html = memcache.get('users-content')
        if not users_html or settings.DEBUG:
            users_html = self.render('users-content.html',
                                     {'users': UserPrefs.all()},
                                     write_to_stream=False)
            memcache.set('users-content', users_html, self.CACHE_TIME)

        self.render('users.html', {'users_content': users_html})
Example #27
0
    def user(self):
        """Returns currently logged in user"""
        user = None
        auth_id = self.session.get('auth_id')

        # Do we have user session info set by auth handler?
        if auth_id:
            user = UserPrefs.get(auth_id)

            if not user:
                del self.session['auth_id']

        return user
Example #28
0
    def get(self, username, recipe_slug):
        publicuser = UserPrefs.all().filter('name = ', username).get()
        if not publicuser:
            self.abort(404)

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug =', recipe_slug)\
                       .get()

        if not recipe:
            self.abort(404)

        self.render_xml(recipe.beerxml)
Example #29
0
    def get(self, username, recipe_slug):
        publicuser = UserPrefs.all().filter('name = ', username).get()
        if not publicuser:
            self.abort(404)

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug =', recipe_slug)\
                       .get()

        if not recipe:
            self.abort(404)

        self.render_xml(recipe.beerxml)
Example #30
0
    def get(self):
        """
        Render the public users list.
        """
        # Try to get users list from memcache instead of datastore
        users_html = memcache.get('users-content')
        if not users_html or settings.DEBUG:
            users_html = self.render('users-content.html', {
                'users': UserPrefs.all()
            }, write_to_stream=False)
            memcache.set('users-content', users_html, self.CACHE_TIME)

        self.render('users.html', {
            'users_content': users_html
        })
Example #31
0
    def get(self):
        """
        Render the donation page. This includes a form to process a credit
        card and show the top donating users.
        """
        # Get a list of the top 10 users who donated
        top_users = UserPrefs.all()\
                             .filter('donated >', 0)\
                             .order('-donated')\
                             .fetch(10)

        self.render('donate.html', {
            'STRIPE_PUBLIC_KEY': settings.STRIPE_PUBLIC_KEY,
            'top_users': top_users,
            'success': self.request.get('success')
        })
Example #32
0
def handle_error(request, response, exception, code, msg):
    """
    Render an error template with a code and message.
    """
    logging.exception(exception)

    # Try to get the current user
    session_store = sessions.get_store(request=request)
    session = session_store.get_session()
    auth_id = session.get("auth_id")
    user = None
    if auth_id:
        user = UserPrefs.get(auth_id)

    # Render and error template
    template = get_template("error.html")
    response.out.write(template.render({"user": user, "code": code, "message": msg}))
Example #33
0
    def get_recipe(self, request):
        """
        Get a recipe by user name and recipe slug.
        """
        publicuser = UserPrefs.all().filter('name =', request.user_name).get()

        if not publicuser:
            raise endpoints.NotFoundException(USER_NOT_FOUND)

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug =', request.slug)\
                       .get()

        if not recipe:
            raise endpoints.NotFoundException(RECIPE_NOT_FOUND)

        return recipe_to_response(recipe)
Example #34
0
    def get_recipe(self, request):
        """
        Get a recipe by user name and recipe slug.
        """
        publicuser = UserPrefs.all().filter('name =', request.user_name).get()

        if not publicuser:
            raise endpoints.NotFoundException(USER_NOT_FOUND)

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug =', request.slug)\
                       .get()

        if not recipe:
            raise endpoints.NotFoundException(RECIPE_NOT_FOUND)

        return recipe_to_response(recipe)
Example #35
0
    def get_users(self, request):
        """
        Get a list of users.
        """
        offset, limit = get_limits(request)

        query = UserPrefs.all()

        if request.order == apimessages.UserOrder.NAME:
            query = query.order('name')
        elif request.order == apimessages.UserOrder.JOINED:
            query = query.order('-joined')

        users = query.fetch(limit, offset=offset)

        items = []
        for user in users:
            items.append(user_to_response(user))

        return apimessages.UserListResponse(**{'items': items})
Example #36
0
    def post(self):
        """
        Import a new recipe or list of recipes from BeerXML to the
        currently logged in user's account.
        """
        user = UserPrefs.get()
        recipesxml = self.request.POST['file'].value

        for recipe in Recipe.new_from_beerxml(recipesxml):
            recipe.owner = user
            recipe.slug = generate_usable_slug(recipe)
            recipe.update_cache();
            key = recipe.put()

            action = UserAction()
            action.owner = user
            action.object_id = key.id()
            action.type = action.TYPE_RECIPE_CREATED
            action.put()

        self.redirect('/users/' + user.name + '/recipes')
Example #37
0
    def post(self):
        """
        Import a new recipe or list of recipes from BeerXML to the
        currently logged in user's account.
        """
        user = UserPrefs.get()
        recipesxml = self.request.POST['file'].value

        for recipe in Recipe.new_from_beerxml(recipesxml):
            recipe.owner = user
            recipe.slug = generate_usable_slug(recipe)
            recipe.update_cache()
            key = recipe.put()

            action = UserAction()
            action.owner = user
            action.object_id = key.id()
            action.type = action.TYPE_RECIPE_CREATED
            action.put()

        self.redirect('/users/' + user.name + '/recipes')
Example #38
0
    def get_users(self, request):
        """
        Get a list of users.
        """
        offset, limit = get_limits(request)

        query = UserPrefs.all()

        if request.order == apimessages.UserOrder.NAME:
            query = query.order('name')
        elif request.order == apimessages.UserOrder.JOINED:
            query = query.order('-joined')

        users = query.fetch(limit, offset=offset)

        items = []
        for user in users:
            items.append(user_to_response(user))

        return apimessages.UserListResponse(**{
            'items': items
        })
Example #39
0
    def post(self):
        """
        Send a new message to a user.
        """
        user = self.user

        if not user:
            return render_json(self, {
                'status': 'error',
                'error': 'User not logged in'
            })

        recipient_name = cgi.escape(self.request.get('recipient'))
        body = cgi.escape(self.request.get('body'))

        recipient = UserPrefs.all()\
                             .filter('name =', recipient_name)\
                             .get()

        if not recipient:
            return render_json(
                self, {
                    'status': 'error',
                    'error':
                    'Recipient %(recipient_name)s not found' % locals()
                })

        msg = Message()
        msg.user_from = user
        msg.user_to = recipient
        msg.body = body
        msg.put()

        # TODO: put this into a transaction
        recipient.unread_messages += 1
        recipient.put()

        self.render_json({'status': 'ok'})
Example #40
0
    def post(self):
        """
        Send a new message to a user.
        """
        user = self.user

        if not user:
            return render_json(self, {
                'status': 'error',
                'error': 'User not logged in'
            })

        recipient_name = cgi.escape(self.request.get('recipient'))
        body = cgi.escape(self.request.get('body'))

        recipient = UserPrefs.all()\
                             .filter('name =', recipient_name)\
                             .get()

        if not recipient:
            return render_json(self, {
                'status': 'error',
                'error': 'Recipient %(recipient_name)s not found' % locals()
            })

        msg = Message()
        msg.user_from = user
        msg.user_to = recipient
        msg.body = body
        msg.put()

        # TODO: put this into a transaction
        recipient.unread_messages += 1
        recipient.put()

        self.render_json({
            'status': 'ok'
        })
Example #41
0
    def get(self, username=None, recipe_slug=None):
        """
        Render the basic recipe history list for the given recipe.
        """
        if not username or not recipe_slug:
            self.abort(404)

        publicuser = UserPrefs.all().filter('name =', username).get()
        if not publicuser:
            self.abort(404)

        recipe = Recipe.all()\
                       .filter('slug = ', recipe_slug)\
                       .filter('owner =', publicuser)\
                       .get()
        if not recipe:
            self.abort(404)

        history = RecipeHistory.all()\
                        .ancestor(recipe)\
                        .order('-created')\
                        .fetch(20)

        # The list of entries we'll use to populate the template along with
        # the current recipe as the first entry
        entries = [{
            'recipe': recipe,
            'edited': recipe.edited,
            'slug': recipe.slug,
            'customtag': 'Most Recent',
            'show_snippet': True
        }]

        # Check if there is any history to diff with
        if len(history) > 0:
            entries[0]['differences'] = self.delete_ignored_keys(
                recipe.diff(history[0]))
        else:
            entries[0]['first'] = True

        # Get a list of differences in the history. Use reduce with a function
        # that returns the right operand to simply find pairwise differences.
        differences = []

        def diff(left, right):
            differences.append(left.diff(right))
            return right

        # Make sure reduce isn't called with no history (throws exception)
        reduce(diff, history) if len(history) > 0 else None

        # Start going through the history looking at differences to decide how
        # we plan on displaying the info to the user (using a snippet or not)
        for i in range(len(differences)):
            # Make sure this entry has differences before further processing
            if self.is_empty(differences[i]):
                continue

            # Set some required properties for the snippet to work
            history[i].owner = publicuser
            history[i].slug = recipe.slug + '/history/' + str(
                history[i].key().id())

            # Create the entry
            entry = {
                'recipe': history[i],
                'differences': self.delete_ignored_keys(differences[i]),
                'edited': history[i].created,
                'slug': history[i].slug,
                'show_snippet': False
            }

            # Check if the name or description changed and we should show
            # a snippet
            for snippetItem in RecipeHistoryHandler.SNIPPET_ITEMS:
                if snippetItem in differences[i][2]:
                    entry['show_snippet'] = True
                    # Make sure the color, ibu, and alcohol were created
                    if not hasattr(history[i], 'color'):
                        history[i].update_cache()
                    break

            # Save the entry
            entries.append(entry)

        # Add the final entry only if it's the original recipe, otherwise it
        # will be a version that should have diffs but we didn't generate any.
        if len(history) > 0:
            last = history[-1]
            delta = timedelta(seconds=1)
            if recipe.created - delta < last.created < recipe.created + delta:
                last.owner = publicuser
                last.slug = recipe.slug + '/history/' + str(last.key().id())
                if not hasattr(last, 'color'):
                    last.update_cache()
                entries.append({
                    'recipe': last,
                    'edited': last.created,
                    'slug': last.slug,
                    'customtag': 'Original',
                    'first': True
                })

        # Perform a second pass in reverse to check for large changes that
        # should show up as a snippet but were missed in the pairwise
        # checking above.
        entries[-1]['show_snippet'] = True
        for i in range(len(entries) - 1, 0, -1):
            # Check if the snippet is already showing
            if entries[i]['show_snippet']:
                last_snippet = entries[i]['recipe']
                continue

            # Check if the name, description, color, ibu, or alcohol changed
            # and we should show a snippet
            for snippetItem in RecipeHistoryHandler.IGNORED_KEYS:
                if not hasattr(entries[i]['recipe'], snippetItem):
                    continue

                attr = getattr(entries[i]['recipe'], snippetItem)
                if type(attr) != int and type(attr) != float:
                    continue

                # See if the change was more than 10%, then show the
                # snippet, else show the orb
                try:
                    change = float(attr) / getattr(last_snippet, snippetItem)
                except:
                    change = 2
                logging.info(snippetItem)
                logging.info(change)
                if change < 0.9 or change > 1.1:
                    last_snippet = entries[i]['recipe']
                    entries[i]['show_snippet'] = True
                    break

        # Stop the template from performing another query for the username
        # when it tries to render the recipe
        recipe.owner = publicuser

        self.render('recipe-history.html', {
            'publicuser': publicuser,
            'recipe': recipe,
            'entries': entries
        })
Example #42
0
    def get(self):
        """
        Render the index page. Currently this renders a 'Coming soon' landing
        page that will eventually be replaced with a proper home page.
        """
        user = self.user

        if user:
            # Try to get rendered output from memcache
            rendered = memcache.get('dashboard-' + user.user_id)
            if rendered and not settings.DEBUG:
                return self.response.out.write(rendered)

            # Fetch following users
            following = user.following_users\
                            .order('name')\
                            .fetch(100)

            user_keys = [user.key()] + [u.key() for u in following]

            # Start async fetch of top recipes
            top_recipes = Recipe.all()\
                                .filter('owner IN', user_keys)\
                                .order('-likes_count')\
                                .run(limit=15)

            # Get and process interesting events
            interesting_events = UserAction.all()\
                                           .filter('owner IN', user_keys)\
                                           .order('-created')\
                                           .fetch(15)

            object_ids = UserAction.gather_object_ids(interesting_events)
            object_ids['users'] = [
                id for id in object_ids['users']
                if id not in [user.key().id()] + user.following
            ]

            # Start async fetch of relevant recipes
            recipes = db.get_async(
                [Key.from_path('Recipe', id) for id in object_ids['recipes']])

            # Convert iterators to  lists of items in memory and setup a map
            # of user id -> user for easy lookups
            following = list(following)
            top_recipes = list(top_recipes)

            user_map = {user.key().id(): user}

            for u in following:
                user_map[u.key().id()] = u

            if object_ids['users']:
                for u in UserPrefs.get_by_id(object_ids['users']):
                    user_map[u.key().id()] = u

            # Setup a map of recipe id -> recipe for easy lookups
            recipe_map = {}

            for r in recipes.get_result():
                recipe_map[r.key().id()] = r

            # Render and cache for 1 minute
            memcache.set(
                'dashboard-' + user.user_id,
                self.render(
                    'dashboard.html', {
                        'following': following,
                        'user_map': user_map,
                        'recipe_map': recipe_map,
                        'top_recipes': top_recipes,
                        'interesting_events': interesting_events
                    }), 60)
        else:
            # Try to get rendered output from memcache
            rendered = memcache.get('index')
            if rendered and not settings.DEBUG:
                return self.response.out.write(rendered)

            recipes = Recipe.all()\
                            .order('-likes_count')\
                            .run(limit=15)

            # Render and cache for 15 minutes
            memcache.set('index',
                         self.render('index.html', {'recipes': recipes}), 900)
Example #43
0
    def get(self, provider):
        # Did we get an error?
        error = self.request.get('error')
        if error:
            raise AuthProviderResponseError(error, provider)

        # At this point the user is successfully logged in, but we need to
        # get an access token in order to call the API which gets user
        # information like id, name, avatar, email, etc. Then we need
        # to actually call that API, and use the response to create a
        # user object within our data store.

        # Get the access code so we can exchange it for a token
        code = self.request.get('code')

        # Get the CSRF state
        state = self.request.get('state')

        if self.session['login_csrf'] != state:
            logging.warning("Login aborted due to possible CSRF!")
            return self.abort()

        # Get the access token using the access code
        key = getattr(settings, provider.upper() + '_OAUTH_KEY')
        secret = getattr(settings, provider.upper() + '_OAUTH_SECRET')

        payload = {
            'code': code,
            'client_id': key,
            'client_secret': secret,
            'redirect_uri': self.callback_url(provider),
            'grant_type': 'authorization_code'
        }

        resp = urlfetch.fetch(
            url=AUTH_URLS[provider][1],
            payload=urlencode(payload),
            method=urlfetch.POST,
            headers={'Content-Type': 'application/x-www-form-urlencoded'})

        if provider in ['google', 'windows_live']:
            auth_info = json.loads(resp.content)
        elif provider in ['facebook']:
            auth_info = dict(urlparse.parse_qsl(resp.content))
        else:
            raise NotImplementedError(
                'Not sure how to parse access token response for ' + provider)

        logging.info(str(auth_info))

        # Use access token to make an API call to get user info
        user_info = getattr(self, '_' + provider + '_user_info')(auth_info)

        # Create or update a user object in the data store
        auth_id = provider + ":" + str(user_info['id'])

        UserPrefs.create_or_update(auth_id, user_info, auth_info)

        # Update session
        self.session['auth_id'] = auth_id

        self.redirect(self.session.pop('next', '/dashboard'))
Example #44
0
    def get(self, provider):
        # Did we get an error?
        error = self.request.get('error')
        if error:
            raise AuthProviderResponseError(error, provider)

        # At this point the user is successfully logged in, but we need to
        # get an access token in order to call the API which gets user
        # information like id, name, avatar, email, etc. Then we need
        # to actually call that API, and use the response to create a
        # user object within our data store.

        # Get the access code so we can exchange it for a token
        code = self.request.get('code')

        # Get the CSRF state
        state = self.request.get('state')

        if self.session['login_csrf'] != state:
            logging.warning("Login aborted due to possible CSRF!")
            return self.abort()

        # Get the access token using the access code
        key = getattr(settings, provider.upper() + '_OAUTH_KEY')
        secret = getattr(settings, provider.upper() + '_OAUTH_SECRET')

        payload = {
            'code': code,
            'client_id': key,
            'client_secret': secret,
            'redirect_uri': self.callback_url(provider),
            'grant_type': 'authorization_code'
        }

        resp = urlfetch.fetch(
            url=AUTH_URLS[provider][1],
            payload=urlencode(payload),
            method=urlfetch.POST,
            headers={'Content-Type': 'application/x-www-form-urlencoded'}
        )

        if provider in ['google', 'windows_live']:
            auth_info = json.loads(resp.content)
        elif provider in ['facebook']:
            auth_info = dict(urlparse.parse_qsl(resp.content))
        else:
            raise NotImplementedError('Not sure how to parse access token response for ' + provider)

        logging.info(str(auth_info))

        # Use access token to make an API call to get user info
        user_info = getattr(self, '_' + provider + '_user_info')(auth_info)

        # Create or update a user object in the data store
        auth_id = provider + ":" + str(user_info['id'])

        UserPrefs.create_or_update(auth_id, user_info, auth_info)

        # Update session
        self.session['auth_id'] = auth_id

        self.redirect(self.session.pop('next', '/dashboard'))
Example #45
0
    def get(self):
        """
        Render the index page. Currently this renders a 'Coming soon' landing
        page that will eventually be replaced with a proper home page.
        """
        user = self.user

        if user:
            # Try to get rendered output from memcache
            rendered = memcache.get('dashboard-' + user.user_id)
            if rendered and not settings.DEBUG:
                return self.response.out.write(rendered)

            # Fetch following users
            following = user.following_users\
                            .order('name')\
                            .fetch(100)

            user_keys = [user.key()] + [u.key() for u in following]

            # Start async fetch of top recipes
            top_recipes = Recipe.all()\
                                .filter('owner IN', user_keys)\
                                .order('-likes_count')\
                                .run(limit=15)

            # Get and process interesting events
            interesting_events = UserAction.all()\
                                           .filter('owner IN', user_keys)\
                                           .order('-created')\
                                           .fetch(15)

            object_ids = UserAction.gather_object_ids(interesting_events)
            object_ids['users'] = [id for id in object_ids['users'] if id not in [user.key().id()] + user.following]

            # Start async fetch of relevant recipes
            recipes = db.get_async([Key.from_path('Recipe', id) for id in object_ids['recipes']])

            # Convert iterators to  lists of items in memory and setup a map
            # of user id -> user for easy lookups
            following = list(following)
            top_recipes = list(top_recipes)

            user_map = {
                user.key().id(): user
            }

            for u in following:
                user_map[u.key().id()] = u

            if object_ids['users']:
                for u in UserPrefs.get_by_id(object_ids['users']):
                    user_map[u.key().id()] = u

            # Setup a map of recipe id -> recipe for easy lookups
            recipe_map = {}

            for r in recipes.get_result():
                recipe_map[r.key().id()] = r

            # Render and cache for 1 minute
            memcache.set('dashboard-' + user.user_id, self.render('dashboard.html', {
                'following': following,
                'user_map': user_map,
                'recipe_map': recipe_map,
                'top_recipes': top_recipes,
                'interesting_events': interesting_events
            }), 60)
        else:
            # Try to get rendered output from memcache
            rendered = memcache.get('index')
            if rendered and not settings.DEBUG:
                return self.response.out.write(rendered)

            recipes = Recipe.all()\
                            .order('-likes_count')\
                            .run(limit=15)

            # Render and cache for 15 minutes
            memcache.set('index', self.render('index.html', {
                'recipes': recipes
            }), 900)
Example #46
0
    def get(self):
        user = self.user

        # Try to get rendered output from memcache
        rendered = memcache.get('dashboard-' + user.user_id)
        if rendered and not settings.DEBUG:
            return self.response.out.write(rendered)

        # Fetch following users
        following = user.following_users\
                        .order('name')\
                        .fetch(100)

        user_keys = [user.key()] + [u.key() for u in following]

        # Start async fetch of top recipes
        top_recipes = Recipe.all()\
                            .filter('owner IN', user_keys)\
                            .order('-grade')\
                            .run(limit=15)

        # Get and process interesting events
        interesting_events = UserAction.all()\
                                       .filter('owner IN', user_keys)\
                                       .order('-created')\
                                       .fetch(15)

        object_ids = UserAction.gather_object_ids(interesting_events)
        object_ids['users'] = [id for id in object_ids['users'] if id not in [user.key().id()] + user.following]

        # Start async fetch of relevant recipes
        recipes = db.get_async([Key.from_path('Recipe', id) for id in object_ids['recipes']])

        # Start async fetch of relevant brews
        brews = db.get_async([Key.from_path('Brew', id) for id in object_ids['brews']])

        # Convert iterators to  lists of items in memory and setup a map
        # of user id -> user for easy lookups
        following = list(following)
        top_recipes = list(top_recipes)

        user_map = {
            user.key().id(): user
        }

        for u in following:
            user_map[u.key().id()] = u

        if object_ids['users']:
            for u in UserPrefs.get_by_id(object_ids['users']):
                user_map[u.key().id()] = u

        # Setup a map of brew id -> brew for easy lookups
        brew_map = {}
        brew_recipe_ids = set()

        for b in brews.get_result():
            brew_recipe_ids.add(b.recipe_key.id())
            brew_map[b.key().id()] = b

        # Async fetch of any recipes brews reference that weren't
        # included in the recipe fetch above...
        brew_recipes = db.get_async([Key.from_path('Recipe', id) for id in brew_recipe_ids if id not in object_ids['recipes']])

        # Setup a map of recipe id -> recipe for easy lookups
        recipe_map = {}

        for r in recipes.get_result():
            recipe_map[r.key().id()] = r

        for r in brew_recipes.get_result():
            recipe_map[r.key().id()] = r

        # Render and cache for 1 minute
        memcache.set('dashboard-' + user.user_id, self.render('dashboard.html', {
            'following': following,
            'user_map': user_map,
            'recipe_map': recipe_map,
            'brew_map': brew_map,
            'top_recipes': top_recipes,
            'interesting_events': interesting_events
        }), self.CACHE_TIME)
Example #47
0
    def process(self, action, username=None, recipe_slug=None):
        """
        Process a request to add or remove a user from the liked list.
        """
        user = UserPrefs.get()
        publicuser = UserPrefs.all()\
                              .filter('name = ', username)\
                              .get()

        if not publicuser:
            self.render_json({
                'status': 'error',
                'error': 'User not found'
            })
            return

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug =', recipe_slug)\
                       .get()

        if not recipe:
            self.render_json({
                'status': 'error',
                'error': 'Recipe not found'
            })
            return

        if action == 'post':
            if user.user_id not in recipe.likes:
                recipe.likes.append(user.user_id)
                recipe.put()

                existing = UserAction.all()\
                                     .filter('owner =', user)\
                                     .filter('type =', UserAction.TYPE_RECIPE_LIKED)\
                                     .filter('object_id =', recipe.key().id())\
                                     .count()

                if not existing:
                    user_action = UserAction()
                    user_action.owner = user
                    user_action.type = user_action.TYPE_RECIPE_LIKED
                    user_action.object_id = recipe.key().id()
                    user_action.put()

        elif action == 'delete':
            if user.user_id in recipe.likes:
                recipe.likes.remove(user.user_id)
                recipe.put()

                existing = UserAction.all()\
                                     .filter('owner =', user)\
                                     .filter('type =', UserAction.TYPE_RECIPE_LIKED)\
                                     .filter('object_id =', recipe.key().id())\
                                     .get()

                if existing:
                    existing.delete()

        return self.render_json({
            'status': 'ok',
            'likes': len(recipe.likes)
        })
Example #48
0
    def get(self):
        user = self.user

        # Try to get rendered output from memcache
        rendered = memcache.get('dashboard-' + user.user_id)
        if rendered and not settings.DEBUG:
            return self.response.out.write(rendered)

        # Fetch following users
        following = user.following_users\
                        .order('name')\
                        .fetch(100)

        user_keys = [user.key()] + [u.key() for u in following]

        # Start async fetch of top recipes
        top_recipes = Recipe.all()\
                            .filter('owner IN', user_keys)\
                            .order('-grade')\
                            .run(limit=15)

        # Get and process interesting events
        interesting_events = UserAction.all()\
                                       .filter('owner IN', user_keys)\
                                       .order('-created')\
                                       .fetch(15)

        object_ids = UserAction.gather_object_ids(interesting_events)
        object_ids['users'] = [
            id for id in object_ids['users']
            if id not in [user.key().id()] + user.following
        ]

        # Start async fetch of relevant recipes
        recipes = db.get_async(
            [Key.from_path('Recipe', id) for id in object_ids['recipes']])

        # Start async fetch of relevant brews
        brews = db.get_async(
            [Key.from_path('Brew', id) for id in object_ids['brews']])

        # Convert iterators to  lists of items in memory and setup a map
        # of user id -> user for easy lookups
        following = list(following)
        top_recipes = list(top_recipes)

        user_map = {user.key().id(): user}

        for u in following:
            user_map[u.key().id()] = u

        if object_ids['users']:
            for u in UserPrefs.get_by_id(object_ids['users']):
                user_map[u.key().id()] = u

        # Setup a map of brew id -> brew for easy lookups
        brew_map = {}
        brew_recipe_ids = set()

        for b in brews.get_result():
            brew_recipe_ids.add(b.recipe_key.id())
            brew_map[b.key().id()] = b

        # Async fetch of any recipes brews reference that weren't
        # included in the recipe fetch above...
        brew_recipes = db.get_async([
            Key.from_path('Recipe', id) for id in brew_recipe_ids
            if id not in object_ids['recipes']
        ])

        # Setup a map of recipe id -> recipe for easy lookups
        recipe_map = {}

        for r in recipes.get_result():
            recipe_map[r.key().id()] = r

        for r in brew_recipes.get_result():
            recipe_map[r.key().id()] = r

        # Render and cache for 1 minute
        memcache.set(
            'dashboard-' + user.user_id,
            self.render(
                'dashboard.html', {
                    'following': following,
                    'user_map': user_map,
                    'recipe_map': recipe_map,
                    'brew_map': brew_map,
                    'top_recipes': top_recipes,
                    'interesting_events': interesting_events
                }), self.CACHE_TIME)
Example #49
0
    def post(self, username=None, recipe_slug=None):
        publicuser = UserPrefs.all()\
                              .filter('name = ', username)\
                              .get()

        if not publicuser:
            self.render_json({'status': 'error', 'error': 'User not found'})
            return

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug =', recipe_slug)\
                       .get()

        if not recipe:
            self.render_json({'status': 'error', 'error': 'Recipe not found'})
            return

        new_recipe = Recipe(
            **{
                'owner': self.user,
                'cloned_from': recipe,
                'color': recipe.color,
                'ibu': recipe.ibu,
                'alcohol': recipe.alcohol,
                'name': recipe.name,
                'description': recipe.description,
                'type': recipe.type,
                'category': recipe.category,
                'style': recipe.style,
                'batch_size': recipe.batch_size,
                'boil_size': recipe.boil_size,
                'bottling_temp': recipe.bottling_temp,
                'bottling_pressure': recipe.bottling_pressure,
                'steep_efficiency': recipe.steep_efficiency,
                'mash_efficiency': recipe.mash_efficiency,
                'primary_days': recipe.primary_days,
                'primary_temp': recipe.primary_temp,
                'secondary_days': recipe.secondary_days,
                'secondary_temp': recipe.secondary_temp,
                'tertiary_days': recipe.tertiary_days,
                'tertiary_temp': recipe.tertiary_temp,
                'aging_days': recipe.aging_days,
                '_ingredients': recipe._ingredients
            })

        new_recipe.slug = generate_usable_slug(new_recipe)
        new_recipe.put()
        new_recipe.update_grade()
        new_recipe.put()

        # Update recipe ranking for sorting
        recipe.update_grade()
        recipe.put()

        action = UserAction()
        action.owner = self.user
        action.type = action.TYPE_RECIPE_CLONED
        action.object_id = new_recipe.key().id()
        action.put()

        return self.render_json({'status': 'ok', 'redirect': new_recipe.url})
Example #50
0
    def get(self, username=None, recipe_slug=None):
        """
        Render the basic recipe history list for the given recipe.
        """
        if not username or not recipe_slug:
            self.abort(404)

        publicuser = UserPrefs.all().filter('name =', username).get()
        if not publicuser:
            self.abort(404)

        recipe = Recipe.all()\
                       .filter('slug = ', recipe_slug)\
                       .filter('owner =', publicuser)\
                       .get()
        if not recipe:
            self.abort(404)

        history = RecipeHistory.all()\
                        .ancestor(recipe)\
                        .order('-created')\
                        .fetch(20)

        # The list of entries we'll use to populate the template along with
        # the current recipe as the first entry
        entries = [{
            'recipe': recipe,
            'edited': recipe.edited,
            'slug': recipe.slug,
            'customtag': 'Most Recent',
            'show_snippet': True
        }]

        # Check if there is any history to diff with
        if len(history) > 0:
            entries[0]['differences'] = self.delete_ignored_keys(recipe.diff(history[0]))
        else:
            entries[0]['first'] = True

        # Get a list of differences in the history. Use reduce with a function
        # that returns the right operand to simply find pairwise differences.
        differences = []
        def diff(left, right):
            differences.append(left.diff(right))
            return right
        # Make sure reduce isn't called with no history (throws exception)
        reduce(diff, history) if len(history) > 0 else None

        # Start going through the history looking at differences to decide how
        # we plan on displaying the info to the user (using a snippet or not)
        for i in range(len(differences)):
            # Make sure this entry has differences before further processing
            if self.is_empty(differences[i]):
                continue

            # Set some required properties for the snippet to work
            history[i].owner = publicuser
            history[i].slug = recipe.slug + '/history/' + str(history[i].key().id())

            # Create the entry
            entry = {
                'recipe': history[i],
                'differences': self.delete_ignored_keys(differences[i]),
                'edited': history[i].created,
                'slug': history[i].slug,
                'show_snippet': False
            }

            # Check if the name or description changed and we should show
            # a snippet
            for snippetItem in RecipeHistoryHandler.SNIPPET_ITEMS:
                if snippetItem in differences[i][2]:
                    entry['show_snippet'] = True
                    # Make sure the color, ibu, and alcohol were created
                    if not hasattr(history[i], 'color'):
                        history[i].update_cache()
                    break

            # Save the entry
            entries.append(entry)

        # Add the final entry only if it's the original recipe, otherwise it
        # will be a version that should have diffs but we didn't generate any.
        if len(history) > 0:
            last = history[-1]
            delta = timedelta(seconds=1)
            if recipe.created - delta < last.created < recipe.created + delta:
                last.owner = publicuser
                last.slug = recipe.slug + '/history/' + str(last.key().id())
                if not hasattr(last, 'color'):
                    last.update_cache()
                entries.append({
                    'recipe': last,
                    'edited': last.created,
                    'slug': last.slug,
                    'customtag': 'Original',
                    'first': True
                })

        # Perform a second pass in reverse to check for large changes that
        # should show up as a snippet but were missed in the pairwise
        # checking above.
        entries[-1]['show_snippet'] = True
        for i in range(len(entries) - 1, 0, -1):
            # Check if the snippet is already showing
            if entries[i]['show_snippet']:
                last_snippet = entries[i]['recipe']
                continue

            # Check if the name, description, color, ibu, or alcohol changed
            # and we should show a snippet
            for snippetItem in RecipeHistoryHandler.IGNORED_KEYS:
                if not hasattr(entries[i]['recipe'], snippetItem):
                    continue 
                
                attr = getattr(entries[i]['recipe'], snippetItem)
                if type(attr) != int and type(attr) != float:
                    continue

                # See if the change was more than 10%, then show the
                # snippet, else show the orb
                try:
                    change = float(attr) / getattr(last_snippet, snippetItem)
                except:
                    change = 2
                logging.info(snippetItem)
                logging.info(change)
                if change < 0.9 or change > 1.1:
                    last_snippet = entries[i]['recipe']
                    entries[i]['show_snippet'] = True
                    break

        # Stop the template from performing another query for the username
        # when it tries to render the recipe
        recipe.owner = publicuser

        self.render('recipe-history.html', {
            'publicuser': publicuser,
            'recipe': recipe,
            'entries': entries
        })
Example #51
0
    def post(self, username=None, recipe_slug=None):
        publicuser = UserPrefs.all()\
                              .filter('name = ', username)\
                              .get()

        if not publicuser:
            self.render_json({
                'status': 'error',
                'error': 'User not found'
            })
            return

        recipe = Recipe.all()\
                       .filter('owner =', publicuser)\
                       .filter('slug =', recipe_slug)\
                       .get()

        if not recipe:
            self.render_json({
                'status': 'error',
                'error': 'Recipe not found'
            })
            return

        new_recipe = Recipe(**{
            'owner': self.user,
            'cloned_from': recipe,
            'color': recipe.color,
            'ibu': recipe.ibu,
            'alcohol': recipe.alcohol,
            'name': recipe.name,
            'description': recipe.description,
            'type': recipe.type,
            'category': recipe.category,
            'style': recipe.style,
            'batch_size': recipe.batch_size,
            'boil_size': recipe.boil_size,
            'bottling_temp': recipe.bottling_temp,
            'bottling_pressure': recipe.bottling_pressure,
            'steep_efficiency': recipe.steep_efficiency,
            'mash_efficiency': recipe.mash_efficiency,
            'primary_days': recipe.primary_days,
            'primary_temp': recipe.primary_temp,
            'secondary_days': recipe.secondary_days,
            'secondary_temp': recipe.secondary_temp,
            'tertiary_days': recipe.tertiary_days,
            'tertiary_temp': recipe.tertiary_temp,
            'aging_days': recipe.aging_days,
            '_ingredients': recipe._ingredients
        })

        new_recipe.slug = generate_usable_slug(new_recipe)
        new_recipe.put()
        new_recipe.update_grade()
        new_recipe.put()

        # Update recipe ranking for sorting
        recipe.update_grade()
        recipe.put()

        action = UserAction()
        action.owner = self.user
        action.type = action.TYPE_RECIPE_CLONED
        action.object_id = new_recipe.key().id()
        action.put()

        return self.render_json({
            'status': 'ok',
            'redirect': new_recipe.url
        })