Develop a Calorie Counter
Develop a Calorie Counter that can be used to estimate the number of calories a person needs to consume each day. This calculator can also provide some simple guidelines for gaining or losing weight. Users can also keep track of how many calories he/she needs and how much he/she consumes in a day. To calculate calories, we have two formulas:
For a male
BMR= 66.47+(13.75 x weight in kg) + (5.003 x height in cm) - (6.755 x age in years)
For a female
BMR=655.1+(9.563 x weight in kg)+(1.850 xheight in cm) - (4.676 x age in years)
Job Specification information:
1. Create a new Django project named Name_ID_CaloryCounter and a Calorie Counter app.
2. Define your Calorie Counter models.
3. Create views for Login (username/email and password) and Registration (username, email, password, confirm password) pages.
4. Create a django-form to take input Name, Age, Gender, Height, Weight etc.
5. Create a Django form to take input daily consumed calories (Item name, Calorie consumed)
6. Create a dashboard where the user can view the required calories for her/him and the consumed calories daily.
7. Define URL Patterns and configure project-level URLs.
8. Implement the required Function and Logic in the view.py file.
Job Specification information:
a. Create a new Django project. (Naming Convention: Name_ID_CaloryCounter)
b. Run migration to create the data tables.
c. Create a super user. (username: admin, password: 1234)
d. Register your models to the Django admin.
models.py
from django.db import models
from django.contrib.auth.models import User
from datetime import date
from django.utils import timezone
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100, default='')
age = models.IntegerField(default=25)
GENDER_CHOICES = [
('M', 'Male'),
('F', 'Female'),
]
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default='M')
height = models.FloatField(help_text="Height in cm", default=170.0)
weight = models.FloatField(help_text="Weight in kg", default=70.0)
def __str__(self):
return self.name
def calculate_bmr(self):
if self.gender == 'M':
bmr = 66.47 + (13.75 * self.weight) + (5.003 * self.height) - (6.755 * self.age)
else:
bmr = 655.1 + (9.563 * self.weight) + (1.850 * self.height) - (4.676 * self.age)
return round(bmr, 2)
class CalorieEntry(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
food_item = models.CharField(max_length=100, null=True)
calories_consumed = models.FloatField()
date = models.DateField(default=date.today)
time = models.TimeField(default=timezone.now)
def __str__(self):
return f"{self.food_item} - {self.calories_consumed} cal"
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from .models import UserProfile, CalorieEntry
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['name', 'age', 'gender', 'height', 'weight']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'age': forms.NumberInput(attrs={'class': 'form-control'}),
'gender': forms.Select(attrs={'class': 'form-select'}),
'height': forms.NumberInput(attrs={'class': 'form-control'}),
'weight': forms.NumberInput(attrs={'class': 'form-control'}),
}
class CalorieEntryForm(forms.ModelForm):
class Meta:
model = CalorieEntry
fields = ['food_item', 'calories_consumed', 'date', 'time']
widgets = {
'food_item': forms.TextInput(attrs={'class': 'form-control'}),
'calories_consumed': forms.NumberInput(attrs={'class': 'form-control'}),
'date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
'time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}),
}
class CustomUserCreationForm(UserCreationForm):
email = forms.EmailField(required=True, widget=forms.EmailInput(attrs={'class': 'form-control'}))
class Meta:
model = User
fields = ("username", "email", "password1", "password2")
widgets = {
'username': forms.TextInput(attrs={'class': 'form-control'}),
'password1': forms.PasswordInput(attrs={'class': 'form-control'}),
'password2': forms.PasswordInput(attrs={'class': 'form-control'}),
}
def save(self, commit=True):
user = super().save(commit=False)
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
admin.py
from django.contrib import admin
from .models import UserProfile, CalorieEntry
@admin.register(UserProfile)
class UserProfileAdmin(admin.ModelAdmin):
list_display = ('name', 'user', 'age', 'gender', 'height', 'weight')
@admin.register(CalorieEntry)
class CalorieEntryAdmin(admin.ModelAdmin):
list_display = ('user', 'food_item', 'calories_consumed', 'date', 'time')
list_filter = ('date', 'user')
views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth import login, authenticate
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .forms import UserProfileForm, CalorieEntryForm, CustomUserCreationForm
from .models import UserProfile, CalorieEntry
from datetime import date
def home(request):
if request.user.is_authenticated:
return redirect('calorie_counter:dashboard')
else:
return redirect('calorie_counter:login')
def register(request):
if request.method == 'POST':
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
messages.success(request, 'Registration successful.')
return redirect('calorie_counter:profile_form')
else:
form = CustomUserCreationForm()
return render(request, 'registration/register.html', {'form': form})
def login_view(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect("calorie_counter:dashboard")
else:
messages.error(request, "Invalid credentials.")
return redirect("calorie_counter:login")
return render(request, 'calorie_counter/login.html')
@login_required
def profile_form(request):
profile, created = UserProfile.objects.get_or_create(user=request.user)
if request.method == 'POST':
form = UserProfileForm(request.POST, instance=profile)
if form.is_valid():
form.save()
messages.success(request, 'Profile updated successfully.')
return redirect('calorie_counter:dashboard')
else:
form = UserProfileForm(instance=profile)
return render(request, 'calorie_counter/profile_form.html', {'form': form})
@login_required
def dashboard(request):
try:
profile = UserProfile.objects.get(user=request.user)
bmr = profile.calculate_bmr()
today = date.today()
entries_today = CalorieEntry.objects.filter(user=request.user, date=today)
total_consumed = sum(entry.calories_consumed for entry in entries_today)
remaining = bmr - total_consumed
context = {
'bmr': bmr,
'total_consumed': total_consumed,
'remaining': remaining,
'entries': entries_today,
}
except UserProfile.DoesNotExist:
messages.warning(request, 'Please complete your profile first.')
return redirect('calorie_counter:profile_form')
return render(request, 'calorie_counter/dashboard.html', context)
@login_required
def add_calorie_entry(request):
if request.method == 'POST':
form = CalorieEntryForm(request.POST)
if form.is_valid():
entry = form.save(commit=False)
entry.user = request.user
entry.save()
messages.success(request, 'Calorie entry added.')
return redirect('calorie_counter:dashboard')
else:
form = CalorieEntryForm()
return render(
request,
"calorie_counter/add_entry.html",
{"form": form, "title": "Add Calorie Entry", 'submit': 'Add',},
)
@login_required
def update_calorie_entry(request, pk):
entry = get_object_or_404(CalorieEntry, pk=pk, user=request.user)
if request.method == 'POST':
form = CalorieEntryForm(request.POST, instance=entry)
if form.is_valid():
form.save()
messages.success(request, 'Calorie entry updated.')
return redirect('calorie_counter:dashboard')
else:
form = CalorieEntryForm(instance=entry)
return render(
request,
"calorie_counter/add_entry.html",
{
"form": form,
"title": "Edit Calorie Entry",
"submit": "Update"
},
)
@login_required
def delete_calorie_entry(request, pk):
entry = get_object_or_404(CalorieEntry, pk=pk, user=request.user)
if request.method == 'POST':
entry.delete()
messages.success(request, 'Calorie entry deleted.')
return redirect('calorie_counter:dashboard')
return render(request, 'calorie_counter/delete_entry.html', {'entry': entry})
def logout_view(request):
from django.contrib.auth import logout
logout(request)
return redirect('calorie_counter:login')
@login_required
def logout(request):
logout(request)
return redirect("calorie_counter:login")
urls.py
from django.urls import path
from . import views
app_name = 'calorie_counter'
urlpatterns = [
path("", views.home, name="home"),
path("register/", views.register, name="register"),
path("login/", views.login_view, name="login"),
path("profile/", views.profile_form, name="profile_form"),
path("dashboard/", views.dashboard, name="dashboard"),
path("add-entry/", views.add_calorie_entry, name="add_entry"),
path("update-entry/<int:pk>/", views.update_calorie_entry, name="update_entry"),
path("delete-entry/<int:pk>/", views.delete_calorie_entry, name="delete_entry"),
]
projects/urls.py
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views
from calorie_counter import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('calorie_counter.urls')),
path('accounts/login/', auth_views.LoginView.as_view(template_name='registration/login.html'), name='login'),
path('accounts/logout/', views.logout_view, name='logout'),
]
project Templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Calorie Counter{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.regilog {
text-decoration: none;
color: #0d6efd;
}
.regilog:hover {
color: #04aa94ff;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="{% url 'calorie_counter:dashboard' %}">Calorie Counter</a>
<div class="navbar-nav ms-auto">
{% if user.is_authenticated %}
<a class="nav-link" href="{% url 'calorie_counter:dashboard' %}">Dashboard</a>
<a class="nav-link" href="{% url 'calorie_counter:profile_form' %}">Profile</a>
<a class="nav-link" href="{% url 'logout' %}">Logout</a>
{% else %}
<a class="nav-link" href="{% url 'calorie_counter:login' %}">Login</a>
<a class="nav-link" href="{% url 'calorie_counter:register' %}">Register</a>
{% endif %}
</div>
</div>
</nav>
<div class="container mt-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
register/register.html
{% extends 'base.html' %}
{% block title %}Register{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Register</h2>
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label for="id_username" class="form-label">Username</label>
<input type="text" class="form-control" id="id_username" name="username" required>
{% if form.username.errors %}
<div class="text-danger">{{ form.username.errors }}</div>
{% endif %}
</div>
<div class="mb-3">
<label for="id_email" class="form-label">Email</label>
<input type="email" class="form-control" id="id_email" name="email" required>
{% if form.email.errors %}
<div class="text-danger">{{ form.email.errors }}</div>
{% endif %}
</div>
<div class="mb-3">
<label for="id_password1" class="form-label">Password</label>
<input type="password" class="form-control" id="id_password1" name="password1" required>
{% if form.password1.errors %}
<div class="text-danger">{{ form.password1.errors }}</div>
{% endif %}
</div>
<div class="mb-3">
<label for="id_password2" class="form-label">Password confirmation</label>
<input type="password" class="form-control" id="id_password2" name="password2" required>
{% if form.password2.errors %}
<div class="text-danger">{{ form.password2.errors }}</div>
{% endif %}
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
<div class="text-center mt-3">
<p>Already have an account? <a class="regilog" href="{% url 'calorie_counter:login' %}"><b>Login here</b></a></p>
</div>
</div>
</div>
{% endblock %}
app template/calorie_counter/login.html
{% extends 'base.html' %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Login</h2>
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
<div class="text-center mt-3">
<p>Don't have an account? <a class="regilog" href="{% url 'calorie_counter:register' %}"><b>Register here</b></a></p>
</div>
</div>
</div>
{% endblock %}
dashboard.html
{% extends 'base.html' %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-8">
<h2>Dashboard</h2>
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Daily Calorie Summary</h5>
<p><strong>BMR:</strong> {{ bmr }} calories</p>
<p><strong>Total Consumed Today:</strong> {{ total_consumed }} calories</p>
<p><strong>Remaining:</strong> {{ remaining }} calories</p>
</div>
</div>
<a href="{% url 'calorie_counter:add_entry' %}" class="btn btn-primary mb-4">Add Calorie Entry</a>
<h3>Today's Entries</h3>
{% if entries %}
<table class="table">
<thead>
<tr>
<th>Food Item</th>
<th>Calories Consumed</th>
<th>Time</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for entry in entries %}
<tr>
<td>{{ entry.food_item }}</td>
<td>{{ entry.calories_consumed }}</td>
<td>{{ entry.time }}</td>
<td>
<a href="{% url 'calorie_counter:update_entry' entry.pk %}" class="btn btn-sm btn-primary">Edit</a>
<a href="{% url 'calorie_counter:delete_entry' entry.pk %}" class="btn btn-sm btn-danger">Delete</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No entries for today.</p>
{% endif %}
</div>
</div>
{% endblock %}
profile.html
{% extends 'base.html' %}
{% block title %}Profile{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Update Profile</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
{% endblock %}
add-entry.html
{% extends 'base.html' %}
{% block title %}Add Calorie Entry{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<h2>{{title}}</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">{{submit}}</button>
</form>
</div>
</div>
{% endblock %}
delete-entry.html
{% extends 'base.html' %}
{% block title %}Delete Calorie Entry{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Delete Calorie Entry</h2>
<p>Are you sure you want to delete the entry for "{{ entry.food_item }}" with {{ entry.calories_consumed }} calories?</p>
<form method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Yes, Delete</button>
<a href="{% url 'calorie_counter:dashboard' %}" class="btn btn-secondary">Cancel</a>
</form>
</div>
</div>
{% endblock %}
settings.html
"""
Django settings for Mahfuz_18_CaloryCounter project.
Generated by 'django-admin startproject' using Django 5.2.7.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-2r%kmai(^nsd02$=ae=uj2+$($^=12bk^!tucgj+5r#fx^p3rr'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'calorie_counter',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'Mahfuz_18_CaloryCounter.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'Mahfuz_18_CaloryCounter.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_URL = 'calorie_counter:login'