Criando o Gerenciador de Tarefas com Python e Flask

O Gerenciador de Tarefas é uma aplicação web criada com Python e Flask para auxiliar na gestão e produtividade.

O projeto possui três funcionalidades principais:

  • receber uma sugestão aleatória de tarefa;
  • utilizar um cronómetro para medir o tempo de uma atividade;
  • calcular a média e o desvio padrão do tempo de duas tarefas.

Para construir a aplicação utilizamos:

  • Python
  • Flask
  • HTML
  • CSS
  • JavaScript
  • Jinja

Estrutura do projeto

				
					Gerenciador-de-Tarefas/
│
├── backend/
│   ├── app.py
│   ├── cronometro.py
│   ├── estatisticas.py
│   └── sugestao.py
│
└── frontend/
    ├── templates/
    │   ├── index.html
    │   ├── tarefas.html
    │   ├── cronometro.html
    │   └── estatisticas.html
    │
    └── static/
        └── css/
            └── style.css
				
			

Passo 1: Instalando o Flask

O Flask é o framework Python utilizado para criar o servidor web da aplicação.

No terminal, execute:

				
					pip install flask
				
			

Passo 2: Criar o sistema de sugestões

Começaremos pela funcionalidade mais simples da aplicação.

Abra o arquivo:

				
					backend/sugestao.py
				
			

Primeiro, importe a biblioteca random:

				
					import random
				
			

Essa biblioteca permite realizar escolhas aleatórias.

Agora crie a função:

				
					def sugerir_tarefa():
    tarefas = [
        "Ler um artigo",
        "Fazer um exercício de Python",
        "Revisar anotações"
    ]

    return random.choice(tarefas)
				
			

A variável tarefas armazena uma lista contendo diferentes atividades. Sempre que a função sugerir_tarefa() for executada, uma das tarefas poderá ser escolhida.

Passo 3: Criar o cronômetro

Agora vamos criar o módulo responsável por registrar o tempo das tarefas.

Abra:

				
					backend/cronometro.py
				
			

Importe a biblioteca time:

				
					import time
				
			

Em seguida, crie a primeira função:

				
					def iniciar_cronometro():
    return time.time()
				
			

A função time.time() retorna um valor representando o momento atual.

Esse valor será utilizado como ponto inicial do cronômetro.

Agora crie a função responsável por pausá-lo:

				
					def pausar_cronometro(inicio):
    fim = time.time()
    duracao = fim - inicio

    return duracao
				
			

Ou seja, subtraímos o momento em que o cronômetro foi iniciado do momento em que ele foi pausado.

O resultado representa quantos segundos transcorreram durante aquele período.

Passo 4: Criar o cálculo das estatísticas

Além de controlar o tempo das atividades, nosso Gerenciador de Tarefas permitirá comparar o tempo gasto em duas tarefas.

Abra:

				
					backend/estatisticas.py
				
			

Importe a biblioteca math:

				
					import math
				
			

Agora crie:

				
					def calcular_estatisticas(tarefa1, tarefa2):
    media = (tarefa1 + tarefa2) / 2
    desvio_padrao = math.sqrt(
    (
        (tarefa1 - media) ** 2
        + (tarefa2 - media) ** 2
    ) / 2
)
    
				
			

A média é calculada somando os dois tempos e dividindo o resultado por 2. Dentro da mesma função, o projeto também calcula o desvio padrão.

Passo 5: Criar a aplicação Flask

Agora podemos conectar os módulos anteriores utilizando Flask.

Abra:

				
					backend/app.py
				
			

Comece com as importações:

				
					from flask import Flask, render_template, request, redirect, url_for, session
import time

import cronometro
import estatisticas
import sugestao
				
			

Como nossos arquivos HTML e CSS estão fora da pasta backend, precisamos informar ao Flask onde encontrá-los.

				
					app = Flask(
    __name__,
    template_folder="../frontend/templates",
    static_folder="../frontend/static"
)
				
			

Depois definimos uma chave para que o Flask possa utilizar sessões:

				
					app.secret_key = "gerenciador-chave-desenvolvimento"
				
			

As sessões serão utilizadas principalmente para guardar o estado do cronômetro durante a navegação.

No final do arquivo app.py, encontramos:

				
					if __name__ == "__main__":
    app.run(debug=True)
				
			

Esse trecho inicia o servidor Flask quando executamos diretamente o arquivo.

Passo 6: Criar a página inicial

gerenciador

O endereço / representa a página inicial do sistema.

				
					@app.route("/")
def inicio():
    return render_template("index.html")
				
			

Quando o usuário acessá-la, Flask renderiza:

				
					index.html
				
			

Na página HTML, o menu principal apresenta três opções:

				
					<a
    href="{{ url_for('tarefas') }}"
    class="botao"
>
    <span class="icone-play">▶</span>
    Tarefas aleatórias
</a>

<a
    href="{{ url_for('pagina_cronometro') }}"
    class="botao"
>
    <span class="icone-play">▶</span>
    Cronômetro
</a>

<a
    href="{{ url_for('pagina_estatisticas') }}"
    class="botao"
>
    <span class="icone-play">▶</span>
    Estatística
</a>
				
			

A função url_for() é utilizada para gerar os endereços das rotas Flask.

Assim, a página inicial funciona como o menu principal da aplicação.

Passo 7: Criar a página de tarefas aleatórias

gerenciador

No backend temos a rota:

				
					@app.route("/tarefas")
def tarefas():
    return render_template(
        "tarefas.html",
        tarefa=None
    )
				
			

Inicialmente enviamos: tarefa=None. Isso significa que nenhuma tarefa foi sorteada ainda.

Agora criamos outra rota:

				
					@app.route("/tarefas/sugerir", methods=["POST"])
def sugerir_tarefa():
    tarefa = sugestao.sugerir_tarefa()

    return render_template(
        "tarefas.html",
        tarefa=tarefa
    )
				
			

Dentro de tarefas.html, verificamos se uma tarefa foi recebida:

				
					<form
    action="{{ url_for('sugerir_tarefa') }}"
    method="POST"
>

    <button
        type="submit"
        class="botao"
    >
        <span class="icone-play">▶</span>
        Clique aqui
    </button>

</form>

{% if tarefa %}

    <div class="resultado-tarefa">
        {{ tarefa }}
    </div>

{% endif %}
				
			

Ao clicar no botão, uma requisição POST é enviada para a rota responsável por escolher a tarefa aleatória.

Passo 8: Criar a página de cronômetro

Agora vamos conectar o módulo cronometro.py à aplicação. A página começa verificando se existe um cronômetro ativo:

				
					ativo = session.get("cronometro_ativo", False)
tempo_acumulado = session.get("tempo_acumulado", 0.0)
				
			

A sessão funciona como um local onde podemos manter algumas informações durante a utilização da aplicação.

Neste caso, armazenamos:

  • se o cronômetro está ativo;
  • o momento em que foi iniciado;
  • o tempo já acumulado.

Se o cronômetro estiver ativo:

				
					if ativo:
    inicio_cronometro = session.get(
        "inicio_cronometro",
        time.time()
    )

    tempo_atual = tempo_acumulado + (
        time.time() - inicio_cronometro
    )
else:
    tempo_atual = tempo_acumulado
				
			

Depois enviamos essas informações para a página HTML:

				
					return render_template(
    "cronometro.html",
    ativo=ativo,
    tempo_atual=tempo_atual
)
				
			

Para iniciar o cronômetro temos:

				
					@app.route("/cronometro/iniciar", methods=["POST"])
def iniciar_cronometro():
    if not session.get("cronometro_ativo", False):
        session["inicio_cronometro"] = cronometro.iniciar_cronometro()
        session["cronometro_ativo"] = True

    return redirect(url_for("pagina_cronometro"))
				
			

Para iniciar o cronômetro temos:

				
					@app.route("/cronometro/pausar", methods=["POST"])
def pausar_cronometro():
    if session.get("cronometro_ativo", False):
        inicio = session.get("inicio_cronometro")
        acumulado = session.get("tempo_acumulado", 0.0)

        if inicio is not None:
            duracao = cronometro.pausar_cronometro(inicio)
            session["tempo_acumulado"] = acumulado + duracao

        session["cronometro_ativo"] = False
        session.pop("inicio_cronometro", None)

    return redirect(url_for("pagina_cronometro"))
				
			

Para zerá-lo utilizamos:

				
					@app.route("/cronometro/zerar", methods=["POST"])
def zerar_cronometro():
    session["cronometro_ativo"] = False
    session["tempo_acumulado"] = 0.0
    session.pop("inicio_cronometro", None)

    return redirect(url_for("pagina_cronometro"))
				
			

O arquivo cronometro.html recebe os dados enviados pelo Flask. Eles são armazenados em um elemento HTML:

				
					<div
    id="dados-cronometro"
    data-ativo="{{ 'true' if ativo else 'false' }}"
    data-tempo="{{ tempo_atual }}"
></div>
				
			

Depois o JavaScript recupera essas informações:

				
					const dadosCronometro =
    document.getElementById("dados-cronometro");

const ativo =
    dadosCronometro.dataset.ativo === "true";

const tempoInicial =
    Number(dadosCronometro.dataset.tempo);
				
			

Criamos então uma função para transformar os segundos em minutos e segundos:

				
					function mostrarTempo(totalSegundos) {

    const segundosInteiros =
        Math.floor(totalSegundos);

    const minutos =
        Math.floor(segundosInteiros / 60);

    const segundos =
        segundosInteiros % 60;

    elementoMinutos.textContent =
        String(minutos).padStart(2, "0");

    elementoSegundos.textContent =
        String(segundos).padStart(2, "0");
}
				
			

Se o cronômetro estiver ativo, utilizamos:

				
					setInterval(function () {

    const tempoDecorrido =
        (performance.now() - referencia) / 1000;

    mostrarTempo(
        tempoInicial + tempoDecorrido
    );

}, 250);
				
			

O navegador atualiza a visualização do cronômetro várias vezes por segundo, enquanto o valor principal continua sendo controlado pelo backend.

Passo 9: Criar a página de estatísticas

Agora vamos criar a rota responsável pelos cálculos. No app.py temos:

				
					@app.route("/estatisticas", methods=["GET", "POST"])
def pagina_estatisticas():
    media = None
    desvio_padrao = None
    erro = None

    if request.method == "POST":
        try:
            tarefa1 = float(request.form["tarefa1"])
            tarefa2 = float(request.form["tarefa2"])

            media, desvio_padrao = estatisticas.calcular_estatisticas(
                tarefa1,
                tarefa2
            )

        except (ValueError, KeyError):
            erro = "Digite valores numéricos válidos."

    return render_template(
        "estatisticas.html",
        media=media,
        desvio_padrao=desvio_padrao,
        erro=erro
    )
				
			
  • GET é utilizado quando acessamos a página.
  • POST é utilizado quando enviamos os valores do formulário.
  • Os valores calculados são enviados novamente para o HTML

No arquivo estatisticas.html, criamos os campos para receber os tempos.

				
					<form
    action="{{ url_for('pagina_estatisticas') }}"
    method="POST"
    class="form-estatisticas"
>

    <div class="grupo-campo">

        <label for="tarefa1">
            Digite o tempo da primeira tarefa:
        </label>

        <input
            type="number"
            id="tarefa1"
            name="tarefa1"
            min="0"
            step="0.01"
            placeholder="00"
            required
        >

    </div>

    <div class="grupo-campo">

        <label for="tarefa2">
            Digite o tempo da segunda tarefa:
        </label>

        <input
            type="number"
            id="tarefa2"
            name="tarefa2"
            min="0"
            step="0.01"
            placeholder="00"
            required
        >

    </div>

    <button
        type="submit"
        class="botao botao-calcular"
    >
        Calcular
    </button>

</form>

<div class="resultados-estatisticas">

    <div class="resultado">

        <h2>Tempo médio:</h2>

        <p>
            {% if media is not none %}
                {{ media }} segundos
            {% else %}
                --
            {% endif %}
        </p>

    </div>

    <div class="resultado">

        <h2>Desvio padrão:</h2>

        <p>
            {% if desvio_padrao is not none %}
                {{ desvio_padrao }} segundos
            {% else %}
                --
            {% endif %}
        </p>

    </div>

    {% if erro %}

        <p class="mensagem-erro">
            {{ erro }}
        </p>

    {% endif %}

</div>
				
			

Passo 10: Criar a aparência da aplicação

Toda a estilização do projeto está concentrada em:

				
					frontend/static/css/style.css
				
			

Primeiro existe um reset básico:

				
					* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
				
			

Depois configuramos o corpo da página:

				
					body {
    min-height: 100vh;

    background-color: #f4f4f4;

    font-family: "Inter", Arial, sans-serif;

    display: flex;
    justify-content: center;
}
				
			

O conteúdo principal utiliza um fundo em gradiente:

				
					.principal {
    background:
        linear-gradient(
            180deg,
            #17112d 0%,
            #18266f 23%,
            #173cb7 58%,
            #344fe6 80%,
            #5b50e9 100%
        );

    color: white;
}
				
			

Os botões possuem formato arredondado:

				
					.botao {
    width: 205px;
    min-height: 38px;

    border: none;
    border-radius: 999px;

    background-color: #000000;
    color: white;
}

.botao:hover {
    transform: scale(1.025);
}
				
			

Tornando o layout responsivo:

				
					@media (max-width: 430px) {

    .pagina {
        max-width: none;
        padding: 0;
    }

    .principal {
        min-height: 100vh;
        max-height: none;
    }
}

@media (max-height: 720px) {

    .titulo-principal {
        padding-top: 100px;
    }

    .menu-principal {
        margin-top: 60px;
    }

    .cronometro-display {
        margin-top: 120px;
    }
}
				
			

Resultado final

Conclusão

Neste tutorial, você aprendeu como criar um Gerenciador de Tarefas com Python e Flask, separando o projeto em diferentes módulos e páginas. Criamos um sistema capaz de gerar tarefas aleatórias utilizando random, controlar o tempo utilizando time, calcular média e desvio padrão utilizando math e integrar todas essas funcionalidades através de rotas Flask. Também utilizamos templates HTML para criar as páginas da aplicação, CSS para construir uma interface responsiva e JavaScript para atualizar visualmente o cronômetro. Essa organização permite perceber como diferentes arquivos e linguagens podem trabalhar juntos na construção de uma aplicação web completa.

Posts Similares

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *