## Create venv for nutrient_counter project and activate it
py -m venv new
new\Scripts\activate.bat
## install Django
pip install django
## create project "n_counter"
django-admin startproject n_counter
## create app "app"
py manage.py startapp app
## add app in INSTALLED_APPS in settings.py
## create model for Food items in models.py
from django.db import models
class Food(models.Model):
name = models.CharField(max_length=250)
carbs= models.FloatField(default=0)
proteins = models.FloatField(default=0)
fats= models.FloatField(default=0)
calories = models.IntegerField(default=0)
## migrate model
py manage.py makemigrations
py manage.py migrate
## register model in admin.py
from django.contrib import admin
from .models import Food
admin.site.register(Food)
## Create superuser for project
py manage.py createsuperuser
## Add __str__ for Food model to be displayed (update models.py)
from django.db import models
class Food(models.Model):
def __str__(self):
return self.name
name = models.CharField(max_length=250)
carbs= models.FloatField(default=0)
proteins = models.FloatField(default=0)
fats= models.FloatField(default=0)
calories = models.IntegerField(default=0)
##Create view for display food items in views.py
from django.shortcuts import render
from .models import Food
def index(request):
foods = Food.objects.all()
return render(request, 'app/index.html', {'foods':foods})
##Create templates folder with internal 'app' in app
##Create index.html in templates/app
<html>
<head>
</head>
<body>
{% for food in foods %}
{{food.name}}<br>
{% endfor %}
</body>
</html>
##register path for view in urls.py
from django.contrib import admin
from django.urls import path
from app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index, name="index")
]
##add select box of food from form (update index.html)
<html>
<head>
</head>
<body>
<form action="">
{% csrf_token %}
<select name="" food="">
{%for food in foods%}
<option value="{{food.name}}">{{food.name}}</option>
{% endfor %}
</select>
</form>
</body>
</html>
## Add food consumption list for users (import default User model). Create model
'Consume' in models.py
from django.contrib.auth.models import User
class Consume(models.Model):
food_consumed = models.ForeignKey(Food, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
## Register model in admin.py
from .models import Food, Consume
admin.site.register(Food)
admin.site.register(Consume)
## Start migrations
py manage.py makemigrations
py manage.py migrate
py manage.py runserver
## Check on the server create consume
##add the functionality of selecting food item for each user separately (create
right association)
##update index.html
<html>
<head>
</head>
<body>
<form method="POST">
{% csrf_token %}
<select name="food_consumed" id="food_consumed">
{% for food in foods %}
<option value="{{food.name}}">{{food.name}}</option>
{% endfor %}
</select>
<button type="submit">Add
</button>
</form>
</body>
</html>
##In views.py update def index according to the POST method and added name and id
in html
from django.shortcuts import render
from .models import Food, Consume
def index(request):
if request.method =="POST":
food_consumed = request.POST['food_consumed']
consume = Food.objects.get(name=food_consumed)
user = request.user
consume = Consume(user=user, food_consumed=consume)
consume.save()
foods = Food.objects.all()
else:
foods = Food.objects.all()
return render(request, 'app/index.html', {'foods':foods})
##Show the listing of consumed food. Update the views.py
...
if request.method =="POST":
food_consumed = request.POST['food_consumed']
consume = Food.objects.get(name=food_consumed)
user = request.user
consume = Consume(user=user, food_consumed=consume)
consume.save()
foods = Food.objects.all()
else:
foods = Food.objects.all()
consumed_food=Consume.objects.filter(user=request.user)
return render(request, 'app/index.html', {'foods':foods,
'consumed_food':consumed_food})
##Update index.html with after <form> part
...
</form>
{% for c in consumed_food %}
{{c.food_consumed.name}} --> {{c.food_consumed.carbs}} -->
{{c.food_consumed.calories}} </br>
{% endfor %}
...
##Add bootstrap tp the html templates (in head tag)
<head>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N"
crossorigin="anonymous">
</head>
##Refine the index.html <form> part
...
<div class="container">
<div class="row">
<div class="col-md-12">
<form method="POST">
<div class="form-group row">
{% csrf_token %}
<label class="col-md-2">
<b>Select Food To Add </b>
</label>
<select class="col-md-6 form-control"
name="food_consumed" id="food_consumed">
{% for food in foods %}
<option
value="{{food.name}}">{{food.name}}</option>
{% endfor %}
</select>
<button class="btn btn-success"
type="submit">Add</button>
</div>
</form>
</div>
</div>
</div>
...
##Create a table of consumed nutrientsinstead of previous listing with '{% for c in
consumed_food %}' loop
...
<div class="row">
<div class="col-md-7">
<div >
<h4> Today's Consumption</h4>
</div>
<table id="table" class="table table-striped table-primary">
<tr class="bg-primary text-white">
<th>Food item</th>
<th>Carbs(gm)</th>
<th>Protein(gm)</th>
<th>Fats(gm)</th>
<th>Calories(Kcal)</th>
</tr>
{% for c in consumed_food %}
<tr>
<td>{{c.food_consumed.name}}</td>
<td>{{c.food_consumed.carbs}}</td>
<td>{{c.food_consumed.proteins}}</td>
<td>{{c.food_consumed.fats}}</td>
<td>{{c.food_consumed.calories}}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
...
## Add the calculations of total numbers of nutrients. Add <script> after </body>
<script>
var table = document.getElementById("table");
var carbs = 0, proteins=0, fats=0, calories=0;
for(var i=1; i<table.rows.length-1;i++){
console.log(table.rows[i].cells[1].innerHTML);
carbs += parseFloat(table.rows[i].cells[1].innerHTML);
proteins += parseFloat(table.rows[i].cells[2].innerHTML);
fats += parseFloat(table.rows[i].cells[3].innerHTML);
calories += parseFloat(table.rows[i].cells[4].innerHTML);
}
</script>
## Add new row for Total counts
...
{% endfor %}
<tr>
<td id="name"><b>Total</b></td>
<td id="totalCarbs"><b></b></td>
<td id="totalProteins"><b></b></td>
<td id="totalFats"><b></b></td>
<td id="totalCalories"><b></b></td>
</tr>
</table>
...
## Add update for <script>
<script>
var table = document.getElementById("table");
var carbs = 0, proteins=0, fats=0, calories=0;
for(var i=1; i<table.rows.length-1;i++){
console.log(table.rows[i].cells[1].innerHTML);
carbs += parseFloat(table.rows[i].cells[1].innerHTML);
carbs = Math.round(carbs);
proteins += parseFloat(table.rows[i].cells[2].innerHTML);
fats += parseFloat(table.rows[i].cells[3].innerHTML);
calories += parseFloat(table.rows[i].cells[4].innerHTML);
}
console.log(carbs);
document.getElementById("totalCarbs").innerHTML = '<b>' + carbs +
'(gm)</b>';
document.getElementById("totalProteins").innerHTML = proteins;
document.getElementById("totalFats").innerHTML = fats;
document.getElementById("totalCalories").innerHTML = '<b>' + calories +
'(kcal)</b>';
</script>
##Add progress bar for calories. in index.html in container div add boostrap
'progress-bar'
...
<div class="container">
<br><br><br>
<h4>Calorie Goal</h4>
<br>
<div class="row">
<div class="col-md-9 offset-1">
<div class="progress">
<div class="progress-bar" role="progressbar" style="width:
0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="0"></div>
</div>
</div>
</div>
<br><br>
<div class="row">
...
##Update the <script> with var for % of calories
...
document.getElementById("totalFats").innerHTML = fats;
document.getElementById("totalCalories").innerHTML = '<b>' + calories +
'(kcal)</b>';
var calPer = (calories/2000)*100;
document.getElementsByClassName("progress-bar")[0].setAttribute("style",
"width:"+calPer+"%");
</script>
...
##Add nav bar for the index.html after "container"
...
<div class="container">
<div class="row">
<div class="col-md-12">
<nav class="navbar navbar-dark bg-primary">
<span class="navbar-brand">Calorie Tracker</span>
</nav>
</div>
</div>
<br><br><br>
...
##In index.html add the headers for Chart.js charts
...
</table>
</div>
<div class="col-md-5" offset-1>
<div class="">
<h4>Today's breakdown</h4>
</div>
<div class="card-header text-white bg-primary">
<h4>Macronutrients breakdown</h4>
</div>
<div class="col-md-12">
</div>
</div>
</div>
</div>
{% for c in consumed_food %}
...
##Go to chart.js official site, get the CDN and copy the HTML add-on. Insert the
copied HTML to the index.html
...
<head>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N"
crossorigin="anonymous">
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
</head>
...
## in index.html setup the column after Macronutrient breakdown
...
<h4>Macronutrients breakdown</h4>
</div>
<div class="col-md-12">
<canvas id="myChart" width="400" height="400"></canvas>
</div>
...
##In <script> part of index.html add the definition of doughnut chart
...
document.getElementsByClassName("progress-bar")[0].setAttribute("style",
"width:"+calPer+"%");
var total = carbs + proteins +fats;
var carbsP= Math.round((carbs/total)*100);
var proteinsP= Math.round((proteins/total)*100);
var fatsP= Math.round((fats/total)*100);
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Carbs '+carbsP+'%', 'Proteins '+proteinsP+'%', 'Fats
'+fatsP+'%'],
datasets: [{
data: [carbsP, proteinsP, fatsP],
backgroundColor: [
'rgba(255, 99, 132, 0.6)',
'rgba(255, 255, 100, 0.6)',
'rgba(255, 79, 12, 0.6)',
],
borderColor: [
'rgba(255, 99, 132, 0.9)',
'rgba(255, 255, 100, 0.9)',
'rgba(255, 79, 12, 0.9)',
],
borderWidth: 1
}]
}
});
</script>
...
##Add the deletion functionality to the list of food items. In views.py add def
delete_consume() and import 'redirect' from shortcuts
def delete_consume(request, id):
consumed_food = Consume.objects.get(id=id)
if request.method == 'POST':
consumed_food.delete()
return redirect('/')
return render(request, 'app/delete.html')
##Create confirmation form template delete.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<form method="POST">
{% csrf_token %}
Are you sure you want to delete the item?
<input type="submit">
</form>
</body>
</html>
##Setup urls.py with new view
from django.contrib import admin
from django.urls import path
from app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index, name="index"),
path('delete/<int:id>/', views.delete_consume, name="delete"),
## Add the remove character in index.html for each item listed
...
<table id="table" class="table table-striped table-primary">
<tr class="bg-primary text-white">
<th>Food item</th>
<th>Carbs(gm)</th>
<th>Protein(gm)</th>
<th>Fats(gm)</th>
<th>Calories(Kcal)</th>
<th>Remove item</th>
</tr>
{% for c in consumed_food %}
<tr>
<td>{{c.food_consumed.name}}</td>
<td>{{c.food_consumed.carbs}}</td>
<td>{{c.food_consumed.proteins}}</td>
<td>{{c.food_consumed.fats}}</td>
<td>{{c.food_consumed.calories}}</td>
<td><a class="btn btn-danger" href="{% url
'delete' c.id %}">Remove</td>
</tr>
{% endfor %}
...
##Change the 'Remove' button name to 'X'