feat: Implement multi-database architecture

This commit introduces a three-database architecture to the application,
as specified in the `db_logic.md` file.

The changes include:
- A SQL initialization script (`db/init/init.sql`) to create the
  `USERS_ALMA`, `vanity_hr`, and `vanity_attendance` databases and their
  respective tables.
- SQLAlchemy models for all tables, organized into separate files
  within the `models` directory.
- Refactoring of the database connection logic in `modules/database.py`
  to support connections to all three databases.
- Creation of a `modules/logger.py` to handle request logging to the
  `USERS_ALMA` database.
- Updates to `docker-compose.yml` to mount the initialization script and
  build the bot image locally.
- Updates to `.env.example` to include the new database environment
  variables.
- Restoration of the data dictionary to `db_tasks.md`.
This commit is contained in:
google-labs-jules[bot]
2025-12-18 19:36:40 +00:00
parent fbb8649748
commit 4e2b6335a6
12 changed files with 575 additions and 209 deletions

View File

@@ -0,0 +1,31 @@
from sqlalchemy import create_engine, Column, Integer, String, Enum, TIMESTAMP
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func
from datetime import datetime
Base = declarative_base()
class RequestLog(Base):
__tablename__ = 'request_logs'
__table_args__ = {'schema': 'USERS_ALMA'}
id = Column(Integer, primary_key=True)
telegram_id = Column(String(50))
username = Column(String(100))
command = Column(String(100))
message = Column(String(500))
created_at = Column(TIMESTAMP, server_default=func.now())
class User(Base):
__tablename__ = 'users'
__table_args__ = {'schema': 'USERS_ALMA'}
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(50), unique=True)
role = Column(Enum('admin', 'manager', 'user'))
first_name = Column(String(100))
last_name = Column(String(100))
email = Column(String(100), unique=True)
cell_phone = Column(String(20))
telegram_id = Column(String(50), unique=True)
created_at = Column(TIMESTAMP, server_default=func.now())
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())

View File

@@ -0,0 +1,32 @@
from sqlalchemy import create_engine, Column, Integer, String, Date, Time, BigInteger, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class AsistenciaRegistros(Base):
__tablename__ = 'asistencia_registros'
__table_args__ = {'schema': 'vanity_attendance'}
id_asistencia = Column(Integer, primary_key=True, autoincrement=True)
numero_empleado = Column(String(15), ForeignKey('vanity_hr.data_empleadas.numero_empleado'))
fecha = Column(Date)
hora_entrada_real = Column(Time)
hora_salida_real = Column(Time)
minutos_retraso = Column(Integer)
minutos_extra = Column(Integer)
sucursal_registro = Column(String(50))
telegram_id_usado = Column(BigInteger)
empleada = relationship("DataEmpleadas", backref="asistencia_registros")
class HorarioEmpleadas(Base):
__tablename__ = 'horario_empleadas'
__table_args__ = {'schema': 'vanity_attendance'}
id_horario = Column(Integer, primary_key=True, autoincrement=True)
numero_empleado = Column(String(15), ForeignKey('vanity_hr.data_empleadas.numero_empleado'))
telegram_id = Column(BigInteger)
dia_semana = Column(String(20))
hora_entrada_teorica = Column(Time)
hora_salida_teorica = Column(Time)
empleada = relationship("DataEmpleadas", backref="horario_empleadas")

View File

@@ -0,0 +1,92 @@
from sqlalchemy import create_engine, Column, Integer, String, Enum, TIMESTAMP, Date, Text, BigInteger, DateTime
from sqlalchemy.dialects.mysql import TINYINT
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
Base = declarative_base()
class DataEmpleadas(Base):
__tablename__ = 'data_empleadas'
__table_args__ = {'schema': 'vanity_hr'}
numero_empleado = Column(String(15), primary_key=True)
puesto = Column(String(50))
sucursal = Column(String(50))
fecha_ingreso = Column(Date)
estatus = Column(String(15))
nombre_completo = Column(String(150))
nombre = Column(String(50))
nombre_preferido = Column(String(50))
apellido_paterno = Column(String(50))
apellido_materno = Column(String(50))
fecha_nacimiento = Column(Date)
lugar_nacimiento = Column(String(50))
rfc = Column(String(13), unique=True)
curp = Column(String(18), unique=True)
email = Column(String(100))
telefono_celular = Column(String(15))
domicilio_calle = Column(String(255))
domicilio_numero_exterior = Column(String(10))
domicilio_numero_interior = Column(String(10))
domicilio_numero_texto = Column(String(50))
domicilio_colonia = Column(String(255))
domicilio_codigo_postal = Column(String(10))
domicilio_ciudad = Column(String(100))
domicilio_estado = Column(String(50))
domicilio_completo = Column(String(255))
emergencia_nombre = Column(String(100))
emergencia_telefono = Column(String(15))
emergencia_parentesco = Column(String(50))
referencia_1_nombre = Column(String(100))
referencia_1_telefono = Column(String(15))
referencia_1_tipo = Column(String(20))
referencia_2_nombre = Column(String(100))
referencia_2_telefono = Column(String(15))
referencia_2_tipo = Column(String(20))
referencia_3_nombre = Column(String(100))
referencia_3_telefono = Column(String(15))
referencia_3_tipo = Column(String(20))
origen_registro = Column(String(50))
telegram_usuario = Column(String(50))
telegram_chat_id = Column(BigInteger)
bot_version = Column(String(20))
fecha_registro = Column(DateTime)
tiempo_registro_minutos = Column(Integer)
fecha_procesamiento = Column(DateTime)
class Vacaciones(Base):
__tablename__ = 'vacaciones'
__table_args__ = {'schema': 'vanity_hr'}
vacaciones_id = Column(String(50), primary_key=True)
numero_empleado = Column(String(15), ForeignKey('vanity_hr.data_empleadas.numero_empleado'))
tipo_solicitud = Column(String(20))
estatus = Column(Enum('pendiente', 'aprobado', 'rechazado', 'cancelado'))
fecha_inicio = Column(Date)
fecha_fin = Column(Date)
dias_solicitados = Column(Integer)
dias_habiles = Column(Integer)
motivo = Column(Text)
con_goce_sueldo = Column(TINYINT)
fecha_solicitud = Column(DateTime)
fecha_procesamiento = Column(DateTime)
origen = Column(String(20))
afecta_nomina = Column(TINYINT)
empleada = relationship("DataEmpleadas")
class Permisos(Base):
__tablename__ = 'permisos'
__table_args__ = {'schema': 'vanity_hr'}
permiso_id = Column(String(50), primary_key=True)
numero_empleado = Column(String(15), ForeignKey('vanity_hr.data_empleadas.numero_empleado'))
categoria = Column(Enum('PERSONAL', 'MEDICO', 'OFICIAL', 'OTRO'))
estatus = Column(Enum('pendiente', 'aprobado', 'rechazado', 'cancelado'))
fecha_inicio = Column(Date)
horario_especifico = Column(String(50))
motivo = Column(Text)
con_goce_sueldo = Column(TINYINT)
afecta_nomina = Column(TINYINT)
empleada = relationship("DataEmpleadas")