Open In App

Task Pomodoro Timer using Django

Last Updated : 16 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

This article explores the development of a Task Pomodoro Timer using Django. Users can set, stop, pause, and delete time intervals for tasks. To engage these features, signing up and logging in are mandatory. The timer offers customization options, allowing users to set seconds, hours, minutes, and categories for efficient time management.

What is Task Pomodoro Timer?

The Task Pomodoro Timer is a Django-based time management tool that enables users to set, stop, pause, and delete task intervals. Built on the principles of the Pomodoro Technique, it helps enhance productivity by allowing customization of time intervals, including seconds, hours, and minutes. Users need to sign up and log in to access and personalize these features for effective time management.

Create Task Pomodoro Timer Using Django

Below is the step-by-step implementation of the Task Pomodoro Timer.

To install Django follow these steps.

Starting the Project Folder

To start the project use this command:

django-admin startproject core
cd core

To start the app use this command

python manage.py startapp home

Now add this app to the ‘settings.py’

INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"home",
]

Setting Necessary Files

models.py: In this example code defines a Django model named “Timers” with fields such as title, hours, minutes, seconds, category, uuid, and priority. It represents attributes for a Pomodoro Timer, with default values specified for some fields. This model facilitates the storage and management of timer-related information in a Django application.

Python3




from django.db import models
 
# models.py
class Timers(models.Model):
    title = models.CharField(max_length=100)
    hours = models.IntegerField(default=0)
    minutes = models.IntegerField(default=25)
    seconds = models.IntegerField(default=0)
    category = models.CharField(max_length=50# Add this line
    uuid = models.IntegerField(default=0)
    priority = models.IntegerField(default=0)


views.py : In this example code defines Django views for a Pomodoro timer web app. It handles user authentication, registration, timer addition, deletion, and displays timers ordered by priority. The ‘pomodoro_timer’ view renders timers and a form, while ‘login’ and ‘signup’ manage user authentication and registration, respectively.

Python3




from django.shortcuts import render, redirect
from .models import Timers
from django.contrib.auth.models import User
from django.contrib import auth
from django.contrib import messages
from .forms import PomodoroForm
from django.db.models import Sum, F, ExpressionWrapper, fields
 
# views.py
 
def pomodoro_timer(request):
  timers = Timers.objects.all().order_by('priority')
  form = PomodoroForm()
   
  if len(timers) == 0:
      return render(request, 'pomodromo_timer.html', {
          'form': form,
          'editable': False,
          'timers': None,
      })
   
  return render(request, 'pomodromo_timer.html', {
      'form': form,
      'editable': False,
      'timers': timers,
  })
 
def login(request):
    if request.method == "POST":
        email = request.POST['email']
        password = request.POST['password']
        if User.objects.filter(username=email).exists():
            user = auth.authenticate(username=email, password=password)
            print(user)
            if user is not None:
                auth.login(request, user)
                return redirect('pomodoro_timer')
            else:
                messages.error(request, 'Invalid credentials')
                return redirect("login")
        else:
            messages.info(request, "Invalid email or password")
            return redirect('login')
    else:
        return render(request, 'login.html')
 
 
def signup(request):
    if request.method == 'POST':
        name = request.POST['username']
        email = request.POST['email']
        password = request.POST['password']
        if User.objects.filter(first_name=name).exists():
            messages.info(request, "Username already taken")
            return redirect('signup')
        elif User.objects.filter(username=email).exists():
            messages.info(request, "Email already taken")
            return redirect('signup')
        else:
            user = User.objects.create_user(first_name=name,
                                            username=email,
                                            password=password)
            print(user)
            print("User registered Successfully")
            user.save()
            return redirect('login')
    else:
        return render(request, 'signup.html')
 
def logout(request):
    auth.logout(request)
    return redirect('pomodoro_timer')
 
def add(request,id):
    editable = True
    if request.method == "POST":
        editable = False
        form = PomodoroForm(request.POST)
        if form.is_valid():
            form_instance = form.save(commit=False)
            form_instance.uuid = id
            form_instance.save()
            return redirect("pomodoro_timer")
    else:
        form = PomodoroForm()
        timers = Timers.objects.all()
        return render(request, 'pomodromo_timer.html', {
            'form': form,
            "editable": editable,
            'timers': timers
        })
 
 
def delete(request, id):
    timers = Timers.objects.get(id=id)
    timers.delete()
    print("Successfully Deleted {{id}}")
    timers = Timers.objects.all()
    form = PomodoroForm()
    if len(timers) == 0:
        return render(request, 'pomodromo_timer.html', {
            'form': form,
            "editable": False,
            'timers': None
        })
    return render(request, 'pomodromo_timer.html', {
        'form': form,
        "editable": False,
        'timers': timers
    })


forms.py : This Django form, named `PomodoroForm`, is associated with the `Timers` model. It includes fields for ‘title,’ ‘hours,’ ‘minutes,’ ‘seconds,’ ‘category,’ and ‘priority.’ The form defines widget attributes to enforce required input and validates that hours, minutes, and seconds are non-negative in the `clean` method, raising a validation error if any are negative.

Python3




from django import forms
from .models import Timers
 
class PomodoroForm(forms.ModelForm):
  class Meta:
      model = Timers
      fields = ['title','hours','minutes','seconds','category','priority']
      widgets = {
          'title': forms.TextInput(attrs={'required': 'required'}),
          'hours': forms.NumberInput(attrs={'required': 'required'}),
          'minutes': forms.NumberInput(attrs={'required': 'required'}),
          'seconds': forms.NumberInput(attrs={'required': 'required'}),
          'category': forms.TextInput(attrs={'required': 'required'}),
          'priority': forms.NumberInput(attrs={'required': 'required'}),
      }
 
  def clean(self):
      cleaned_data = super().clean()
      hours = cleaned_data.get('hours', 0)
      minutes = cleaned_data.get('minutes', 0)
      seconds = cleaned_data.get('seconds', 0)
 
      if hours < 0 or minutes < 0 or seconds < 0:
          raise forms.ValidationError("Hours, minutes, and seconds must be non-negative.")
 
      return cleaned_data


core/urls.py : This Django code sets up URL patterns, linking the ‘admin/’ path to Django’s admin interface and incorporating URLs from the ‘home’ app for the root path. The ‘include’ function enables integrating app-specific URLs into the project’s configuration.

Python3




from django.contrib import admin
from django.urls import path, include
 
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('home.urls')),
]


home/urls.py : This Django code defines URL patterns for different views: ‘pomodoro_timer,’ ‘login,’ ‘signup,’ ‘logout,’ ‘add/<int:id>,’ and ‘delete/<str:id>.’ It associates these paths with their respective views, allowing parameterized input for ‘add’ and ‘delete’ views.

Python3




from django.urls import path
from .views import pomodoro_timer, login, signup, logout, add, delete
 
urlpatterns = [
    path('', pomodoro_timer, name='pomodoro_timer'),
    path('login/', login, name='login'),
    path('signup/', signup, name='signup'),
    path('logout/', logout, name='logout'),
    path('add/<int:id>', add, name="add"),
    path('delete/<str:id>/', delete, name="delete"),
]


Creating User Interface

login.html : This HTML code defines a Bootstrap-based login page. It includes Bootstrap styles and scripts, displays error messages if any, and provides a form with email and password input fields. The form has a submit button for logging in and a link to the registration page if the user doesn’t have an account.

HTML




<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap demo</title>
          rel="stylesheet"
          integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
          crossorigin="anonymous">
 
  </head>
  <body>
    <div class="bg-body-danger m-3">
      {% for message in messages %}
      <h3>{{message}}</h3>
      {% endfor %}
    </div>
    <div class="container mt-5 mx-auto w-50">
        <h1 class="h2 mb-3">Login Page</h1>
        <form action="{% url 'login' %}" method="POST">
          {% csrf_token %}
            <div class="form-group lh-lg">
                <label for="email">Email:</label>
                <input type="text" id="email" name="email" class="form-control" required>
            </div>
            <div class="form-group">
                <label for="password">Password:</label>
                <input type="password" id="password" name="password" class="form-control" required>
            </div>
            <input type="submit" value="Login" class="btn btn-primary mt-3 text-center">
          <p>Don't have an account ? <a href="{% url 'signup' %}">Register</a></p>
        </form>
    </div>
            integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
            crossorigin="anonymous"></script>
  </body>
</html>


pomodromo.html :This HTML page integrates Bootstrap for styling, includes custom CSS and JS files, and features a navigation bar. It displays a Pomodoro timer section with start, pause, reset buttons and dynamically shows user-specific timers, allowing start, pause, reset, and deletion. An editable form for adding new timers is included, and the page adapts based on user authentication status.

HTML




{% load static %}
<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Bootstrap demo</title>
              rel="stylesheet"
              integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
              crossorigin="anonymous">
      <link rel='stylesheet' href="{% static 'style.css'%}">
      <script src="{% static 'app.js' %}"></script>
 
    </head>
    <body>
    <section class="navbar--section fw-bold">
        <nav class="navbar navbar-expand-lg bg-body-tertiary">
            <div class="container-fluid">
                <a class="navbar-brand" href="#">Pomodoro Timer</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
                        data-bs-target="#navbarNavAltMarkup"
                        aria-controls="navbarNavAltMarkup" aria-expanded="false"
                        aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="collapse navbar-collapse justify-content-end" id="navbarNavAltMarkup">
                    <div class="navbar-nav">
                        {% if user.is_authenticated %}
                        <a class="nav-link mx-3" href="{% url 'add'  user.id %}">
                          <img width="30" src="https://img.icons8.com/ios/50/add--v1.png"
                               alt="add--v1"/>
                        </a>
                        <a class="btn btn-primary p-2 fw-bold" href="{% url 'logout' %}">Logout</a>
                        {% else %}
                        <a class="nav-link" href="{% url 'login' %}">Login/SignUp</a>
                        {% endif %}
                    </div>
                </div>
            </div>
        </nav>
    </section>
 
    <section class="timer--section">
        <div class="container">
            <div class="row">
 
                <div class="col-lg-12 mt-5 h4 text-center">
                    <p class="h2 text-center">Pomodoro</p>
                    <div class="timer ">
                        <span id="hours">00</span>:<span id="minutes">25</span>:<span
                                                          id="seconds">00</span>
                    </div>
 
                    <button class="btn btn-primary" onclick="startMainTimer()">Start</button>
                    <button class="btn btn-primary" onclick="pauseTimer()">Pause</button>
                    <button class="btn btn-primary" onclick="resetTimer()">Reset</button>
                </div>
            </div>
        </div>
    </section>
       
    <section class="custom--time">
        <div class="container">
            <p class=" h3">Your Tasks By Categories</p>
            {% if user.is_authenticated%}
 
 
            <div class="row">
                {% if editable %}
                <form id="pomodoroForm" method="POST" action="{% url 'add' user.id %}">
                    {% csrf_token %}
                    <div class="h5 col-lg-12  mt-5 editable--form">
                        {{form}}
                        <div class="editable--form--btn text-center">
                            <button class=" btn btn-primary " type="submit">Save</button>
                        </div>
                    </div>
                </form>
                {% else %}
 
                {% if timers is None %}
                <div class="h5 col-lg-12 mt-5 text-center">
                    <p class="h4 text-center">No timer is set</p>
                </div>
 
                {% else %}
 
                {% for timer in timers %}
                  {% if timer.uuid == user.id %}
 
              <div class="container mt-5 p-5  bg-opacity-10 rounded ">
                <div class="row text-center">
                    <div class="col ">
                        <p class="fw-bolder text-center fs-3">Category : {{ timer.category }}</p>
                        <p class="fw-bolder text-center">Title : {{ timer.title }}</p>
                    </div>
                    <div class="col d-flex align-items-center justify-content-center timer">
                      <span id="hours{{ timer.id }}">{{ timer.hours }}</span>:
                      <span id="minutes{{ timer.id }}">{{ timer.minutes }}</span>:
                      <span id="seconds{{ timer.id }}">{{ timer.seconds }}</span>
 
                      <button class="btn btn-primary m-2"
                              onclick="startITimer('{{ timer.id }}')">Start</button>
                      <button class="btn btn-primary m-2"
                              onclick="pauseITimer('{{ timer.id }}')">Pause</button>
                      <button class="btn btn-primary m-2"
                              onclick="resetITimer('{{ timer.id }}')">Reset</button>
                            <a class="btn btn-danger m-2" 
                               href="/delete/{{ timer.id }}">Delete</a>
                        <span id="initialMinutes{{ timer.id }}"
                              style="display: none;">{{ timer.minutes }}</span>
                        <span id="initialSeconds{{ timer.id }}"
                              style="display: none;">{{ timer.seconds }}</span>
 
                          <!-- Add an additional hidden input field to store the timer ID -->
                          <input type="hidden" class="timerId" value="{{ timer.id }}">
                    </div>
                </div>
 
                <div class="round-time-bar" data-style="smooth" style="--duration: 150;">
                    <div id="progressBar{{ timer.id }}"></div>
                </div>
     
                  </div>
              {%endif%}
                {% endfor %}
                {% endif %}
                {% endif %}
            </div>
            {% else %}
            <div class="row">
 
                <div class="col-lg-12 p-5 mt-5 text-center">
 
                    <p class="h3">Please <a href="{% url 'login' %}"
                       class="btn btn-primary">Login</a> to see your custom timers</p>
                </div>
            </div>
            {% endif %}
      </div>
    </section>
     
            integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
            crossorigin="anonymous"></script>
  </body>
</html>


signup.html : This HTML code creates a Bootstrap-styled signup page with a form. It includes fields for name, email, and password, along with validation attributes. The page displays error messages and provides links for users to navigate between signup and login pages.

HTML




<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap demo</title>
          rel="stylesheet"
          integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
          crossorigin="anonymous">
 
  </head>
  <body>
    <div class="bg-body-danger m-3">
      {% for message in messages %}
      <h3>{{message}}</h3>
      {% endfor %}
    </div>
    <div class="container mt-5 mx-auto w-50">
        <h1 class="h2 mb-3">SignUp Page</h1>
        <form action="{% url 'signup' %}" method="POST">
          {% csrf_token %}
          <div class="form-group lh-lg">
              <label for="username">Name:</label>
              <input type="text" id="username" name="username" class="form-control" required>
          </div>
            <div class="form-group lh-lg">
                <label for="email">Email:</label>
                <input type="text" id="email" name="email" class="form-control" required>
            </div>
            <div class="form-group">
                <label for="password">Password:</label>
                <input type="password" id="password" name="password" class="form-control" required>
            </div>
            <input type="submit" value="Signup" class="btn btn-primary mt-3 text-center">
          <p>Already have an account ? <a href="{% url 'login' %}">Login</a></p>
        </form>
    </div>
            integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
            crossorigin="anonymous"></script>
  </body>
</html>


Static Folder

style.css: This CSS code styles a Pomodoro timer web page, adjusting the positioning, font size, and margins for elements like buttons and timers. It includes a round progress bar for visualizing timer progress and positions an element to the right using the `.add` class.

CSS




.add {
    margin-left: 70%;
    margin-right: 0%;
}
 
.timer {
    font-size: 2em;
    margin-bottom: 20px;
}
 
button {
    padding: 10px 20px;
    font-size: 1em;
    cursor: pointer;
    margin: 0 5px;
}
 
.editable--form {
    display: flex;
    flex-direction: column;
    padding: 1rem;
    width: 50%;
    margin: 0 auto;
}
 
.editable--form--btn {
    padding: 2rem;
}
.round-time-bar {
width: 100%;
height: 20px;
background-color: #f0f0f0;
border-radius: 10px;
overflow: hidden;
}
 
.round-time-bar div {
height: 100%;
background-color: #4caf50; /* Change this color to your preferred color */
border-radius: 10px;
width: 0; /* Set initial width to zero *//* This duration should match the timer duration */
}


app.js: This JavaScript code defines functions for a main Pomodoro timer and individual timers. It includes functionalities for starting, updating, completing, pausing, resuming, and resetting timers. The main timer runs for 25 minutes, while individual timers are customizable. The code also incorporates audio notifications using the ‘Airtel Mp3 – Airtel Song.mp3’ file.

Javascript




// Main pomodoro timer
let mainTimer;
let mainSeconds = 0;
let mainMinutes = 25;
let mainHours = 0;
let mainIsTimerRunning = false;
 
const audio = new Audio('static/media/audio/Airtel Mp3 - Airtel Song.mp3');
 
function startMainTimer() {
    if (!mainIsTimerRunning) {
        mainTimer = setInterval(updateMainTimer, 1000);
        mainIsTimerRunning = true;
    }
}
 
function updateMainTimer() {
    mainSeconds--;
 
    if (mainSeconds < 0) {
        mainSeconds = 59;
 
        mainMinutes--;
 
        if (mainMinutes < 0) {
            mainMinutes = 59;
 
            mainHours--;
 
            if (mainHours < 0) {
                clearInterval(mainTimer);
                timerComplete();
                return;
            }
        }
    }
 
    updateMainTimerDisplay();
}
 
function updateMainTimerDisplay() {
    const formattedMainHours = padTime(mainHours);
    const formattedMainMinutes = padTime(mainMinutes);
    const formattedMainSeconds = padTime(mainSeconds);
 
    document.getElementById('hours').innerText = formattedMainHours;
    document.getElementById('minutes').innerText = formattedMainMinutes;
    document.getElementById('seconds').innerText = formattedMainSeconds;
}
 
function timerComplete() {
    audio.play();
}
 
function pauseTimer() {
    clearInterval(mainTimer);
    mainIsTimerRunning = false;
}
 
function resetTimer() {
    clearInterval(mainTimer);
    mainIsTimerRunning = false;
    mainSeconds = 0;
    mainHours = 0;
    mainMinutes = 25;
    updateMainTimerDisplay();
}
 
function padTime(time) {
    return (time < 10) ? `0${time}` : time;
}
 
 
 
// For individual timer
const timers = {};
 
function startITimer(timerId) {
    const initialMinutes = parseInt(document.getElementById(`initialMinutes${timerId}`).innerText) || 0;
    const initialSeconds = parseInt(document.getElementById(`initialSeconds${timerId}`).innerText) || 0;
 
    const totalSeconds = initialMinutes * 60 + initialSeconds;
 
    timers[timerId] = {
        timer: setInterval(() => updateITimer(timerId), 1000),
        isTimerRunning: true,
        hours: 0,
        minutes: initialMinutes,
        seconds: initialSeconds,
        totalSeconds: totalSeconds,
        progressBar: document.getElementById(`progressBar${timerId}`)
    };
 
    setProgressBarDuration(timerId, totalSeconds);
    updateITimerDisplay(timerId);
}
 
function setProgressBarDuration(timerId, duration) {
    const progressBar = timers[timerId].progressBar;
    progressBar.style.setProperty('--duration', duration + 's');
}
 
function updateITimer(timerId) {
    let timer = timers[timerId];
 
    // Check if the timer is running
    if (timer.isTimerRunning) {
        timer.seconds--;
 
        if (timer.seconds < 0) {
            timer.seconds = 59;
            timer.minutes--;
 
            if (timer.minutes < 0) {
                timer.minutes = 59;
                timer.hours--;
 
                if (timer.hours < 0) {
                    clearInterval(timer.timer);
                    timerComplete(timerId);
                    timer.isTimerRunning = false;
                    return;
                }
            }
        }
 
        updateITimerDisplay(timerId);
        updateProgressBar(timerId);
    }
}
 
function updateProgressBar(timerId) {
    let timer = timers[timerId];
 
    // Check if the timer is still running
    if (timer.isTimerRunning) {
        let remainingSeconds = timer.hours * 3600 + timer.minutes * 60 + timer.seconds;
        let progressPercentage = ((timer.totalSeconds - remainingSeconds) / timer.totalSeconds) * 100;
 
        timer.progressBar.style.width = `${progressPercentage}%`;
    }
}
 
 
function pauseITimer(timerId) {
    const timer = timers[timerId];
 
    if (timer.isTimerRunning) {
        clearInterval(timer.timer);
        timer.isTimerRunning = false;
    }
}
 
function resumeITimer(timerId) {
    const timer = timers[timerId];
 
    if (!timer.isTimerRunning) {
        timer.timer = setInterval(() => updateITimer(timerId), 1000);
        timer.isTimerRunning = true;
    }
}
 
function resetITimer(timerId) {
    const timerIdInput = document.querySelector(`input.timerId[value='${timerId}']`);
    const initialMinutes = parseInt(document.getElementById(`initialMinutes${timerId}`).innerText) || 0;
    const initialSeconds = parseInt(document.getElementById(`initialSeconds${timerId}`).innerText) || 0;
 
    let timer = timers[timerId];
    clearInterval(timer.timer);
    timer.isTimerRunning = false;
    timer.seconds = initialSeconds;
    timer.hours = 0;
    timer.minutes = initialMinutes;
    updateITimerDisplay(timerId);
}
 
function timerComplete(timerId) {
    const audio1 = new Audio('static/media/audio/Airtel Mp3 - Airtel Song.mp3');
    console.log("Played...")
    audio1.play();
}
 
function updateITimerDisplay(timerId) {
    let timer = timers[timerId];
    const formattedHours = padTime(timer.hours);
    const formattedMinutes = padTime(timer.minutes);
    const formattedSeconds = padTime(timer.seconds);
 
    document.getElementById(`hours${timerId}`).innerText = formattedHours;
    document.getElementById(`minutes${timerId}`).innerText = formattedMinutes;
    document.getElementById(`seconds${timerId}`).innerText = formattedSeconds;
}
 
function padTime(time) {
    return (time < 10) ? `0${time}` : time;
}


admin.py:Here we are registering our models.

Python3




from django.contrib import admin
from .models import Timers
 
# Register your models here.
admin.site.register(Timers)


Deployement of the Project

Run these commands to apply the migrations:

python3 manage.py makemigrations
python3 manage.py migrate

Run the server with the help of following command:

python3 manage.py runserver

Output

first-

second

third-

iooo



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads