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, })
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 = self.user 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 })
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 })
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 })
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})
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 })
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]
def get(self): """ Render the public users list. """ self.render('users.html', { 'users': UserPrefs.all() })
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 })
def process(self, username, action): """ Follow the given user. """ user = self.user 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' })
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' })
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)
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)})
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 })
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 })
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 })
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
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})
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)
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 })
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') })
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})
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)
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})
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 })
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'})
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' })
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})
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) })
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 })
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 })
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 })