Files
controles/functions/database.py

327 lines
12 KiB
Python
Raw Normal View History

from sqlalchemy import create_engine, Column, Integer, String, Boolean, Numeric, Date, ForeignKey, DateTime
from sqlalchemy.orm import relationship, sessionmaker
2025-01-08 00:19:49 -03:00
from sqlalchemy.ext.declarative import declarative_base
from werkzeug.security import generate_password_hash, check_password_hash
import pyotp
import os
2025-03-24 14:50:42 -03:00
from pathlib import Path
from sqlalchemy.pool import NullPool
# 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'
# Configurar engine com NullPool
engine = create_engine(
f'sqlite:///{db_path}',
echo=True,
poolclass=NullPool # Usar NullPool ao invés do pool padrão
)
2025-01-08 00:19:49 -03:00
Base = declarative_base()
SessionLocal = sessionmaker(bind=engine)
2025-01-08 00:19:49 -03:00
def get_db_connection():
"""
Retorna uma nova sessão do banco de dados
"""
2025-03-24 14:50:42 -03:00
try:
return SessionLocal()
finally:
engine.dispose()
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()
2025-01-08 00:19:49 -03:00
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)
email = Column(String(100), unique=True)
telefone = Column(String(15))
endereco = Column(String(255))
filiado = Column(Boolean, default=False)
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")
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)
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)
pagamentos = relationship("Pagamento", back_populates="tipo_pagamento")
class Pagamento(Base):
__tablename__ = 'pagamentos'
id = Column(Integer, primary_key=True, autoincrement=True)
militante_id = Column(Integer, ForeignKey('militantes.id'))
tipo_pagamento_id = Column(Integer, ForeignKey('tipos_pagamento.id'))
valor = Column(Numeric(10, 2), nullable=False)
data_pagamento = Column(Date, nullable=False)
militante = relationship("Militante", back_populates="pagamentos")
tipo_pagamento = relationship("TipoPagamento", 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, autoincrement=True)
nome = Column(String(100), nullable=False)
relatorios_cotas = relationship("RelatorioCotasMensais", back_populates="setor")
relatorios_vendas = relationship("RelatorioVendasMateriais", back_populates="setor")
usuarios = relationship("Usuario", back_populates="setor")
2025-01-08 00:19:49 -03:00
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 Usuario(Base):
__tablename__ = 'usuarios'
id = Column(Integer, primary_key=True, autoincrement=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))
role = relationship("Role", back_populates="usuarios")
setor = relationship("Setor", back_populates="usuarios")
def __init__(self, username, password, is_admin=False):
self.username = username
self.password_hash = generate_password_hash(password)
self.is_admin = is_admin
self.otp_secret = pyotp.random_base32() # Gerar segredo OTP na criação
self.ativo = True
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def verify_otp(self, otp_code):
"""Verifica se o código OTP fornecido é válido"""
if not self.otp_secret:
print(f"Erro: Usuário {self.username} não tem segredo OTP configurado")
return False
totp = pyotp.TOTP(self.otp_secret)
is_valid = totp.verify(otp_code)
print(f"Verificando OTP para {self.username}")
print(f"Segredo: {self.otp_secret}")
2025-03-24 14:50:42 -03:00
print(f"Código fornecido: {otp_code}")
print(f"Resultado: {'válido' if is_valid else 'inválido'}")
return is_valid
def get_otp_uri(self):
"""Gera a URI para o QR code do OTP"""
if not self.otp_secret:
self.otp_secret = pyotp.random_base32()
totp = pyotp.TOTP(self.otp_secret)
return totp.provisioning_uri(
name=self.username,
issuer_name="Sistema de Controles"
)
class Role(Base):
__tablename__ = 'roles'
id = Column(Integer, primary_key=True, autoincrement=True)
nome = Column(String(50), unique=True, nullable=False)
nivel = Column(Integer, nullable=False) # Nível hierárquico (1: admin, 2: coordenador, 3: militante)
usuarios = relationship("Usuario", back_populates="role")
permissoes = relationship("RolePermissao", back_populates="role")
class Permissao(Base):
__tablename__ = 'permissoes'
id = Column(Integer, primary_key=True, autoincrement=True)
nome = Column(String(50), unique=True, nullable=False)
descricao = Column(String(255))
roles = relationship("RolePermissao", back_populates="permissao")
class RolePermissao(Base):
__tablename__ = 'roles_permissoes'
role_id = Column(Integer, ForeignKey('roles.id'), primary_key=True)
permissao_id = Column(Integer, ForeignKey('permissoes.id'), primary_key=True)
role = relationship("Role", back_populates="permissoes")
permissao = relationship("Permissao", back_populates="roles")
# Remover o banco de dados existente (se existir)
2025-03-24 14:50:42 -03:00
if os.path.exists(db_path):
os.remove(db_path)
2025-03-24 14:50:42 -03:00
def init_database():
"""Inicializa o banco de dados com dados básicos"""
print("Inicializando banco de dados...")
# Criar todas as tabelas
Base.metadata.create_all(engine)
session = SessionLocal()
try:
2025-03-24 14:50:42 -03:00
# Verificar se já existe um admin
admin = session.query(Usuario).filter_by(username="admin").first()
2025-03-24 14:50:42 -03:00
if not admin:
print("Criando role de administrador...")
# Criar role de admin
admin_role = session.query(Role).filter_by(nome="Administrador").first()
if not admin_role:
admin_role = Role(nome="Administrador", nivel=1)
session.add(admin_role)
session.commit()
print("Criando usuário admin...")
# Criar usuário admin
admin = Usuario(
username="admin",
password="admin123",
is_admin=True
)
admin.email = "admin@example.com"
admin.role_id = admin_role.id
session.add(admin)
session.commit()
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}")
else:
print("Usuário admin já existe")
except Exception as e:
2025-03-24 14:50:42 -03:00
print(f"Erro na inicialização do banco: {e}")
session.rollback()
2025-03-24 14:50:42 -03:00
raise
finally:
session.close()
2025-03-24 14:50:42 -03:00
# Inicializar o banco de dados automaticamente quando o módulo for importado
init_database()
# Executar a criação dos dados iniciais
if __name__ == "__main__":
2025-03-24 14:50:42 -03:00
init_database()