Excel e documentos de trabalho

Criar uma tabela e um gráfico mensais a partir de registros de trabalho

Valide as datas e as horas de trabalho do CSV e salve a quantidade de registros e o total de horas de cada mês em um CSV e um gráfico de barras.

Ver o sumário

A tradução foi feita com IA. Confira o código, as unidades e os valores junto com o original. A revisão por falantes nativos de cada idioma ainda não foi concluída. 한국어

Para quem éProfissionais administrativos que querem processar resumos mensais recorrentes com código simples em Python

Preparação
  • Prepare o Python 3.12 e um terminal. Confira a versão com python --version.
  • Extraia todo o ZIP baixado e abra o terminal na pasta que contém example.py.
  • Execute primeiro com os dados de prática incluídos e compare o resultado com o original.
  • São usados work_log.csv e Matplotlib. O CSV deve ter codificação UTF-8 ou UTF-8 BOM.

01Começar definindo o que uma linha representa

Cada linha é considerada um registro de trabalho, e hours é somado ao mês de date. record_count conta linhas, não pessoas nem dias trabalhados. hours representa horas decimais: 1.50 significa 1 hora e 30 minutos, não 1 hora e 50 minutos.

Os dados incluídos são 9 registros de trabalho fictícios dos meses 1~3 de 2026. Não é uma ferramenta para medir o tempo trabalhado nem avaliar o desempenho, mas uma prática para somar por mês números já informados.

02Conferir as colunas e os valores de entrada

ColunaFormatoCritério de conferência
record_idTexto como R001Deve ser único no arquivo
date2026-01-06YYYY-MM-DD, uma data real
task자료 정리Não pode estar vazio
hours2.50Número finito maior ou igual a 0, com no máximo duas casas decimais

Não inclua linhas de título, linhas de totais nem números com vírgulas. Se um ID duplicado for detectado, o programa para para evitar uma contagem dupla. Mesmo que a tarefa seja igual, IDs diferentes são somados como registros distintos.

03Preparar os arquivos e executar

  1. Extraia o ZIP e confira se o arquivo de entrada indicado em README.txt está no mesmo local que example.py. Não execute diretamente de dentro do ZIP.
  2. Execute os comandos abaixo, um por vez, na pasta que contém example.py. Se no Windows o comando python não estiver disponível, mas py estiver, substitua python por py -3.12 em cada comando.
  3. Quando aparecer a mensagem de conclusão, abra a nova pasta outputs. Se outputs já existir, guarde-a primeiro com outro nome e execute novamente.
bash
python --version
python -m pip install -r requirements.txt
python example.py

A instalação exige conexão com a internet. BASE define os caminhos de entrada e saída a partir da localização de example.py, não da localização atual do terminal.

04Código completo e método de agregação

As datas são validadas com date.fromisoformat, e é criada uma chave mensal no formato YYYY-MM. As horas são somadas com Decimal e registradas na tabela com duas casas decimais. A conversão para números de ponto flutuante ocorre somente ao desenhar o gráfico. É usado o modo Agg, portanto um PNG é salvo em vez de abrir uma janela de gráfico.

example.py
"""Summarize fictional work-log records by calendar month."""
import csv
import math
from collections import defaultdict
from datetime import date
from decimal import Decimal, InvalidOperation
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

BASE = Path(__file__).resolve().parent
HEADERS = ["record_id", "date", "task", "hours"]


def main():
    destination = BASE / "outputs"
    if destination.exists():
        raise ValueError("outputs already exists; rename it before running again.")
    totals = defaultdict(lambda: {"records": 0, "hours": Decimal("0")})
    seen = set()
    with (BASE / "work_log.csv").open(encoding="utf-8-sig", newline="") as file:
        reader = csv.DictReader(file)
        if reader.fieldnames != HEADERS:
            raise ValueError(f"Expected headers: {HEADERS}")
        for line, row in enumerate(reader, start=2):
            if None in row or any(value is None or not value.strip() for value in row.values()):
                raise ValueError(f"Row {line}: missing or extra field.")
            if row["record_id"] in seen:
                raise ValueError(f"Row {line}: duplicate record_id.")
            seen.add(row["record_id"])
            parsed_date = date.fromisoformat(row["date"])
            if parsed_date.isoformat() != row["date"]:
                raise ValueError(f"Row {line}: use YYYY-MM-DD dates.")
            try:
                hours = Decimal(row["hours"])
            except InvalidOperation as error:
                raise ValueError(f"Row {line}: hours must be a number.") from error
            if not hours.is_finite() or hours < 0 or hours.as_tuple().exponent < -2:
                raise ValueError(f"Row {line}: hours must be finite, nonnegative, with at most 2 decimals.")
            month = parsed_date.strftime("%Y-%m")
            totals[month]["records"] += 1
            totals[month]["hours"] += hours
    if not totals:
        raise ValueError("No work records were found.")

    months = sorted(totals)
    figure, axis = plt.subplots(figsize=(8, 4.6), layout="constrained")
    values = [float(totals[month]["hours"]) for month in months]
    if not all(math.isfinite(value) and math.isfinite(value * 1.2) for value in values):
        plt.close(figure)
        raise ValueError("Monthly total is too large to plot as a finite number.")
    bars = axis.bar(months, values, color="#2B6578", width=0.55)
    axis.bar_label(bars, labels=[f"{value:.2f}" for value in values], padding=4)
    axis.set(title="Monthly work hours | Fictional data", xlabel="Calendar month", ylabel="Hours")
    axis.set_ylim(0, max(values) * 1.2 if max(values) > 0 else 1)
    axis.spines[["top", "right"]].set_visible(False)
    axis.set_axisbelow(True)
    axis.grid(axis="y", alpha=0.2)
    destination.mkdir(exist_ok=False)
    with (destination / "monthly_summary.csv").open("x", encoding="utf-8-sig", newline="") as file:
        writer = csv.writer(file)
        writer.writerow(["month", "record_count", "total_hours"])
        for month in months:
            writer.writerow([month, totals[month]["records"], f"{totals[month]['hours']:.2f}"])
    figure.savefig(destination / "monthly_hours.png", dpi=160)
    plt.close(figure)
    total_hours = sum((values["hours"] for values in totals.values()), Decimal("0"))
    print(f"Records: {len(seen)}; months: {len(months)}; hours: {total_hours:.2f}")
    print("Created outputs/monthly_summary.csv and outputs/monthly_hours.png.")


if __name__ == "__main__":
    try:
        main()
    except (OSError, ValueError, csv.Error) as error:
        raise SystemExit(f"Stopped: {error}") from error

05Ler a tabela e o gráfico de resumo

São criados outputs/monthly_summary.csv e outputs/monthly_hours.png. O gráfico mostra apenas os meses com dados de entrada, ordenados por ano e mês. Os eixos em inglês foram escolhidos para permitir a execução sem instalar fontes coreanas adicionais.

monthrecord_counttotal_hours
2026-0136.50
2026-0237.50
2026-0339.50
Total geral de conferência923.50
Gráfico de horas mensais de registros de trabalho fictícios. Mês 1: 6.5 horas; mês 2: 7.5 horas; mês 3: 9.5 horas.
Gráfico de horas mensais de registros de trabalho fictícios. Mês 1: 6.5 horas; mês 2: 7.5 horas; mês 3: 9.5 horas.

06Conferir os totais e a mudança de mês

  1. Confira no CSV de resumo se o mês 1 soma 2.50 + 1.00 + 3.00 = 6.50 horas.
  2. Confira se a soma mensal de record_count corresponde aos 9 registros de entrada.
  3. Confira se a soma de total_hours é 23.50 e compare com os valores das barras do gráfico.
  4. Copie a pasta de prática, altere a data do último registro para 2026-04-01 e execute novamente. Confira se o resultado fica dividido em 5.50 horas para o mês 3 e 4.00 horas para o mês 4.

07Quando o programa para por causa de datas ou números

ProblemaComo corrigir
duplicate record_idConfira se o mesmo registro foi inserido duas vezes.
day is out of range / Invalid isoformatConfira se há datas impossíveis, como 2026-02-30, ou formatos de data incorretos.
hours must be a numberInforme apenas números para as horas, como 2.5. Remova as unidades de tempo e as vírgulas de separação de milhares.
hours must be finite…Verifique valores negativos, NaN, Infinity ou valores com três ou mais casas decimais.
Os caracteres coreanos aparecem incorretamenteSalve novamente o CSV original em UTF-8.
outputs already existsPreserve os resultados anteriores e altere o nome da pasta de resultados.

08O que saber antes de incluir em um relatório

  • Registros que contêm apenas a data são agrupados por mês do calendário. Não são processados horários de início ou término, turnos noturnos, fusos horários nem tarefas que passam da meia-noite.
  • Meses sem dados não são convertidos em linhas de 0 horas, pois é necessário distinguir registros ausentes de um valor real de 0 horas.
  • Apenas IDs duplicados são bloqueados. Se a mesma tarefa foi inserida duas vezes com IDs diferentes, é necessário conferir os dados originais.
  • O CSV de resumo e o PNG refletem o momento da execução. Se você alterar o CSV, deverá executar novamente para obter novos resultados.

Registro de execução e verificação

Windows 11 (10.0.26200), CPython 3.12.14 (64 bits). openpyxl 3.1.5, Matplotlib 3.10.8. As versões das bibliotecas usadas em cada exemplo estão fixadas em requirements.txt.

  • Conferidas 3 linhas mensais, 23.50 horas e 9 registros
  • Verificadas a criação do PNG e os resultados ao mudar um registro de mês
  • Verificados erros de datas incorretas, IDs duplicados, horas negativas ou NaN e cabeçalhos
  • Conferido que o hash do original permanece igual e que os resultados existentes são preservados
Limites da verificação
  • Não é verificada a correspondência com as regras de sistemas reais de recursos humanos ou controle de ponto.

Princípios de redação e verificação de todo o site

Arquivos de exemplo para executar

Inclui código, dados de entrada e instruções de execução. Extraia o ZIP e leia primeiro o README.txt.

Baixar ZIP de exemplo

O código de exemplo, os nomes de arquivos e as chaves de entrada são mantidos como no original. Consulte também os comandos e os procedimentos de conferência do texto traduzido.

Material de prática original · Guarde os arquivos originais separadamente antes de executar.

Fontes de referência

As explicações e os exemplos são de elaboração própria. Os comportamentos e conceitos relacionados podem ser consultados nas fontes oficiais abaixo.