+server.js 5.29 KB
import { API_HOST } from '$lib/api.js';
const API_BASE = API_HOST + '/api/organismo';

let cachedClasificador = null;
let cachedEstados = null;
let cachedTimestamp = 0;
const CACHE_TTL = 5 * 60 * 1000;

// Organismo hierarchy: grupo → subgrupo → organismo
// Parent is derived from clasificador fields, not from code pattern

async function loadAndCacheAll() {
  const now = Date.now();
  if (cachedEstados && (now - cachedTimestamp) < CACHE_TTL) return;

  const clasRes = await fetch(`${API_BASE}/clasificador`);
  if (!clasRes.ok) throw new Error('Failed to fetch clasificador');
  cachedClasificador = await clasRes.json();

  const estados = new Map();
  const batchSize = 80;

  for (let i = 0; i < cachedClasificador.length; i += batchSize) {
    const batch = cachedClasificador.slice(i, i + batchSize);
    const results = await Promise.all(batch.map(async (item) => {
      try {
        const code = item.organismo;
        const res = await fetch(`${API_BASE}/${code}`);
        if (!res.ok) return null;
        const data = await res.json();
        return { code: String(code), estados: data.estados || [] };
      } catch { return null; }
    }));
    results.filter(Boolean).forEach(r => estados.set(r.code, r.estados));
  }

  cachedEstados = estados;
  cachedTimestamp = now;
}

export async function GET({ url }) {
  const gestion = parseInt(url.searchParams.get('gestion') || '2025');
  const entidad = url.searchParams.get('entidad') || '0';

  try {
    if (entidad === '0') {
      await loadAndCacheAll();

      const results = [];
      const grupoSet = new Set();
      const subgrupoSet = new Set();

      for (const item of cachedClasificador) {
        const code = String(item.organismo);
        const estados = cachedEstados.get(code);
        if (!estados) continue;
        const yearData = estados.find(d => d.gestion === gestion);
        if (!yearData || !yearData.total) continue;

        const grupoKey = `g_${item.organismo_grupo}`;
        const subgrupoKey = `sg_${item.organismo_grupo}_${item.organismo_subgrupo}`;

        // Add grupo node if not yet added
        if (!grupoSet.has(grupoKey)) {
          grupoSet.add(grupoKey);
          results.push({
            gestion,
            nivel: 'grupo',
            organismo: grupoKey,
            desc_organismo: item.desc_organismo_grupo || `Grupo ${item.organismo_grupo}`,
            parent: null,
            devengado: 0
          });
        }

        // Add subgrupo node if not yet added
        if (!subgrupoSet.has(subgrupoKey)) {
          subgrupoSet.add(subgrupoKey);
          results.push({
            gestion,
            nivel: 'subgrupo',
            organismo: subgrupoKey,
            desc_organismo: item.desc_organismo_subgrupo || `Subgrupo ${item.organismo_subgrupo}`,
            parent: grupoKey,
            devengado: 0
          });
        }

        results.push({
          gestion,
          nivel: 'organismo',
          organismo: code,
          desc_organismo: item.desc_organismo || '',
          parent: subgrupoKey,
          devengado: yearData.total
        });
      }

      return new Response(JSON.stringify(results), {
        headers: { 'Content-Type': 'application/json' }
      });
    } else {
      if (!cachedClasificador) {
        const clasRes = await fetch(`${API_BASE}/clasificador`);
        cachedClasificador = await clasRes.json();
      }

      const results = [];
      const grupoSet2 = new Set();
      const subgrupoSet2 = new Set();
      const batchSize = 80;

      for (let i = 0; i < cachedClasificador.length; i += batchSize) {
        const batch = cachedClasificador.slice(i, i + batchSize);
        const batchResults = await Promise.all(batch.map(async (item) => {
          try {
            const code = String(item.organismo);
            const res = await fetch(`${API_BASE}/${code}/entidades/${entidad}`);
            if (!res.ok) return null;
            const data = await res.json();
            const yearData = data.find(d => d.gestion === gestion);
            if (!yearData || !yearData.monto) return null;
            return { item, code, monto: yearData.monto };
          } catch { return null; }
        }));

        batchResults.filter(Boolean).forEach(({ item, code, monto }) => {
          const grupoKey = `g_${item.organismo_grupo}`;
          const subgrupoKey = `sg_${item.organismo_grupo}_${item.organismo_subgrupo}`;

          if (!grupoSet2.has(grupoKey)) {
            grupoSet2.add(grupoKey);
            results.push({ gestion, nivel: 'grupo', organismo: grupoKey, desc_organismo: item.desc_organismo_grupo || '', parent: null, devengado: 0 });
          }
          if (!subgrupoSet2.has(subgrupoKey)) {
            subgrupoSet2.add(subgrupoKey);
            results.push({ gestion, nivel: 'subgrupo', organismo: subgrupoKey, desc_organismo: item.desc_organismo_subgrupo || '', parent: grupoKey, devengado: 0 });
          }
          results.push({ gestion, nivel: 'organismo', organismo: code, desc_organismo: item.desc_organismo || '', parent: subgrupoKey, devengado: monto });
        });
      }

      return new Response(JSON.stringify(results), {
        headers: { 'Content-Type': 'application/json' }
      });
    }
  } catch (err) {
    return new Response(JSON.stringify({ error: 'Failed to build treemap' }), {
      status: 500, headers: { 'Content-Type': 'application/json' }
    });
  }
}