from functions.database import get_db_session, Militante, EmailMilitante, Endereco from sqlalchemy.orm import joinedload from datetime import datetime from typing import List, Dict, Optional from services.cache_service import cache_service, cached, CacheKeys, invalidate_cache_pattern import logging logger = logging.getLogger(__name__) class MilitanteModel: """Model para operações com militantes""" @staticmethod @invalidate_cache_pattern("militantes:*") def criar_militante(data: Dict) -> Dict: """Cria um novo militante""" db = get_db_session() try: # Criar endereço se fornecido endereco_id = None if data.get('endereco'): endereco = Endereco(**data['endereco']) db.add(endereco) db.flush() endereco_id = endereco.id # Criar militante militante = Militante( nome=data['nome'], cpf=data['cpf'], titulo_eleitoral=data.get('titulo_eleitoral'), data_nascimento=data.get('data_nascimento'), data_entrada_oci=data.get('data_entrada_oci'), data_efetivacao_oci=data.get('data_efetivacao_oci'), telefone1=data.get('telefone1'), telefone2=data.get('telefone2'), profissao=data.get('profissao'), regime_trabalho=data.get('regime_trabalho'), empresa=data.get('empresa'), contratante=data.get('contratante'), instituicao_ensino=data.get('instituicao_ensino'), tipo_instituicao=data.get('tipo_instituicao'), sindicato=data.get('sindicato'), cargo_sindical=data.get('cargo_sindical'), dirigente_sindical=data.get('dirigente_sindical', False), central_sindical=data.get('central_sindical'), endereco_id=endereco_id, celula_id=data.get('celula_id'), registrado_por=data.get('registrado_por') ) db.add(militante) db.flush() # Criar email se fornecido if data.get('email'): email = EmailMilitante( militante_id=militante.id, endereco_email=data['email'] ) db.add(email) db.commit() # Cache the new militante cache_key = CacheKeys.militante_detail(militante.id) militante_data = MilitanteModel.formatar_dados_militante(militante) cache_service.set(cache_key, militante_data, 1800) # 30 minutes logger.info(f"Militante {militante.id} criado e cacheado") return { 'status': 'success', 'message': 'Militante criado com sucesso', 'militante_id': militante.id } except Exception as e: db.rollback() logger.error(f"Erro ao criar militante: {e}") return { 'status': 'error', 'message': f'Erro ao criar militante: {str(e)}' } finally: db.close() @staticmethod @cached(expire=1800, key_prefix="militantes") # Cache for 30 minutes def listar_militantes() -> List[Militante]: """Lista todos os militantes""" db = get_db_session() try: militantes = db.query(Militante).options( joinedload(Militante.emails), joinedload(Militante.endereco), joinedload(Militante.celula) ).order_by(Militante.nome).all() # Cache individual militantes for militante in militantes: cache_key = CacheKeys.militante_detail(militante.id) militante_data = MilitanteModel.formatar_dados_militante(militante) cache_service.set(cache_key, militante_data, 1800) logger.debug(f"Listados {len(militantes)} militantes e cacheados") return militantes except Exception as e: logger.error(f"Erro ao listar militantes: {e}") return [] finally: db.close() @staticmethod def buscar_por_id(militante_id: int) -> Optional[Militante]: """Busca um militante por ID""" # Try cache first cache_key = CacheKeys.militante_detail(militante_id) cached_militante = cache_service.get(cache_key) if cached_militante: logger.debug(f"Cache hit para militante {militante_id}") # Convert cached data back to Militante object if needed return cached_militante # Cache miss, get from database db = get_db_session() try: militante = db.query(Militante).options( joinedload(Militante.emails), joinedload(Militante.endereco) ).get(militante_id) if militante: # Cache the militante militante_data = MilitanteModel.formatar_dados_militante(militante) cache_service.set(cache_key, militante_data, 1800) logger.debug(f"Cache miss para militante {militante_id}, cacheado") return militante except Exception as e: logger.error(f"Erro ao buscar militante {militante_id}: {e}") return None finally: db.close() @staticmethod @invalidate_cache_pattern("militantes:*") def atualizar_militante(militante_id: int, data: Dict) -> Dict: """Atualiza um militante existente""" db = get_db_session() try: militante = db.query(Militante).get(militante_id) if not militante: return { 'status': 'error', 'message': 'Militante não encontrado' } # Atualizar dados básicos militante.nome = data.get('nome', militante.nome) militante.cpf = data.get('cpf', militante.cpf) militante.titulo_eleitoral = data.get('titulo_eleitoral', militante.titulo_eleitoral) militante.telefone1 = data.get('telefone1', militante.telefone1) militante.telefone2 = data.get('telefone2', militante.telefone2) militante.profissao = data.get('profissao', militante.profissao) militante.regime_trabalho = data.get('regime_trabalho', militante.regime_trabalho) militante.empresa = data.get('empresa', militante.empresa) militante.contratante = data.get('contratante', militante.contratante) militante.instituicao_ensino = data.get('instituicao_ensino', militante.instituicao_ensino) militante.tipo_instituicao = data.get('tipo_instituicao', militante.tipo_instituicao) militante.sindicato = data.get('sindicato', militante.sindicato) militante.cargo_sindical = data.get('cargo_sindical', militante.cargo_sindical) militante.dirigente_sindical = data.get('dirigente_sindical', militante.dirigente_sindical) militante.central_sindical = data.get('central_sindical', militante.central_sindical) # Atualizar datas if data.get('data_nascimento'): militante.data_nascimento = data['data_nascimento'] if data.get('data_entrada_oci'): militante.data_entrada_oci = data['data_entrada_oci'] if data.get('data_efetivacao_oci'): militante.data_efetivacao_oci = data['data_efetivacao_oci'] # Atualizar endereço if data.get('endereco') and militante.endereco: endereco = militante.endereco endereco.cep = data['endereco'].get('cep', endereco.cep) endereco.estado = data['endereco'].get('estado', endereco.estado) endereco.cidade = data['endereco'].get('cidade', endereco.cidade) endereco.bairro = data['endereco'].get('bairro', endereco.bairro) endereco.rua = data['endereco'].get('rua', endereco.rua) endereco.numero = data['endereco'].get('numero', endereco.numero) endereco.complemento = data['endereco'].get('complemento', endereco.complemento) # Atualizar email if data.get('email') and militante.emails: militante.emails[0].endereco_email = data['email'] db.commit() # Update cache cache_key = CacheKeys.militante_detail(militante_id) militante_data = MilitanteModel.formatar_dados_militante(militante) cache_service.set(cache_key, militante_data, 1800) logger.info(f"Militante {militante_id} atualizado e cache atualizado") return { 'status': 'success', 'message': 'Militante atualizado com sucesso' } except Exception as e: db.rollback() logger.error(f"Erro ao atualizar militante {militante_id}: {e}") return { 'status': 'error', 'message': f'Erro ao atualizar militante: {str(e)}' } finally: db.close() @staticmethod @invalidate_cache_pattern("militantes:*") def excluir_militante(militante_id: int) -> Dict: """Exclui um militante""" db = get_db_session() try: militante = db.query(Militante).get(militante_id) if not militante: return { 'status': 'error', 'message': 'Militante não encontrado' } db.delete(militante) db.commit() # Remove from cache cache_key = CacheKeys.militante_detail(militante_id) cache_service.delete(cache_key) logger.info(f"Militante {militante_id} excluído e removido do cache") return { 'status': 'success', 'message': 'Militante excluído com sucesso' } except Exception as e: db.rollback() logger.error(f"Erro ao excluir militante {militante_id}: {e}") return { 'status': 'error', 'message': f'Erro ao excluir militante: {str(e)}' } finally: db.close() @staticmethod @cached(expire=1800, key_prefix="militantes") def buscar_por_cpf(cpf: str) -> Optional[Militante]: """Busca um militante por CPF""" db = get_db_session() try: militante = db.query(Militante).filter_by(cpf=cpf).first() if militante: # Cache the militante cache_key = CacheKeys.militante_detail(militante.id) militante_data = MilitanteModel.formatar_dados_militante(militante) cache_service.set(cache_key, militante_data, 1800) return militante except Exception as e: logger.error(f"Erro ao buscar militante por CPF {cpf}: {e}") return None finally: db.close() @staticmethod def formatar_dados_militante(militante: Militante) -> Dict: """Formata os dados de um militante para retorno JSON""" def formatar_data_segura(data): try: if not data: return None return data.strftime('%Y-%m-%d') except Exception as e: logger.error(f"Erro ao formatar data: {str(e)}, valor: {data}") return None return { 'id': militante.id, 'nome': militante.nome, 'cpf': militante.cpf, 'titulo_eleitoral': militante.titulo_eleitoral, 'data_nascimento': formatar_data_segura(militante.data_nascimento), 'data_entrada_oci': formatar_data_segura(militante.data_entrada_oci), 'data_efetivacao_oci': formatar_data_segura(militante.data_efetivacao_oci), 'telefone1': militante.telefone1, 'telefone2': militante.telefone2, 'profissao': militante.profissao, 'regime_trabalho': militante.regime_trabalho, 'empresa': militante.empresa, 'contratante': militante.contratante, 'instituicao_ensino': militante.instituicao_ensino, 'tipo_instituicao': militante.tipo_instituicao, 'sindicato': militante.sindicato, 'cargo_sindical': militante.cargo_sindical, 'dirigente_sindical': militante.dirigente_sindical, 'central_sindical': militante.central_sindical, 'responsabilidades': militante.responsabilidades, 'estado': militante.estado.value if militante.estado else None, 'celula_id': militante.celula_id, 'email': militante.emails[0].endereco_email if militante.emails else None, 'endereco': { 'cep': militante.endereco.cep if militante.endereco else None, 'estado': militante.endereco.estado if militante.endereco else None, 'cidade': militante.endereco.cidade if militante.endereco else None, 'bairro': militante.endereco.bairro if militante.endereco else None, 'rua': militante.endereco.rua if militante.endereco else None, 'numero': militante.endereco.numero if militante.endereco else None, 'complemento': militante.endereco.complemento if militante.endereco else None } if militante.endereco else None }