Prestakuntzen aginte-zentroa (VIII): Automatizazioak, oroigarriak eta esperientzia integratua
Zortzigarren entrega honetan prestakuntzen planifikatzailearen funtzionalitateen integrazioa burutu dugu. Oroigarriak, elkarreraginak eta saioak automatikoki eta sinkronizatuta lan egiten dute jada. Gainera, Oroigarrien ikuspegia gehitu dugu, jakinarazpen eta abisu lokalekin.
Automatizazioak, oroigarriak eta esperientzia integratua
8. fase honek garapenaren funtzionalitatezko itxiera dakar:
datuak, negozio-logika eta interfazea (UI) konektatu ditugu, sistemak prestakuntzen aginte-zentro erabat operatibo gisa funtziona dezan.
Planifikatzaileak orain oroigarri automatikoak sortzen ditu, saioak eta elkarreraginak sinkronizatzen ditu eta guztia Oroigarrien ikuspegi berrian erakusten du, interfaze nagusian integratuta.
1. Oroigarri automatikoen zerbitzua (servicio_recordatorios.py)
Zerbitzu honek oroigarriak sortzeko, egiaztatzeko eta abisatzeko logika zentralizatzen du.
Elkarreragin komertzialetan eta programatutako saioetan oinarritzen da.
from datetime import datetime, timedelta
from planificador.data.repositories.interaccion_repo import InteraccionRepository
from planificador.data.repositories.sesion_repo import SesionRepository
from planificador.data.repositories.recordatorio_repo import RecordatorioRepository
from planificador.common.registro import get_logger
log = get_logger(__name__)
class ServicioRecordatorios:
"""
Gestiona la creación y verificación de recordatorios automáticos
a partir de interacciones y sesiones planificadas.
"""
@staticmethod
def generar_desde_interacciones():
interacciones = InteraccionRepository.listar_todas()
nuevos = []
for inter in interacciones:
if not inter.get("crear_recordatorio"):
continue
fecha_accion = inter.get("fecha_proxima_accion")
if not fecha_accion:
continue
recordatorio = {
"tipo": inter["tipo"],
"cliente": inter["id_cliente"],
"fecha": fecha_accion,
"descripcion": inter.get("proxima_accion", ""),
}
RecordatorioRepository.crear(recordatorio)
nuevos.append(recordatorio)
log.info(f"Recordatorio generado desde interacción {inter['id_interaccion']}: {recordatorio}")
log.info(f"Total recordatorios generados: {len(nuevos)}")
return nuevos
@staticmethod
def comprobar_sesiones_proximas(horas_anticipacion=24):
"""
Devuelve lista de sesiones próximas dentro del rango de anticipación (horas).
"""
sesiones = SesionRepository.listar_todas()
proximas = []
ahora = datetime.now()
for ses in sesiones:
fecha_sesion = datetime.strptime(
f"{ses['fecha']} {ses['hora_inicio']}", "%Y-%m-%d %H:%M"
)
diff = fecha_sesion - ahora
if timedelta(0) <= diff <= timedelta(hours=horas_anticipacion):
proximas.append(ses)
return proximas
@staticmethod
def avisar_proximos_eventos():
"""
Combina interacciones y sesiones próximas en una lista de avisos.
"""
avisos = []
interacciones = InteraccionRepository.listar_todas()
sesiones = ServicioRecordatorios.comprobar_sesiones_proximas()
for i in interacciones:
if i.get("fecha_proxima_accion") == datetime.now().strftime("%Y-%m-%d"):
avisos.append({
"tipo": "interacción",
"cliente": i["id_cliente"],
"descripcion": i.get("proxima_accion", ""),
"fecha": i["fecha_proxima_accion"]
})
for s in sesiones:
avisos.append({
"tipo": "sesión",
"cliente": s["id_contratacion"],
"descripcion": f"Sesión próxima ({s['fecha']} {s['hora_inicio']})",
"fecha": s["fecha"]
})
log.info(f"Generados {len(avisos)} avisos combinados.")
return avisos
2. Oroigarrien biltegia (recordatorio_repo.py)
planificador/data/repositories/ direktorioan kokatutako biltegi berria da, sortutako oroigarriak gordetzeko.
from planificador.data.db_manager import get_connection
from planificador.common.registro import get_logger
log = get_logger(__name__)
class RecordatorioRepository:
"""
Repositorio CRUD para los recordatorios automáticos.
"""
@staticmethod
def crear(recordatorio):
with get_connection() as conn:
conn.execute("""
INSERT INTO Recordatorio (tipo, cliente, fecha, descripcion)
VALUES (?, ?, ?, ?)
""", (recordatorio["tipo"], recordatorio["cliente"],
recordatorio["fecha"], recordatorio["descripcion"]))
conn.commit()
@staticmethod
def listar_todos():
with get_connection() as conn:
cur = conn.execute("""
SELECT id, tipo, cliente, fecha, descripcion
FROM Recordatorio
ORDER BY fecha ASC
""")
return [dict(row) for row in cur.fetchall()]
3. Oroigarrien ikuspegia (vista_recordatorios.py)
UI geruzan txertatutako modulu berri bat da, interfaze nagusian fitxa gehigarri gisa agertzen dena.
from PyQt6.QtWidgets import (
QWidget, QVBoxLayout, QLabel, QTableWidget, QTableWidgetItem,
QPushButton, QHBoxLayout, QMessageBox
)
from planificador.common.registro import get_logger
log = get_logger(__name__)
try:
from planificador.data.repositories.recordatorio_repo import RecordatorioRepository
from planificador.servicios.servicio_recordatorios import ServicioRecordatorios
except Exception:
RecordatorioRepository = None
ServicioRecordatorios = None
log.warning("Repositorios o servicios no disponibles en vista_recordatorios.")
class VistaRecordatorios(QWidget):
"""
Vista para consultar y generar recordatorios automáticos.
"""
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout()
self.setLayout(layout)
titulo = QLabel("Recordatorios automáticos")
titulo.setStyleSheet("font-size: 18px; font-weight: bold; margin-bottom: 8px;")
layout.addWidget(titulo)
self.tabla = QTableWidget()
self.tabla.setColumnCount(4)
self.tabla.setHorizontalHeaderLabels(["Tipo", "Cliente", "Fecha", "Descripción"])
layout.addWidget(self.tabla)
botones = QHBoxLayout()
self.btn_recargar = QPushButton("Recargar")
self.btn_generar = QPushButton("Generar desde interacciones")
botones.addWidget(self.btn_recargar)
botones.addWidget(self.btn_generar)
layout.addLayout(botones)
self.btn_recargar.clicked.connect(self.cargar_recordatorios)
self.btn_generar.clicked.connect(self.generar_desde_interacciones)
self.cargar_recordatorios()
def cargar_recordatorios(self):
self.tabla.setRowCount(0)
if not RecordatorioRepository:
QMessageBox.warning(self, "Error", "Repositorio no disponible.")
return
try:
datos = RecordatorioRepository.listar_todos()
if not datos:
log.info("No hay recordatorios disponibles.")
return
self.tabla.setRowCount(len(datos))
for i, rec in enumerate(datos):
self.tabla.setItem(i, 0, QTableWidgetItem(str(rec.get("tipo", ""))))
self.tabla.setItem(i, 1, QTableWidgetItem(str(rec.get("cliente", ""))))
self.tabla.setItem(i, 2, QTableWidgetItem(str(rec.get("fecha", ""))))
self.tabla.setItem(i, 3, QTableWidgetItem(str(rec.get("descripcion", ""))[:100]))
except Exception as e:
log.error(f"Error al cargar recordatorios: {e}")
QMessageBox.warning(self, "Error", f"No se pudieron cargar recordatorios: {e}")
def generar_desde_interacciones(self):
if not ServicioRecordatorios:
QMessageBox.warning(self, "Error", "Servicio no disponible.")
return
try:
nuevos = ServicioRecordatorios.generar_desde_interacciones()
QMessageBox.information(self, "Recordatorios", f"Generados {len(nuevos)} nuevos recordatorios.")
self.cargar_recordatorios()
except Exception as e:
log.error(f"Error generando recordatorios: {e}")
QMessageBox.warning(self, "Error", f"No se pudieron generar recordatorios: {e}")
4. Interfaz nagusian integratzea (main_window.py)
main_window.py moduluan fitxa gehigarri bat txertatu da Oroigarrientzat, funtzio guztiak ingurune bakarrean biltzeko.
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QListWidget,
QStackedWidget, QStatusBar, QMessageBox
)
from PyQt6.QtGui import QAction
import sys
from planificador.ui.vistas.vista_clientes import VistaClientes
from planificador.ui.vistas.vista_calendario import VistaCalendario
from planificador.ui.vistas.vista_formaciones import VistaFormaciones
from planificador.ui.vistas.vista_interacciones import VistaInteracciones
from planificador.ui.vistas.vista_configuracion import VistaConfiguracion
from planificador.ui.vistas.vista_recordatorios import VistaRecordatorios
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Planificador de Formaciones")
self.resize(1200, 800)
contenedor = QWidget()
layout = QHBoxLayout(contenedor)
self.menu_lateral = QListWidget()
self.menu_lateral.addItems([
"Clientes", "Calendario", "Formaciones", "Interacciones", "Recordatorios", "Configuración"
])
self.menu_lateral.setMaximumWidth(200)
self.menu_lateral.currentRowChanged.connect(self._cambiar_vista)
self.vistas = QStackedWidget()
self.vistas.addWidget(VistaClientes())
self.vistas.addWidget(VistaCalendario())
self.vistas.addWidget(VistaFormaciones())
self.vistas.addWidget(VistaInteracciones())
self.vistas.addWidget(VistaRecordatorios())
self.vistas.addWidget(VistaConfiguracion())
layout.addWidget(self.menu_lateral)
layout.addWidget(self.vistas, 1)
self.setCentralWidget(contenedor)
self.setStatusBar(QStatusBar())
def _cambiar_vista(self, indice):
self.vistas.setCurrentIndex(indice)
def main():
app = QApplication(sys.argv)
ventana = MainWindow()
ventana.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
Azken emaitza
Integrazio honekin, prestakuntzen planifikatzaileak dagoeneko:
- ✅ Oroigarri automatikoak sortzen ditu elkarreraginen eta saioen arabera.
- ✅ Oroigarri ikuspegi berezian bistaratzen ditu jakinarazpenak.
- ✅ Hurrengo ekintzak eta abisuak toki bakar batetik kudea daitezke.
- ✅ Elkarreragin, saio eta kontratazioen arteko koherentzia osoa bermatzen du.
- ✅ Erabiltzailearen esperientzia hobetzen du, fluxu naturalago eta bisualago batekin.
Hurrengo urratsak
Hurrengo entregan exekutagarria argitaratuko dugu eta proiektuaren dokumentazio tekniko osoa osatuko dugu.
Planifikatzailea ez da jada agenda soil bat, baizik eta prestakuntza-prozesu eta bezero-harremanak modu adimentsuan lotzen dituen aginte-zentro integratua.