from datetime import datetime, timedelta from werkzeug.security import generate_password_hash, check_password_hash from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Text, Numeric, Date, Enum, create_engine, text from sqlalchemy.orm import sessionmaker, relationship, backref import os import pyotp from pathlib import Path from sqlalchemy.pool import NullPool import secrets from flask_mail import Message from flask import url_for import enum from flask_login import UserMixin from .rbac import Role, Permission, role_permissions, user_roles from .base import Base, engine, Session import logging import qrcode from PIL import Image import re # Configurar caminho do banco de dados db_dir = Path.home() / '.local' / 'share' / 'controles' db_dir.mkdir(parents=True, exist_ok=True) db_path = db_dir / 'database.db' DATABASE_URL = f"sqlite:///{db_path}" engine = create_engine(DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) def get_db_connection(): """Retorna uma nova sessão do banco de dados""" Session = sessionmaker(bind=engine) db = Session() try: # Configurar SQLite para melhor tratamento de concorrência db.execute(text("PRAGMA journal_mode=WAL")) db.execute(text("PRAGMA busy_timeout=5000")) return db except: db.close() raise def execute_query(query, params=None): """ Executa uma query usando SQLAlchemy """ session = get_db_connection() try: result = session.execute(query, params) session.commit() return result except Exception as e: session.rollback() raise e finally: session.close() class EstadoMilitante(enum.Enum): ATIVO = 'ativo' DESLIGADO = 'desligado' SUSPENSO = 'suspenso' AFASTADO = 'afastado' class Celula(Base): __tablename__ = 'celulas' id = Column(Integer, primary_key=True, autoincrement=True) nome = Column(String(100), nullable=False) setor_id = Column(Integer, ForeignKey('setores.id', use_alter=True, name='fk_celula_setor')) cr_id = Column(Integer, ForeignKey('comites_regionais.id', use_alter=True, name='fk_celula_cr')) secretario = Column(Integer, ForeignKey('militantes.id', use_alter=True, name='fk_celula_secretario')) responsavel_financas = Column(Integer, ForeignKey('militantes.id', use_alter=True, name='fk_celula_responsavel_financas')) quadro_orientador = Column(String(255)) # Relacionamentos setor = relationship("Setor", back_populates="celulas") cr = relationship("ComiteRegional", back_populates="celulas") militantes = relationship("Militante", back_populates="celula", foreign_keys="[Militante.celula_id]") secretario_rel = relationship("Militante", foreign_keys=[secretario]) responsavel_financas_rel = relationship("Militante", foreign_keys=[responsavel_financas]) pagamentos = relationship("PagamentoCelula", back_populates="celula") usuarios = relationship("Usuario", back_populates="celula") class ComiteRegional(Base): __tablename__ = 'comites_regionais' id = Column(Integer, primary_key=True, autoincrement=True) nome = Column(String(100), nullable=False) responsavel_financas = Column(Integer, ForeignKey('militantes.id', use_alter=True, name='fk_cr_responsavel_financas')) responsavel_formacao = Column(Integer, ForeignKey('militantes.id', use_alter=True, name='fk_cr_responsavel_formacao')) secretario_organizacao = Column(Integer, ForeignKey('militantes.id', use_alter=True, name='fk_cr_secretario_organizacao')) correspondente_jornal = Column(Integer, ForeignKey('militantes.id', use_alter=True, name='fk_cr_correspondente_jornal')) # Relacionamentos responsavel_financas_rel = relationship("Militante", foreign_keys=[responsavel_financas]) responsavel_formacao_rel = relationship("Militante", foreign_keys=[responsavel_formacao]) secretario_organizacao_rel = relationship("Militante", foreign_keys=[secretario_organizacao]) correspondente_jornal_rel = relationship("Militante", foreign_keys=[correspondente_jornal]) setores = relationship("Setor", back_populates="cr") celulas = relationship("Celula", back_populates="cr") usuarios = relationship("Usuario", back_populates="cr") class EmailMilitante(Base): __tablename__ = 'emails_militantes' id = Column(Integer, primary_key=True, autoincrement=True) militante_id = Column(Integer, ForeignKey('militantes.id')) endereco_email = Column(String(100)) militante = relationship("Militante", back_populates="emails") class Endereco(Base): __tablename__ = 'enderecos' id = Column(Integer, primary_key=True, autoincrement=True) estado = Column(String(2)) cidade = Column(String(50)) bairro = Column(String(50)) rua = Column(String(100)) numero = Column(String(10)) complemento = Column(String(50)) cep = Column(String(9)) militantes = relationship("Militante", back_populates="endereco") class RedeSocial(Base): __tablename__ = 'redes_sociais' id = Column(Integer, primary_key=True, autoincrement=True) militante_id = Column(Integer, ForeignKey('militantes.id')) tipo = Column(String(20)) # Instagram, TikTok, Discord, etc. identificador = Column(String(100)) militante = relationship("Militante", back_populates="redes_sociais") class Militante(Base): __tablename__ = 'militantes' id = Column(Integer, primary_key=True, autoincrement=True) nome = Column(String(100), nullable=False) cpf = Column(String(14), unique=True) # Novos campos básicos titulo_eleitoral = Column(String(20)) data_nascimento = Column(Date) data_entrada_oci = Column(Date) data_efetivacao_oci = Column(Date) # Campos de contato telefone1 = Column(String(15)) telefone2 = Column(String(15)) # Relacionamento para múltiplos emails emails = relationship("EmailMilitante", back_populates="militante") # Endereço endereco_id = Column(Integer, ForeignKey('enderecos.id', use_alter=True, name='fk_militante_endereco')) endereco = relationship("Endereco", back_populates="militantes") # Redes sociais redes_sociais = relationship("RedeSocial", back_populates="militante") # Campos profissionais profissao = Column(String(100)) regime_trabalho = Column(String(50)) # CLT, Estatutário, etc. empresa = Column(String(100)) contratante = Column(String(100)) # Para terceirizados # Campos acadêmicos instituicao_ensino = Column(String(100)) tipo_instituicao = Column(String(20)) # Federal, Estadual, etc. # Campos sindicais sindicato = Column(String(100)) cargo_sindical = Column(String(50)) dirigente_sindical = Column(Boolean) central_sindical = Column(String(100)) # Responsável pelo cadastro registrado_por = Column(Integer, ForeignKey('militantes.id', use_alter=True, name='fk_militante_registrado_por')) # Campos existentes celula_id = Column(Integer, ForeignKey('celulas.id', use_alter=True, name='fk_militante_celula')) responsabilidades = Column(Integer, default=0) otp_secret = Column(String(32)) temp_token = Column(String(64)) temp_token_expiry = Column(DateTime) # Novo campo para Quadro-Orientador quadro_orientador = Column(Boolean, default=False) # Campos para Aspirante aspirante = Column(Boolean, default=True) # Por padrão, todo novo militante é aspirante data_inicio_aspirante = Column(DateTime, default=datetime.utcnow) avaliacao_aspirante = Column(Text) data_avaliacao_aspirante = Column(DateTime) # Campos para estado do militante estado = Column(Enum(EstadoMilitante), default=EstadoMilitante.ATIVO) data_desligamento = Column(DateTime) motivo_desligamento = Column(Text) # Relacionamentos existentes cotas_mensais = relationship("CotaMensal", back_populates="militante") pagamentos = relationship("Pagamento", back_populates="militante") materiais_vendidos = relationship("MaterialVendido", back_populates="militante") vendas_jornais = relationship("VendaJornalAvulso", back_populates="militante") assinaturas = relationship("AssinaturaAnual", back_populates="militante") celula = relationship("Celula", back_populates="militantes", foreign_keys=[celula_id]) comprovantes = relationship("Comprovante", back_populates="militante") # Constantes para responsabilidades SECRETARIO = 1 TESOUREIRO = 2 IMPRENSA = 4 MNS = 8 MPS = 16 JUVENTUDE = 32 QUADRO_ORIENTADOR = 64 ASPIRANTE = 128 RESPONSAVEL_FINANCAS = 256 RESPONSAVEL_IMPRENSA = 512 @staticmethod def get_responsabilidades_list(): return [ (Militante.SECRETARIO, "Secretário"), (Militante.TESOUREIRO, "Tesoureiro"), (Militante.IMPRENSA, "Imprensa"), (Militante.MNS, "MNS"), (Militante.MPS, "MPS"), (Militante.JUVENTUDE, "Juventude"), (Militante.QUADRO_ORIENTADOR, "Quadro-Orientador"), (Militante.ASPIRANTE, "Aspirante"), (Militante.RESPONSAVEL_FINANCAS, "Responsável de Finanças"), (Militante.RESPONSAVEL_IMPRENSA, "Responsável de Imprensa") ] def set_responsabilidades(self, resp_list): """ Define as responsabilidades do militante resp_list: lista de inteiros representando as responsabilidades """ self.responsabilidades = sum(resp_list) def get_responsabilidades(self): """ Retorna lista de responsabilidades ativas """ resp = [] for valor, nome in self.get_responsabilidades_list(): if self.responsabilidades & valor: resp.append(nome) return resp def generate_temp_token(self): """ Gera um token temporário para acesso ao QR code """ self.temp_token = secrets.token_urlsafe(32) self.temp_token_expiry = datetime.now() + timedelta(hours=48) return self.temp_token def send_otp_email(self, mail): """ Envia email com link para QR code """ token = self.generate_temp_token() qr_url = url_for('get_qr_code', token=token, _external=True) msg = Message( 'Configuração de Autenticação em Duas Etapas', recipients=[self.email] ) msg.body = f""" Olá {self.nome}, Para configurar sua autenticação em duas etapas, acesse o link abaixo: {qr_url} Este link expirará em 48 horas. Instruções: 1. Instale um aplicativo autenticador (Google Authenticator, Microsoft Authenticator) 2. Acesse o link acima 3. Escaneie o QR code com o aplicativo 4. Use o código gerado para fazer login no sistema Atenciosamente, Sistema de Controles """ mail.send(msg) def generate_username(self): """Gera um nome de usuário único baseado no primeiro nome e um código""" from sqlalchemy import func db = get_db_connection() try: # Pega o primeiro nome primeiro_nome = self.nome.split()[0].lower() # Conta quantos usuários já existem com esse prefixo count = db.query(func.count(Usuario.id)).filter( Usuario.username.like(f"{primeiro_nome}%") ).scalar() # Gera o código (número sequencial) codigo = str(count + 1).zfill(3) return f"{primeiro_nome}{codigo}" finally: db.close() class CotaMensal(Base): __tablename__ = 'cotas_mensais' id = Column(Integer, primary_key=True, autoincrement=True) militante_id = Column(Integer, ForeignKey('militantes.id')) valor_antigo = Column(Numeric(10, 2), nullable=False) valor_novo = Column(Numeric(10, 2), nullable=False) data_alteracao = Column(Date, nullable=False) data_vencimento = Column(Date, nullable=False) pago = Column(Boolean, default=False) militante = relationship("Militante", back_populates="cotas_mensais") class TipoPagamento(Base): __tablename__ = 'tipos_pagamento' id = Column(Integer, primary_key=True, autoincrement=True) descricao = Column(String(100), nullable=False) class Pagamento(Base): __tablename__ = 'pagamentos' id = Column(Integer, primary_key=True, autoincrement=True) militante_id = Column(Integer, ForeignKey('militantes.id')) tipo_pagamento = Column(String(50)) # Cota, Jornal, Assinatura, etc. mes_referencia = Column(Date) numero_jornal = Column(String(20)) numero_inicial_assinatura = Column(String(20)) numero_final_assinatura = Column(String(20)) campanha_financeira = Column(String(50)) valor = Column(Numeric(10, 2), nullable=False) data_pagamento = Column(Date, nullable=False) militante = relationship("Militante", back_populates="pagamentos") class TipoMaterial(Base): __tablename__ = 'tipos_materiais' id = Column(Integer, primary_key=True, autoincrement=True) descricao = Column(String(100), nullable=False) materiais_vendidos = relationship("MaterialVendido", back_populates="tipo_material") assinaturas = relationship("AssinaturaAnual", back_populates="tipo_material") class MaterialVendido(Base): __tablename__ = 'materiais_vendidos' id = Column(Integer, primary_key=True, autoincrement=True) militante_id = Column(Integer, ForeignKey('militantes.id')) tipo_material_id = Column(Integer, ForeignKey('tipos_materiais.id')) descricao = Column(String(255), nullable=False) valor = Column(Numeric(10, 2), nullable=False) data_venda = Column(Date, nullable=False) militante = relationship("Militante", back_populates="materiais_vendidos") tipo_material = relationship("TipoMaterial", back_populates="materiais_vendidos") class VendaJornalAvulso(Base): __tablename__ = 'vendas_jornais_avulsos' id = Column(Integer, primary_key=True, autoincrement=True) militante_id = Column(Integer, ForeignKey('militantes.id')) quantidade = Column(Integer, nullable=False) valor_total = Column(Numeric(10, 2), nullable=False) data_venda = Column(Date, nullable=False) militante = relationship("Militante", back_populates="vendas_jornais") class AssinaturaAnual(Base): __tablename__ = 'assinaturas_anuais' id = Column(Integer, primary_key=True, autoincrement=True) militante_id = Column(Integer, ForeignKey('militantes.id')) tipo_material_id = Column(Integer, ForeignKey('tipos_materiais.id')) quantidade = Column(Integer, nullable=False) valor_total = Column(Numeric(10, 2), nullable=False) data_inicio = Column(Date, nullable=False) data_fim = Column(Date, nullable=False) militante = relationship("Militante", back_populates="assinaturas") tipo_material = relationship("TipoMaterial", back_populates="assinaturas") class Setor(Base): __tablename__ = 'setores' id = Column(Integer, primary_key=True) nome = Column(String(100), nullable=False) cr_id = Column(Integer, ForeignKey('comites_regionais.id', use_alter=True, name='fk_setor_cr')) responsavel = Column(Integer, ForeignKey('militantes.id', use_alter=True, name='fk_setor_responsavel')) responsavel_financas = Column(Integer, ForeignKey('militantes.id', use_alter=True, name='fk_setor_responsavel_financas')) # Relacionamentos cr = relationship("ComiteRegional", back_populates="setores") responsavel_rel = relationship("Militante", foreign_keys=[responsavel]) responsavel_financas_rel = relationship("Militante", foreign_keys=[responsavel_financas]) usuarios = relationship("Usuario", back_populates="setor") celulas = relationship("Celula", back_populates="setor") relatorios_cotas = relationship("RelatorioCotasMensais", back_populates="setor") relatorios_vendas = relationship("RelatorioVendasMateriais", back_populates="setor") class ComiteCentral(Base): __tablename__ = 'comites_centrais' id = Column(Integer, primary_key=True, autoincrement=True) nome = Column(String(100), nullable=False) relatorios_cotas = relationship("RelatorioCotasMensais", back_populates="comite") relatorios_vendas = relationship("RelatorioVendasMateriais", back_populates="comite") class RelatorioCotasMensais(Base): __tablename__ = 'relatorio_cotas_mensais' id = Column(Integer, primary_key=True, autoincrement=True) setor_id = Column(Integer, ForeignKey('setores.id')) comite_id = Column(Integer, ForeignKey('comites_centrais.id')) total_cotas = Column(Numeric(10, 2), nullable=False) data_relatorio = Column(Date, nullable=False) setor = relationship("Setor", back_populates="relatorios_cotas") comite = relationship("ComiteCentral", back_populates="relatorios_cotas") class RelatorioVendasMateriais(Base): __tablename__ = 'relatorio_vendas_materiais' id = Column(Integer, primary_key=True, autoincrement=True) setor_id = Column(Integer, ForeignKey('setores.id')) comite_id = Column(Integer, ForeignKey('comites_centrais.id')) total_vendas = Column(Numeric(10, 2), nullable=False) data_relatorio = Column(Date, nullable=False) setor = relationship("Setor", back_populates="relatorios_vendas") comite = relationship("ComiteCentral", back_populates="relatorios_vendas") class TipoUsuario(enum.Enum): ADMIN = "admin" CR_RESPONSAVEL = "cr_responsavel" SETOR_RESPONSAVEL = "setor_responsavel" USUARIO = "usuario" class Usuario(Base, UserMixin): __tablename__ = 'usuarios' id = Column(Integer, primary_key=True) username = Column(String(50), unique=True, nullable=False) password_hash = Column(String(255), nullable=False) email = Column(String(100), unique=True, nullable=False) otp_secret = Column(String(32)) role_id = Column(Integer, ForeignKey('roles.id')) setor_id = Column(Integer, ForeignKey('setores.id')) ativo = Column(Boolean, default=True) is_admin = Column(Boolean, default=False) ultimo_login = Column(DateTime) ultimo_logout = Column(DateTime) motivo_logout = Column(String(100)) cr_id = Column(Integer, ForeignKey('comites_regionais.id')) celula_id = Column(Integer, ForeignKey('celulas.id')) session_timeout = Column(Integer, default=30) tipo = Column(String(17), nullable=False) ultima_atividade = Column(DateTime, default=datetime.utcnow) # Relacionamento com militante militante_id = Column(Integer, ForeignKey('militantes.id')) militante = relationship("Militante", backref=backref("usuario", uselist=False)) # Relacionamentos roles = relationship("Role", secondary="user_roles", back_populates="users") setor = relationship('Setor', back_populates='usuarios') cr = relationship('ComiteRegional', back_populates='usuarios') celula = relationship('Celula', back_populates='usuarios') def __init__(self, username, email=None, is_admin=False): self.username = username self.email = email self.is_admin = is_admin self.email = email self.ativo = True self.session_timeout = 30 self.tipo = "USUARIO" self.ultima_atividade = datetime.utcnow() def set_password(self, password): self.password_hash = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) def update_last_activity(self): self.ultima_atividade = datetime.utcnow() def is_session_expired(self): if not self.ultima_atividade: return True time_diff = datetime.utcnow() - self.ultima_atividade return time_diff.total_seconds() > (self.session_timeout * 60) def check_session_timeout(self): """Verifica se a sessão do usuário expirou""" if not self.ultima_atividade: return True time_diff = datetime.utcnow() - self.ultima_atividade return time_diff.total_seconds() > (self.session_timeout * 60) def has_permission(self, permission_name): """Verifica se o usuário tem uma permissão específica""" if self.is_admin: # Se for admin, tem todas as permissões return True # Verifica se o usuário tem a permissão através de suas roles for role in self.roles: for permission in role.permissions: if permission.nome == permission_name: return True return False def has_role(self, role_nivel): """Verifica se o usuário tem um determinado nível de role""" for role in self.roles: if role.nivel == role_nivel: return True return False def get_otp_uri(self): """Gera a URI para autenticação em duas etapas""" if not self.otp_secret: self.otp_secret = pyotp.random_base32() return pyotp.totp.TOTP(self.otp_secret).provisioning_uri( self.username, issuer_name="Sistema de Controles" ) def verify_otp(self, code): """Verifica se um código OTP é válido""" if not self.otp_secret: print(f"Erro: OTP secret não configurado para o usuário {self.username}") return False print(f"Verificando OTP para usuário {self.username}") print(f"OTP Secret: {self.otp_secret}") print(f"Código fornecido: {code}") totp = pyotp.totp.TOTP(self.otp_secret) is_valid = totp.verify(code) print(f"Resultado da verificação: {'Válido' if is_valid else 'Inválido'}") print(f"Tempo atual: {datetime.utcnow()}") print(f"Período atual: {totp.timecode(datetime.utcnow())}") return is_valid def logout(self): """Registra o logout do usuário""" self.ultimo_logout = datetime.utcnow() self.motivo_logout = "Logout manual" self.ultima_atividade = None class PagamentoCelula(Base): __tablename__ = 'pagamentos_celula' id = Column(Integer, primary_key=True, autoincrement=True) celula_id = Column(Integer, ForeignKey('celulas.id')) data = Column(Date) valor = Column(Numeric(10, 2)) metodo_pagamento = Column(String(20)) # PIX, Dinheiro, etc. codigo_pix = Column(String(100)) descricao = Column(String(255)) registrado_por = Column(Integer, ForeignKey('militantes.id')) celula = relationship("Celula", back_populates="pagamentos") registrado_por_rel = relationship("Militante", foreign_keys=[registrado_por]) class Atividade(Base): __tablename__ = 'atividades' id = Column(Integer, primary_key=True, autoincrement=True) descricao = Column(String(255)) data = Column(Date) responsavel1 = Column(Integer, ForeignKey('militantes.id')) responsavel2 = Column(Integer, ForeignKey('militantes.id')) responsavel1_rel = relationship("Militante", foreign_keys=[responsavel1]) responsavel2_rel = relationship("Militante", foreign_keys=[responsavel2]) materiais = relationship("MaterialAtividade", back_populates="atividade") class MaterialAtividade(Base): __tablename__ = 'materiais_atividades' id = Column(Integer, primary_key=True, autoincrement=True) atividade_id = Column(Integer, ForeignKey('atividades.id')) tipo = Column(String(20)) # Jornal, Revista, etc. quantidade = Column(Integer) detalhes = Column(String(255)) atividade = relationship("Atividade", back_populates="materiais") class Relatorio(Base): __tablename__ = 'relatorios' id = Column(Integer, primary_key=True, autoincrement=True) tipo = Column(String(50)) # Semanal, Quinzenal, Mensal periodo_inicio = Column(Date) periodo_fim = Column(Date) gerado_por = Column(Integer, ForeignKey('militantes.id')) conteudo = Column(Text) # Relacionamento hierárquico celula_id = Column(Integer, ForeignKey('celulas.id')) setor_id = Column(Integer, ForeignKey('setores.id')) cr_id = Column(Integer, ForeignKey('comites_regionais.id')) gerado_por_rel = relationship("Militante", foreign_keys=[gerado_por]) celula = relationship("Celula", foreign_keys=[celula_id]) setor = relationship("Setor", foreign_keys=[setor_id]) cr = relationship("ComiteRegional", foreign_keys=[cr_id]) class CampanhaFinanceira(Base): __tablename__ = 'campanhas_financeiras' id = Column(Integer, primary_key=True, autoincrement=True) nome = Column(String(100), nullable=False) descricao = Column(Text) data_inicio = Column(Date, nullable=False) data_fim = Column(Date, nullable=False) meta = Column(Numeric(10, 2), nullable=False) valor_arrecadado = Column(Numeric(10, 2), default=0) status = Column(String(20), default='Em andamento') # Em andamento, Concluída, Cancelada comprovantes = relationship("Comprovante", back_populates="campanha") class TipoComprovante(Base): __tablename__ = 'tipos_comprovante' id = Column(Integer, primary_key=True) descricao = Column(String(50), nullable=False) valor = Column(Numeric(10, 2), nullable=False) class CentralizacaoComprovante(Base): __tablename__ = 'centralizacoes_comprovante' id = Column(Integer, primary_key=True, autoincrement=True) comprovante_id = Column(Integer, ForeignKey('comprovantes.id'), nullable=False) tipo_comprovante = Column(String(50), nullable=False) # Cota, Jornal, Assinatura, etc. valor = Column(Numeric(10, 2), nullable=False) comprovante = relationship("Comprovante", back_populates="centralizacoes") class Comprovante(Base): __tablename__ = 'comprovantes' id = Column(Integer, primary_key=True) militante_id = Column(Integer, ForeignKey('militantes.id'), nullable=False) data_comprovante = Column(Date, nullable=False) forma_pagamento = Column(String(20), nullable=False) # PIX, transferência/DOC, depósito, maquininha campanha_id = Column(Integer, ForeignKey('campanhas_financeiras.id')) militante = relationship("Militante", back_populates="comprovantes") transacoes_pix = relationship("TransacaoPIX", back_populates="comprovante") campanha = relationship("CampanhaFinanceira", back_populates="comprovantes") centralizacoes = relationship("CentralizacaoComprovante", back_populates="comprovante", cascade="all, delete-orphan") class TransacaoPIX(Base): __tablename__ = 'transacoes_pix' id = Column(Integer, primary_key=True, autoincrement=True) chave_pix = Column(String(100)) valor = Column(Numeric(10, 2)) data_geracao = Column(DateTime) data_pagamento = Column(DateTime) status = Column(String(20)) # Pendente, Pago, Expirado qr_code = Column(Text) comprovante_id = Column(Integer, ForeignKey('comprovantes.id')) comprovante = relationship("Comprovante", back_populates="transacoes_pix") def init_database(): """Inicializa o banco de dados com dados básicos""" print("Inicializando banco de dados...") session = get_db_connection() try: # Criar todas as tabelas Base.metadata.drop_all(engine) # Remover todas as tabelas existentes Base.metadata.create_all(engine) # Criar roles padrão roles = [ ("Administrador", Role.SECRETARIO_GERAL), ("Secretário", Role.SECRETARIO_CELULA), ("Militante", Role.MILITANTE_BASICO) ] for nome, nivel in roles: if not session.query(Role).filter_by(nome=nome).first(): role = Role(nome=nome, nivel=nivel) session.add(role) session.commit() # Criar setores padrão setores = ["Setor 1", "Setor 2", "Setor 3"] for nome in setores: if not session.query(Setor).filter_by(nome=nome).first(): setor = Setor(nome=nome) session.add(setor) session.commit() # Criar comitês padrão comites = ["Comitê 1", "Comitê 2", "Comitê 3"] for nome in comites: if not session.query(ComiteCentral).filter_by(nome=nome).first(): comite = ComiteCentral(nome=nome) session.add(comite) session.commit() # Verificar se existe QR code do admin admin_otp_secret = None qr_path = 'admin_qr.png' if os.path.exists(qr_path): try: # Tentar ler o QR code existente from pyzbar.pyzbar import decode qr_data = decode(Image.open(qr_path)) if qr_data: # O URI do OTP está no formato: otpauth://totp/Sistema%20de%20Controles:admin?secret=XXXXX&issuer=Sistema%20de%20Controles uri = qr_data[0].data.decode('utf-8') # Extrair o secret do URI match = re.search(r'secret=([A-Z0-9]+)', uri) if match: admin_otp_secret = match.group(1) print("OTP existente encontrado no QR code") except Exception as e: print(f"Erro ao ler QR code existente: {e}") if not admin_otp_secret: # Se não conseguiu ler o QR code ou ele não existe, gera um novo admin_otp_secret = pyotp.random_base32() print(f"Novo OTP gerado: {admin_otp_secret}") # Criar usuário admin admin_role = session.query(Role).filter_by(nome="Administrador").first() setor = session.query(Setor).first() admin = Usuario( username="admin", email="admin@example.com", is_admin=True ) admin.set_password("admin123") admin.tipo = "ADMIN" admin.otp_secret = admin_otp_secret admin.roles.append(admin_role) admin.setor = setor session.add(admin) session.commit() # Gerar QR code apenas se não existir if not os.path.exists(qr_path): totp = pyotp.totp.TOTP(admin_otp_secret) provisioning_uri = totp.provisioning_uri("admin", issuer_name="Sistema de Controles") qr = qrcode.QRCode(version=1, box_size=10, border=5) qr.add_data(provisioning_uri) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") img.save(qr_path) print("=== Usuário Admin Criado ===") print(f"Username: admin") print(f"Senha: admin123") print(f"Email: {admin.email}") print(f"OTP Secret: {admin_otp_secret}") print(f"QR Code: {qr_path}") # Importar e executar o seed após criar todas as dependências from seed_data import seed_database print("\nPopulando banco de dados com dados de teste...") seed_database() print("Dados de teste criados com sucesso!") except Exception as e: print(f"Erro na inicialização do banco: {e}") session.rollback() raise finally: session.close() if __name__ == "__main__": init_database()