Пример #1
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
        })
Пример #2
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
        })
Пример #3
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})
Пример #4
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
            })
Пример #5
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]
Пример #6
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,
            })
Пример #7
0
def generate_usable_slug(recipe):
    """
    Generate a usable slug for a given recipe. This method will try to slugify
    the recipe name and then append an integer if needed, increasing this
    integer until no existing recipe would be overwritten.
    """
    slug = slugify(recipe.name)

    # Reuse existing slug if we can
    if recipe.slug and recipe.slug == slug:
        return recipe.slug

    append = 0
    while True:
        count = Recipe.all()\
                      .filter('owner =', recipe.owner)\
                      .filter('slug =', slug)\
                      .count()

        if not count:
            break

        append += 1
        slug = slugify(recipe.name) + str(append)

    return slug
Пример #8
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]
Пример #9
0
def generate_usable_slug(recipe):
    """
    Generate a usable slug for a given recipe. This method will try to slugify
    the recipe name and then append an integer if needed, increasing this
    integer until no existing recipe would be overwritten.
    """
    slug = slugify(recipe.name)

    # Reuse existing slug if we can
    if recipe.slug and recipe.slug == slug:
        return recipe.slug

    append = 0
    while True:
        count = Recipe.all()\
                      .filter('owner =', recipe.owner)\
                      .filter('slug =', slug)\
                      .count()

        if not count:
            break

        append += 1
        slug = slugify(recipe.name) + str(append)

    return slug
Пример #10
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,
            })
Пример #11
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)})
Пример #12
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
            })
Пример #13
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
        })
Пример #14
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
        })
Пример #15
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
Пример #16
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
Пример #17
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)
Пример #18
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)
Пример #19
0
    def top_interesting_recipes(self):
        """
        Get a list of the top recipes that are interesting for this user,
        meaning that either the recipes belong to this user or any user
        that she is following. The list is ranked by the number of likes
        that each recipe has.
        """
        from models.recipe import Recipe

        following = self.following_users.run(batch_size=100)

        return Recipe.all()\
                     .filter('owner IN', [self] + list(following))\
                     .order('-likes_count')\
                     .fetch(15)
Пример #20
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})
Пример #21
0
    def delete(self, username=None, recipe_slug=None):
        """
        Handle recipe delete. This will remove a recipe and return success
        or failure.
        """
        user = self.user

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

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

        if recipe:
            # Delete all actions pointing to this recipe
            actions = UserAction.all()\
                                .filter('type IN', [UserAction.TYPE_RECIPE_CREATED,
                                                    UserAction.TYPE_RECIPE_EDITED,
                                                    UserAction.TYPE_RECIPE_CLONED,
                                                    UserAction.TYPE_RECIPE_LIKED])\
                                .filter('object_id =', recipe.key().id())\
                                .fetch(1000)

            for action in actions:
                action.delete()

            # Delete the actual recipe itself
            recipe.delete()

            self.render_json({
                'status': 'ok',
                'redirect': '/users/%(username)s/recipes' % {
                    'username': user.name
                }
            })
        else:
            self.render_json({
                'status': 'error',
                'error': 'Unable to delete recipe'
            })
Пример #22
0
    def delete(self, username=None, recipe_slug=None):
        """
        Handle recipe delete. This will remove a recipe and return success
        or failure.
        """
        user = self.user

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

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

        if recipe:
            # Delete all actions pointing to this recipe
            actions = UserAction.all()\
                                .filter('type IN', [UserAction.TYPE_RECIPE_CREATED,
                                                    UserAction.TYPE_RECIPE_EDITED,
                                                    UserAction.TYPE_RECIPE_CLONED,
                                                    UserAction.TYPE_RECIPE_LIKED])\
                                .filter('object_id =', recipe.key().id())\
                                .fetch(1000)

            for action in actions:
                action.delete()

            # Delete the actual recipe itself
            recipe.delete()

            self.render_json({
                'status': 'ok',
                'redirect': '/users/%(username)s/recipes' % {
                    'username': user.name
                }
            })
        else:
            self.render_json({
                'status': 'error',
                'error': 'Unable to delete recipe'
            })
Пример #23
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)
Пример #24
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.
        """
        # Try to get recipes list from memcache instead of datastore
        recipes_html = memcache.get('index-recipes')
        if not recipes_html or settings.DEBUG:
            # No such luck... query the data store
            recipes = Recipe.all()\
                            .order('-grade')\
                            .run(limit=15)
            recipes_html = self.render('index-recipes.html',
                                       {'recipes': recipes},
                                       write_to_stream=False)
            memcache.set('index-recipes', recipes_html, self.CACHE_TIME)

        self.render('index.html', {'recipes_html': recipes_html})
Пример #25
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)
Пример #26
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.
        """
        # Try to get recipes list from memcache instead of datastore
        recipes_html = memcache.get('index-recipes')
        if not recipes_html or settings.DEBUG:
            # No such luck... query the data store
            recipes = Recipe.all()\
                            .order('-grade')\
                            .run(limit=15)
            recipes_html = self.render('index-recipes.html', {
                'recipes': recipes
            }, write_to_stream=False)
            memcache.set('index-recipes', recipes_html, self.CACHE_TIME)

        self.render('index.html', {
            'recipes_html': recipes_html
        })
Пример #27
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
        })
Пример #28
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)
Пример #29
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)
        })
Пример #30
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
        })
Пример #31
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)
Пример #32
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
        })
Пример #33
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})
Пример #34
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)
Пример #35
0
            self.render_json({
                'status': 'error',
                'error': str(e),
                'input': recipe_json
            })
            return

        # Load recipe from db or create a new one
        new_recipe = False
        if not recipe_slug:
            recipe = Recipe()
            recipe.owner = user
            new_recipe = True
        else:
            recipe = Recipe.all()\
                           .filter('owner =', user)\
                           .filter('slug =', recipe_slug)\
                           .get()

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

        # Ensure you own this recipe
        if not recipe or recipe.owner.name != user.name:
            self.render_json({
                'status': 'error',
                'error': 'Permission denied: you are not the recipe owner!'
            })
Пример #36
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)
Пример #37
0
            self.render_json({
                'status': 'error',
                'error': str(e),
                'input': recipe_json
            })
            return

        # Load recipe from db or create a new one
        new_recipe = False
        if not recipe_slug:
            recipe = Recipe()
            recipe.owner = user
            new_recipe = True
        else:
            recipe = Recipe.all()\
                           .filter('owner =', user)\
                           .filter('slug =', recipe_slug)\
                           .get()

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

        # Ensure you own this recipe
        if not recipe or recipe.owner.name != user.name:
            self.render_json({
                'status':
                'error',
                'error':