dataLoader.js•7.24 kB
#!/usr/bin/env node
import fs from 'fs/promises';
import path from 'path';
import { validator } from '../utils/validator.js';
/**
* 🗂️ Módulo de Carga de Datos TreePod Financial MCP
* Implementa la Guía de Trabajo Fundamental: Sin datos hardcodeados, fuentes reales
*/
const basePath = '/Users/janetsepulvedacorrea/Desktop/AGENTES';
const dataPath = path.join(basePath, 'shared-data');
const webAppPath = path.join(basePath, 'TreePod-Sistema/app-treepod/data');
const competitorPath = path.join(basePath, 'treepod-competitive-agent/reports');
export class DataLoader {
constructor() {
this.cache = new Map();
this.cacheExpiry = new Map();
this.cacheDuration = 5 * 60 * 1000; // 5 minutos
}
/**
* Carga datos financieros SOLO de fuentes reales
*/
async loadFinancialData() {
const cacheKey = 'financial_data';
// Verificar cache válido
if (this.isCacheValid(cacheKey)) {
validator.log('info', 'Datos financieros obtenidos del cache');
return this.cache.get(cacheKey);
}
const filePath = path.join(webAppPath, 'sample-data.json');
// Validar que el archivo existe
const fileExists = await validator.validateFileExists(filePath, 'datos financieros');
if (!fileExists) {
validator.log('error', 'Archivo de datos financieros no encontrado');
return null; // NO devolver datos inventados
}
// Validar estructura de datos
const requiredFields = ['ingresos_total', 'gastos_total', 'ocupacion_promedio', 'reservas_totales'];
const validation = await validator.validateJsonData(filePath, requiredFields, 'datos financieros');
if (!validation.valid) {
validator.log('error', `Datos financieros inválidos: ${validation.error || 'campos faltantes'}`);
return null;
}
// Cache y retornar datos válidos
this.setCache(cacheKey, validation.data);
validator.log('info', 'Datos financieros cargados exitosamente desde fuente real');
return validation.data;
}
/**
* Carga estado del negocio desde bus inter-agentes
*/
async loadBusinessStatus() {
const cacheKey = 'business_status';
if (this.isCacheValid(cacheKey)) {
validator.log('info', 'Estado del negocio obtenido del cache');
return this.cache.get(cacheKey);
}
const filePath = path.join(dataPath, 'inter-agent-bus.json');
const fileExists = await validator.validateFileExists(filePath, 'estado del negocio');
if (!fileExists) {
validator.log('error', 'Archivo de estado del negocio no encontrado');
return null;
}
const validation = await validator.validateJsonData(filePath, [], 'estado del negocio');
if (!validation.valid) {
validator.log('error', `Estado del negocio inválido: ${validation.error}`);
return null;
}
this.setCache(cacheKey, validation.data);
validator.log('info', 'Estado del negocio cargado exitosamente');
return validation.data;
}
/**
* Carga datos de competencia desde reportes reales
*/
async loadCompetitionData() {
const cacheKey = 'competition_data';
if (this.isCacheValid(cacheKey)) {
validator.log('info', 'Datos de competencia obtenidos del cache');
return this.cache.get(cacheKey);
}
// Buscar el reporte más reciente
const reportFiles = [
'competition-analysis.json',
'enhanced-competitive-data-2025-07-24.json',
'sernatur_real_data_report_2025-07-24.json'
];
for (const fileName of reportFiles) {
const filePath = path.join(competitorPath, fileName);
const fileExists = await validator.validateFileExists(filePath, `datos de competencia (${fileName})`);
if (fileExists) {
const validation = await validator.validateJsonData(filePath, [], 'datos de competencia');
if (validation.valid) {
this.setCache(cacheKey, validation.data);
validator.log('info', `Datos de competencia cargados desde: ${fileName}`);
return validation.data;
}
}
}
validator.log('error', 'No se encontraron datos válidos de competencia');
return null;
}
/**
* Carga estado REAL de domos (elimina hardcodeo)
* Intenta múltiples fuentes antes de fallar
*/
async loadDomosStatus() {
const cacheKey = 'domos_status';
if (this.isCacheValid(cacheKey)) {
validator.log('info', 'Estado de domos obtenido del cache');
return this.cache.get(cacheKey);
}
// Intentar cargar desde múltiples fuentes
const sources = [
{ path: path.join(dataPath, 'domos-status.json'), name: 'estado de domos dedicado' },
{ path: path.join(webAppPath, 'sample-data.json'), name: 'datos principales', field: 'domos_status' },
{ path: path.join(dataPath, 'inter-agent-bus.json'), name: 'bus inter-agentes', field: 'domos' }
];
for (const source of sources) {
const fileExists = await validator.validateFileExists(source.path, source.name);
if (fileExists) {
const validation = await validator.validateJsonData(source.path, [], source.name);
if (validation.valid) {
let domosData = validation.data;
// Si necesita extraer un campo específico
if (source.field && domosData[source.field]) {
domosData = domosData[source.field];
}
// Validar que tiene estructura de domos
if (this.isValidDomosData(domosData)) {
this.setCache(cacheKey, domosData);
validator.log('info', `Estado de domos cargado desde: ${source.name}`);
return domosData;
}
}
}
}
validator.log('error', 'No se pudo cargar estado real de domos desde ninguna fuente');
return null; // NO devolver datos hardcodeados
}
/**
* Valida si los datos de domos tienen estructura correcta
*/
isValidDomosData(data) {
if (!Array.isArray(data)) return false;
return data.every(domo =>
domo.name &&
domo.status &&
['occupied', 'available', 'maintenance', 'cleaning'].includes(domo.status)
);
}
/**
* Sistema de cache con expiración
*/
isCacheValid(key) {
if (!this.cache.has(key)) return false;
const expiry = this.cacheExpiry.get(key);
if (!expiry || Date.now() > expiry) {
this.cache.delete(key);
this.cacheExpiry.delete(key);
return false;
}
return true;
}
setCache(key, data) {
this.cache.set(key, data);
this.cacheExpiry.set(key, Date.now() + this.cacheDuration);
}
/**
* Limpia cache manualmente
*/
clearCache() {
this.cache.clear();
this.cacheExpiry.clear();
validator.log('info', 'Cache limpiado manualmente');
}
/**
* Obtiene estadísticas del cache para debugging
*/
getCacheStats() {
return {
entries: this.cache.size,
keys: Array.from(this.cache.keys()),
lastAccess: Array.from(this.cacheExpiry.entries()).map(([key, expiry]) => ({
key,
expiresIn: Math.max(0, expiry - Date.now())
}))
};
}
}
// Instancia global del cargador de datos
export const dataLoader = new DataLoader();