Python: Projeto: Notas Escolares (Pt.3)

Nas duas lições anteriores, construímos a camada de dados e a camada de lógica. Nesta lição, unimos tudo — escrevendo uma interface de menu de linha de comando completa. Após completar esta lição, você terá um sistema de gerenciamento de notas escolares verdadeiramente utilizável.


1. Código Completo do Sistema

Combine o código das três lições anteriores em um arquivo, mais a interface de menu:

▶ Exemplo: Sistema Completo de Gerenciamento de Notas

TEXT 📖 Somente leitura
import json
import os
import statistics

# ====== Configuração ======
DATA_FILE = "grade_system.json"

DEFAULT_DATA = {
    "students": {},
    "subjects": ["Português", "Matemática", "Inglês"],
    "classes": []
}

# ====== Camada de Dados ======

def load_data():
    if not os.path.exists(DATA_FILE):
        save_data(DEFAULT_DATA.copy())
        return DEFAULT_DATA.copy()
    with open(DATA_FILE, "r", encoding="utf-8") as f:
        return json.load(f)

def save_data(data):
    with open(DATA_FILE, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

def add_student(student_id, name, class_name):
    data = load_data()
    if student_id in data["students"]:
        return False, f"Matrícula {student_id} já existe!"
    data["students"][student_id] = {"name": name, "class_name": class_name, "scores": {}}
    if class_name not in data["classes"]:
        data["classes"].append(class_name)
    save_data(data)
    return True, f"Aluno adicionado: {name} ({student_id})"

def delete_student(student_id):
    data = load_data()
    if student_id not in data["students"]:
        return False, f"Matrícula {student_id} não existe!"
    name = data["students"][student_id]["name"]
    del data["students"][student_id]
    save_data(data)
    return True, f"Aluno removido: {name}"

def add_score(student_id, subject, score):
    data = load_data()
    if student_id not in data["students"]:
        return False, f"Matrícula {student_id} não existe!"
    if subject not in data["subjects"]:
        return False, f"Disciplina {subject} não existe!"
    if not isinstance(score, (int, float)) or score < 0 or score > 100:
        return False, "A nota deve estar entre 0 e 100!"
    data["students"][student_id]["scores"][subject] = score
    save_data(data)
    return True, f"Inserida nota de {data['students'][student_id]['name']} em {subject}: {score}"

# ====== Camada de Lógica ======

def get_student_report(student_id):
    data = load_data()
    student = data["students"].get(student_id)
    if not student:
        return None
    report = {"name": student["name"], "class_name": student["class_name"],
              "scores": dict(student["scores"])}
    scores = list(student["scores"].values())
    if scores:
        report["average"] = round(sum(scores) / len(scores), 1)
        report["total"] = sum(scores)
        report["max_score"] = max(scores)
        report["min_score"] = min(scores)
    else:
        report["average"] = report["total"] = report["max_score"] = report["min_score"] = None
    return report

def get_class_ranking(class_name=None):
    data = load_data()
    results = []
    for sid, info in data["students"].items():
        if class_name and info["class_name"] != class_name:
            continue
        scores = list(info["scores"].values())
        total = sum(scores) if scores else 0
        avg = round(total / len(scores), 1) if scores else 0
        results.append({"id": sid, "name": info["name"], "class": info["class_name"],
                        "total": total, "avg": avg, "count": len(scores)})
    results.sort(key=lambda x: x["total"], reverse=True)
    for i, r in enumerate(results, 1):
        r["rank"] = i
    return results

def get_subject_stats():
    data = load_data()
    stats = {}
    for subject in data["subjects"]:
        scores = []
        for info in data["students"].values():
            if subject in info["scores"]:
                scores.append(info["scores"][subject])
        if scores:
            stats[subject] = {"avg": round(sum(scores) / len(scores), 1),
                              "max": max(scores), "min": min(scores), "count": len(scores)}
    return stats

# ====== Camada de Apresentação ======

def show_main_menu():
    print("\n" + "=" * 40)
    print("Sistema de Gerenciamento de Notas")
    print("=" * 40)
    print("1. Gerenciamento de Alunos")
    print("2. Gerenciamento de Notas")
    print("3. Estatísticas e Relatórios")
    print("4. Visualizar Dados")
    print("5. Sair")
    print("=" * 40)

def show_student_menu():
    print("\n--- Gerenciamento de Alunos ---")
    print("1. Adicionar Aluno")
    print("2. Remover Aluno")
    print("3. Voltar ao Menu Principal")

def show_score_menu():
    print("\n--- Gerenciamento de Notas ---")
    print("1. Inserir Nota")
    print("2. Voltar ao Menu Principal")

def show_stats_menu():
    print("\n--- Estatísticas e Relatórios ---")
    print("1. Boletim Individual")
    print("2. Ranking Geral")
    print("3. Médias por Disciplina")
    print("4. Voltar ao Menu Principal")

def student_management():
    """Submenu de gerenciamento de alunos"""
    while True:
        show_student_menu()
        choice = input("Digite a opção: ").strip()
        
        if choice == "1":
            sid = input("Matrícula: ").strip()
            name = input("Nome: ").strip()
            cls = input("Turma: ").strip()
            success, msg = add_student(sid, name, cls)
            print(f"  {'OK' if success else 'Erro'}: {msg}")
        
        elif choice == "2":
            sid = input("Digite a matrícula para remover: ").strip()
            success, msg = delete_student(sid)
            print(f"  {'OK' if success else 'Erro'}: {msg}")
        
        elif choice == "3":
            break
        
        else:
            print("Opção inválida!")

def score_management():
    """Submenu de gerenciamento de notas"""
    while True:
        show_score_menu()
        choice = input("Digite a opção: ").strip()
        
        if choice == "1":
            sid = input("Matrícula: ").strip()
            subject = input("Disciplina: ").strip()
            try:
                score = float(input("Nota (0-100): ").strip())
            except ValueError:
                print("Erro: A nota deve ser um número!")
                continue
            success, msg = add_score(sid, subject, score)
            print(f"  {'OK' if success else 'Erro'}: {msg}")
        
        elif choice == "2":
            break
        
        else:
            print("Opção inválida!")

def stats_query():
    """Submenu de consulta de estatísticas"""
    while True:
        show_stats_menu()
        choice = input("Digite a opção: ").strip()
        
        if choice == "1":
            sid = input("Digite a matrícula: ").strip()
            report = get_student_report(sid)
            if not report:
                print("Erro: Aluno não encontrado!")
                continue
            print(f"\nBoletim — {report['name']} ({report['class_name']})")
            print("-" * 30)
            for subject, score in report["scores"].items():
                print(f"  {subject}: {score}")
            if report["total"] is not None:
                print(f"  Total: {report['total']}")
                print(f"  Média: {report['average']}")
                print(f"  Maior: {report['max_score']}")
                print(f"  Menor: {report['min_score']}")
            else:
                print("  (Nenhuma nota ainda)")
        
        elif choice == "2":
            ranking = get_class_ranking()
            if not ranking:
                print("Sem dados!")
                continue
            print("\nRanking Geral")
            print(f"{'Rank':<4}{'Nome':<10}{'Turma':<10}{'Total':<8}{'Média':<6}")
            print("-" * 38)
            for r in ranking:
                print(f"{r['rank']:<4}{r['name']:<10}{r['class']:<10}{r['total']:<8}{r['avg']:<6}")
        
        elif choice == "3":
            stats = get_subject_stats()
            if not stats:
                print("Nenhum dado de nota!")
                continue
            print("\nEstatísticas por Disciplina")
            print(f"{'Disciplina':<12}{'Média':<10}{'Máx':<8}{'Mín':<8}{'Contagem':<4}")
            print("-" * 38)
            for subject, info in stats.items():
                print(f"{subject:<12}{info['avg']:<10}{info['max']:<8}{info['min']:<8}{info['count']:<4}")
        
        elif choice == "4":
            break
        
        else:
            print("Opção inválida!")

def view_data():
    """Visualizar dados brutos"""
    data = load_data()
    students = data["students"]
    if not students:
        print("Nenhum dado de aluno ainda")
        return
    
    print(f"\nTotal: {len(students)} alunos")
    for sid, info in sorted(students.items()):
        scores = info["scores"]
        score_str = ", ".join(f"{s}:{v}" for s, v in scores.items()) if scores else "Sem notas"
        print(f"  {sid} {info['name']} ({info['class_name']}) -> {score_str}")

# ====== Programa Principal ======

def main():
    print("Bem-vindo ao Sistema de Gerenciamento de Notas!")
    
    while True:
        show_main_menu()
        choice = input("Selecione opção (1-5): ").strip()
        
        if choice == "1":
            student_management()
        elif choice == "2":
            score_management()
        elif choice == "3":
            stats_query()
        elif choice == "4":
            view_data()
        elif choice == "5":
            print("Obrigado por usar o sistema! Até logo!")
            break
        else:
            print("Opção inválida. Digite 1-5.")


if __name__ == "__main__":
    main()

▶ Exemplo: Leitura/Escrita de Dados JSON (Dificuldade ⭐)

PYTHON
import json

data = {
    "students": {
        "001": {"name": "Alice", "scores": {"Matemática": 90, "Inglês": 85}},
        "002": {"name": "Bob", "scores": {"Matemática": 78, "Inglês": 92}}
    },
    "subjects": ["Matemática", "Inglês"]
}

json_str = json.dumps(data, ensure_ascii=False, indent=2)
print("=== Serialização ===")
print(json_str)

loaded = json.loads(json_str)
print("\n=== Desserialização ===")
for sid, info in loaded["students"].items():
    avg = sum(info["scores"].values()) / len(info["scores"])
    print(f"{info['name']} (Matrícula: {sid}) média: {avg:.1f}")
▶ Experimente

Saída:

TEXT 📖 Somente leitura
=== Serialização ===
{
  "students": {
    "001": {
      "name": "Alice",
      "scores": {
        "Matemática": 90,
        "Inglês": 85
      }
    },
    "002": {
      "name": "Bob",
      "scores": {
        "Matemática": 78,
        "Inglês": 92
      }
    }
  },
  "subjects": [
    "Matemática",
    "Inglês"
  ]
}

=== Desserialização ===
Alice (Matrícula: 001) média: 87.5
Bob (Matrícula: 002) média: 85.0

▶ Exemplo: Sistema de Menu por Linha de Comando (Dificuldade ⭐⭐)

PYTHON
def show_menu():
    print("\n===== Menu Simples =====")
    print("1. Ver Lista")
    print("2. Adicionar Item")
    print("3. Sair")

items = ["Maçã", "Banana"]

choices = ["1", "2", "3"]

for choice in choices:
    show_menu()
    print(f"Usuário selecionou: {choice}")

    if choice == "1":
        if not items:
            print("Lista vazia")
        else:
            for i, item in enumerate(items, 1):
                print(f"  {i}. {item}")

    elif choice == "2":
        new_item = "Laranja"
        items.append(new_item)
        print(f"Adicionado: {new_item}")

    elif choice == "3":
        print("Até logo!")
        break
▶ Experimente

Saída:

TEXT 📖 Somente leitura
===== Menu Simples =====
1. Ver Lista
2. Adicionar Item
3. Sair
Usuário selecionou: 1
  1. Maçã
  2. Banana

===== Menu Simples =====
1. Ver Lista
2. Adicionar Item
3. Sair
Usuário selecionou: 2
Adicionado: Laranja

===== Menu Simples =====
1. Ver Lista
2. Adicionar Item
3. Sair
Usuário selecionou: 3
Até logo!


2. Execução de Exemplo

TEXT 📖 Somente leitura
Bem-vindo ao Sistema de Gerenciamento de Notas!

========================================
Sistema de Gerenciamento de Notas
========================================
1. Gerenciamento de Alunos
2. Gerenciamento de Notas
3. Estatísticas e Relatórios
4. Visualizar Dados
5. Sair
========================================
Selecione opção (1-5): 1

--- Gerenciamento de Alunos ---
1. Adicionar Aluno
2. Remover Aluno
3. Voltar ao Menu Principal
Digite a opção: 1
Matrícula: 2024001
Nome: João Silva
Turma: Turma 1
  OK: Aluno adicionado: João Silva (2024001)

3. Resumo do Projeto

Camada Funções Responsabilidade
Camada de Dados load/save_data E/S de arquivo JSON
Camada de Entidade add/delete_student CRUD de alunos
Camada de Entidade add_score CRUD de notas
Camada de Lógica get_student_report Boletim individual
Camada de Lógica get_class_ranking Cálculo de ranking
Camada de Lógica get_subject_stats Estatísticas por disciplina
Camada de Apresentação main/main_menu Laço do menu
Camada de Apresentação student/score/stats_management Submenus

❓ Perguntas Frequentes

P: O laço while True no menu de comandos não causa um laço infinito? R: Não, porque cada menu tem uma opção (ex.: digitar 0 ou um número específico) que executa break para sair do laço. Desde que cada menu tenha uma opção "sair," é seguro. P: A palavra-chave in é suficiente para busca difusa, ou preciso de expressões regulares? R: Para este projeto, keyword in name é suficiente. Se você precisar de buscas mais complexas (corresponder apenas no início, insensível a maiúsculas, combinações de múltiplas palavras-chave), você precisaria de expressões regulares — abordado na Lição 31. P: Como empacoto o projeto para outros usarem? R: A forma mais simples é enviar o arquivo .py diretamente — qualquer um com Python instalado pode executá-lo. Para criar um executável (.exe), use PyInstaller: pip install pyinstaller && pyinstaller --onefile main.py.


📖 Resumo



📝 Exercícios

  1. Iniciante (Dificuldade ⭐): Execute o sistema completo, adicione 3 alunos e suas notas, e gere um relatório de ranking.

  2. Intermediário (Dificuldade ⭐⭐): Adicione um recurso de "exportar relatório" ao sistema que exporte os resultados do ranking para um arquivo ranking.csv (usando o módulo csv).

  3. Avançado (Dificuldade ⭐⭐⭐): Sem consultar o código acima, construa um "Sistema de Gerenciamento de Vendas de Livros" do zero. Recursos incluem: adicionar livro (título, autor, preço unitário, estoque), registrar venda (título do livro, quantidade -> diminui estoque automaticamente), visualizar relatório de estoque (ordenado por volume de vendas). Use JSON para persistência de dados.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%