def register(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] db = get_db() error = None if not username: error = 'Username is required.' elif not password: error = 'Password is required.' elif db.execute( 'SELECT id FROM user WHERE username = ?', (username,) ).fetchone() is not None: error = 'User {} is already registered.'.format(username) if error is None: db.execute( 'INSERT INTO user (username, password) VALUES (?, ?)', (username, generate_password_hash(password)) ) db.commit() return redirect(url_for('blog.profile')) flash(error) return render_template('auth/register.html')
def profile(): db = get_db() if request.method == 'POST': try: bmi = calc_bmi(request.form['weight'],request.form['feet'],request.form['inches']) bmr = calc_bmr(request.form['gender'], request.form['age'], request.form['weight'], request.form['feet'], request.form['inches']) cals_per_day = calc_calperday(request.form['activity'], request.form['goal'], bmr) except Exception as err: print( type(err) ) bmi = '--' bmr = '--' cals_per_day = '--' db.execute( 'UPDATE user ' 'SET fullname=?, ' 'gender=?, ' 'age=?, ' 'weight=?, ' 'feet=?, ' 'inches=?, ' 'activity=?, ' 'goal=?, ' 'bmi=?, ' 'bmr=?, ' 'cals_per_day=?, ' 'restrictions=?, ' 'is_config=?', (request.form['fullname'], request.form['gender'], request.form['age'], request.form['weight'], request.form['feet'], request.form['inches'], request.form['activity'], request.form['goal'], bmi, bmr, cals_per_day, request.form['restrictions'], 1) ) db.commit() flash('Saved!','success') return redirect(url_for('blog.profile')) else: user_info = db.execute( 'SELECT * ' 'FROM user ' 'WHERE id = ?', (session.get('user_id'),) ).fetchone() return render_template('profile.html', user=user_info)
def favs(): db = get_db() liked = get_liked(session.get('user_id')) disliked = get_disliked(session.get('user_id')) restrictions = get_restrictions(session.get('user_id')) ingredients = [] if request.method == 'POST': try: respose = request.form['recipe_id'] # Parse out the type of button pressed (like or dislike) vote = re.search('[a-z]+', respose).group() # Parse out the recipe id from the response recipe_id = re.search('[0-9]+', respose).group() if vote == 'like': # Unlike if you click the button and it's already liked if recipe_id in liked: liked.remove(recipe_id) # Like the recipe else: liked.append(recipe_id) # Un-dislike if it is disliked if recipe_id in disliked: disliked.remove(recipe_id) else: # Un-dislike if you click the button and it's already disliked if recipe_id in disliked: disliked.remove(recipe_id) # Dislike the recipe else: disliked.append(recipe_id) # Unlike if it is liked if recipe_id in liked: liked.remove(recipe_id) db.execute('UPDATE user ' 'SET liked=?', (','.join(liked), )) db.execute('UPDATE user ' 'SET disliked=?', (','.join(disliked), )) db.commit() return redirect(url_for('blog.favs')) except BadRequestKeyError: print('No recipe_id key') recipes = get_recipes(ingredients, restrictions, session.get('user_id')) return render_template('browse.html', recipes=recipes, liked=liked, disliked=disliked, keywords=ingredients, restrictions=restrictions, recipe_num=0)
def load_logged_in_user(): user_id = session.get('user_id') if user_id is None: g.user = None else: g.user = get_db().execute( 'SELECT * FROM user WHERE id = ?', (user_id,) ).fetchone()
def train(self, restrictions, min_df=10, n_closest=3): """ Fit the engine model to the given data Parameters ========== restrictions : list List containing strings of dietary restrictions. min_df : int Minimum number of occurences of a given ingredient in recipes. n_closest : int The number of closest recipes. """ # Read in cleaned data from CSV data = pd.read_csv('FoodFlix/static/data/clean_ingredients.csv', header=0) data.set_index('recipe_id', inplace=True) # Drop recipes that contain keywords from the dietary restrictions if restrictions: data = ( data[~data['ingredients'].str.contains('|'.join(restrictions))] ) # Put ingredients in a list to be passed to TF-IDF ingredients = list(data['ingredients']) # Build the TF-IDF Model using 1, 2, and 3-grams on words vectorizer = TfidfVectorizer(analyzer='word', ngram_range=(1, 3), min_df=min_df, stop_words='english', max_features=512) # Fit the TF-IDF model using the given data X = vectorizer.fit_transform(ingredients) # Store this to a SQL database db = get_db() # Store TF-IDF features to database as well tfidf_df = pd.DataFrame(X.toarray(), index=data.index) tfidf_df.to_sql(name='tfidf', con=db, if_exists='replace') return
def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] db = get_db() error = None user = db.execute( 'SELECT * FROM user WHERE username = ?', (username,) ).fetchone() if user is None: error = 'Incorrect username.' elif not check_password_hash(user['password'], password): error = 'Incorrect password.' if error is None: session.clear() session['user_id'] = user['id'] return redirect(url_for('blog.browse')) flash(error) return render_template('auth/login.html')
def browse(): ### Get query arguments: # Recipe number: try: recipe_num = int(request.args['recipe_num']) if recipe_num < 0: recipe_num = 0 except (ValueError, KeyError): recipe_num = 0 # Ingredients: ingredients = [] ingr_str = '' try: ingredients = request.args['ingredients'].split( ' ') # get ingr as array ingredients = [ ingr for ingr in ingredients if ingr != '' and ingr != ',' ] #remove empty and commas ingr_str = request.args['ingredients'] except BadRequestKeyError: print('No Ingredients key') ### Get link to DB db = get_db() ### Get user info liked = get_liked(session.get('user_id')) disliked = get_disliked(session.get('user_id')) restrictions = get_restrictions(session.get('user_id')) if request.method == 'POST': try: respose = request.form['recipe_id'] # Parse out the type of button pressed (like or dislike) vote = re.search('[a-z]+', respose).group() # Parse out the recipe id from the response recipe_id = re.search('[0-9]+', respose).group() if vote == 'like': # Unlike if you click the button and it's already liked if recipe_id in liked: liked.remove(recipe_id) # Like the recipe else: liked.append(recipe_id) # Un-dislike if it is disliked if recipe_id in disliked: disliked.remove(recipe_id) else: # Un-dislike if you click the button and it's already disliked if recipe_id in disliked: disliked.remove(recipe_id) # Dislike the recipe else: disliked.append(recipe_id) # Unlike if it is liked if recipe_id in liked: liked.remove(recipe_id) db.execute('UPDATE user ' 'SET liked=?', (','.join(liked), )) db.execute('UPDATE user ' 'SET disliked=?', (','.join(disliked), )) db.commit() return redirect( url_for('blog.browse', _anchor=recipe_id, recipe_num=recipe_num, ingredients=ingr_str)) except BadRequestKeyError: print('No recipe_id key') ### Finally get the recipes from DB recipes = get_recipes(ingredients, restrictions, '') recipes = recipes[recipe_num:recipe_num + 10] return render_template('browse.html', recipes=recipes, liked=liked, disliked=disliked, ingredients=ingredients, ingr_str=ingr_str, restrictions=restrictions, recipe_num=recipe_num)
def recommender(): MIN_LIKED_RECIPES = 3 # Connect to the database db = get_db() # Get data on what the user has liked so far user_query = db.execute('SELECT * ' 'FROM user ').fetchone() # Pull information on what the user likes to use in the engine try: liked_str = user_query['liked'] liked = liked_str.split(',') except: liked = [] if len(liked) < MIN_LIKED_RECIPES: flash( f'You need to like at least {MIN_LIKED_RECIPES} recipes to get ' 'recommendations', 'danger') recipes = [] return render_template("browse.html", recipes=recipes) # Get any dietary restrictions to include in the recommendation restriction_query = db.execute('SELECT restrictions ' 'FROM user ').fetchone() restrictions = restriction_query['restrictions'].split() # Build the food recommender engine and train engine = FoodFlixEngine() engine.train(restrictions=restrictions) # Pull some recipe recommendations recipes = engine.predict(cals_per_day=user_query['cals_per_day'], add_random=False) liked = get_liked(session.get('user_id')) disliked = get_disliked(session.get('user_id')) if request.method == 'POST': try: respose = request.form['recipe_id'] # Parse out the type of button pressed (like or dislike) vote = re.search('[a-z]+', respose).group() # Parse out the recipe id from the response recipe_id = re.search('[0-9]+', respose).group() if vote == 'like': # Like the recipe liked.append(recipe_id) else: # Dislike the recipe disliked.append(recipe_id) db.execute('UPDATE user ' 'SET liked=?', (','.join(liked), )) db.execute('UPDATE user ' 'SET disliked=?', (','.join(disliked), )) db.commit() return redirect(url_for('blog.recommender')) except BadRequestKeyError: print('No recipe_id key') return render_template("browse.html", recipes=recipes, recipe_num=0)
def predict(self, cals_per_day, w_like=1.5, w_dislike=0.3, n_closest=4, add_random=False): """ Generate predictions Parameters ========== cals_per_day : float Total recommended calories per day for a user. w_like : float Weighting for liked recipes in computing recommendations. w_dislike : float Weighting for disliked recipes in computing recommendations. n_closest : int Number of recommended recipes add_random : bool Whether including a random suggestion for testing """ # Divide the daily calories into five meals cals_per_day /= 5 # Connect to the database to grab user information db = get_db() # Get liked and disliked recipes liked = get_liked(session.get('user_id')) disliked = get_disliked(session.get('user_id')) # The TF-IDF db uses recipe_id as the index as integers... # TODO kjb: make all recipe_id indices either string or integer liked = [int(l) for l in liked] disliked = [int(l) for l in disliked] # Grab the TF-IDF features to compute scores tfidf_query = 'SELECT * FROM tfidf;' tfidf = pd.read_sql(sql=tfidf_query, con=db, index_col='recipe_id') # Grab the feature vectors for the liked and disliked recipes likes = tfidf.loc[liked] dislikes = tfidf.loc[disliked] # Compute the Rocchio topic topic = self.compute_rocchio_topic(likes, dislikes, w_like, w_dislike) # Find recipes similar to the Rocchio topic similarity = cosine_similarity(np.atleast_2d(topic), tfidf) similarity = pd.Series(similarity[0], index=tfidf.index) recommendations = similarity.sort_values(ascending=False) recommendations = list(recommendations.index) # Create a container to hold recommended recipes recipes = [] n_recs = 0 for rec in recommendations: recipe_query = db.execute( 'SELECT * ' 'FROM recipes ' 'WHERE recipe_id == ? ', (rec,) ).fetchone() # Only recommend things that you don't already like if recipe_query['recipe_id'] not in liked: recipe_dict = dict( recipe_query ) #convert to dict to allow addition of elements recipe_dict['generator'] = 'Recommender TF-IDF' # # Recommend only recipes around your cal/week goal # cals = int(recipe_query['calorie_count'].replace('cals','')) # if cals > cals_per_day * 0.8 and cals < cals_per_day * 1.2: recipes.append(recipe_dict) n_recs+=1 if n_recs >= n_closest: break # Include random suggestion if enabled if add_random: # Get all recipes from DB all_recipes = get_recipes('','','') recipe_rand = dict( random.choice(all_recipes) ) recipe_rand['generator'] = 'Random' recipes.append( recipe_rand ) # Shuffle around random.shuffle(recipes) return recipes