diff --git a/Guia4/main.py b/Guia4/main.py index e9e667d..883a9b6 100644 --- a/Guia4/main.py +++ b/Guia4/main.py @@ -1,8 +1,37 @@ -from Guia3.src import * +import os +from src.perguntadiscursiva import PerguntaDiscursiva +from src.correcao import Correcao def main(): - pass + print("=== Iniciando Teste do Sistema de Quiz com LLM (Guia 4) ===") + + + if not os.environ.get("GROQ_API_KEY"): + print("\n⚠️ AVISO: A variável de ambiente GROQ_API_KEY não foi detectada.") + print("Defina-a no terminal antes de rodar: set GROQ_API_KEY=sua_chave\n") + + + pergunta = PerguntaDiscursiva() + pergunta.texto = "Explique o que é Encapsulamento em Programação Orientada a Objetos." + pergunta.resposta_esperada = "É o mecanismo que esconde os detalhes internos de uma classe e protege os dados usando modificadores de acesso (privado/protegido) e métodos getters/setters." + resposta_do_aluno = "Serve para esconder os dados do código lá dentro da classe usando private para ninguém mexer direto e usar métodos para acessar." + + print(f"\nQuestão: {pergunta.texto}") + print(f"Resposta do Aluno: '{resposta_do_aluno}'") + print("\nEnviando para correção na API do Groq (Llama 3)... Aguarde...") + + # 4. Executando a correção através da classe utilitária + resultado = Correcao.corrigir_discursiva(pergunta, resposta_do_aluno) + + # 5. Exibindo o veredito da Inteligência Artificial + print("\n================ RESULTADO DA CORREÇÃO ================") + print(f"Acertou? : {resultado.get('correta')}") + print(f"Pontuação (0-1): {resultado.get('pontuacao')}") + print(f"Feedback : {resultado.get('feedback')}") + print(f"Explicação : {resultado.get('explicacao')}") + print("=======================================================") + if __name__ == "__main__": main() \ No newline at end of file diff --git a/Guia4/src/__init__.py b/Guia4/src/__init__.py index 9283af9..a339d48 100644 --- a/Guia4/src/__init__.py +++ b/Guia4/src/__init__.py @@ -1,9 +1,3 @@ -from .alternativa import Alternativa + from .pergunta import Pergunta -from .perguntadiscursiva import PerguntaDiscursiva -from .perguntamultiplaescolha import PerguntaMultiplaEscolha -from .questionario import Questionario -from .resposta import Resposta -from .respostadiscursiva import RespostaDiscursiva -from .respostaobjetiva import RespostaObjetiva -from .tentativaquestionario import TentativaQuestionario \ No newline at end of file +from .perguntadiscursiva import PerguntaDiscursiva \ No newline at end of file diff --git a/Guia4/src/alternativa.py b/Guia4/src/alternativa.py index 4dde61f..496fd5e 100644 --- a/Guia4/src/alternativa.py +++ b/Guia4/src/alternativa.py @@ -1,4 +1,5 @@ -from typing import List, Tuple, Dict - class Alternativa: - pass \ No newline at end of file + def __init__(self, texto: str, correta: bool, explicacao: str = None): + self.texto = texto + self.correta = correta + self.explicacao = explicacao \ No newline at end of file diff --git a/Guia4/src/correcao.py b/Guia4/src/correcao.py index bdf2fa4..fe3e8e1 100644 --- a/Guia4/src/correcao.py +++ b/Guia4/src/correcao.py @@ -1,4 +1,22 @@ -from typing import List, Tuple, Dict +from typing import Dict, Any, Optional +from src.perguntadiscursiva import PerguntaDiscursiva class Correcao: - pass \ No newline at end of file + @staticmethod + def criar_prompt_correcao(pergunta: PerguntaDiscursiva, resposta_aluno: str) -> str: + resposta_esperada = getattr(pergunta, "resposta_esperada", 'não informada') + return ( + f"Enunciado da Questão: {pergunta.texto}\n" + f"Gabarito/Resposta Esperada: {resposta_esperada}\n" + f"Resposta submetida pelo Aluno: {resposta_aluno}\n\n" + f"Avalie se a resposta do aluno é conceitualmente equivalente ao gabarito. " + f"Determine a pontuação (de 0.0 a 1.0), se está correta (True/False) e gere um feedback estruturado." + ) + + @staticmethod + def corrigir_discursiva(pergunta: PerguntaDiscursiva, resposta_aluno: str, service=None) -> Dict[str, Any]: + if service is None: + from src.llmservice import LLMService + service = LLMService() + + return service.corrigir_resposta(pergunta, resposta_aluno) \ No newline at end of file diff --git a/Guia4/src/llmservice.py b/Guia4/src/llmservice.py index e6e91b5..61062e8 100644 --- a/Guia4/src/llmservice.py +++ b/Guia4/src/llmservice.py @@ -1,4 +1,65 @@ -from typing import List, Tuple, Dict +import os +import json +from typing import Dict, Any +import requests class LLMService: - pass \ No newline at end of file + def __init__(self, api_key: str = None, model: str = "llama-3.3-70b-versatile"): + + self.api_key = api_key or os.environ.get("GROQ_API_KEY", "") + self.model = model + self.base_url = "https://api.groq.com/openai/v1/chat/completions" + + def corrigir_resposta(self, pergunta, resposta_aluno: str) -> Dict[str, Any]: + + from src.correcao import Correcao + prompt = Correcao.criar_prompt_correcao(pergunta, resposta_aluno) + + try: + return json.loads(self._fazer_chamada_api(prompt)) + except Exception as e: + + self._tratar_erro(e) + return { + "correta": False, + "pontuacao": 0.0, + "feedback": "Erro temporário na comunicação com o avaliador de IA.", + "explicacao": "A requisição falhou ou retornou um formato inválido." + } + + def _fazer_chamada_api(self, prompt: str) -> str: + if not self.api_key: + raise ValueError("Chave de API GROQ_API_KEY não configurada no ambiente.") + + headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json" + } + + payload = { + "model": self.model, + "messages": [ + { + "role": "system", + "content": "Você é um professor avaliador rigoroso. Você DEVE responder exclusivamente em formato JSON com as chaves: 'correta' (boolean), 'pontuacao' (float de 0.0 a 1.0), 'feedback' (string) e 'explicacao' (string)." + }, + { + "role": "user", + "content": prompt + } + ], + "response_format": {"type": "json_object"}, + "temperature": 0.0 + } + + response = requests.post(self.base_url, headers=headers, json=payload, timeout=10) + + if response.status_code != 200: + raise RuntimeError(f"Erro na API Groq ({response.status_code}): {response.text}") + + dados = response.json() + return dados["choices"][0]["message"]["content"] + + def _tratar_erro(self, e: Exception) -> None: + + print(f"[LLMService Error]: {str(e)}") \ No newline at end of file diff --git a/Guia4/src/pergunta.py b/Guia4/src/pergunta.py index 5b3763d..862f375 100644 --- a/Guia4/src/pergunta.py +++ b/Guia4/src/pergunta.py @@ -1,4 +1,6 @@ -from typing import List, Tuple, Dict +from abc import ABC, abstractmethod -class Pergunta: - pass \ No newline at end of file +class Pergunta(ABC): + def __init__(self, texto: str, pontuacao: float = 0.0): + self.texto = texto + self.pontuacao = pontuacao \ No newline at end of file diff --git a/Guia4/src/perguntadiscursiva.py b/Guia4/src/perguntadiscursiva.py index f4c26af..74379fa 100644 --- a/Guia4/src/perguntadiscursiva.py +++ b/Guia4/src/perguntadiscursiva.py @@ -1,4 +1,6 @@ -from typing import List, Tuple, Dict +from src.pergunta import Pergunta -class PerguntaDiscursiva: - pass \ No newline at end of file +class PerguntaDiscursiva(Pergunta): + def __init__(self, texto: str, pontuacao: float = 0.0, resposta_correta: str = ""): + super().__init__(texto, pontuacao) + self.resposta_correta = resposta_correta \ No newline at end of file diff --git a/Guia4/src/perguntamultiplaescolha.py b/Guia4/src/perguntamultiplaescolha.py index bcbe94d..45f3d32 100644 --- a/Guia4/src/perguntamultiplaescolha.py +++ b/Guia4/src/perguntamultiplaescolha.py @@ -1,4 +1,51 @@ -from typing import List, Tuple, Dict +from src.alternativa import Alternativa +from src.perguntamultiplaescolha import PerguntaMultiplaEscolha -class PerguntaMultiplaEscolha: - pass \ No newline at end of file + +def criar_pergunta(): + alternativas = [ + Alternativa("Java", False), + Alternativa("Python", True), + Alternativa("C", False), + ] + + return PerguntaMultiplaEscolha( + texto="Qual linguagem é interpretada?", + alternativas=alternativas, + explicacao_geral="Python normalmente é interpretada." + ) + + +def test_validar_resposta_correta(): + pergunta = criar_pergunta() + + assert pergunta.validar_resposta(1) is True + + +def test_validar_resposta_errada(): + pergunta = criar_pergunta() + + assert pergunta.validar_resposta(0) is False + + +def test_get_alternativa_correta(): + pergunta = criar_pergunta() + + correta = pergunta.get_alternativa_correta() + + assert correta.texto == "Python" + assert correta.correta is True + + +def test_get_tipo(): + pergunta = criar_pergunta() + + assert pergunta.get_tipo() == "multipla_escolha" + + +def test_get_explicacao(): + pergunta = criar_pergunta() + + assert pergunta.get_explicacao() == ( + "Python normalmente é interpretada." + ) \ No newline at end of file diff --git a/Guia4/src/questionario.py b/Guia4/src/questionario.py index 7525582..13e0142 100644 --- a/Guia4/src/questionario.py +++ b/Guia4/src/questionario.py @@ -1,4 +1,27 @@ -from typing import List, Tuple, Dict +import pytest + +from src.questionario import Questionario +from src.perguntadiscursiva import PerguntaDiscursiva +from src.tentativaquestionario import TentativaQuestionario + + +def test_adicionar_pergunta(): + questionario = Questionario("Quiz POO") + + pergunta = PerguntaDiscursiva( + texto="O que é encapsulamento?" + ) + + questionario.adicionar_pergunta(pergunta) + + assert len(questionario.perguntas) == 1 + + +def test_criar_attempt(): + questionario = Questionario("Quiz Redes") + + tentativa = questionario.criar_attempt("valter") + + assert isinstance(tentativa, TentativaQuestionario) + assert tentativa.usuario == "valter" -class Questionario: - pass diff --git a/Guia4/src/resposta.py b/Guia4/src/resposta.py index 846d771..fd9a6d6 100644 --- a/Guia4/src/resposta.py +++ b/Guia4/src/resposta.py @@ -1,4 +1,8 @@ -from typing import List, Tuple, Dict +import pytest -class Resposta: - pass \ No newline at end of file +from src.resposta import Resposta + + +def test_nao_instanciar_resposta_abstract(): + with pytest.raises(TypeError): + Resposta(None) \ No newline at end of file diff --git a/Guia4/src/respostadiscursiva.py b/Guia4/src/respostadiscursiva.py index 4ea6dbb..b1201d4 100644 --- a/Guia4/src/respostadiscursiva.py +++ b/Guia4/src/respostadiscursiva.py @@ -1,4 +1,42 @@ -from typing import List, Tuple, Dict +from src.perguntadiscursiva import PerguntaDiscursiva +from src.respostadiscursiva import RespostaDiscursiva -class RespostaDiscursiva: - pass \ No newline at end of file + +def criar_pergunta(): + return PerguntaDiscursiva( + texto="O que significa CPU?", + resposta_esperada="Central Processing Unit" + ) + + +def test_resposta_discursiva_correta(): + pergunta = criar_pergunta() + + resposta = RespostaDiscursiva( + pergunta=pergunta, + texto_resposta="Central Processing Unit" + ) + + assert resposta.esta_correta is True + + +def test_resposta_discursiva_errada(): + pergunta = criar_pergunta() + + resposta = RespostaDiscursiva( + pergunta=pergunta, + texto_resposta="Memória RAM" + ) + + assert resposta.esta_correta is False + + +def test_calcular_pontuacao(): + pergunta = criar_pergunta() + + resposta = RespostaDiscursiva( + pergunta=pergunta, + texto_resposta="Central Processing Unit" + ) + + assert resposta.calcular_pontuacao() == 1.0 \ No newline at end of file diff --git a/Guia4/src/respostaobjetiva.py b/Guia4/src/respostaobjetiva.py index 72ed2d0..e2a0739 100644 --- a/Guia4/src/respostaobjetiva.py +++ b/Guia4/src/respostaobjetiva.py @@ -1,4 +1,59 @@ -from typing import List, Tuple, Dict +from src.alternativa import Alternativa +from src.perguntamultiplaescolha import PerguntaMultiplaEscolha +from src.respostaobjetiva import RespostaObjetiva -class RespostaObjetiva: - pass \ No newline at end of file + +def criar_pergunta(): + alternativas = [ + Alternativa("HTTP", False), + Alternativa("TCP/IP", True), + ] + + return PerguntaMultiplaEscolha( + texto="Qual protocolo é base da internet?", + alternativas=alternativas + ) + + +def test_resposta_objetiva_correta(): + pergunta = criar_pergunta() + + resposta = RespostaObjetiva( + pergunta=pergunta, + indice_escolhido=1 + ) + + assert resposta.esta_correta is True + + +def test_resposta_objetiva_errada(): + pergunta = criar_pergunta() + + resposta = RespostaObjetiva( + pergunta=pergunta, + indice_escolhido=0 + ) + + assert resposta.esta_correta is False + + +def test_calcular_pontuacao_correta(): + pergunta = criar_pergunta() + + resposta = RespostaObjetiva( + pergunta=pergunta, + indice_escolhido=1 + ) + + assert resposta.calcular_pontuacao() == 1.0 + + +def test_calcular_pontuacao_errada(): + pergunta = criar_pergunta() + + resposta = RespostaObjetiva( + pergunta=pergunta, + indice_escolhido=0 + ) + + assert resposta.calcular_pontuacao() == 0.0 \ No newline at end of file diff --git a/Guia4/src/tentativaquestionario.py b/Guia4/src/tentativaquestionario.py index 9947dd1..e0f4ede 100644 --- a/Guia4/src/tentativaquestionario.py +++ b/Guia4/src/tentativaquestionario.py @@ -1,4 +1,90 @@ -from typing import List, Tuple, Dict +from src.alternativa import Alternativa +from src.perguntamultiplaescolha import PerguntaMultiplaEscolha +from src.perguntadiscursiva import PerguntaDiscursiva +from src.questionario import Questionario +from src.tentativaquestionario import TentativaQuestionario -class TentativaQuestionario: - pass \ No newline at end of file + +def criar_questionario(): + q = Questionario("Quiz") + + p1 = PerguntaMultiplaEscolha( + texto="2 + 2?", + alternativas=[ + Alternativa("3", False), + Alternativa("4", True), + ] + ) + + p2 = PerguntaDiscursiva( + texto="Sigla CPU", + resposta_esperada="Central Processing Unit" + ) + + q.adicionar_pergunta(p1) + q.adicionar_pergunta(p2) + + return q + + +def test_registrar_resposta_objetiva(): + q = criar_questionario() + + tentativa = TentativaQuestionario( + questionario=q, + usuario="valter" + ) + + tentativa.registrar_resposta(0, 1) + + assert len(tentativa.respostas) == 1 + + +def test_registrar_resposta_discursiva(): + q = criar_questionario() + + tentativa = TentativaQuestionario( + questionario=q, + usuario="valter" + ) + + tentativa.registrar_resposta( + 1, + "Central Processing Unit" + ) + + assert len(tentativa.respostas) == 1 + + +def test_calcular_pontuacao(): + q = criar_questionario() + + tentativa = TentativaQuestionario( + questionario=q, + usuario="valter" + ) + + tentativa.registrar_resposta(0, 1) + tentativa.registrar_resposta( + 1, + "Central Processing Unit" + ) + + assert tentativa.calcular_pontuacao() == 2.0 + + +def test_finalizar(): + q = criar_questionario() + + tentativa = TentativaQuestionario( + questionario=q, + usuario="valter" + ) + + tentativa.registrar_resposta(0, 1) + + pontuacao, feedback = tentativa.finalizar() + + assert pontuacao >= 0 + assert isinstance(feedback, str) + assert tentativa.is_finalizado() is True \ No newline at end of file diff --git a/academia/README.md b/academia/README.md new file mode 100644 index 0000000..3ea9487 --- /dev/null +++ b/academia/README.md @@ -0,0 +1,130 @@ +# Guia 5 — Projeto de Sistema com Orientação a Objetos + +## Contexto + +O **FitTrack** é um sistema completo de gestão de unidades fitness desenvolvido em Python. O projeto consolida e aplica na prática os conceitos fundamentais do paradigma de Orientação a Objetos (POO): classes, objetos, construtores, métodos, encapsulamento, atributos de classe, herança, polimorfismo e composição. + +O foco da aplicação é centralizar o ecossistema corporativo de uma academia, gerenciando de forma inteligente o cruzamento de dados de pessoas pelo CPF e automatizando regras de negócios e contratuais. + +--- + +## 1. Diagrama UML + +### Diagrama de Classes Principal + +```text + +-----------------------------------+ + | Membro | + |-----------------------------------| + | - nome: str | + | - idade: int | + | - genero: str | + | - email: str | + | - contato: str | + | - endereco: str | + | - CPF: str | + +-----------------------------------+ + | + +--------------------------+--------------------------+ + | | | + v v v ++-----------------+ +-------------------+ +-----------------+ +| Aluno | | Funcionario | | Personal | +|-----------------| |-------------------| |-----------------| +| - plano: str | | - cargo: str | | - cref: str | +| - tipo_aluno:str| | - salario_base:flt| | - valor_hora:flt| +| - personal_vinc | | - tipo_contrato:st| | - taxa_academia | ++-----------------+ +-------------------+ +-----------------+ + | + v + +-------------------+ + | Terceirizado | + |-------------------| + | - regime: str | + | - servico: str | + | - valor_custo: flt| + +-------------------+ +Descrição das Classes +Membro: Classe base abstrata que define o contrato comum e armazena os dados cadastrais obrigatórios de qualquer indivíduo vinculado à instituição. + +Aluno: Herda de Membro. Adiciona regras de matrícula, seleção automatizada de planos e vinculação direta com a equipe interna ou personais cadastrados externos. + +Funcionario: Herda de Membro. Representa o corpo de colaboradores corporativos da empresa, dividindo-os nos regimes de contratação CLT ou Estágio. + +Personal: Herda de Membro. Representa os profissionais credenciados que utilizam a infraestrutura da academia mediante uma taxa mensal tabelada de uso de espaço. + +Terceirizado: Herda de Funcionario/Membro. Gerencia prestadores de serviço externos atuando sob regime Terceirizado Contratual ou Diarista Autônomo. + +RH: Classe utilitária baseada em composição e métodos específicos para realizar rotinas trabalhistas como registro de ponto, cálculo de FGTS e férias. + +Esquema de Pastas do Projeto +Plaintext +academia/ +├── src/ +│ ├── __init__.py +│ ├── aluno.py +│ ├── funcionario.py +│ ├── membro.py +│ ├── personal.py +│ ├── RH.py +│ └── terceirizados.py +├── tests/ +│ ├── __init__.py +│ ├── test_aluno.py +│ ├── test_funcionario.py +│ ├── test_personal.py +│ └── test_terceirizados.py +├── requirements.txt +├── Guia5_FitTrack_README.md +└── main.py +Como preparar o ambiente +Siga rigorosamente a sequência descrita abaixo para garantir a reprodutibilidade da aplicação em qualquer ambiente de desenvolvimento do Professor Raimundo Valter. + +1. Criar ambiente virtual (venv) +O ambiente virtual isola as execuções do sistema da instalação global do Python na máquina, prevenindo conflitos de escopo. Na pasta raiz do projeto (..\academia>), execute o comando para construir a pasta isolada: + +Bash +python -m venv .venv +2. Ativar ambiente +A activation direciona o terminal para utilizar os binários e scripts contidos dentro do diretório virtualizado criado. + +Windows +Na pasta raiz do projeto, execute o comando via CMD ou PowerShell: + +DOS +.\.venv\Scripts\activate +Linux/macOS +Na pasta raiz do projeto, execute o comando: + +Bash +source .venv/bin/activate +3. Instalar dependências +O arquivo requirements.txt mapeia a lista de dependências externas do projeto. Como a arquitetura do FitTrack priorizou a performance e a portabilidade utilizando apenas pacotes nativos da biblioteca padrão do Python (como unittest, sys, os e datetime), o arquivo atua de forma descritiva e garante que nenhuma biblioteca externa seja baixada sem necessidade. + +Para executar o leitor de dependências padrão, utilize: + +Bash +pip install -r requirements.txt +4. Testes +A suíte de testes automatizados foi dividida de forma modular em arquivos independentes por entidade. Ela valida as regras de negócio individuais (como o cálculo de taxas de personais, regras contratuais e upgrades do RH) aplicando conceitos robustos de cobertura de código. + +Para rodar a validação em lote e obter o relatório detalhado de acertos no terminal, execute o comando de descoberta automática: + +Bash +python -m unittest discover -s tests -v +5. Execução +Para iniciar o sistema interativo em modo terminal, execute o arquivo principal: + +Bash +python main.py +Na tela que abrir, você poderá interagir com o sistema por meio de um menu numérico (1 a 5). O usuário poderá experimentar as seguintes funcionalidades inteligentes: + +Vitrine e Equipe Fixa de Personal Trainers (Opção 1): Ao matricular um aluno e optar por vincular um Personal Trainer, o sistema atua como uma plataforma e lista de forma nativa e automatizada a equipe interna pré-configurada da academia (Moniq Peixoto, Sthefany Silva, David Wytalo e Zaion Luz) exibindo também suas especialidades. O sistema permite escolher um profissional pelo seu número identificador ou recusar a lista digitando 'N' para incluir um profissional externo de sua livre escolha. + +Cadastro Inteligente por CPF (Opção 1, 2, 3 e 4): Se você cadastrar um Personal Trainer parceiro com um CPF específico (Opção 3) e, em seguida, tentar matricular um Aluno (Opção 1) com esse mesmo CPF, o sistema identificará o registro existente e reaproveitará todos os dados pessoais básicos automaticamente, eliminando a digitação redundante. Novos profissionais cadastrados via menu passam a fazer parte dinamicamente da vitrine de ofertas para futuros alunos. + +Upgrade de Contrato de Estágio para CLT (Opção 2): Ao tentar cadastrar um funcionário com um CPF que já pertence a um Estagiário ativo, o sistema oferecerá um fluxo automático de promoção para CLT, solicitando apenas o novo cargo e o novo salário-base. + +Precificação Corporativa Automatizada: Ao selecionar os planos de alunos ou o regime de diaristas terceirizados, o sistema injetará de forma autônoma os custos de diárias (R$ 80,00) e taxas de espaço (R$ 250,00), impedindo que o operador insira valores manuais arbitrários. + +Validações de Idades Legais: O sistema barra automaticamente credenciamentos de Personal Trainers menores de 18 anos e bloqueia vínculos de trabalho para menores de 14 anos, exigindo também dados de responsáveis legais para alunos menores de idade. \ No newline at end of file diff --git a/academia/README.md.txt b/academia/README.md.txt new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/academia/README.md.txt @@ -0,0 +1 @@ + diff --git a/academia/__pycache__/main.cpython-311.pyc b/academia/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000..261b1ff Binary files /dev/null and b/academia/__pycache__/main.cpython-311.pyc differ diff --git a/academia/main.py b/academia/main.py new file mode 100644 index 0000000..53a63f9 --- /dev/null +++ b/academia/main.py @@ -0,0 +1,363 @@ +import sys +import os +from datetime import datetime + +raiz_projeto = os.path.dirname(os.path.abspath(__file__)) +pasta_src = os.path.join(raiz_projeto, "src") +if pasta_src not in sys.path: + sys.path.insert(0, pasta_src) + +from aluno import Aluno +from funcionario import Funcionario +from personal import Personal +from terceirizados import Terceirizado +from RH import RH + +PRECO_PLANOS = { + "1": ("Basic", 90.00), + "2": ("Plus", 140.00), + "3": ("Premium", 200.00), + "4": ("Fidelidade", 220.00), + "5": ("Diarista", 35.00) +} +TAXA_FIXA_PERSONAL = 250.00 +CUSTO_DIARIA_PRESTADOR = 80.00 + +alunos_cadastrados = [] +funcionarios_cadastrados = [] +prestadores_cadastrados = [] + +personais_cadastrados = [ + Personal(nome="Moniq Peixoto", idade=25, genero="Feminino", email="moniq@fittrack.com", contato="85999991111", + endereco="Rua da Academia, 100", cpf="00000000001", especialidade="Musculacao", valor=0.0, + cref="CREF-000001", banco="Banco do Brasil", agencia="0001", conta="1000-1", taxa_academia=0.0), + Personal(nome="Sthefany Silva", idade=24, genero="Feminino", email="sthefany@fittrack.com", contato="85999992222", + endereco="Rua da Academia, 100", cpf="00000000002", especialidade="Pilates / Funcional", valor=0.0, + cref="CREF-000002", banco="Itau", agencia="0001", conta="2000-2", taxa_academia=0.0), + Personal(nome="David Wytalo", idade=27, genero="Masculino", email="david@fittrack.com", contato="85999993333", + endereco="Rua da Academia, 100", cpf="00000000003", especialidade="Cardio / HIIT", valor=0.0, + cref="CREF-000003", banco="Bradesco", agencia="0001", conta="3000-3", taxa_academia=0.0), + Personal(nome="Zaion Luz", idade=26, genero="Masculino", email="zaion@fittrack.com", contato="85999994444", + endereco="Rua da Academia, 100", cpf="00000000004", especialidade="Crosstraining", valor=0.0, + cref="CREF-000004", banco="Santander", agencia="0001", conta="4000-4", taxa_academia=0.0) +] + +def buscar_pessoa_por_cpf(cpf): + for lista in [alunos_cadastrados, funcionarios_cadastrados, personais_cadastrados, prestadores_cadastrados]: + for p in lista: + p_cpf = getattr(p, 'CPF', None) or getattr(p, 'cpf', None) + if p_cpf == cpf: + return p + return None + +def ler_inteiro(mensagem): + while True: + try: return int(input(mensagem)) + except ValueError: print("❌ Erro: Digite um número inteiro válido.") + +def validar_contato(): + while True: + contato = input("Contato (Apenas numeros, minimo 8 digitos): ").strip() + if contato.isdigit() and len(contato) >= 8: return contato + print("❌ Contato Invalido!") + +def validar_cpf(): + while True: + cpf = input("CPF (Somente 11 numeros): ").strip() + if cpf.isdigit() and len(cpf) == 11: return cpf + print("❌ CPF Invalido! Digite exatamente 11 numeros.") + +def validar_email(): + while True: + email = input("Email: ").strip() + if "@" in email and "." in email.split("@")[-1]: return email + print("❌ Email Invalido!") + +def capturar_endereco(): + print("\n[ Informacoes de Endereco ]") + tipo = "Rua" + opcao_tipo = input("Tipo: 1. Rua | 2. Avenida | 3. Outro: ").strip() + if opcao_tipo == "2": tipo = "Avenida" + elif opcao_tipo == "3": tipo = input("Digite o tipo: ").strip().capitalize() + + nome_logradouro = input(f"Nome do(a) {tipo}: ").strip() + numero = input("Numero (Em branco para S/N): ").strip() or "S/N" + bairro = input("Bairro: ").strip() + + while True: + cep = input("CEP (Somente 8 numeros): ").strip() + if len(cep) == 8 and cep.isdigit(): break + print("❌ CEP Invalido!") + + return f"{tipo} {nome_logradouro}, No {numero}, Bairro: {bairro}, CEP: {cep}" + +def validar_data(mensagem): + while True: + data_str = input(mensagem).strip() + try: + datetime.strptime(data_str, "%Y-%m-%d") + return data_str + except ValueError: + print("❌ Use o formato AAAA-MM-DD.") + +def validar_horario(mensagem): + while True: + hora_str = input(mensagem).strip() + try: + datetime.strptime(hora_str, "%H:%M") + return hora_str + except ValueError: + print("❌ Use o formato HH:MM.") + +def menu_cadastro_aluno(): + print("\n--- CADASTRO DE ALUNO ---") + cpf = validar_cpf() + pessoa_existente = buscar_pessoa_por_cpf(cpf) + + if pessoa_existente: + print(f"⚠️ Resgatando dados de: {pessoa_existente.nome}") + nome = pessoa_existente.nome + idade = pessoa_existente.idade + genero = p_existente.genero if (p_existente := pessoa_existente) and hasattr(p_existente, 'genero') else "Nao Informado" + email = pessoa_existente.email + contato = pessoa_existente.contato + endereco = pessoa_existente.endereco + nome_social = getattr(pessoa_existente, 'nome_social', None) + else: + nome = input("Nome Completo: ").strip() + nome_social = input("Nome Social (Em branco se nao houver): ").strip() or None + idade = ler_inteiro("Idade: ") + genero = input("Genero: ").strip() + email = validar_email() + contato = validar_contato() + endereco = capturar_endereco() + + necessidades_especiais = input("Restricao medica? (Em branco se nao houver): ").strip() or None + + print("\n--- SELECAO DE PLANOS ---") + for k, v in PRECO_PLANOS.items(): + print(f"{k}. {v[0]} - R$ {v[1]:.2f}") + + while True: + op = input("Escolha o plano (1-5): ").strip() + if op in PRECO_PLANOS: + plano, valor_plano = PRECO_PLANOS[op] + tipo_aluno = "DIARISTA" if op == "5" else "MENSALISTA" + break + print("❌ Opcao invalida!") + + auto_saude = (input("Declaracao de saude (APTO/INAPTO): ").strip().upper() == "APTO") + objetivo = input("Objetivo do Treino: ").strip() + forma_pagamento = input("Forma de Pagamento (Pix/Boleto/Cartao): ").strip() + dia_vencimento = 10 + + personal_vinculado = None + vincular = input("Deseja treinar com personal? (S/N): ").strip().upper() + + if vincular == "S": + print("\nDeseja treinar com os disponiveis da academia?") + print("Digite o numero do profissional ou digite 'N' (Nao) para sair/informar outro:") + + for i, p in enumerate(personais_cadastrados, 1): + print(f"{i}. {p.nome} (Especialidade: {p.especialidade})") + + escolha = input("Opcao: ").strip() + + if escolha.upper() == 'N': + op_externo = input("Deseja informar um personal externo que nao esta na lista? (S/N): ").strip().upper() + if op_externo == "S": + personal_vinculado = input("Digite o nome do Personal Externo: ").strip() + else: + try: + idx = int(escolha) - 1 + if 0 <= idx < len(personais_cadastrados): + personal_vinculado = personais_cadastrados[idx].nome + print(f"✅ Personal {personal_vinculado} vinculado com sucesso!") + else: + print("❌ Opcao invalida! Nenhum personal foi vinculado.") + except ValueError: + print("❌ Opcao invalida! Nenhum personal foi vinculado.") + + nome_resp, contato_resp, autorizacao = None, None, False + if idade < 18: + nome_resp = input("Nome do Responsavel: ").strip() + contato_resp = validar_contato() + autorizacao = True + + try: + aluno = Aluno(nome, idade, genero, email, contato, endereco, cpf, tipo_aluno, personal_vinculado, + autorizacao, nome_resp, contato_resp, nome_social, necessidades_especiais, forma_pagamento, + auto_saude, objective=objetivo, plano=plano, dia_vencimento=dia_vencimento) + print(f"\n✅ Aluno matriculado!") + aluno.exibir() + return aluno + except Exception as e: + print(f"❌ Erro: {e}") + return None + +def menu_cadastro_funcionario(): + print("\n--- CADASTRO DE FUNCIONARIO ---") + cpf = validar_cpf() + + for f in funcionarios_cadastrados: + if (getattr(f, 'CPF', None) or getattr(f, 'cpf', None)) == cpf: + if f.tipo_contrato == "ESTAGIARIO": + if input(f"📈 Promover o estagiario {f.nome} para CLT? (S/N): ").strip().upper() == "S": + f.tipo_contrato = "CLT" + f.cargo = input("Novo Cargo CLT: ").strip() + f.salario_base = ler_inteiro("Novo Salario Base: R$ ") + print("🎉 Promovido com sucesso!") + f.exibir() + return None + print("❌ Ja cadastrado como CLT.") + return None + + pessoa_existente = buscar_pessoa_por_cpf(cpf) + if pessoa_existente: + nome, idade = pessoa_existente.nome, pessoa_existente.idade + genero = p_existente.genero if (p_existente := pessoa_existente) and hasattr(p_existente, 'genero') else "Nao Informado" + email, contato, endereco = pessoa_existente.email, pessoa_existente.contato, pessoa_existente.endereco + else: + nome = input("Nome Completo: ").strip() + idade = ler_inteiro("Idade: ") + if idade < 14: print("❌ Proibido menor de 14 anos!"); return None + genero = input("Genero: ").strip() + email = validar_email() + contato = validar_contato() + endereco = capturar_endereco() + + tipo_contrato = "CLT" if input("Contrato: 1. CLT | 2. Estagio: ").strip() == "1" else "ESTAGIARIO" + cargo = input("Cargo: ").strip() + salario_base = ler_inteiro("Salario Base: R$ ") + ctps = input("CTPS: ").strip() + data_adm = datetime.now().strftime("%Y-%m-%d") + h_entrada = validar_horario("Entrada (HH:MM): ") + h_saida = validar_horario("Saida (HH:MM): ") + banco, agencia, conta = input("Banco: ").strip(), input("Agencia: ").strip(), input("Conta: ").strip() + + nome_resp, contato_resp = (input("Responsavel Legal: ").strip(), validar_contato()) if idade < 18 else (None, None) + + try: + novo_func = Funcionario(nome, idade, genero, email, contato, endereco, cpf, cargo, salario_base, + ctps, tipo_contrato, data_adm, h_entrada, h_saida, banco, agencia, conta, + nome_resp, contato_resp) + print("\n✅ Funcionario cadastrado!") + novo_func.exibir() + return novo_func + except Exception as e: + print(f"❌ Erro: {e}") + return None + +def menu_cadastro_personal(): + print("\n--- CADASTRO DE PERSONAL TRAINER ---") + cpf = validar_cpf() + pessoa_existente = buscar_pessoa_por_cpf(cpf) + + if pessoa_existente: + if pessoa_existente.idade < 18: + print("❌ Erro: Menores de idade nao podem atuar como Personal.") + return None + nome, idade = pessoa_existente.nome, pessoa_existente.idade + genero = p_existente.genero if (p_existente := presidential_var) and hasattr(p_existente, 'genero') else "Nao Informado" + email, contato, endereco = presidential_var.email, presidential_var.contato, presidential_var.endereco if (presidential_var := pessoa_existente) else (None, None, None) + nome_social = getattr(pessoa_existente, 'nome_social', None) + else: + nome = input("Nome Completo: ").strip() + nome_social = input("Nome Social (Em branco se nao houver): ").strip() or None + idade = ler_inteiro("Idade: ") + if idade < 18: print("❌ Menores de idade nao podem possuir CREF."); return None + genero = input("Genero: ").strip() + email = validar_email() + contato = validar_contato() + endereco = capturar_endereco() + + especialidade = input("Especialidade: ").strip() + valor_hora = ler_inteiro("Valor Cobrado por Hora/Aula: R$ ") + cref = input("Registro CREF: ").strip() + + taxa_academia = TAXA_FIXA_PERSONAL + print(f"ℹ️ Taxa de uso do espaco fixada pelo sistema: R$ {taxa_academia:.2f}") + + banco, agencia, conta = input("Banco: ").strip(), input("Agencia: ").strip(), input("Conta: ").strip() + + try: + novo_personal = Personal(nome=nome, idade=idade, genero=genero, email=email, contato=contato, + endereco=endereco, cpf=cpf, especialidade=especialidade, valor=valor_hora, + cref=cref, banco=banco, agencia=agencia, conta=conta, taxa_academia=taxa_academia, + nome_social=nome_social) + novo_personal.exibir() + return novo_personal + except Exception as e: + print(f"❌ Erro: {e}") + return None + +def menu_cadastro_terceirizado(): + print("\n--- CADASTRO DE PRESTADOR DE SERVICO ---") + cpf = validar_cpf() + pessoa_existente = buscar_pessoa_por_cpf(cpf) + + if p_existente := pessoa_existente: + nome, idade = p_existente.nome, p_existente.idade + genero = p_existente.genero if hasattr(p_existente, 'genero') else "Nao Informado" + email, contato, endereco = p_existente.email, p_existente.contato, p_existente.endereco + else: + nome = input("Nome Completo: ").strip() + idade = ler_inteiro("Idade: ") + genero = input("Genero: ").strip() + email = validar_email() + contato = validar_contato() + endereco = capturar_endereco() + + regime = input("Regime (TERCEIRIZADO ou DIARISTA): ").strip().upper() + + if regime == "TERCEIRIZADO": + empresa = input("Nome da Empresa Parceira: ").strip() + valor_custo = ler_inteiro("Valor Mensal do Contrato de Repasse: R$ ") + else: + empresa = None + valor_custo = CUSTO_DIARIA_PRESTADOR + print(f"ℹ️ Custo da diaria predefinido pelo corporativo: R$ {valor_custo:.2f}") + + servico = input("Servico Prestado (Ex: Limpeza, Recepcao): ").strip() + data_inicio = validar_data("Data de Inicio (AAAA-MM-DD): ") + data_fim = validar_data("Data de Fim (AAAA-MM-DD): ") + banco, agencia, conta = input("Banco: ").strip(), input("Agencia: ").strip(), input("Conta: ").strip() + + try: + novo_prestador = Terceirizado(nome, idade, genero, email, contato, endereco, cpf, regime, + servico, valor_custo, data_inicio, data_fim, banco, agencia, conta, empresa) + print(f"\n✅ Prestador registrado!") + novo_prestador.exibir() + return novo_prestador + except Exception as e: + print(f"❌ Erro: {e}") + return None + +def main(): + while True: + print("\n" + "="*40) + print(" SISTEMA FITTRACK - GESTAO DE ACADEMIA ") + print("="*40) + print("1. Cadastrar Aluno\n2. Cadastrar Funcionario\n3. Cadastrar Personal\n4. Cadastrar Prestador\n5. Sair") + print("="*40) + opcao = input("Opcao: ").strip() + + if opcao == "1": + aluno = menu_cadastro_aluno() + if aluno: alunos_cadastrados.append(aluno) + elif opcao == "2": + func = menu_cadastro_funcionario() + if func: funcionarios_cadastrados.append(func) + elif opcao == "3": + personal = menu_cadastro_personal() + if personal: personais_cadastrados.append(personal) + elif opcao == "4": + prestador = menu_cadastro_terceirizado() + if prestador: prestadores_cadastrados.append(prestador) + elif opcao == "5": + break + else: + print("❌ Opcao invalida!") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/academia/src/RH.py b/academia/src/RH.py new file mode 100644 index 0000000..472f00c --- /dev/null +++ b/academia/src/RH.py @@ -0,0 +1,110 @@ +from datetime import datetime + +class RH: + def registrar_ponto(self, funcionario, horas_trabalhadas): + if "Demitido" in funcionario.status: + print(f"ERRO RH: Não é possível registrar ponto de {funcionario.nome}. Funcionário desligado.") + return + + if funcionario.tipo_contrato == "ESTAGIARIO" and horas_trabalhadas > 4: + print(f"ALERTA RH: O estagiário {funcionario.nome} não pode trabalhar mais de 4 horas por dia!") + funcionario.horas_acumuladas += 4 + return + + if funcionario.tipo_contrato == "CLT": + if horas_trabalhadas > 8: + horas_extras = horas_trabalhadas - 8 + funcionario.horas_acumuladas += horas_extras + print(f"RH: {funcionario.nome} realizou {horas_extras}h extras hoje.") + + if funcionario.horas_acumuladas >= 8: + funcionario.banco_folgas += 1 + funcionario.horas_acumuladas -= 8 + print(f"NOTIFICAÇÃO RH: {funcionario.nome} acumulou 8h extras e ganhou 1 folga no banco.") + else: + print(f"RH: Ponto registrado normalmente para {funcionario.nome} ({horas_trabalhadas}h trabalhadas).") + + def aplicar_advertencia(self, funcionario, motivo): + if "Demitido" in funcionario.status: + print(f"ERRO RH: Não é possível aplicar advertência. {funcionario.nome} já está desligado.") + return + + if not hasattr(funcionario, 'advertencias'): + funcionario.advertencias = 0 + + funcionario.advertencias += 1 + print(f"NOTIFICAÇÃO RH: Advertência escrita aplicada a {funcionario.nome}. Motivo: {motivo}. Total: {funcionario.advertencias}/3") + + if funcionario.advertencias >= 3: + funcionario.status = "Demitido - Justa Causa" + print(f"🚨🚨 ALERTA MÁXIMO RH: O funcionário {funcionario.nome} foi DEMITIDO POR JUSTA CAUSA devido ao acúmulo de 3 advertências escritas!") + + def calcular_fgts(self, funcionario): + if funcionario.tipo_contrato != "CLT": + return f"Funcionário {funcionario.nome} é {funcionario.tipo_contrato}. Não possui direito ao recolhimento de FGTS." + + fgts_mensal = funcionario.salario_base * 0.08 + + data_admissao = datetime.strptime(funcionario.data_contratacao, "%Y-%m-%d") + data_atual = datetime.now() + dias_trabalhados = (data_atual - data_admissao).days + meses_trabalhados = max(1, dias_trabalhados // 30) + + fgts_acumulado = fgts_mensal * meses_trabalhados + + print(f"--- EXTRATO FGTS: {funcionario.nome} ---") + print(f"Depósito Mensal (8%): R$ {fgts_mensal:.2f}") + print(f"Tempo de Contribuição: {meses_trabalhados} meses") + print(f"Saldo Total Estimado em Conta FGTS: R$ {fgts_acumulado:.2f}") + return fgts_acumulado + + def gerenciar_sobre_aviso(self, funcionario, status): + if "Demitido" in funcionario.status or funcionario.tipo_contrato == "ESTAGIARIO": + print(f"ERRO RH: Estagiários ou funcionários demitidos não entram em regime de sobre-aviso.") + return + funcionario.sobre_aviso = status + + def calcular_direito_ferias(self, funcionario, vendeu_ferias=False): + if "Demitido" in funcionario.status: + print("Funcionário já desligado da empresa.") + return 0 + + data_admissao = datetime.strptime(funcionario.data_contratacao, "%Y-%m-%d") + data_atual = datetime.now() + dias_trabalhados = (data_atual - data_admissao).days + + print(f"--- ANÁLISE DE FÉRIAS: {funcionario.nome} ---") + print(f"Tempo de casa: {dias_trabalhados} dias.") + + if dias_trabalhados >= 365: + print("Status Férias: Direito a férias adquirido (Período Aquisitivo Completo).") + + if funcionario.tipo_contrato == "CLT": + if vendeu_ferias: + dias_descanso = 20 + dias_vendidos = 10 + valor_abono = (funcionario.salario_base / 30) * 10 + valor_abono_com_terco = valor_abono + (valor_abono / 3) + + print(f"Opção Escolhida: Venda de 1/3 das férias (Abono Pecuniário).") + print(f" Dias de descanso: {dias_descanso} dias.") + print(f" Dias vendidos: {dias_vendidos} dias.") + print(f" Valor extra a receber pelas férias vendidas: R$ {valor_abono_com_terco:.2f}") + return dias_descanso + else: + print("Opção Escolhida: Tirar os 30 dias completos de descanso.") + return 30 + else: + print("Contrato de Estágio: Direito a 30 dias de recessos (Não permite venda).") + return 30 + else: + dias_restantes = 365 - dias_trabalhados + print(f"Status Férias: Em período aquisitivo. Faltam {dias_restantes} dias para o direito.") + return 0 + + def gerar_escala(self, funcionario, dias_semana): + if "Demitido" in funcionario.status: + return + print(f"ESCALA DE TRABALHO DE {funcionario.nome.upper()}:") + print(f"Dias da Semana: {dias_semana}") + print(f"Turno: {funcionario.horario_entrada} às {funcionario.horario_saida}") \ No newline at end of file diff --git a/academia/src/__init__.py b/academia/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/academia/src/__pycache__/RH.cpython-311.pyc b/academia/src/__pycache__/RH.cpython-311.pyc new file mode 100644 index 0000000..83ee87f Binary files /dev/null and b/academia/src/__pycache__/RH.cpython-311.pyc differ diff --git a/academia/src/__pycache__/__init__.cpython-311.pyc b/academia/src/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..1c49f23 Binary files /dev/null and b/academia/src/__pycache__/__init__.cpython-311.pyc differ diff --git a/academia/src/__pycache__/aluno.cpython-311.pyc b/academia/src/__pycache__/aluno.cpython-311.pyc new file mode 100644 index 0000000..835b74d Binary files /dev/null and b/academia/src/__pycache__/aluno.cpython-311.pyc differ diff --git a/academia/src/__pycache__/funcionario.cpython-311.pyc b/academia/src/__pycache__/funcionario.cpython-311.pyc new file mode 100644 index 0000000..0f91213 Binary files /dev/null and b/academia/src/__pycache__/funcionario.cpython-311.pyc differ diff --git a/academia/src/__pycache__/membro.cpython-311.pyc b/academia/src/__pycache__/membro.cpython-311.pyc new file mode 100644 index 0000000..1050200 Binary files /dev/null and b/academia/src/__pycache__/membro.cpython-311.pyc differ diff --git a/academia/src/__pycache__/personal.cpython-311.pyc b/academia/src/__pycache__/personal.cpython-311.pyc new file mode 100644 index 0000000..18d90d9 Binary files /dev/null and b/academia/src/__pycache__/personal.cpython-311.pyc differ diff --git a/academia/src/__pycache__/terceirizados.cpython-311.pyc b/academia/src/__pycache__/terceirizados.cpython-311.pyc new file mode 100644 index 0000000..5ec07e4 Binary files /dev/null and b/academia/src/__pycache__/terceirizados.cpython-311.pyc differ diff --git a/academia/src/aluno.py b/academia/src/aluno.py new file mode 100644 index 0000000..bdc9fef --- /dev/null +++ b/academia/src/aluno.py @@ -0,0 +1,177 @@ +from membro import Membro +from datetime import datetime + +class Aluno(Membro): + def __init__(self, nome, idade, genero, email, contato, endereco, CPF, + tipo_aluno, personal=None, + autorizacao_responsavel=False, nome_responsavel=None, contato_responsavel=None, + nome_social=None, necessidades_especiais=None, forma_pagamento=None, + auto_declaracao_saude=False, objective=None, plano=None, + numero_cartao=None, titular_cartao=None, validade_cartao=None, cvv=None, + dia_vencimento=10, data_matricula=None): + + # Validação para menor de 18 anos + if idade < 18: + if not nome_responsavel or not contato_responsavel: + raise ValueError("CADASTRO NEGADO: dados do responsável obrigatórios para menores de 18 anos") + if not autorizacao_responsavel: + raise ValueError("CADASTRO NEGADO: autorização do responsável obrigatória") + + super().__init__(nome, idade, genero, email, contato, endereco, CPF) + + self.tipo_aluno = tipo_aluno + self.personal = personal + self.autorizacao_responsavel = autorizacao_responsavel + self.nome_responsavel = nome_responsavel + self.contato_responsavel = contato_responsavel + + self.nome_social = nome_social + self.necessidades_especiais = necessidades_especiais + self.forma_pagamento = forma_pagamento + self.auto_declaracao_saude = auto_declaracao_saude + self.objetivo = objective + self.plano = plano + self.dia_vencimento = dia_vencimento + self.data_matricula = data_matricula if data_matricula else datetime.now().strftime("%Y-%m-%d") + + self.servicos = [] + self.infracoes = 0 + self.status_pagamento = "Em dia" + + # Atributos protegidos do Cartão + self._numero_cartao = numero_cartao + self._titular_cartao = titular_cartao + self._validade_cartao = validade_cartao + self._cvv = cvv + + # CORREÇÃO CRÍTICA: Definir o status ANTES de tentar adicionar o serviço/plano + if not auto_declaracao_saude: + self.status = "Bloqueado - Sem Declaração de Saúde" + else: + self.status = "Ativo" + + # Agora o método adicionar_servico consegue ler o self.status sem quebrar! + if plano: + self.adicionar_servico(plano) + + @property + def numero_cartao(self): + return self._numero_cartao + + @property + def titular_cartao(self): + return self._titular_cartao + + @property + def validade_cartao(self): + return self._validade_cartao + + @property + def cvv(self): + return self._cvv + + def adicionar_servico(self, servico): + if self.status == "Convidado a se retirar" or "Bloqueado" in self.status: + return + + servico_lower = servico.lower() + + if "plus" in servico_lower: + self.servicos = ["Plano Plus"] + elif "fidelidade" in servico_lower: + self.servicos = ["Plano Fidelidade"] + elif "premium" in servico_lower: + self.servicos = ["Plano Premium"] + elif "basic" in servico_lower: + self.servicos = ["Plano Basic"] + elif "diarista" in servico_lower: + self.servicos = ["Plano Diarista"] + else: + if not self.servicos: + self.servicos.append(servico) + + def aplicar_infracao(self, motivo): + if self.status == "Convidado a se retirar": + return + + self.infracoes += 1 + + if self.infracoes >= 3: + self.status = "Convidado a se retirar" + self.servicos = [] + + def controlar_financeiro(self, dias_atraso): + if self.status == "Convidado a se retirar": + return + + if dias_atraso > 0: + self.status_pagamento = "Saldo Devedor" + if dias_atraso > 3: + self.status = "Bloqueado - Saldo Devedor" + else: + self.status_pagamento = "Em dia" + if self.status == "Bloqueado - Saldo Devedor": + self.status = "Ativo" + + def calcular_mensalidade(self): + if self.status == "Convidado a se retirar": + return 0.0 + + precos_planos = { + "Plano Basic": 90.0, + "Plano Plus": 140.0, + "Plano Premium": 200.0, + "Plano Fidelidade": 220.0, + "Plano Diarista": 35.0 + } + + nome_plano_atual = self.servicos[0] if self.servicos else "Plano Basic" + valor = precos_planos.get(nome_plano_atual, 90.0) + + if self.personal: + if self.tipo_aluno.lower() == "diarista": + valor += 50.0 + else: + valor += 100.0 + + return valor + + def exibir(self): + print("\n" + "----------------------------------------") + print(" INFORMAÇÕES DO ALUNO ") + print("----------------------------------------") + print(f"Data de Matrícula: {self.data_matricula}") + print(f"Nome: {self.nome}") + print(f"Nome Social: {self.nome_social if self.nome_social else 'Nenhum'}") + print(f"Idade: {self.idade}") + print(f"Gênero: {self.genero}") + print(f"Email: {self.email}") + print(f"Contato: {self.contato}") + print(f"Endereço: {self.endereco}") + print(f"CPF: {self.CPF}") + print(f"Tipo de Aluno: {self.tipo_aluno}") + print(f"Plano Contratado: {self.servicos[0] if self.servicos else 'Nenhum'}") + print(f"Necessidades Especiais: {self.necessidades_especiais if self.necessidades_especiais else 'Nenhuma'}") + print(f"Personal Trainer: {self.personal if self.personal else 'Nenhum'}") + print(f"Objetivo do Treino: {self.objetivo if self.objetivo else 'Não Informado'}") + print(f"Forma de Pagamento: {self.forma_pagamento}") + + if self.forma_pagamento == "Cartão de Crédito" and self._numero_cartao: + print(f"Dados do Cartão: Final {self._numero_cartao[-4:]} | Titular: {self._titular_cartao} | Validade: {self._validade_cartao}") + + print(f"Autodeclaração de Saúde: {'Apto (Sim)' if self.auto_declaracao_saude else 'Inapto (Não)'}") + print(f"Status Pagamento: {self.status_pagamento}") + print(f"Status do Aluno: {self.status}") + print(f"Vencimento: Todo dia {self.dia_vencimento}") + print(f"Valor Total Mensalidade: R$ {self.calcular_mensalidade():.2f}") + print("----------------------------------------") + + if self.idade < 18: + print("[ Informações do Responsável Legal ]") + if self.autorizacao_responsavel: + print("Status: AUTORIZADO PELO RESPONSÁVEL") + print(f"Responsável: {self.nome_responsavel}") + print(f"Contato do Responsável: {self.contato_responsavel}") + else: + print("Status: ❌ NÃO AUTORIZADO") + print("----------------------------------------") \ No newline at end of file diff --git a/academia/src/funcionario.py b/academia/src/funcionario.py new file mode 100644 index 0000000..c1fd212 --- /dev/null +++ b/academia/src/funcionario.py @@ -0,0 +1,81 @@ + +from membro import Membro + +class Funcionario(Membro): + def __init__(self, nome, idade, genero, email, contato, endereco, CPF, cargo, salario_base, + carteira_trabalho, tipo_contrato, data_contratacao, horario_entrada, horario_saida, + banco, agencia, conta, nome_responsavel = None, contato_responsavel = None, + nome_social = None, necessidades_especiais = None): + + if idade < 14 or (tipo_contrato.upper() == "ESTAGIARIO" and idade < 18): + if not nome_responsavel or not contato_responsavel: + raise ValueError(f"CONTRATAÇÃO NEGADA: O funcionário {nome} é menor de idade para o contrato {tipo_contrato}. É OBRIGATÓRIO informar o Nome e o Contato do responsável legal no ato do cadastro!") + + super().__init__(nome, idade, genero, email, contato, endereco, CPF) + self.cargo = cargo + self.salario_base = salario_base + self.carteira_trabalho = carteira_trabalho + self.tipo_contrato = tipo_contrato.upper() + self.data_contratacao = data_contratacao + self.horario_entrada = horario_entrada + self.horario_saida = horario_saida + self.horas_acumuladas = 0 + self.banco_folgas = 0 + self.sobre_aviso = False + self.nome_social = nome_social + self.necessidades_especiais = necessidades_especiais + + self._banco = banco + self._agencia = agencia + self._conta = conta + + self.nome_responsavel = nome_responsavel + self.contato_responsavel = contato_responsavel + + self.treino_gratis = True + self.beneficios_adicionais = "Vale Transporte + Vale Alimentação" if self.tipo_contrato == "CLT" else "Nenhum" + + self.status = "Ativo" + + @property + def banco(self): + return self._banco + + @property + def agencia(self): + return self._agencia + + @property + def conta(self): + return self._conta + + def calcular_mensalidade(self): + if self.status == "Demitido": + return 0.0 + return self.salario_base + + def exibir(self): + print("--" * 20) + print(" INFORMAÇÕES DO FUNCIONÁRIO ") + print("--" * 20) + print(f"Nome: {self.nome}") + print(f"Idade: {self.idade}") + print(f"Cargo: {self.cargo} | Contrato: {self.tipo_contrato}") + print(f"CTPS: {self.carteira_trabalho} | Admissão: {self.data_contratacao}") + print(f"Horário: {self.horario_entrada} às {self.horario_saida}") + + conta_oculta = f"***{self._conta[-4:]}" if len(self._conta) > 4 else "****" + print(f"Dados Bancários: Banco {self._banco} | Ag: {self._agencia} | Conta: {conta_oculta}") + + print(f"Horas no Banco: {self.horas_acumuladas} | Folgas a Tirar: {self.banco_folgas}") + print(f"Sobre-aviso: {'Sim' if self.sobre_aviso else 'Não'}") + print(f"Treinar Grátis: {'Sim (Benefício Ativo)' if self.treino_gratis else 'Não'}") + print(f"Benefícios de Contrato: {self.beneficios_adicionais}") + print(f"Status: {self.status}") + print(f"Salário Líquido a Receber: R$ {self.calcular_mensalidade():.2f}") + + if self.idade < 18 and self.nome_responsavel: + print(f"Responsável Legal: {self.nome_responsavel} - Contato: {self.contato_responsavel}") + + print("--" * 20) + diff --git a/academia/src/membro.py b/academia/src/membro.py new file mode 100644 index 0000000..0c16ca3 --- /dev/null +++ b/academia/src/membro.py @@ -0,0 +1,19 @@ +from abc import ABC, abstractmethod +class Membro(ABC): + def __init__(self, nome, idade, genero, email, contato, endereco, CPF): + self.nome = nome + self.idade = idade + self.genero = genero + self.email = email + self.contato = contato + self._endereco = endereco + self._CPF = CPF + @property + def endereco(self): + return self._endereco + @property + def CPF(self): + return self._CPF + @abstractmethod + def calcular_mensalidade(self): + pass \ No newline at end of file diff --git a/academia/src/personal.py b/academia/src/personal.py new file mode 100644 index 0000000..c62756e --- /dev/null +++ b/academia/src/personal.py @@ -0,0 +1,60 @@ +from membro import Membro + +class Personal(Membro): + def __init__(self, nome, idade, genero, email, contato, endereco, + cpf, especialidade, valor, cref, banco, agencia, conta, taxa_academia, + nome_social = None, necessidades_especiais = None): + + super().__init__(nome, idade, genero, email, contato, endereco, cpf) + self.especialidade = especialidade + self.valor = valor + self.cref = cref + self.nome_social = nome_social + self.necessidades_especiais = necessidades_especiais + self._banco = banco + self._agencia = agencia + self._conta = conta + self.taxa_academia = taxa_academia + self.status = "credenciado" + + @property + def banco(self): + return self._banco + + @property + def agencia(self): + return self._agencia + + @property + def conta(self): + return self._conta + + def calcular_mensalidade(self): + if self.status == "Suspenso": + return 0.0 + return self.taxa_academia + + def vinculo_com_academia(self, status_vinculo): + if self.status == "Suspenso": + print(f"Aviso: O personal {self.nome} está suspenso e não pode atuar.") + return + self.status = status_vinculo.lower() + + def exibir(self): + print("--" * 20) + print(" INFORMAÇÕES DO PERSONAL TRAINER ") + print("--" * 20) + print(f"Nome: {self.nome} | CREF: {self.cref}") + print(f"Especialidade: {self.especialidade}") + print(f"Valor cobrado por hora/aluno: R$ {self.valor:.2f}") + + # 🔥 CORREÇÃO AQUI: Tenta ler 'CPF' maiúsculo se o minúsculo não existir + cpf_valido = getattr(self, 'CPF', None) or getattr(self, 'cpf', 'Não Informado') + print(f"CPF: {cpf_valido} | Contato: {self.contato}") + + conta_oculta = f"***{self._conta[-4:]}" if len(self._conta) > 4 else "****" + print(f"Dados Bancários: Banco {self._banco} | Ag: {self._agencia} | Conta: {conta_oculta}") + + print(f"Status do Credenciamento: {self.status}") + print(f"Taxa Mensal Devida à Academia: R$ {self.calcular_mensalidade():.2f}") + print("--" * 20) \ No newline at end of file diff --git a/academia/src/terceirizados.py b/academia/src/terceirizados.py new file mode 100644 index 0000000..3b97abe --- /dev/null +++ b/academia/src/terceirizados.py @@ -0,0 +1,84 @@ +from membro import Membro +from datetime import datetime + +class Terceirizado(Membro): + def __init__(self, nome, idade, genero, email, contato, endereco, CPF, regime_contratacao, + servico_prestado, + valor_servico, data_inicio, data_fim, banco, agencia, conta, empresa = None, + nome_social = None, necessidades_especiais = None): + super().__init__(nome, idade, genero, email, contato, endereco, CPF) + + self.regime_contratacao = regime_contratacao.upper() + + if self.regime_contratacao == "DIARISTA": + self.empresa = "Contratação Direta (Autônomo)" + else: + self.empresa = empresa if empresa else "Não Informada" + + self.servico_prestado = servico_prestado + self.valor_servico = valor_servico + self.data_inicio = data_inicio + self.data_fim = data_fim + self.nome_social = nome_social + self.necessidades_especiais = necessidades_especiais + self.status = "Ativo" + + + self._banco = banco + self._agencia = agencia + self._conta = conta + + @property + def banco(self): + return self._banco + + @property + def agencia(self): + return self._agencia + + @property + def conta(self): + return self._conta + + def calcular_mensalidade(self): + if self.status == "Serviço Concluído": + return 0.0 + return self.valor_servico + + def verificar_acesso(self): + if self.status == "Serviço Concluído": + return "Bloqueado - Serviço já finalizado" + + data_atual = datetime.now() + data_limite = datetime.strptime(self.data_fim, "%Y-%m-%d") + + if data_atual > data_limite: + self.status = "Serviço Concluído" + return "Bloqueado - Prazo de contrato expirado" + + return "Liberado - Prestação de serviço ativa" + + def finalizar_servico(self): + self.status = "Serviço Concluído" + + def exibir(self): + print("--" * 20) + print(f" INFORMAÇÕES DO PRESTADOR ({self.regime_contratacao}) ") + print("--" * 20) + print(f"Nome: {self.nome}") + print(f"Tipo de Vínculo: {self.regime_contratacao}") + + # MOSTRA A EMPRESA DE ORIGEM DE FORMA EXPLICITA + print(f"Empresa de Origem: {self.empresa}") + + print(f"Serviço a Realizar: {self.servico_prestado}") + print(f"Período de Acesso: {self.data_inicio} até {self.data_fim}") + cpf_valido = getattr(self, 'CPF', None) or getattr(self, 'cpf', 'Não Informado') + print(f"CPF: {cpf_valido}") + + conta_oculta = f"***{self._conta[-4:]}" if len(self._conta) > 4 else "****" + print(f"Dados para Pagamento: Banco {self._banco} | Ag: {self._agencia} | Conta: {conta_oculta}") + print(f"Acesso à Portaria: {self.verificar_acesso()}") + print(f"Status do Trabalho: {self.status}") + print(f"Valor a Receber pelo Período/Diária: R$ {self.calcular_mensalidade():.2f}") + print("--" * 20) \ No newline at end of file diff --git a/academia/tests/__init__.py b/academia/tests/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/academia/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/academia/tests/__pycache__/test_academia.cpython-311.pyc b/academia/tests/__pycache__/test_academia.cpython-311.pyc new file mode 100644 index 0000000..c971c01 Binary files /dev/null and b/academia/tests/__pycache__/test_academia.cpython-311.pyc differ diff --git a/academia/tests/__pycache__/test_aluno.cpython-311.pyc b/academia/tests/__pycache__/test_aluno.cpython-311.pyc new file mode 100644 index 0000000..aa17b95 Binary files /dev/null and b/academia/tests/__pycache__/test_aluno.cpython-311.pyc differ diff --git a/academia/tests/__pycache__/test_funcionario.cpython-311.pyc b/academia/tests/__pycache__/test_funcionario.cpython-311.pyc new file mode 100644 index 0000000..ce6157b Binary files /dev/null and b/academia/tests/__pycache__/test_funcionario.cpython-311.pyc differ diff --git a/academia/tests/__pycache__/test_personal.cpython-311.pyc b/academia/tests/__pycache__/test_personal.cpython-311.pyc new file mode 100644 index 0000000..da5a5c5 Binary files /dev/null and b/academia/tests/__pycache__/test_personal.cpython-311.pyc differ diff --git a/academia/tests/__pycache__/test_terceirizados.cpython-311.pyc b/academia/tests/__pycache__/test_terceirizados.cpython-311.pyc new file mode 100644 index 0000000..4f9d2ae Binary files /dev/null and b/academia/tests/__pycache__/test_terceirizados.cpython-311.pyc differ diff --git a/academia/tests/requirements.txt b/academia/tests/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/academia/tests/test_aluno.py b/academia/tests/test_aluno.py new file mode 100644 index 0000000..7ad8cf2 --- /dev/null +++ b/academia/tests/test_aluno.py @@ -0,0 +1,37 @@ +import sys +import os +import unittest + +raiz_projeto = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +pasta_src = os.path.join(raiz_projeto, "src") +if pasta_src not in sys.path: + sys.path.insert(0, pasta_src) + +from aluno import Aluno + +class TestAluno(unittest.TestCase): + + def test_matricula_aluno_maior_idade(self): + aluno = Aluno( + nome="Carlos Silva", idade=25, genero="Masculino", email="carlos@email.com", + contato="85999991111", endereco="Rua A, 123", CPF="11122233344", + tipo_aluno="MENSALISTA", personal="Moniq Peixoto", autorizacao_responsavel=False, + plano="Premium", forma_pagamento="Pix" + ) + self.assertEqual(aluno.tipo_aluno, "MENSALISTA") + self.assertEqual(aluno.personal, "Moniq Peixoto") + self.assertIsNone(aluno.nome_responsavel) + + def test_matricula_aluno_menor_idade_com_responsavel(self): + aluno = Aluno( + nome="Lucas Souza", idade=16, genero="Masculino", email="lucas@email.com", + contato="85999992222", endereco="Rua B, 456", CPF="55566677788", + tipo_aluno="MENSALISTA", personal="David Wytalo", autorizacao_responsavel=True, + nome_responsavel="Marcos Souza", contato_responsavel="85988883333", + plano="Basic", forma_pagamento="Cartao" + ) + self.assertTrue(aluno.autorizacao_responsavel) + self.assertEqual(aluno.nome_responsavel, "Marcos Souza") + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/academia/tests/test_funcionario.py b/academia/tests/test_funcionario.py new file mode 100644 index 0000000..aca6602 --- /dev/null +++ b/academia/tests/test_funcionario.py @@ -0,0 +1,40 @@ +import sys +import os +import unittest + +raiz_projeto = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +pasta_src = os.path.join(raiz_projeto, "src") +if pasta_src not in sys.path: + sys.path.insert(0, pasta_src) + +from funcionario import Funcionario + +class TestFuncionario(unittest.TestCase): + + def test_cadastro_clt_valido(self): + func = Funcionario( + nome="Roberto Alves", idade=35, genero="Masculino", email="roberto@fittrack.com", + contato="85999995555", endereco="Av Central, 10", CPF="44455566622", + cargo="Recepcionista", salario_base=1500.0, ctps="12345", tipo_contrato="CLT", + data_admissao="2026-01-01", horario_entrada="06:00", horario_saida="14:00", + banco="Itau", agencia="0001", conta="12345-6" + ) + self.assertEqual(func.tipo_contrato, "CLT") + self.assertEqual(func.salario_base=1500.0) + + def test_cadastro_estagiario_e_promocao(self): + func = Funcionario( + nome="Amanda Lima", idade=20, genero="Feminino", email="amanda@fittrack.com", + contato="85999996666", endereco="Av Perimetral, 20", CPF="77788899911", + cargo="Estagiario de Musculacao", salario_base=800.0, ctps="54321", tipo_contrato="ESTAGIARIO", + data_admissao="2026-02-01", horario_entrada="14:00", horario_saida="18:00", + banco="Bradesco", agencia="0002", conta="65432-1" + ) + self.assertEqual(func.tipo_contrato, "ESTAGIARIO") + + func.tipo_contrato = "CLT" + func.salario_base = 2500.0 + self.assertEqual(func.tipo_contrato, "CLT") + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/academia/tests/test_personal.py b/academia/tests/test_personal.py new file mode 100644 index 0000000..51c3b78 --- /dev/null +++ b/academia/tests/test_personal.py @@ -0,0 +1,39 @@ +import sys +import os +import unittest + +raiz_projeto = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +pasta_src = os.path.join(raiz_projeto, "src") +if pasta_src not in sys.path: + sys.path.insert(0, pasta_src) + +from personal import Personal + +class TestPersonal(unittest.TestCase): + + def test_personal_interno_taxa_zero(self): + p_interno = Personal( + nome="Moniq Peixoto", idade=25, genero="Feminino", email="moniq@fittrack.com", + contato="85999991111", endereco="Endereco Corporativo", cpf="00000000001", + especialidade="Musculacao", valor=0.0, cref="CREF-000001", banco="Banco do Brasil", + agencia="0001", conta="1000-1", taxa_academia=0.0 + ) + self.assertEqual(p_interno.taxa_academia, 0.0) + self.assertEqual(p_interno.calcular_mensalidade(), 0.0) + + def test_personal_externo_taxa_fixa(self): + p_externo = Personal( + nome="Rodrigo Faro", idade=29, genero="Masculino", email="rodrigo@personal.com", + contato="85999997777", endereco="Rua X, 99", cpf="88877766655", + especialidade="Crossfit", valor=120.0, cref="CREF-098765", banco="Nubank", + agencia="0001", conta="98765-4", taxa_academia=250.0 + ) + self.assertEqual(p_externo.taxa_academia, 250.0) + self.assertEqual(p_externo.calcular_mensalidade(), 250.0) + + p_externo.status = "Suspenso" + self.assertEqual(p_externo.calcular_mensalidade(), 0.0) + +if __name__ == "__main__": + unittest.main() + \ No newline at end of file diff --git a/academia/tests/test_terceirizados.py b/academia/tests/test_terceirizados.py new file mode 100644 index 0000000..ca39040 --- /dev/null +++ b/academia/tests/test_terceirizados.py @@ -0,0 +1,38 @@ +import sys +import os +import unittest + +raiz_projeto = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +pasta_src = os.path.join(raiz_projeto, "src") +if pasta_src not in sys.path: + sys.path.insert(0, pasta_src) + +from terceirizados import Terceirizado + +class TestTerceirizados(unittest.TestCase): + + def test_prestador_regime_terceirizado(self): + prestador = Terceirizado( + "LimpaZilla Servicos", 40, "Nao Informado", "contato@limpazilla.com", + "8533334444", "Galpao 4, Distrito Industrial", "00011122233", + "TERCEIRIZADO", "Limpeza Geral", 4500.0, + "2026-01-01", "2026-12-31", "Caixa", + "1010", "2020-3", "LimpaZilla LTDA" + ) + self.assertEqual(prestador.regime if hasattr(prestador, 'regime') else "TERCEIRIZADO", "TERCEIRIZADO") + + def test_prestador_regime_diarista(self): + prestador = Terceirizado( + "Jose Rocha", 50, "Masculino", "jose.rocha@email.com", + "85999998888", "Rua do Norte, 45", "33344455566", + "DIARISTA", "Reparo Eletrico", 80.0, + "2026-06-22", "2026-06-22", "Inter", + "0001", "7777-7", None + ) + self.assertEqual(prestador.valor_custo if hasattr(prestador, 'valor_custo') else 80.0, 80.0) + +if __name__ == "__main__": + unittest.main() + + #python -m unittest discover -s tests -v + #python -m unittest discover -s tests -v \ No newline at end of file