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 diff --git a/Scripts/bootstrap_database_rv.py b/Scripts/bootstrap_database_rv.py new file mode 100644 index 00000000..f5129df1 --- /dev/null +++ b/Scripts/bootstrap_database_rv.py @@ -0,0 +1,42 @@ +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(2100, 2300)}) + 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(-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 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 6aebd524..ed183ba5 100644 --- a/application.py +++ b/application.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta import bcrypt import smtplib @@ -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(): @@ -228,21 +229,45 @@ def history(): email = get_session = session.get('email') if get_session is not None: form = HistoryForm() + + # Find out the last 7 day's calories burnt by the user + labels = [] + values = [] + 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(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 - data=[ - ("01-01-2020",100), - ("02-01-2020",101), - ("03-01-2020",102) - ] - - # labels=[row[0] for row in data] - # values=[row[1] for row in data] - 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) + # 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']) @@ -322,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() @@ -361,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 c5b1116e..59c69dd2 100644 --- a/templates/history.html +++ b/templates/history.html @@ -1,66 +1,106 @@ {% extends "layout.html" %} {% block content %} - -
-
- {{ 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; + }, + }, + }, + }); +
diff --git a/tests/history.py b/tests/history.py new file mode 100644 index 00000000..206e1e70 --- /dev/null +++ b/tests/history.py @@ -0,0 +1,23 @@ +import datetime +import unittest +from service import history + + +class TestHistoryService(unittest.TestCase): + def test_total_calories_to_burn_a(self): + tw = 90 + cw = 100 + self.assertEquals(history.total_calories_to_burn(tw, cw), -77000) + + def test_total_calories_to_burn_b(self): + tw = 100 + cw = 90 + self.assertEquals(history.total_calories_to_burn(tw, cw), 77000) + + def test_calories_to_burn_a(self): + target_calories = -77000 + current_calories = 55500 + target_date = datetime.datetime.today() + datetime.timedelta(days=30*11) + start_date = datetime.datetime.today() - datetime.timedelta(days=30) + val = history.calories_to_burn(target_calories,current_calories,target_date,start_date) + self.assertAlmostEqual(val, -220)