feat(integrations): support history_days in fatigue candidates and ensure data envelope for WF2

This commit is contained in:
Marcos Fargas
2026-10-12 18:17:23 +02:00
parent a17b1eae56
commit 52cebab3e6
2 changed files with 45 additions and 9 deletions

View File

@@ -24,11 +24,20 @@ export async function GET(req: NextRequest) {
return NextResponse.json({ error: rulesErr.message }, { status: 500 }); return NextResponse.json({ error: rulesErr.message }, { status: 500 });
} }
const lookbackDays = rules?.lookback_days || 7; const lookbackDays = Number(rules?.lookback_days || 7);
const minHistory = lookbackDays * 2;
// 2. Calcular fecha de inicio según lookback_days // Query param history_days (por defecto 28, mínimo lookback_days * 2)
const url = new URL(req.url);
const rawHistory = url.searchParams.get('history_days');
let historyDays = rawHistory ? parseInt(rawHistory, 10) : 28;
if (isNaN(historyDays) || historyDays < minHistory) {
historyDays = Math.max(28, minHistory);
}
// 2. Calcular fecha de inicio según history_days
const sinceDate = new Date(); const sinceDate = new Date();
sinceDate.setDate(sinceDate.getDate() - lookbackDays); sinceDate.setDate(sinceDate.getDate() - historyDays);
const sinceStr = sinceDate.toISOString().split('T')[0]; const sinceStr = sinceDate.toISOString().split('T')[0];
// 3. Buscar creatividades actualmente publicadas para este cliente // 3. Buscar creatividades actualmente publicadas para este cliente
@@ -51,7 +60,7 @@ export async function GET(req: NextRequest) {
if (metaIds.length > 0) { if (metaIds.length > 0) {
const { data: metrics, error: metricsErr } = await adminClient const { data: metrics, error: metricsErr } = await adminClient
.from('creative_metrics_daily') .from('creative_metrics_daily')
.select('*') .select('date, impressions, reach, clicks, ctr, frequency, spend, results, cpa, meta_ad_id')
.eq('tenant_id', tenantId) .eq('tenant_id', tenantId)
.in('meta_ad_id', metaIds) .in('meta_ad_id', metaIds)
.gte('date', sinceStr) .gte('date', sinceStr)
@@ -63,22 +72,43 @@ export async function GET(req: NextRequest) {
(metrics || []).forEach((m: any) => { (metrics || []).forEach((m: any) => {
const list = metricsMap.get(m.meta_ad_id) || []; const list = metricsMap.get(m.meta_ad_id) || [];
list.push(m); list.push({
date: m.date,
impressions: Number(m.impressions || 0),
reach: Number(m.reach || 0),
clicks: Number(m.clicks || 0),
ctr: Number(m.ctr || 0),
frequency: Number(m.frequency || 0),
spend: Number(m.spend || 0),
results: Number(m.results || 0),
cpa: m.cpa !== null && m.cpa !== undefined ? Number(m.cpa) : null,
});
metricsMap.set(m.meta_ad_id, list); metricsMap.set(m.meta_ad_id, list);
}); });
} }
const candidates = (publishedCreatives || []).map((c: any) => ({ const creatives = (publishedCreatives || []).map((c: any) => ({
...c, id: c.id,
client_id: c.tenant_id, meta_ad_id: c.meta_ad_id,
hook: c.hook,
angle: c.angle,
campaign_name: c.campaign_name,
metrics: metricsMap.get(c.meta_ad_id) || [], metrics: metricsMap.get(c.meta_ad_id) || [],
})); }));
const dataPayload = {
rules: rules || null,
creatives,
};
return NextResponse.json({ return NextResponse.json({
data: dataPayload,
client_id: tenantId, client_id: tenantId,
tenant_id: tenantId, tenant_id: tenantId,
lookback_days: lookbackDays, lookback_days: lookbackDays,
history_days: historyDays,
rules: rules || null, rules: rules || null,
candidates, creatives,
candidates: creatives,
}); });
} }

View File

@@ -131,5 +131,11 @@ export async function POST(req: NextRequest) {
status: 'fatigada', status: 'fatigada',
signals, signals,
generation_request: generationRequest, generation_request: generationRequest,
data: {
creative_id: creativeId,
status: 'fatigada',
signals,
generation_request: generationRequest,
},
}); });
} }