From 70d9cd129b6f79c2921ae133b1ca263cd5aecbc6 Mon Sep 17 00:00:00 2001 From: Utsav Lal Date: Fri, 13 Oct 2023 10:21:21 -0400 Subject: [PATCH 1/4] Adding script to add fake values in DB --- Scripts/bootstrap_database_rv.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Scripts/bootstrap_database_rv.py diff --git a/Scripts/bootstrap_database_rv.py b/Scripts/bootstrap_database_rv.py new file mode 100644 index 00000000..5f608462 --- /dev/null +++ b/Scripts/bootstrap_database_rv.py @@ -0,0 +1,31 @@ +import datetime +import random + +from pymongo import MongoClient + + +def add_fake_values_to_database(email: str, days: int): + mongo = MongoClient('mongodb://127.0.0.1:27017/test') + db = mongo['test'] + calories = db.get_collection('calories') + for i in range(0, days + 1): + date = datetime.date.today() - datetime.timedelta(days=i) + calories.insert_one({'date': str(date), 'email': email, 'calories': random.randint(200, 300)}) + print("Positive Values have been added to the database") + + +def add_fake_negetive_values_to_database(email: str, days: int): + mongo = MongoClient('mongodb://127.0.0.1:27017/test') + db = mongo['test'] + calories = db.get_collection('calories') + for i in range(0, days + 1): + if random.randint(0, 69420) % 2 == 0: + continue + date = datetime.date.today() - datetime.timedelta(days=i) + calories.insert_one({'date': str(date), 'email': email, 'calories': random.randint(-200, 0)}) + print("Negative values have been added to the database") + + +if __name__ == '__main__': + add_fake_values_to_database('', 7) + add_fake_negetive_values_to_database('', 7) From 2b504cf5bb746276bf6d8ca580c7bd2a425a0d75 Mon Sep 17 00:00:00 2001 From: Utsav Lal Date: Fri, 13 Oct 2023 10:26:46 -0400 Subject: [PATCH 2/4] Updating to a better .gitignore --- .gitignore | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 8d144e7f..a7f7885a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,25 @@ -node_modules/ -package-lock.json -__pycache__ -.idea -.DS_Store \ No newline at end of file +.DS_Store +.env +.flaskenv +*.pyc +*.pyo +env/ +venv/ +.venv/ +env* +dist/ +build/ +*.egg +*.egg-info/ +.tox/ +.cache/ +.pytest_cache/ +.idea/ +docs/_build/ +.vscode + +# Coverage reports +htmlcov/ +.coverage +.coverage.* +*,cover \ No newline at end of file From 1160f78e71b1457fbfaab1f1d0836d9c64651a83 Mon Sep 17 00:00:00 2001 From: Utsav Lal Date: Fri, 13 Oct 2023 10:32:32 -0400 Subject: [PATCH 3/4] Adding corrected graph for last 7 day calories --- application.py | 64 +++++++++++++------ templates/history.html | 142 ++++++++++++++++++++++++++--------------- 2 files changed, 135 insertions(+), 71 deletions(-) diff --git a/application.py b/application.py index 5d6d255a..5db0e663 100644 --- a/application.py +++ b/application.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta import bcrypt import smtplib @@ -193,16 +193,16 @@ def user_profile(): 'height', 'weight', 'goal', 'target_weight'}) if temp is not None: mongo.db.profile.update_one({'email': email}, - {'$set': {'weight': temp['weight'], - 'height': temp['height'], - 'goal': temp['goal'], - 'target_weight': temp['target_weight']}}) + {'$set': {'weight': temp['weight'], + 'height': temp['height'], + 'goal': temp['goal'], + 'target_weight': temp['target_weight']}}) else: mongo.db.profile.insert_one({'email': email, - 'height': height, - 'weight': weight, - 'goal': goal, - 'target_weight': target_weight}) + 'height': height, + 'weight': weight, + 'goal': goal, + 'target_weight': target_weight}) flash(f'User Profile Updated', 'success') return render_template('display_profile.html', status=True, form=form) @@ -223,21 +223,49 @@ def history(): email = get_session = session.get('email') if get_session is not None: form = HistoryForm() - - data=[ - ("01-01-2020",100), - ("02-01-2020",101), - ("03-01-2020",102) - ] + data = [] # labels=[row[0] for row in data] # values=[row[1] for row in data] - labels=[] - values=[] + labels = [] + values = [] for row in data: labels.append(row[0]) values.append(row[1]) - return render_template('history.html', form=form,labels=labels,values=values) + start_date = (datetime.today() - timedelta(days=7)) + end_date = datetime.today() + bucket_boundaries = [(start_date + timedelta(days=i)).strftime('%Y-%m-%d') for i in range(7)] + date_range_filter = { + '$match': { + 'date': { + '$gte': start_date.strftime('%Y-%m-%d'), + '$lte': end_date.strftime('%Y-%m-%d') + }, + } + } + total_calories_each_day = { + '$bucket': { + 'groupBy': '$date', + 'boundaries': bucket_boundaries, + 'default': 'Other', + 'output': { + 'date': { + '$max': '$date' + }, + 'total_calories': { + '$sum': '$calories' + } + } + } + } + filtered_calories = mongo.db.calories.aggregate([ + date_range_filter, + total_calories_each_day + ]) + for calorie_each_day in filtered_calories: + labels.append(calorie_each_day['date']) + values.append(calorie_each_day['total_calories']) + return render_template('history.html', form=form, labels=labels, values=values) @app.route("/ajaxhistory", methods=['POST']) diff --git a/templates/history.html b/templates/history.html index c5b1116e..c6b02483 100644 --- a/templates/history.html +++ b/templates/history.html @@ -1,66 +1,102 @@ {% extends "layout.html" %} {% block content %} - -
-
- {{ form.hidden_tag() }} -
- History -
- - -
-
-
- {{ form.submit(class="btn btn-outline-info") }} - -
-
-
-
-
History
-
-

-


-

-


-

-


-
+ + {#
#} + {#
#} + {# {{ form.hidden_tag() }}#} + {#
#} + {# History#} + {#
#} + {# #} + {# #} + {#
#} + {#
#} + {#
#} + {# {{ form.submit(class="btn btn-outline-info") }}#} + {##} + {#
#} + {#
#} + {#
#} + {#
#} + {#
History
#} + {#
#} + {#

#} + {#


#} + {#

#} + {#


#} + {#

#} + {#


#} + {#
#} -
- - -
+
- + +
- - + scales: { + x: { + grid: { + display: false + } + }, + y: { + grid: { + display: false + } + } + }, + animation: { + onComplete: () => { + delayed = true; + }, + delay: (context) => { + let delay = 0; + if (context.type === 'data' && context.mode === 'default' && !delayed) { + delay = context.dataIndex * 150 + context.datasetIndex * 100; + } + return delay; + }, + }, + }, + }); +
From c741670a28bff268c5e44e157c97db9b79591522 Mon Sep 17 00:00:00 2001 From: Utsav Lal Date: Sun, 15 Oct 2023 19:12:02 -0400 Subject: [PATCH 4/4] Adding last 7 day graph and future calorie calculation --- Scripts/bootstrap_database_rv.py | 19 ++++-- application.py | 101 +++++++++++++++---------------- service/history.py | 69 +++++++++++++++++++++ static/main.css | 5 ++ templates/history.html | 76 ++++++++++++----------- tests/history.py | 23 +++++++ 6 files changed, 201 insertions(+), 92 deletions(-) create mode 100644 service/history.py create mode 100644 tests/history.py diff --git a/Scripts/bootstrap_database_rv.py b/Scripts/bootstrap_database_rv.py index 5f608462..f5129df1 100644 --- a/Scripts/bootstrap_database_rv.py +++ b/Scripts/bootstrap_database_rv.py @@ -10,7 +10,7 @@ def add_fake_values_to_database(email: str, days: int): calories = db.get_collection('calories') for i in range(0, days + 1): date = datetime.date.today() - datetime.timedelta(days=i) - calories.insert_one({'date': str(date), 'email': email, 'calories': random.randint(200, 300)}) + calories.insert_one({'date': str(date), 'email': email, 'calories': random.randint(2100, 2300)}) print("Positive Values have been added to the database") @@ -22,10 +22,21 @@ def add_fake_negetive_values_to_database(email: str, days: int): if random.randint(0, 69420) % 2 == 0: continue date = datetime.date.today() - datetime.timedelta(days=i) - calories.insert_one({'date': str(date), 'email': email, 'calories': random.randint(-200, 0)}) + calories.insert_one({'date': str(date), 'email': email, 'calories': random.randint(-500, 0)}) print("Negative values have been added to the database") +def clear_calories_db(): + mongo = MongoClient('mongodb://127.0.0.1:27017/test') + db = mongo['test'] + calories = db.get_collection('calories') + calories.delete_many({}) + + if __name__ == '__main__': - add_fake_values_to_database('', 7) - add_fake_negetive_values_to_database('', 7) + # Add email and database will be populated with random values for that email + add_fake_values_to_database('', 30) + add_fake_negetive_values_to_database('', 30) + + # Uncomment this line to clear calories collection + # clear_calories_db() diff --git a/application.py b/application.py index cc5b2aed..ed183ba5 100644 --- a/application.py +++ b/application.py @@ -12,6 +12,7 @@ from flask_pymongo import PyMongo from tabulate import tabulate from forms import HistoryForm, RegistrationForm, LoginForm, CalorieForm, UserProfileForm, EnrollForm,WorkoutForm +from service import history as history_service app = Flask(__name__) app.secret_key = 'secret' @@ -198,23 +199,23 @@ def workout(): now = datetime.now() now = now.strftime('%Y-%m-%d') get_session = session.get('email') - + if get_session is not None: form = WorkoutForm() if form.validate_on_submit(): if request.method == 'POST': email = session.get('email') burn = request.form.get('burnout') - - + + mongo.db.calories.insert_one({'date': now, 'email': email, 'calories': -int(burn)}) - + flash(f'Successfully updated the data', 'success') return redirect(url_for('workout')) else: return redirect(url_for('home')) return render_template('workout.html', form=form, time=now) - + @app.route("/history", methods=['GET']) def history(): @@ -229,48 +230,44 @@ def history(): if get_session is not None: form = HistoryForm() - data = [] - # labels=[row[0] for row in data] - # values=[row[1] for row in data] + # Find out the last 7 day's calories burnt by the user labels = [] values = [] - for row in data: - labels.append(row[0]) - values.append(row[1]) - start_date = (datetime.today() - timedelta(days=7)) - end_date = datetime.today() - bucket_boundaries = [(start_date + timedelta(days=i)).strftime('%Y-%m-%d') for i in range(7)] - date_range_filter = { - '$match': { - 'date': { - '$gte': start_date.strftime('%Y-%m-%d'), - '$lte': end_date.strftime('%Y-%m-%d') - }, - } - } - total_calories_each_day = { - '$bucket': { - 'groupBy': '$date', - 'boundaries': bucket_boundaries, - 'default': 'Other', - 'output': { - 'date': { - '$max': '$date' - }, - 'total_calories': { - '$sum': '$calories' - } - } - } - } - filtered_calories = mongo.db.calories.aggregate([ - date_range_filter, - total_calories_each_day - ]) + pipeline = history_service.get_calories_per_day_pipeline(7) + filtered_calories = mongo.db.calories.aggregate(pipeline) for calorie_each_day in filtered_calories: + if calorie_each_day['_id'] == 'Other': + continue + net_calories = int(calorie_each_day['total_calories']) - 2000 labels.append(calorie_each_day['date']) - values.append(calorie_each_day['total_calories']) - return render_template('history.html', form=form, labels=labels, values=values) + values.append(str(net_calories)) + + # The first day when the user registered or started using the app + user_start_date = mongo.db.user.find({'email' : email})[0]['start_date'] + user_target_date = mongo.db.user.find({'email' : email})[0]['target_date'] + target_weight = mongo.db.user.find({'email' : email})[0]['target_weight'] + current_weight = mongo.db.user.find({'email' : email})[0]['weight'] + + # Find out the actual calories which user needed to burn/gain to achieve goal from the start day + target_calories_to_burn = history_service.total_calories_to_burn( + target_weight=int(target_weight), current_weight=int(current_weight)) + print(f'########## {target_calories_to_burn}') + + # Find out how many calories user has gained or burnt uptill now + calories_till_today = mongo.db.calories.aggregate( + history_service.get_calories_burnt_till_now_pipeline(email, user_start_date)) + current_calories = 0 + for calorie in calories_till_today: + current_calories += calorie['SUM'] + # current_calories = [x for x in calories_till_today][0]['SUM'] if len(list(calories_till_today)) != 0 else 0 + + # Find out no of calories user has to burn/gain in future per day + calories_to_burn = history_service.calories_to_burn(target_calories_to_burn, current_calories, + target_date=datetime.strptime(user_target_date, '%Y-%m-%d'), + start_date=datetime.strptime(user_start_date, '%Y-%m-%d')) + + return render_template('history.html', form=form, labels=labels, values=values, burn_rate=calories_to_burn, + target_date=user_target_date) @app.route("/ajaxhistory", methods=['POST']) @@ -350,34 +347,34 @@ def send_email(): data = list(mongo.db.calories.find({'email': email}, {'date','email','calories','burnout'})) table = [['Date','Email ID','Calories','Burnout']] for a in data: - tmp = [a['date'],a['email'],a['calories'],a['burnout']] - table.append(tmp) - + tmp = [a['date'],a['email'],a['calories'],a['burnout']] + table.append(tmp) + friend_email = str(request.form.get('share')).strip() friend_email = str(friend_email).split(',') server = smtplib.SMTP_SSL("smtp.gmail.com",465) #Storing sender's email address and password sender_email = "calorie.app.server@gmail.com" sender_password = "Temp@1234" - + #Logging in with sender details server.login(sender_email,sender_password) message = 'Subject: Calorie History\n\n Your Friend wants to share their calorie history with you!\n {}'.format(tabulate(table)) for e in friend_email: print(e) server.sendmail(sender_email,e,message) - + server.quit() - + myFriends = list(mongo.db.friends.find( {'sender': email, 'accept': True}, {'sender', 'receiver', 'accept'})) myFriendsList = list() - + for f in myFriends: myFriendsList.append(f['receiver']) allUsers = list(mongo.db.user.find({}, {'name', 'email'})) - + pendingRequests = list(mongo.db.friends.find( {'sender': email, 'accept': False}, {'sender', 'receiver', 'accept'})) pendingReceivers = list() @@ -389,7 +386,7 @@ def send_email(): {'receiver': email, 'accept': False}, {'sender', 'receiver', 'accept'})) for p in pendingApprovals: pendingApproves.append(p['sender']) - + return render_template('friends.html', allUsers=allUsers, pendingRequests=pendingRequests, active=email, pendingReceivers=pendingReceivers, pendingApproves=pendingApproves, myFriends=myFriends, myFriendsList=myFriendsList) diff --git a/service/history.py b/service/history.py new file mode 100644 index 00000000..4e1f6ef5 --- /dev/null +++ b/service/history.py @@ -0,0 +1,69 @@ +from datetime import datetime, timedelta + + +def get_calories_per_day_pipeline(days: int): + start_date = (datetime.today() - timedelta(days=days)) + end_date = datetime.today() + bucket_boundaries = [(start_date + timedelta(days=i)).strftime('%Y-%m-%d') for i in range(days + 1)] + date_range_filter = { + '$match': { + 'date': { + '$gte': start_date.strftime('%Y-%m-%d'), + '$lte': end_date.strftime('%Y-%m-%d') + }, + } + } + total_calories_each_day = { + '$bucket': { + 'groupBy': '$date', + 'boundaries': bucket_boundaries, + 'default': 'Other', + 'output': { + 'date': { + '$max': '$date' + }, + 'total_calories': { + '$sum': '$calories' + } + } + } + } + return [ + date_range_filter, + total_calories_each_day + ] + + +def get_calories_burnt_till_now_pipeline(email: str, start_date: str): + end_date = datetime.today().strftime('%Y-%m-%d') + return [ + { + '$match': { + 'date': { + '$gte': start_date, + '$lte': end_date + }, + 'email': email + } + }, { + '$group': { + '_id': 'sum of calories', + 'SUM': { + '$sum': '$calories' + } + } + } + ] + + +def total_calories_to_burn(target_weight: int, current_weight: int): + return int((target_weight - current_weight) * 7700) + + +def calories_to_burn(target_calories: int, current_calories: int, target_date: datetime, start_date: datetime): + actual_current_calories = current_calories - ((datetime.today() - start_date).days * 2000) + + new_target = target_calories - actual_current_calories + + days_remaining = (target_date - datetime.today()).days + return int(new_target / days_remaining) diff --git a/static/main.css b/static/main.css index ef5a8764..734fcb70 100644 --- a/static/main.css +++ b/static/main.css @@ -148,3 +148,8 @@ height: 35vh; object-fit: contain; } + + .user-chart { + display: flex; + justify-content: center; + } diff --git a/templates/history.html b/templates/history.html index c6b02483..59c69dd2 100644 --- a/templates/history.html +++ b/templates/history.html @@ -4,42 +4,34 @@ - {#
#} - {#
#} - {# {{ form.hidden_tag() }}#} - {#
#} - {# History#} - {#
#} - {# #} - {# #} - {#
#} - {#
#} - {#
#} - {# {{ form.submit(class="btn btn-outline-info") }}#} - {##} - {#
#} - {#
#} - {#
#} - {#
#} - {#
History
#} - {#
#} - {#

#} - {#


#} - {#

#} - {#


#} - {#

#} - {#


#} - {#
#} - - -
- - + +
+
+ +
+
+