IOI-RUN / models.py
Roudrigus's picture
Update models.py
5f0cf9b verified
# -*- coding: utf-8 -*-
from sqlalchemy import (
Column,
Integer,
String,
Date,
DateTime,
Boolean,
ForeignKey,
Text
)
from sqlalchemy.orm import relationship
# If you face cyclic import issues, place Base here via declarative_base:
# from sqlalchemy.orm import declarative_base
# Base = declarative_base()
from datetime import datetime
from banco import Base
from sqlalchemy.sql import func # server_default em AvisoGlobal
# =====================================================
# TABELA EQUIPAMENTOS
# =====================================================
class Equipamento(Base):
__tablename__ = "equipamentos"
id = Column(Integer, primary_key=True, index=True)
# Identificação
fpso1 = Column(String, index=True, nullable=False)
fpso = Column(String, index=True, nullable=False)
data_coleta = Column(String, index=True, nullable=False) # sugestão futura: Date
# Responsáveis
especialista = Column(String, nullable=False)
conferente = Column(String, nullable=False)
osm = Column(String, nullable=False)
# Operacional
modal = Column(String, index=True, nullable=False)
quant_equip = Column(Integer, default=0)
mrob = Column(String, nullable=False)
# Métricas
linhas_osm = Column(Integer, default=0)
linhas_mrob = Column(Integer, default=0)
linhas_erros = Column(Integer, default=0)
# Erros
erro_storekeeper = Column(String)
erro_operacao = Column(String)
erro_especialista = Column(String)
erro_outros = Column(String)
# Dados complementares
inclusao_exclusao = Column(String)
po = Column(String)
part_number = Column(String)
material = Column(String)
solicitante = Column(String, nullable=False)
motivo = Column(String)
requisitante = Column(String, nullable=False)
nota_fiscal = Column(String, nullable=False)
impacto = Column(String, nullable=False)
dimensao = Column(String, nullable=False)
observacoes = Column(String)
# Dia de inclusão
dia_inclusao = Column(String, nullable=True, index=True)
# Auditoria
data_hora_input = Column(DateTime, default=datetime.utcnow, index=True)
# =====================================================
# TABELA FPSOS
# =====================================================
class FPSO(Base):
__tablename__ = "fpsos"
id = Column(Integer, primary_key=True)
nome = Column(String, unique=True, nullable=False, index=True)
ativo = Column(Boolean, default=True)
data_cadastro = Column(DateTime, default=datetime.utcnow)
# =====================================================
# LOG DE AUDITORIA (OFICIAL)
# =====================================================
class LogAcesso(Base):
__tablename__ = "log_acesso"
id = Column(Integer, primary_key=True)
usuario = Column(String, nullable=False, index=True)
acao = Column(String, nullable=False)
tabela = Column(String, nullable=True)
registro_id = Column(Integer, nullable=True)
data_hora = Column(DateTime, default=datetime.utcnow, index=True)
# =====================================================
# USUÁRIOS
# =====================================================
class Usuario(Base):
__tablename__ = "usuarios"
id = Column(Integer, primary_key=True)
# login & segurança
usuario = Column(String, unique=True, nullable=False, index=True)
senha = Column(String, nullable=False) # Armazena o HASH (bcrypt) da senha
# perfil & status
perfil = Column(String, nullable=False) # admin | usuario | consulta
ativo = Column(Boolean, default=True)
# auditoria
data_criacao = Column(DateTime, default=datetime.utcnow)
# UI/contato
nome = Column(String, nullable=True, index=True)
email = Column(String, unique=True, index=True, nullable=True)
# aniversário
data_aniversario = Column(Date, nullable=True)
# ---- Compatibilidade (alias) ----
@property
def senha_hash(self) -> str | None:
"""
Alias compatível para códigos que esperam 'senha_hash'.
Retorna o valor da coluna 'senha' (que deve conter o HASH bcrypt).
Não altera o schema e não exige migration.
"""
return self.senha
def __repr__(self) -> str:
return f"<Usuario id={self.id} usuario={self.usuario!r} perfil={self.perfil!r} ativo={self.ativo}>"
# =====================================================
# QUIZ - PERGUNTAS
# =====================================================
class QuizPergunta(Base):
__tablename__ = "quiz_perguntas"
id = Column(Integer, primary_key=True)
pergunta = Column(String, nullable=False)
ativo = Column(Boolean, default=True)
data_criacao = Column(DateTime, default=datetime.utcnow)
respostas = relationship(
"QuizResposta",
back_populates="pergunta",
cascade="all, delete-orphan"
)
# =====================================================
# QUIZ - RESPOSTAS
# =====================================================
class QuizResposta(Base):
__tablename__ = "quiz_respostas"
id = Column(Integer, primary_key=True)
pergunta_id = Column(Integer, ForeignKey("quiz_perguntas.id"), nullable=False)
texto = Column(String, nullable=False)
correta = Column(Boolean, default=False)
pergunta = relationship("QuizPergunta", back_populates="respostas")
# =====================================================
# QUIZ - PONTUAÇÃO / RANKING
# =====================================================
class QuizPontuacao(Base):
__tablename__ = "quiz_pontuacoes"
id = Column(Integer, primary_key=True)
usuario = Column(String, nullable=False, index=True)
pontos = Column(Integer, nullable=False, default=0)
data = Column(DateTime, default=datetime.utcnow, index=True)
# =====================================================
# VÍDEOS - CATEGORIAS
# =====================================================
class VideoCategoria(Base):
__tablename__ = "video_categorias"
id = Column(Integer, primary_key=True)
nome = Column(String, nullable=False, unique=True)
ativo = Column(Boolean, default=True)
data_criacao = Column(DateTime, default=datetime.utcnow)
# =====================================================
# VÍDEOS
# =====================================================
class Video(Base):
__tablename__ = "videos"
id = Column(Integer, primary_key=True)
titulo = Column(String, nullable=False)
descricao = Column(String)
url = Column(String, nullable=False)
categoria_id = Column(Integer, ForeignKey("video_categorias.id"))
categoria = relationship("VideoCategoria")
ativo = Column(Boolean, default=True)
data_criacao = Column(DateTime, default=datetime.utcnow)
# =====================================================
# CALENDÁRIO - EVENTOS / LEMBRETES
# =====================================================
class EventoCalendario(Base):
__tablename__ = "eventos_calendario"
id = Column(Integer, primary_key=True)
titulo = Column(String, nullable=False)
descricao = Column(String)
data_evento = Column(Date, nullable=False)
data_lembrete = Column(Date)
ativo = Column(Boolean, default=True)
usuario_criacao = Column(String, nullable=False)
data_criacao = Column(DateTime, default=datetime.utcnow)
# =====================================================
# IOI-RUN - SUGESTÕES DO SISTEMA
# =====================================================
class IOIRunSugestao(Base):
__tablename__ = "ioirun_sugestao"
id = Column(Integer, primary_key=True, index=True)
# Identificação do autor
usuario = Column(String, nullable=False, index=True)
# Conteúdo
area = Column(String, nullable=True, index=True)
mensagem = Column(Text, nullable=False)
# Resposta do time (admin)
resposta = Column(Text, nullable=True)
status = Column(String, default="pendente", nullable=False, index=True) # pendente | respondida
responsavel = Column(String, nullable=True)
# Auditoria
data_envio = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
data_resposta = Column(DateTime, nullable=True)
# =====================================================
# RNC - REGISTRO DE NÃO CONFORMIDADES (FOR-SGQ-08)
# =====================================================
class RNC(Base):
__tablename__ = "rnc"
id = Column(Integer, primary_key=True, index=True)
# Identificação
codigo = Column(String(20), unique=True, index=True) # ex.: RNC-2026-0001
titulo = Column(String(200), nullable=False)
descricao = Column(Text, nullable=False)
# Cabeçalho do formulário
data_form = Column(Date, nullable=True, index=True) # Data do formulário
emitente = Column(String(120), nullable=True, index=True)
rnc_cliente_numero = Column(String(50), nullable=True)
cliente_emitente = Column(String(120), nullable=True)
area_solicitante = Column(String(120), nullable=True, index=True)
area_notificada = Column(String(120), nullable=True, index=True)
origem = Column(String(50), nullable=True, index=True) # Auditoria Interna/Externa/Outras
# Envolvidos
envolvido1_nome = Column(String(120), nullable=True)
envolvido1_matricula = Column(String(50), nullable=True)
envolvido1_funcao = Column(String(120), nullable=True)
envolvido2_nome = Column(String(120), nullable=True)
envolvido2_matricula = Column(String(50), nullable=True)
envolvido2_funcao = Column(String(120), nullable=True)
# Classificação
tipo = Column(String(50), nullable=True)
severidade = Column(String(20), nullable=True)
prioridade = Column(String(20), nullable=True)
# Status e prazos
status = Column(String(30), default="Aberta", nullable=False, index=True)
data_abertura = Column(DateTime, default=datetime.utcnow, index=True)
prazo = Column(DateTime, nullable=True, index=True)
encerrada_em = Column(DateTime, nullable=True, index=True)
# Responsáveis
responsavel = Column(String(120), nullable=True, index=True)
criado_por = Column(String(120), nullable=False, index=True)
# Complementares
cliente = Column(String(120), nullable=True)
local = Column(String(120), nullable=True)
# Análise das causas
metodologia = Column(String(120), nullable=True) # ex.: Ishikawa, 5 Porquês
causa_raiz = Column(Text, nullable=True) # descrição da causa raiz
ishikawa_json = Column(Text, nullable=True) # opcional: armazenar estrutura Ishikawa em JSON
# Auditoria
data_hora_input = Column(DateTime, default=datetime.utcnow, index=True)
# Relacionamentos
comentarios = relationship("RNCComentario", back_populates="rnc", cascade="all, delete-orphan")
acoes = relationship("RNCAcaoCorretiva", back_populates="rnc", cascade="all, delete-orphan")
anexos = relationship("RNCAnexo", back_populates="rnc", cascade="all, delete-orphan")
# =====================================================
# RNC - COMENTÁRIOS / TIMELINE
# =====================================================
class RNCComentario(Base):
__tablename__ = "rnc_comentario"
id = Column(Integer, primary_key=True, index=True)
rnc_id = Column(Integer, ForeignKey("rnc.id"), nullable=False, index=True)
data = Column(DateTime, default=datetime.utcnow, index=True)
autor = Column(String(120), nullable=False, index=True)
mensagem = Column(Text, nullable=False)
status_novo = Column(String(30), nullable=True, index=True)
prazo_novo = Column(DateTime, nullable=True, index=True)
responsavel_novo = Column(String(120), nullable=True, index=True)
rnc = relationship("RNC", back_populates="comentarios")
# =====================================================
# RNC - AÇÕES CORRETIVAS / PREVENTIVAS
# =====================================================
class RNCAcaoCorretiva(Base):
__tablename__ = "rnc_acao"
id = Column(Integer, primary_key=True, index=True)
rnc_id = Column(Integer, ForeignKey("rnc.id"), nullable=False, index=True)
descricao = Column(Text, nullable=False)
responsavel = Column(String(120), nullable=True, index=True)
prazo = Column(DateTime, nullable=True, index=True)
status = Column(String(30), default="Planejada", nullable=False, index=True)
eficacia = Column(String(30), nullable=True, index=True)
conclusao_em = Column(DateTime, nullable=True, index=True)
rnc = relationship("RNC", back_populates="acoes")
# =====================================================
# RNC - ANEXOS
# =====================================================
class RNCAnexo(Base):
__tablename__ = "rnc_anexo"
id = Column(Integer, primary_key=True, index=True)
rnc_id = Column(Integer, ForeignKey("rnc.id"), nullable=False, index=True)
nome_arquivo = Column(String(255), nullable=False)
caminho = Column(String(500), nullable=False)
conteudo_tipo = Column(String(120), nullable=True)
enviado_por = Column(String(120), nullable=True, index=True)
enviado_em = Column(DateTime, default=datetime.utcnow, index=True)
rnc = relationship("RNC", back_populates="anexos")
# =====================================================
# AVISO GLOBAL (banner superior)
# =====================================================
class AvisoGlobal(Base):
__tablename__ = "aviso_global"
id = Column(Integer, primary_key=True, index=True)
mensagem = Column(Text, nullable=False)
# Estilo/visual
bg_color = Column(String(32), default="#FFF3CD")
text_color = Column(String(32), default="#664D03")
largura = Column(String(16), default="100%")
efeito = Column(String(16), default="marquee")
velocidade = Column(Integer, default=20)
font_size = Column(Integer, default=14)
# Controle/estado
ativo = Column(Boolean, default=True, index=True)
# Auditoria
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
# =====================================================
# RECEBIMENTO — PLANILHA OFICIAL (UM REGISTRO POR LINHA)
# Alinhado ao layout oficial de 37 colunas
# =====================================================
class RecebimentoRegistro(Base):
__tablename__ = "recebimento_registros"
# PK interno + ID da planilha
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
id_planilha = Column(Integer, nullable=True, index=True, unique=True) # ID da planilha, se houver
# Datas
data = Column(Date, nullable=True)
data_emissao = Column(Date, nullable=True)
# Horas (texto HH:MM:SS para compatibilidade)
hora_chegada_portaria = Column(String(8), nullable=True)
hora_chegada_ifs = Column(String(8), nullable=True)
hora_saida_ifs_wms = Column(String(8), nullable=True)
hora_liberacao_operacao = Column(String(8), nullable=True)
hora_chegada_operacao = Column(String(8), nullable=True)
hora_saida_operacao = Column(String(8), nullable=True)
hora_retorno_operacao = Column(String(8), nullable=True)
hora_liberacao_motorista = Column(String(8), nullable=True)
# Dados principais
placa_veiculo = Column(String(50), nullable=True)
transportadora = Column(String(255), nullable=True)
po = Column(String(60), nullable=True)
incoterms = Column(String(30), nullable=True)
qtd_sku = Column(Integer, nullable=True)
nota_fiscal = Column(String(80), nullable=True)
fornecedor = Column(String(255), nullable=True)
# Bools (SIM/NÃO/N/A)
quimicos = Column(Boolean, nullable=True)
fds = Column(Boolean, nullable=True)
repetro = Column(Boolean, nullable=True)
aprovado = Column(Boolean, nullable=True)
# Status/Texto
natureza_operacao = Column(String(120), nullable=True)
tipo_operacao = Column(String(120), nullable=True)
barco = Column(String(80), nullable=True)
# Campo alinhado com a coluna "DIVERGENCIA" do layout oficial
divergencia = Column(String(200), nullable=True)
ifs = Column(String(120), nullable=True)
wms = Column(String(120), nullable=True)
fotografia = Column(String(255), nullable=True)
entrega = Column(String(120), nullable=True)
projeto = Column(String(120), nullable=True)
good_receipt = Column(String(120), nullable=True)
divergencia_recebimento = Column(String(255), nullable=True)
qualidade = Column(String(120), nullable=True)
divergencia_qualidade = Column(String(255), nullable=True)
observacao = Column(Text, nullable=True)
agendamento = Column(String(120), nullable=True)
responsavel = Column(String(120), nullable=True)
# Novos campos (opcionais) para colunas adicionais do layout
po_alt = Column(String(60), nullable=True) # mapeia "P.O" (alternativo)
pn = Column(String(120), nullable=True) # mapeia "PN"
lot_batch = Column(String(120), nullable=True) # mapeia "LOT BATCH"
# Auditoria mínima
created_by = Column(String(150), nullable=True)
updated_by = Column(String(150), nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)