Excel e documentos de trabalho

Transforme registros de trabalho em uma tabela e gráfico de resumo mensal

Valide as datas e horas de trabalho em um CSV e depois salve as contagens mensais de registros e o total de horas como 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. English

Para quem éProfissionais de escritório que querem lidar com resumos mensais recorrentes de trabalho usando código Python simples

Preparação
  • Tenha Python 3.12 e um terminal prontos. Verifique a versão com python --version.
  • Extraia todo o ZIP baixado e abra um terminal na pasta que contém example.py.
  • Comece com os dados de prática incluídos e depois compare os resultados com os arquivos originais.
  • Este exemplo usa work_log.csv e Matplotlib. O CSV deve usar encoding UTF-8 ou UTF-8 BOM.

01Comece definindo o que uma linha significa

Trate cada linha como um registro de trabalho e some hours ao mês que contém sua date. record_count é o número de linhas, não o número de pessoas nem de dias trabalhados. hours usa horas decimais. 1.50 significa 1 hora e 30 minutos, não 1 hora e 50 minutos.

Os dados incluídos contêm 9 registros fictícios de trabalho de janeiro a março de 2026. Este é um exercício de somar números existentes por mês, não uma ferramenta para medir tempo de trabalho nem avaliar desempenho.

02Verifique as colunas e valores de entrada

ColunaFormatoRegra de validação
record_idTexto como R001Deve ser único dentro do arquivo
date2026-01-06YYYY-MM-DD; uma data que realmente exista
task자료 정리Não deve estar vazio
hours2.50Um número finito igual ou maior que 0, com até duas casas decimais

Não adicione title rows, total rows nem números contendo vírgulas. Se o script encontrar um duplicate ID, ele para para evitar dupla contagem. Records com IDs diferentes são contados separadamente mesmo que suas task descriptions sejam iguais.

03Prepare os arquivos e execute o exemplo

  1. Extraia o ZIP e verifique se o input file listado em README.txt está na mesma pasta que example.py. Não execute o exemplo de dentro do ZIP archive.
  2. Execute os commands abaixo, uma linha por vez, na pasta que contém example.py. No Windows, se python não estiver disponível mas py estiver, substitua python em cada command por py -3.12.
  3. Depois que a completion message aparecer, abra a nova pasta outputs. Se outputs já existir, renomeie-a primeiro para preservar os resultados anteriores e depois execute o exemplo novamente.
bash
python --version
python -m pip install -r requirements.txt
python example.py

A instalação requer conexão com a internet. BASE no código define os paths de input e output em relação a example.py, e não à terminal folder atual.

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

O código valida datas com date.fromisoformat e cria month keys no formato YYYY-MM. Ele soma hours com Decimal e os grava na tabela com duas casas decimais. Os values são convertidos para ordinary floating-point numbers apenas para plotting. Agg mode salva um PNG em vez de abrir uma chart window.

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

05Leia a tabela de resumo e o gráfico

O script cria outputs/monthly_summary.csv e outputs/monthly_hours.png. O chart mostra apenas os months presentes no input, ordenados por year e month. Os axis labels em inglês permitem executar o exemplo sem instalar uma Korean font separada.

monthrecord_counttotal_hours
2026-0136.50
2026-0237.50
2026-0339.50
Total geral para verificação923.50
Monthly hours chart for fictional work records. January: 6.5 hours; February: 7.5 hours; March: 9.5 hours.
Monthly hours chart for fictional work records. January: 6.5 hours; February: 7.5 hours; March: 9.5 hours.

06Verifique os totais e os limites entre meses

  1. No summary CSV, verifique se janeiro totaliza 2.50 + 1.00 + 3.00 = 6.50 hours.
  2. Verifique se a soma dos valores mensais de record_count corresponde aos 9 input records.
  3. Verifique se total_hours soma 23.50 e compare o valor de cada mês com o número em sua chart bar.
  4. Copie a practice folder, altere a date do último record para 2026-04-01 e execute novamente. Verifique se as hours são divididas em 5.50 para março e 4.00 para abril.

07Quando uma data ou número faz o script parar

ProblemaComo corrigir
duplicate record_idVerifique se o mesmo record foi inserido duas vezes.
day is out of range / Invalid isoformatVerifique datas impossíveis como 2026-02-30 ou um date format incorreto.
hours must be a numberDigite apenas um número como 2.5 para hours. Remova unit labels e thousands separators.
hours must be finite…Verifique valores negativos, NaN, Infinity ou mais de duas casas decimais.
Korean text appears garbledSalve novamente o CSV original em UTF-8.
outputs already existsMantenha os resultados anteriores e depois renomeie a result folder.

08Conheça os limites antes de usar os resultados em um relatório

  • Records que contêm apenas dates são agrupados por calendar month. Start and end times, night shifts, time zones e tasks que atravessam a meia-noite não são tratados.
  • Months sem input não são adicionados como linhas com 0 hours. Missing records devem ser diferenciados de 0 hours reais.
  • Apenas duplicate IDs são bloqueados. Verifique nos dados originais se a mesma task foi inserida duas vezes com IDs diferentes.
  • O summary CSV e o PNG refletem os dados no momento da execução. Depois de editar o CSV, execute o script novamente para obter novos resultados.

Registro de execução e verificação

Windows 11 (10.0.26200), CPython 3.12.14 (64-bit). openpyxl 3.1.5, Matplotlib 3.10.8. As bibliotecas usadas por cada exemplo estão fixadas em requirements.txt.

  • Foram confirmadas 3 linhas mensais, 23.50 hours e 9 records
  • Foram verificados a criação do PNG e os resultados após alterar um record através de um month boundary
  • Foram verificadas datas inválidas, duplicate IDs, negative/NaN hours e header errors
  • Foram confirmados os hashes originais inalterados e a preservação dos resultados existentes
Limites da verificação
  • A correspondência com as aggregation rules de sistemas reais de HR ou attendance está fora do escopo da verificação.

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.