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
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 ⭐)
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}")
Saída:
=== 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 ⭐⭐)
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
Saída:
===== 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
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 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
📝 Exercícios
-
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.