Projeto Final: Gerenciamento de Notas Escolares (Parte 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.
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
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()
Execução de Exemplo
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)
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 Trueno 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 executabreakpara sair do laço. Desde que cada menu tenha uma opção "sair," é seguro. P: A palavra-chaveiné 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.pydiretamente — qualquer um com Python instalado pode executá-lo. Para criar um executável (.exe), usePyInstaller:pip install pyinstaller && pyinstaller --onefile main.py.
📖 Resumo
- Arquitetura de três camadas: Camada de Dados -> Camada de Lógica -> Camada de Apresentação, cada uma com responsabilidades claras
- Camada de Dados: encapsulamento de leitura/escrita de arquivo JSON, operações CRUD básicas
- Camada de Lógica: estatísticas de notas, ranking, regras de negócio de análise
- Camada de Apresentação: interface de menu, entrada/saída, interação com usuário
- Tratamento de exceções: validação de entrada, tratamento de dados vazios, tratamento de arquivo não encontrado
- O projeto totaliza aproximadamente 200 linhas de código principal, implementando um sistema completo de gerenciamento de notas escolares
📝 Atividades
-
Iniciante (Dificuldade ⭐): Execute o sistema completo, adicione 3 alunos e suas notas, e gere um relatório de ranking.
-
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ódulocsv). -
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.



