mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 11:05:23 +02:00
fix: logic to show BSR improvements (rank drops) in green
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import XLSX from 'xlsx';
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
import Papa from 'papaparse';
|
||||
|
||||
const ADS_FILE = 'Ads Weekly.xlsx';
|
||||
const DATA_FILE = 'dist/Amazon-Sell-Out-2023-2025.csv'; // Need to check if this exists locally or if I need to fetch it.
|
||||
|
||||
async function main() {
|
||||
console.log('--- AD SPEND BY PRODUCT LINE (UK, 2026) ---');
|
||||
|
||||
// 1. Build Metadata Map (ASIN -> Line) from Sales Records
|
||||
const metaMap = new Map<string, string>();
|
||||
|
||||
// Check local CSV if it exists, otherwise we might need to fetch it
|
||||
const dropboxUrl = "https://www.dropbox.com/scl/fi/b9zxn4z5i7sxwfakk5g5y/Amazon-Sell-Out-2023-2025.csv?rlkey=uoto6v0mm99py8nszy8ldtez8&st=pzn1zkrg&dl=1";
|
||||
console.log('Fetching Sales data to build ASIN-Line mapping...');
|
||||
const csvResponse = await fetch(dropboxUrl);
|
||||
const csvText = await csvResponse.text();
|
||||
|
||||
console.log('Parsing CSV...');
|
||||
const results = Papa.parse(csvText, { header: true, skipEmptyLines: true });
|
||||
const rows = results.data as any[];
|
||||
|
||||
if (rows.length > 0) {
|
||||
console.log('CSV Columns:', Object.keys(rows[0]));
|
||||
}
|
||||
|
||||
rows.forEach(r => {
|
||||
const asin = String(rowValue(r, ['CUSTOMER REFERENCE', 'ASIN'])).trim().toUpperCase();
|
||||
const line = rowValue(r, ['PRODUCT Line', 'Line']) || 'Unassigned';
|
||||
if (asin && !metaMap.has(asin)) {
|
||||
metaMap.set(asin, line);
|
||||
}
|
||||
});
|
||||
console.log(`Built mapping for ${metaMap.size} unique ASINs.`);
|
||||
|
||||
// Helper to get case-insensitive row value
|
||||
function rowValue(row: any, aliases: string[]): string {
|
||||
for (const a of aliases) {
|
||||
if (row[a]) return row[a];
|
||||
const lowerA = a.toLowerCase();
|
||||
const found = Object.keys(row).find(k => k.toLowerCase() === lowerA);
|
||||
if (found) return row[found];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// 2. Parse Ads Data
|
||||
if (!existsSync(ADS_FILE)) {
|
||||
console.error(`Error: ${ADS_FILE} not found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const buf = readFileSync(ADS_FILE);
|
||||
const wb = XLSX.read(buf, { type: 'buffer' });
|
||||
|
||||
const spendByLine = new Map<string, number>();
|
||||
let totalUkSpend = 0;
|
||||
let foundMatches: any[] = [];
|
||||
|
||||
for (const sheetName of wb.SheetNames) {
|
||||
const ws = wb.Sheets[sheetName];
|
||||
const rows: any[] = XLSX.utils.sheet_to_json(ws, { defval: '' });
|
||||
|
||||
rows.forEach((r: any) => {
|
||||
const countryRaw = String(r.Country || '').toUpperCase();
|
||||
const asin = String(r.ASIN || '').trim().toUpperCase();
|
||||
const cost = parseFloat(r.Cost) || 0;
|
||||
const line = metaMap.get(asin) || 'Unassigned';
|
||||
|
||||
// Still track official UK 2026 total for the summary
|
||||
if (sheetName === '2026' && (countryRaw === 'UK' || countryRaw === 'GB' || countryRaw === 'AMAZON UK' || countryRaw === 'AMAZON GB')) {
|
||||
spendByLine.set(line, (spendByLine.get(line) || 0) + cost);
|
||||
totalUkSpend += cost;
|
||||
}
|
||||
|
||||
// DEBUG: Find ALL "INKEE" records anywhere that might explain the 126
|
||||
if (line.toUpperCase() === 'INKEE' && cost > 0) {
|
||||
foundMatches.push({
|
||||
Sheet: sheetName,
|
||||
Country: countryRaw,
|
||||
ASIN: asin,
|
||||
Cost: cost,
|
||||
Week: r.Week
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('\n--- ALL INKEE AD RECORDS FOUND (ANY COUNTRY/YEAR) ---');
|
||||
console.table(foundMatches.slice(0, 50)); // Showing first 50
|
||||
if (foundMatches.length > 50) console.log(`... and ${foundMatches.length - 50} more records.`);
|
||||
|
||||
const totalsByGroup = foundMatches.reduce((acc, curr) => {
|
||||
const key = `${curr.Sheet} | ${curr.Country}`;
|
||||
acc[key] = (acc[key] || 0) + curr.Cost;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
console.log('\nSummary of INKEE Spend by Sheet and Country:');
|
||||
console.table(totalsByGroup);
|
||||
|
||||
// 3. Output results
|
||||
console.log('\nOfficial Results for Amazon UK (2026 YTD) based on sheet "2026":');
|
||||
const sortedLines = Array.from(spendByLine.entries())
|
||||
.sort((a, b) => b[1] - a[1]);
|
||||
|
||||
console.log('--------------------------------------------------');
|
||||
console.log(`${'Product Line'.padEnd(30)} | ${'Ad Spend (£)'.padStart(15)}`);
|
||||
console.log('--------------------------------------------------');
|
||||
sortedLines.forEach(([line, spend]) => {
|
||||
console.log(`${line.padEnd(30)} | ${spend.toFixed(2).padStart(15)}`);
|
||||
});
|
||||
console.log('--------------------------------------------------');
|
||||
console.log(`${'TOTAL'.padEnd(30)} | ${totalUkSpend.toFixed(2).padStart(15)}`);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -600,7 +600,7 @@ const ExperimentDetailView: React.FC<{
|
||||
<td className={`py-4 text-sm font-bold flex items-center gap-1 ${isGood ? 'text-emerald-400' : 'text-[#f43f5e]'}`}>
|
||||
<div className="flex flex-col">
|
||||
<span className="flex items-center gap-1">
|
||||
<svg className={`w-3.5 h-3.5 ${isGood ? (res.lift_percent >= 0 ? '' : 'rotate-180') : 'rotate-180'}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}><path strokeLinecap="round" strokeLinejoin="round" d="M5 15l7-7 7 7" /></svg>
|
||||
<svg className={`w-3.5 h-3.5 ${isPositiveGood ? (res.lift_percent >= 0 ? '' : 'rotate-180') : (res.lift_percent <= 0 ? '' : 'rotate-180')}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}><path strokeLinecap="round" strokeLinejoin="round" d="M5 15l7-7 7 7" /></svg>
|
||||
{res.lift_percent >= 0 ? '+' : ''}{res.lift_percent.toFixed(1)}%
|
||||
</span>
|
||||
<span className="text-[10px] text-[#64748b] font-medium mt-0.5 ml-1">
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import { readFileSync } from 'fs';
|
||||
import { processAdsExcel, mergeSalesAndAdsData } from './services/dataProcessor';
|
||||
import { computeDiD } from './services/experimentAnalysis';
|
||||
import { CombinedKPIs } from './types';
|
||||
|
||||
// Load .env.local if it exists
|
||||
import { config } from 'dotenv';
|
||||
config({ path: '.env.local' });
|
||||
|
||||
async function run() {
|
||||
const supabase = createClient(
|
||||
process.env.SUPABASE_URL || '',
|
||||
process.env.SUPABASE_SERVICE_KEY || ''
|
||||
);
|
||||
|
||||
// Get the INKEE DE experiment
|
||||
const { data: experiments, error } = await supabase
|
||||
.from('experiments')
|
||||
.select('*')
|
||||
.ilike('name', '%INKEE%')
|
||||
.eq('marketplace', 'DE');
|
||||
|
||||
if (error) {
|
||||
console.error('Supabase error:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!experiments || experiments.length === 0) {
|
||||
console.log('No INKEE DE experiments found. Listing all experiments...');
|
||||
const { data: all } = await supabase.from('experiments').select('name, marketplace, start_date, end_date, asins').limit(20);
|
||||
console.log(JSON.stringify(all, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
for (const exp of experiments) {
|
||||
console.log(`\n=== Experiment: "${exp.name}" ===`);
|
||||
console.log(` Marketplace: ${exp.marketplace}`);
|
||||
console.log(` Start: ${exp.start_date} → End: ${exp.end_date}`);
|
||||
console.log(` Baseline: ${exp.baseline_start_date} → ${exp.baseline_end_date}`);
|
||||
console.log(` ASINs (${exp.asins?.length || 0}):`, exp.asins);
|
||||
console.log(` Control ASINs (${exp.control_asins?.length || 0}):`, exp.control_asins);
|
||||
}
|
||||
|
||||
// Now load the Ads data and check all ASINs
|
||||
console.log('\n=== Loading Ads data ===');
|
||||
const buf = readFileSync('/tmp/Ads-Weekly.xlsx').buffer;
|
||||
const adsData = await processAdsExcel(buf);
|
||||
const merged = mergeSalesAndAdsData([], adsData) as CombinedKPIs[];
|
||||
|
||||
const inkeeExp = experiments[0];
|
||||
const allAsins = inkeeExp.asins?.map((a: string) => a.trim().toUpperCase()) || [];
|
||||
|
||||
console.log(`\n=== Checking ${allAsins.length} ASINs in Ads data (weeks 7-8 of 2026) ===`);
|
||||
let totalCostW7W8 = 0;
|
||||
let totalAdRevW7W8 = 0;
|
||||
let missingFromAds: string[] = [];
|
||||
|
||||
for (const asin of allAsins) {
|
||||
const adRecords = merged.filter(r =>
|
||||
r.asin === asin &&
|
||||
r.year === 2026 &&
|
||||
[7, 8].includes(r.week) &&
|
||||
(r.marketplace || r.customer || '').toLowerCase().includes('de')
|
||||
);
|
||||
|
||||
if (adRecords.length === 0) {
|
||||
missingFromAds.push(asin);
|
||||
} else {
|
||||
const cost = adRecords.reduce((s, r) => s + (r.cost || 0), 0);
|
||||
const adRev = adRecords.reduce((s, r) => s + (r.salesAds || 0), 0);
|
||||
const clicks = adRecords.reduce((s, r) => s + (r.clicks || 0), 0);
|
||||
if (cost > 0 || adRev > 0 || clicks > 0) {
|
||||
console.log(` ${asin}: W7+W8 cost=${cost.toFixed(2)} adRev=${adRev.toFixed(2)} clicks=${clicks}`);
|
||||
totalCostW7W8 += cost;
|
||||
totalAdRevW7W8 += adRev;
|
||||
} else {
|
||||
console.log(` ${asin}: W7+W8 cost=0 adRev=0 clicks=0 (zero activity)`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nMissing from Ads file entirely (${missingFromAds.length}):`, missingFromAds);
|
||||
console.log(`\nTotal W7+W8 2026 cost: €${totalCostW7W8.toFixed(2)}`);
|
||||
console.log(`Total W7+W8 2026 adRev: €${totalAdRevW7W8.toFixed(2)}`);
|
||||
|
||||
if (totalCostW7W8 > 0) {
|
||||
console.log(`Implied ACOS: ${(totalCostW7W8 / (totalAdRevW7W8 || 1) * 100).toFixed(1)}%`);
|
||||
}
|
||||
|
||||
// Run actual DiD
|
||||
const did = computeDiD(inkeeExp, merged);
|
||||
console.log('\n=== DiD Result ===');
|
||||
console.log('ACOS:', did.metrics['acos']);
|
||||
console.log('ROAS:', did.metrics['roas']);
|
||||
console.log('Revenue:', did.metrics['revenue']);
|
||||
console.log('Units:', did.metrics['units']);
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
@@ -0,0 +1,243 @@
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
interface Experiment {
|
||||
id: string;
|
||||
name: string;
|
||||
start_date: string;
|
||||
end_date?: string;
|
||||
marketplace: string;
|
||||
asins: string[];
|
||||
baseline_start_date?: string;
|
||||
baseline_end_date?: string;
|
||||
}
|
||||
|
||||
// Configura Supabase con credenciales hardcodeadas para debug
|
||||
const supabase = createClient(
|
||||
'https://qjioywarwdbxmdihyrti.supabase.co',
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFqaW95d2Fyd2RieG1kaWh5cnRpIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc3MTUxMDU0NCwiZXhwIjoyMDg3MDg2NTQ0fQ.oXxdNBDvMSyfMRaf57oCb8cUS2Bqm73CeyM19v3GJPI'
|
||||
);
|
||||
|
||||
async function debugBodynessExperiment() {
|
||||
console.log('=== DEBUG BODYNESS ES EXPERIMENT ===\n');
|
||||
|
||||
// 1. Obtener el experimento
|
||||
const { data: expData, error: expError } = await supabase
|
||||
.from('experiments')
|
||||
.select('*')
|
||||
.ilike('name', '%bodyness%')
|
||||
.single();
|
||||
|
||||
if (expError || !expData) {
|
||||
console.log('❌ No se encontró el experimento');
|
||||
console.log('Error:', expError);
|
||||
return;
|
||||
}
|
||||
|
||||
const experiment: Experiment = expData;
|
||||
console.log('📋 Experimento:', experiment.name);
|
||||
console.log('📅 Start:', experiment.start_date);
|
||||
console.log('📅 End:', experiment.end_date);
|
||||
console.log('🌍 Marketplace:', experiment.marketplace);
|
||||
console.log('📦 ASINs:', experiment.asins);
|
||||
console.log('');
|
||||
|
||||
// 2. Obtener datos de ventas (sin filtro de año primero) - verificar total
|
||||
const { count, error: countError } = await supabase
|
||||
.from('vendor_daily_data')
|
||||
.select('*', { count: 'exact', head: true });
|
||||
|
||||
console.log(`📊 Total registros en vendor_daily_data: ${count || 'unknown'}`);
|
||||
console.log('');
|
||||
|
||||
// Buscar específicamente los ASINs del experimento
|
||||
console.log('🔍 Buscando ASINs del experimento en la DB...');
|
||||
const { data: asinData, error: asinError } = await supabase
|
||||
.from('vendor_daily_data')
|
||||
.select('*')
|
||||
.in('asin', experiment.asins);
|
||||
|
||||
if (asinError) {
|
||||
console.log('Error buscando ASINs:', asinError.message);
|
||||
} else {
|
||||
console.log(`✅ Registros encontrados para los ASINs del experimento: ${asinData?.length || 0}`);
|
||||
if (asinData && asinData.length > 0) {
|
||||
const asinsEncontrados = [...new Set(asinData.map(r => r.asin))];
|
||||
console.log('ASINs encontrados:', asinsEncontrados);
|
||||
const marketsEncontrados = [...new Set(asinData.map(r => r.market))];
|
||||
console.log('Markets encontrados:', marketsEncontrados);
|
||||
}
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// Obtener datos filtrados por los ASINs del experimento (usamos asinData que ya tiene los datos)
|
||||
const salesData = asinData || [];
|
||||
console.log(`📊 Total registros para ASINs del experimento: ${salesData.length}`);
|
||||
console.log('');
|
||||
|
||||
// Ver muestra de datos
|
||||
console.log('📋 Muestra de datos (primeros 3 registros - todos los campos):');
|
||||
salesData.slice(0, 3).forEach((r: any, i) => {
|
||||
console.log(` ${i+1}.`, JSON.stringify(r, null, 2));
|
||||
});
|
||||
console.log('');
|
||||
|
||||
// Ver rango de fechas
|
||||
const dates = salesData.map(r => r.date).filter(Boolean).sort();
|
||||
if (dates.length > 0) {
|
||||
console.log(`📅 Rango de fechas: ${dates[0]} → ${dates[dates.length - 1]}`);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// 4. Ver marketplace en los datos
|
||||
const marketplaces = [...new Set(salesData.map(r => r.market || 'N/A'))];
|
||||
console.log('🌍 Marketplaces en los datos:', marketplaces);
|
||||
console.log('');
|
||||
|
||||
// 5. Filtrar por marketplace del experimento (ES)
|
||||
const mktFilter = (experiment.marketplace || '').toLowerCase();
|
||||
console.log(`Filtrando por marketplace: "${experiment.marketplace}"`);
|
||||
|
||||
const filteredByMkt = salesData.filter(r => {
|
||||
const mkt = (r.market || '').toLowerCase();
|
||||
return !mktFilter || mktFilter === 'all' || mkt.includes(mktFilter) || mktFilter.includes(mkt);
|
||||
});
|
||||
|
||||
console.log(`✅ Registros después de filtrar por marketplace: ${filteredByMkt.length}`);
|
||||
console.log('');
|
||||
|
||||
// 6. Agrupar por semana ISO (usando el campo date)
|
||||
const weeksMap = new Map<string, { units: number; records: number; dates: string[] }>();
|
||||
filteredByMkt.forEach((r: any) => {
|
||||
const dateStr = r.date;
|
||||
if (!dateStr) return;
|
||||
const date = new Date(dateStr);
|
||||
const iso = getISOWeek(date);
|
||||
const key = `${iso.year}-W${String(iso.week).padStart(2, '0')}`;
|
||||
const existing = weeksMap.get(key) || { units: 0, records: 0, dates: [] as string[] };
|
||||
existing.units += (r.units || r.unitsTotal || 0);
|
||||
existing.records++;
|
||||
if (!existing.dates.includes(dateStr)) existing.dates.push(dateStr);
|
||||
weeksMap.set(key, existing);
|
||||
});
|
||||
|
||||
console.log('📅 Unidades por semana (TODOS los datos):');
|
||||
const sortedWeeks = Array.from(weeksMap.entries()).sort();
|
||||
sortedWeeks.forEach(([week, data]) => {
|
||||
console.log(` ${week}: ${data.units} unidades (${data.records} registros) - fechas: ${data.dates.sort().join(', ')}`);
|
||||
});
|
||||
console.log('');
|
||||
|
||||
// 7. Ver semanas dentro del período del experimento (Jan 22 - Feb 4, 2026)
|
||||
const startDate = new Date(experiment.start_date!);
|
||||
const endDate = new Date(experiment.end_date!);
|
||||
|
||||
console.log('📅 Período del experimento:', startDate.toISOString().split('T')[0], '→', endDate.toISOString().split('T')[0]);
|
||||
|
||||
// Calcular semanas ISO que toca el experimento
|
||||
const startISO = getISOWeek(startDate);
|
||||
const endISO = getISOWeek(endDate);
|
||||
console.log(`📅 Semanas ISO: ${startISO.year}-W${startISO.week} → ${endISO.year}-W${endISO.week}`);
|
||||
|
||||
const afterWeeks: string[] = [];
|
||||
let currentYear = startISO.year;
|
||||
let currentWeek = startISO.week;
|
||||
|
||||
while (true) {
|
||||
const weekKey = `${currentYear}-W${String(currentWeek).padStart(2, '0')}`;
|
||||
afterWeeks.push(weekKey);
|
||||
|
||||
if (currentYear === endISO.year && currentWeek === endISO.week) {
|
||||
break;
|
||||
}
|
||||
|
||||
currentWeek++;
|
||||
const weeksInYear = getWeeksInYear(currentYear);
|
||||
if (currentWeek > weeksInYear) {
|
||||
currentWeek = 1;
|
||||
currentYear++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`📅 Semanas incluidas en AFTER: ${afterWeeks.join(', ')}`);
|
||||
console.log('');
|
||||
|
||||
// 8. Calcular unidades en esas semanas
|
||||
let afterUnits = 0;
|
||||
let afterWeekCount = 0;
|
||||
|
||||
console.log('📊 Unidades en semanas AFTER:');
|
||||
sortedWeeks.forEach(([week, data]) => {
|
||||
if (afterWeeks.includes(week)) {
|
||||
afterUnits += data.units;
|
||||
afterWeekCount++;
|
||||
console.log(` ✅ ${week}: ${data.units} unidades`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('');
|
||||
console.log(`📊 Total unidades en AFTER: ${afterUnits}`);
|
||||
console.log(`📊 Número de semanas con datos: ${afterWeekCount}`);
|
||||
console.log(`📊 Media calculada: ${afterUnits} / ${afterWeekCount} = ${(afterUnits / afterWeekCount).toFixed(2)}`);
|
||||
console.log('');
|
||||
|
||||
// 9. Ver baseline (3 semanas antes)
|
||||
const baselineEndTs = getISOWeekMonday(startISO.year, startISO.week);
|
||||
const baselineStartTs = baselineEndTs - (afterWeeks.length * 7 * 86400000);
|
||||
|
||||
console.log('📅 Baseline período:', new Date(baselineStartTs).toISOString().split('T')[0], '→', new Date(baselineEndTs).toISOString().split('T')[0]);
|
||||
|
||||
// Calcular semanas del baseline
|
||||
const baselineWeeks: string[] = [];
|
||||
let baselineDate = new Date(baselineStartTs);
|
||||
while (baselineDate.getTime() < baselineEndTs) {
|
||||
const iso = getISOWeek(baselineDate);
|
||||
const weekKey = `${iso.year}-W${String(iso.week).padStart(2, '0')}`;
|
||||
if (!baselineWeeks.includes(weekKey)) {
|
||||
baselineWeeks.push(weekKey);
|
||||
}
|
||||
baselineDate.setDate(baselineDate.getDate() + 7);
|
||||
}
|
||||
|
||||
console.log(`📅 Semanas del BASELINE: ${baselineWeeks.join(', ')}`);
|
||||
|
||||
let beforeUnits = 0;
|
||||
let beforeWeekCount = 0;
|
||||
console.log('📊 Unidades en semanas BASELINE:');
|
||||
sortedWeeks.forEach(([week, data]) => {
|
||||
if (baselineWeeks.includes(week)) {
|
||||
beforeUnits += data.units;
|
||||
beforeWeekCount++;
|
||||
console.log(` ✅ ${week}: ${data.units} unidades`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('');
|
||||
console.log(`📊 Total unidades en BASELINE: ${beforeUnits}`);
|
||||
console.log(`📊 Número de semanas con datos: ${beforeWeekCount}`);
|
||||
console.log(`📊 Media calculada: ${beforeUnits} / ${beforeWeekCount} = ${(beforeUnits / beforeWeekCount).toFixed(2)}`);
|
||||
}
|
||||
|
||||
// Funciones helper ISO week
|
||||
function getISOWeek(date: Date): { year: number; week: number } {
|
||||
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
||||
const dayNum = d.getUTCDay() || 7;
|
||||
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
||||
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
||||
const weekNum = Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
|
||||
return { year: d.getUTCFullYear(), week: weekNum };
|
||||
}
|
||||
|
||||
function getWeeksInYear(year: number): number {
|
||||
const dec28 = new Date(Date.UTC(year, 11, 28));
|
||||
const iso = getISOWeek(dec28);
|
||||
return iso.week;
|
||||
}
|
||||
|
||||
function getISOWeekMonday(year: number, week: number): number {
|
||||
const jan4 = new Date(Date.UTC(year, 0, 4));
|
||||
const dayOfWeek = jan4.getUTCDay() || 7;
|
||||
const week1Monday = new Date(Date.UTC(year, 0, 4 - (dayOfWeek - 1)));
|
||||
return week1Monday.getTime() + (week - 1) * 7 * 86400000;
|
||||
}
|
||||
|
||||
debugBodynessExperiment().catch(console.error);
|
||||
@@ -0,0 +1,50 @@
|
||||
import XLSX from 'xlsx';
|
||||
import { readFileSync } from 'fs';
|
||||
import { processAdsExcel } from './services/dataProcessor';
|
||||
|
||||
async function run() {
|
||||
const buf = readFileSync('/tmp/Ads-Weekly.xlsx').buffer;
|
||||
const adsData = await processAdsExcel(buf);
|
||||
|
||||
// Show ALL INKEE DE ads with their cost/salesAds
|
||||
const inkeeDE = adsData.filter(a =>
|
||||
a.asin.toUpperCase().startsWith('B0CQ') &&
|
||||
a.country === 'Amazon DE'
|
||||
);
|
||||
|
||||
console.log('=== ALL INKEE DE ADS RECORDS ===');
|
||||
console.log('Count:', inkeeDE.length);
|
||||
|
||||
// Group by ASIN
|
||||
const byAsin = new Map<string, any[]>();
|
||||
for (const a of inkeeDE) {
|
||||
const list = byAsin.get(a.asin) || [];
|
||||
list.push(a);
|
||||
byAsin.set(a.asin, list);
|
||||
}
|
||||
|
||||
for (const [asin, records] of byAsin.entries()) {
|
||||
const totalCost = records.reduce((s, r) => s + r.cost, 0);
|
||||
const totalSales = records.reduce((s, r) => s + r.attributedSales30d, 0);
|
||||
const totalClicks = records.reduce((s, r) => s + r.clicks, 0);
|
||||
|
||||
console.log(`\nASIN: ${asin}`);
|
||||
console.log(` Records: ${records.length} | TotalCost: ${totalCost.toFixed(2)} | TotalSales: ${totalSales.toFixed(2)} | TotalClicks: ${totalClicks}`);
|
||||
|
||||
for (const r of records) {
|
||||
console.log(` Year:${r.year} Week:${r.week} cost:${r.cost} salesAds:${r.attributedSales30d} clicks:${r.clicks}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Show acos calculation possibility
|
||||
const withCost = inkeeDE.filter(a => a.cost > 0);
|
||||
console.log('\nINKEE DE records with cost > 0:', withCost.length);
|
||||
|
||||
// What week numbers are we talking about?
|
||||
// INKEE DE experiment likely runs in Jan-Feb 2026 or similar
|
||||
// Week 1 of 2026 = Jan 5-11, Week 6 = Feb 9-15, Week 8 = Feb 23 - Mar 1
|
||||
console.log('\nWeek 1 2026 start (Sun):', new Date(2026, 0, 4).toISOString().split('T')[0]);
|
||||
console.log('Week 8 2026 start (Sun):', new Date(2026, 0, 4 + 7 * 7).toISOString().split('T')[0]);
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
@@ -0,0 +1,42 @@
|
||||
import XLSX from 'xlsx';
|
||||
import { readFileSync } from 'fs';
|
||||
import { processAdsExcel, mergeSalesAndAdsData } from './services/dataProcessor';
|
||||
|
||||
async function run() {
|
||||
const buf = readFileSync('/tmp/Ads-Weekly.xlsx').buffer;
|
||||
const adsData = await processAdsExcel(buf);
|
||||
|
||||
// Simulate with empty sales - ads-only records
|
||||
const merged = mergeSalesAndAdsData([], adsData);
|
||||
|
||||
console.log('Total merged records (ads-only):', merged.length);
|
||||
|
||||
// Find INKEE DE ads-only records
|
||||
const inkeeDE = merged.filter(m =>
|
||||
m.asin.startsWith('B0CQ') &&
|
||||
(m.marketplace || m.customer || '').includes('DE')
|
||||
);
|
||||
|
||||
console.log('\nINKEE DE in merged:', inkeeDE.length);
|
||||
for (const r of inkeeDE) {
|
||||
console.log(` ASIN:${r.asin} year:${r.year} week:${r.week} marketplace:"${r.marketplace}" customer:"${r.customer}" cost:${r.cost} salesAds:${r.salesAds} clicks:${r.clicks}`);
|
||||
}
|
||||
|
||||
// Now test the aggregation (just for "DE" marketplace filter)
|
||||
const asinSet = new Set(['B0CQ247T2V', 'B0CQTLZRCV']);
|
||||
|
||||
for (const marketplace of ['DE', 'Amazon DE', 'de', 'amazon de', '']) {
|
||||
const matching = inkeeDE.filter(r => {
|
||||
const mkt = (r.marketplace || r.customer || '').toLowerCase();
|
||||
if (!marketplace || marketplace === 'All') return true;
|
||||
return mkt.includes(marketplace.toLowerCase());
|
||||
});
|
||||
console.log(`\nFilter mkt="${marketplace}": ${matching.length} matching INKEE DE records`);
|
||||
if (matching.length > 0) {
|
||||
const totalCost = matching.reduce((s, r) => s + (r.cost || 0), 0);
|
||||
console.log(` Total cost: ${totalCost.toFixed(2)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
@@ -0,0 +1,42 @@
|
||||
import XLSX from 'xlsx';
|
||||
import { readFileSync } from 'fs';
|
||||
import { processAdsExcel, mergeSalesAndAdsData } from './services/dataProcessor';
|
||||
|
||||
async function run() {
|
||||
// Load ads directly
|
||||
const buf = readFileSync('/tmp/Ads-Weekly.xlsx').buffer;
|
||||
const adsData = await processAdsExcel(buf);
|
||||
|
||||
console.log('Total ads records:', adsData.length);
|
||||
|
||||
// Find INKEE ads
|
||||
const inkeeAds = adsData.filter(a => a.asin.includes('B0CQ'));
|
||||
console.log('INKEE ads rows:', inkeeAds.length);
|
||||
|
||||
if (inkeeAds.length > 0) {
|
||||
console.log('INKEE ad years:', [...new Set(inkeeAds.map(a => a.year))]);
|
||||
console.log('INKEE ad weeks:', [...new Set(inkeeAds.map(a => a.week))].sort((a, b) => a - b));
|
||||
console.log('INKEE cost sample:', inkeeAds.slice(0, 3).map(a => ({ year: a.year, week: a.week, asin: a.asin, cost: a.cost, country: a.country })));
|
||||
|
||||
// What does "DE" country map to?
|
||||
const deAds = inkeeAds.filter(a => a.country.includes('DE'));
|
||||
console.log('\nINKEE DE ads count:', deAds.length);
|
||||
if (deAds.length > 0) {
|
||||
console.log('First DE ad:', deAds[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Now simulate merge with a mock sales record
|
||||
const mockSales = [
|
||||
{
|
||||
id: 'test', year: 2025, week: 1, asin: 'B0CQTLZRCV', customer: 'Amazon DE',
|
||||
sellOut: 500, units: 5, month: 'Jan', sku: 'TEST', title: 'INKEE', line: 'Inkee', articleName: 'INKEE'
|
||||
}
|
||||
] as any[];
|
||||
|
||||
const merged = mergeSalesAndAdsData(mockSales, adsData);
|
||||
const inkeeRow = merged.find(m => m.asin === 'B0CQTLZRCV' && m.year === 2025);
|
||||
console.log('\nMerged INKEE row:', inkeeRow ? { cost: inkeeRow.cost, salesAds: inkeeRow.salesAds, clicks: inkeeRow.clicks } : 'NOT FOUND');
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
@@ -0,0 +1,70 @@
|
||||
import XLSX from 'xlsx';
|
||||
import { readFileSync } from 'fs';
|
||||
import { processAdsExcel, mergeSalesAndAdsData } from './services/dataProcessor';
|
||||
import { computeDiD } from './services/experimentAnalysis';
|
||||
import { getExperimentAsins } from './services/experiments';
|
||||
import { Experiment, CombinedKPIs } from './types';
|
||||
|
||||
// Simulate the getWeekStartSunday function from experimentAnalysis.ts
|
||||
function getWeekStartSunday(year: number, week: number): number {
|
||||
const jan1 = new Date(year, 0, 1);
|
||||
const day = jan1.getDay();
|
||||
const startYear = new Date(year, 0, 1 - day);
|
||||
return startYear.getTime() + (week - 1) * 7 * 86400000;
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const buf = readFileSync('/tmp/Ads-Weekly.xlsx').buffer;
|
||||
const adsData = await processAdsExcel(buf);
|
||||
|
||||
const merged = mergeSalesAndAdsData([], adsData);
|
||||
|
||||
// Show the timestamps for INKEE DE weeks
|
||||
const inkeeDE = merged.filter(m =>
|
||||
m.asin.startsWith('B0CQ') &&
|
||||
(m.marketplace || m.customer || '').includes('DE')
|
||||
);
|
||||
|
||||
console.log('=== INKEE DE TIMESTAMP ANALYSIS ===');
|
||||
for (const r of inkeeDE.slice(0, 5)) {
|
||||
const weekStartTs = getWeekStartSunday(r.year, r.week);
|
||||
const weekStartDate = new Date(weekStartTs).toISOString().split('T')[0];
|
||||
console.log(`ASIN:${r.asin} ${r.year}-W${r.week} weekStart:${weekStartDate} cost:${r.cost.toFixed(2)}`);
|
||||
}
|
||||
|
||||
console.log('\n=== WEEK DATE RANGES ===');
|
||||
for (let week = 1; week <= 8; week++) {
|
||||
const ts2025 = getWeekStartSunday(2025, week);
|
||||
const ts2026 = getWeekStartSunday(2026, week);
|
||||
const d2025 = new Date(ts2025).toISOString().split('T')[0];
|
||||
const d2026 = new Date(ts2026).toISOString().split('T')[0];
|
||||
console.log(`Week ${week}: 2025 → ${d2025} | 2026 → ${d2026}`);
|
||||
}
|
||||
|
||||
// Now simulate an experiment that spans the existing data range
|
||||
// The experiment "INKEE DE" - let's test with dates in Jan 2026 (W1-W4)
|
||||
const testExperiment: Experiment = {
|
||||
id: 'test',
|
||||
name: 'INKEE DE Test',
|
||||
type: 'advertising',
|
||||
status: 'active',
|
||||
asins: ['B0CQ247T2V', 'B0CQTLZRCV'],
|
||||
control_asins: [],
|
||||
marketplace: 'DE',
|
||||
start_date: '2026-01-05', // Start of week 2, 2026
|
||||
end_date: '2026-02-22', // End of week 8, 2026
|
||||
baseline_start_date: '2025-01-06', // Week 2, 2025
|
||||
baseline_end_date: '2025-02-23', // Week 8, 2025
|
||||
primary_metric: 'acos',
|
||||
notes: '',
|
||||
owner: ''
|
||||
};
|
||||
|
||||
console.log('\n=== DiD SIMULATION ===');
|
||||
const did = computeDiD(testExperiment, merged as CombinedKPIs[]);
|
||||
console.log('ACOS:', did.metrics['acos']);
|
||||
console.log('ROAS:', did.metrics['roas']);
|
||||
console.log('Units:', did.metrics['units']);
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
import XLSX from 'xlsx';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
const buf = readFileSync('/tmp/Ads-Weekly.xlsx');
|
||||
const wb = XLSX.read(buf, { type: 'buffer' });
|
||||
|
||||
console.log('=== SHEETS:', wb.SheetNames, '\n');
|
||||
|
||||
for (const sheetName of wb.SheetNames) {
|
||||
const ws = wb.Sheets[sheetName];
|
||||
const rows: any[] = XLSX.utils.sheet_to_json(ws, { defval: '' });
|
||||
|
||||
const weeks = [...new Set(rows.map((r: any) => r.Week))].sort((a: any, b: any) => a - b);
|
||||
console.log(`Sheet ${sheetName}: ${rows.length} rows | weeks ${weeks[0]} to ${weeks[weeks.length - 1]}`);
|
||||
|
||||
const inkeeRows = rows.filter((r: any) =>
|
||||
String(r.ASIN || '').toUpperCase().startsWith('B0CQ')
|
||||
);
|
||||
if (inkeeRows.length > 0) {
|
||||
console.log(` INKEE rows: ${inkeeRows.length}`);
|
||||
console.log(' First INKEE row:', JSON.stringify(inkeeRows[0]));
|
||||
const inkeeWeeks = [...new Set(inkeeRows.map((r: any) => r.Week))].sort((a: any, b: any) => a - b);
|
||||
console.log(' INKEE weeks:', inkeeWeeks.join(', '));
|
||||
} else {
|
||||
console.log(` No INKEE rows (B0CQ...) in ${sheetName}`);
|
||||
const deRows = rows.filter((r: any) => String(r.Country || '') === 'DE');
|
||||
const uniqueAsins = [...new Set(deRows.map((r: any) => r.ASIN))].slice(0, 8);
|
||||
console.log(' Some DE ASINs:', uniqueAsins.join(', '));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import XLSX from 'xlsx';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
const buf = readFileSync('/Users/christianvidalwolf/github/CrazeAnalytix/docs/plans/Ads_Performance_Export_2026-02-26.xlsx');
|
||||
const wb = XLSX.read(buf, { type: 'buffer' });
|
||||
|
||||
console.log('Sheets:', wb.SheetNames);
|
||||
|
||||
for (const sheetName of wb.SheetNames) {
|
||||
const ws = wb.Sheets[sheetName];
|
||||
const rows: any[] = XLSX.utils.sheet_to_json(ws, { defval: '' });
|
||||
|
||||
if (rows.length === 0) { console.log(`Sheet "${sheetName}" is empty`); continue; }
|
||||
|
||||
console.log(`\n=== Sheet: "${sheetName}" (${rows.length} rows) ===`);
|
||||
const headers = Object.keys(rows[0]);
|
||||
console.log('Columns:', headers.join(' | '));
|
||||
console.log('First 3 rows:');
|
||||
rows.slice(0, 3).forEach(r => console.log(' ', JSON.stringify(r).slice(0, 300)));
|
||||
|
||||
// Look for key columns
|
||||
const weekCol = headers.find(h => /week|woche|semana/i.test(h));
|
||||
const asinCol = headers.find(h => /asin/i.test(h));
|
||||
const countryCol = headers.find(h => /country|marketplace|portfolio|land|país/i.test(h));
|
||||
const costCol = headers.find(h => /^cost$|^spend$|^ausgaben$|^gasto$/i.test(h) || (/cost|spend/i.test(h) && !/acos|of sales/i.test(h)));
|
||||
const salesCol = headers.find(h => /sales|umsatz|ventas/i.test(h) && !/acos|cost of/i.test(h));
|
||||
|
||||
console.log(`\nKey columns: week="${weekCol}" | asin="${asinCol}" | country="${countryCol}" | cost="${costCol}" | sales="${salesCol}"`);
|
||||
|
||||
if (weekCol && costCol) {
|
||||
const weeks = [...new Set(rows.map(r => r[weekCol!]))];
|
||||
console.log('Unique weeks:', weeks.sort());
|
||||
const totalCost = rows.reduce((s, r) => s + (parseFloat(String(r[costCol!]).replace(',', '.')) || 0), 0);
|
||||
const totalSales = salesCol ? rows.reduce((s, r) => s + (parseFloat(String(r[salesCol!]).replace(',', '.')) || 0), 0) : 0;
|
||||
console.log(`Total cost: ${totalCost.toFixed(2)}, Total sales: ${totalSales.toFixed(2)}`);
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import fs from 'fs';
|
||||
import { processCSV } from './services/dataProcessor';
|
||||
import { processAdsExcel, mergeSalesAndAdsData } from './services/dataProcessor';
|
||||
import { computeDiD } from './services/experimentAnalysis';
|
||||
|
||||
async function runTest() {
|
||||
console.log("Loading sales data...");
|
||||
const csvStr = fs.readFileSync('public/Craze analytix 2026.csv', 'utf-8');
|
||||
const sales = await processCSV(csvStr);
|
||||
|
||||
console.log("Loading ads data...");
|
||||
const excelBuffer = fs.readFileSync('public/ads.xlsx').buffer;
|
||||
const ads = await processAdsExcel(excelBuffer);
|
||||
|
||||
console.log("Sales rows:", sales.length);
|
||||
console.log("Ads rows:", ads.length);
|
||||
|
||||
// Check INKEE DE explicitly
|
||||
const inkeeSales = sales.filter(s => s.asin.includes('B0CQ'))
|
||||
console.log("INKEE Sales rows:", inkeeSales.length);
|
||||
|
||||
const inkeeAds = ads.filter(a => a.asin.includes('B0CQ'))
|
||||
console.log("INKEE Ad rows:", inkeeAds.length);
|
||||
|
||||
console.log("Merging...");
|
||||
const merged = mergeSalesAndAdsData(sales, ads);
|
||||
|
||||
const mergedInkee = merged.filter(m => m.asin.includes('B0CQ') && m.year === 2024);
|
||||
|
||||
const totalCost = mergedInkee.reduce((sum, r) => sum + (r.cost || 0), 0);
|
||||
const totalAdSales = mergedInkee.reduce((sum, r) => sum + (r.salesAds || 0), 0);
|
||||
|
||||
console.log("Merged INKEE total cost 2024:", totalCost);
|
||||
console.log("Merged INKEE total ad sales 2024:", totalAdSales);
|
||||
|
||||
const experiment = {
|
||||
id: "INKEE_DE",
|
||||
name: "INKEE DE Test",
|
||||
status: "active",
|
||||
type: "advertising",
|
||||
asins: ["B0CQXNBGBQ"], // Assuming this is INKEE DE
|
||||
control_asins: [],
|
||||
marketplace: "Amazon DE", // Matching the UI
|
||||
start_date: "2024-03-01",
|
||||
end_date: "2024-04-15",
|
||||
baseline_start_date: "2024-01-01",
|
||||
baseline_end_date: "2024-02-28",
|
||||
primary_metric: "acos"
|
||||
} as any;
|
||||
|
||||
console.log("Running DiD...");
|
||||
const did = computeDiD(experiment, merged);
|
||||
console.log("DiD Output:", JSON.stringify(did.metrics, null, 2));
|
||||
|
||||
}
|
||||
|
||||
runTest().catch(console.error);
|
||||
@@ -0,0 +1,94 @@
|
||||
// Verificar fechas ISO para el experimento BODYNESS
|
||||
|
||||
function getISOWeek(date: Date): { year: number; week: number } {
|
||||
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
||||
const dayNum = d.getUTCDay() || 7;
|
||||
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
||||
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
||||
const weekNum = Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
|
||||
return { year: d.getUTCFullYear(), week: weekNum };
|
||||
}
|
||||
|
||||
function getISOWeekMonday(year: number, week: number): number {
|
||||
const jan4 = new Date(Date.UTC(year, 0, 4));
|
||||
const dayOfWeek = jan4.getUTCDay() || 7;
|
||||
const week1Monday = new Date(Date.UTC(year, 0, 4 - (dayOfWeek - 1)));
|
||||
return week1Monday.getTime() + (week - 1) * 7 * 86400000;
|
||||
}
|
||||
|
||||
function getWeeksInYear(year: number): number {
|
||||
const dec28 = new Date(Date.UTC(year, 11, 28));
|
||||
const iso = getISOWeek(dec28);
|
||||
return iso.week;
|
||||
}
|
||||
|
||||
function calculateISOWeekRange(startDate: Date, endDate: Date): {
|
||||
numWeeks: number;
|
||||
expandedStartTs: number;
|
||||
expandedEndTs: number;
|
||||
weekKeys: string[];
|
||||
} {
|
||||
const startISO = getISOWeek(startDate);
|
||||
const endISO = getISOWeek(endDate);
|
||||
|
||||
const weekKeys: string[] = [];
|
||||
let currentYear = startISO.year;
|
||||
let currentWeek = startISO.week;
|
||||
|
||||
while (true) {
|
||||
const weekKey = `${currentYear}-W${String(currentWeek).padStart(2, '0')}`;
|
||||
weekKeys.push(weekKey);
|
||||
|
||||
if (currentYear === endISO.year && currentWeek === endISO.week) {
|
||||
break;
|
||||
}
|
||||
|
||||
currentWeek++;
|
||||
const weeksInYear = getWeeksInYear(currentYear);
|
||||
if (currentWeek > weeksInYear) {
|
||||
currentWeek = 1;
|
||||
currentYear++;
|
||||
}
|
||||
}
|
||||
|
||||
const expandedStartTs = getISOWeekMonday(startISO.year, startISO.week);
|
||||
const expandedEndTs = getISOWeekMonday(endISO.year, endISO.week) + 7 * 86400000;
|
||||
|
||||
return {
|
||||
numWeeks: weekKeys.length,
|
||||
expandedStartTs,
|
||||
expandedEndTs,
|
||||
weekKeys,
|
||||
};
|
||||
}
|
||||
|
||||
// Experimento: 22 enero - 4 febrero 2026
|
||||
const startDate = new Date('2026-01-22');
|
||||
const endDate = new Date('2026-02-04');
|
||||
|
||||
console.log('=== Experimento BODYNESS ES ===');
|
||||
console.log('Start:', startDate.toISOString().split('T')[0], '(day', startDate.getDay(), ')');
|
||||
console.log('End:', endDate.toISOString().split('T')[0], '(day', endDate.getDay(), ')');
|
||||
console.log('');
|
||||
|
||||
const startISO = getISOWeek(startDate);
|
||||
const endISO = getISOWeek(endDate);
|
||||
console.log('Start ISO:', `W${startISO.week}-${startISO.year}`);
|
||||
console.log('End ISO:', `W${endISO.week}-${endISO.year}`);
|
||||
console.log('');
|
||||
|
||||
const isoRange = calculateISOWeekRange(startDate, endDate);
|
||||
console.log('ISO Week Range:');
|
||||
console.log(' numWeeks:', isoRange.numWeeks);
|
||||
console.log(' weekKeys:', isoRange.weekKeys);
|
||||
console.log(' expandedStart:', new Date(isoRange.expandedStartTs).toISOString().split('T')[0]);
|
||||
console.log(' expandedEnd:', new Date(isoRange.expandedEndTs).toISOString().split('T')[0]);
|
||||
console.log('');
|
||||
|
||||
// Verificar cada semana
|
||||
isoRange.weekKeys.forEach(week => {
|
||||
const [year, weekNum] = week.split('-W');
|
||||
const monday = getISOWeekMonday(Number(year), Number(weekNum));
|
||||
const sunday = monday + 6 * 86400000 + 86399999;
|
||||
console.log(` ${week}: ${new Date(monday).toISOString().split('T')[0]} (Mon) → ${new Date(sunday).toISOString().split('T')[0]} (Sun)`);
|
||||
});
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import { computeDiD } from './services/experimentAnalysis';
|
||||
import { CombinedKPIs, Experiment } from './types';
|
||||
|
||||
const salesData: CombinedKPIs[] = [
|
||||
{
|
||||
id: '1', marketplace: 'Amazon DE', customer: 'Amazon DE', month: 'Jan-24', week: 1, year: 2024,
|
||||
asin: 'B0C123', title: 'Test', line: 'Test', sku: 'T-1',
|
||||
salesTotal: 1000, unitsTotal: 100, salesAds: 500, unitsAds: 50, cost: 100,
|
||||
clicks: 100, impressions: 1000, conversions: 5, salesOrganic: 500, unitsOrganic: 50,
|
||||
paidSalesShare: 50, organicSalesShare: 50, acos: 20, tacos: 10, roas: 5, ctr: 10, cpc: 1, cvrUnits: 5, glanceViews: 1000
|
||||
},
|
||||
{
|
||||
id: '2', marketplace: 'Amazon DE', customer: 'Amazon DE', month: 'Feb-24', week: 5, year: 2024,
|
||||
asin: 'B0C123', title: 'Test', line: 'Test', sku: 'T-1',
|
||||
salesTotal: 2000, unitsTotal: 200, salesAds: 2000, unitsAds: 200, cost: 200,
|
||||
clicks: 200, impressions: 2000, conversions: 10, salesOrganic: 0, unitsOrganic: 0,
|
||||
paidSalesShare: 100, organicSalesShare: 0, acos: 10, tacos: 10, roas: 10, ctr: 10, cpc: 1, cvrUnits: 5, glanceViews: 2000
|
||||
}
|
||||
];
|
||||
|
||||
const experiment: Experiment = {
|
||||
id: 'exp1',
|
||||
name: 'Test Exp',
|
||||
type: 'advertising',
|
||||
status: 'active',
|
||||
asins: ['B0C123'],
|
||||
control_asins: [],
|
||||
marketplace: 'DE',
|
||||
start_date: '2024-01-20',
|
||||
end_date: '2024-02-28',
|
||||
baseline_start_date: '2023-12-01',
|
||||
baseline_end_date: '2024-01-19',
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
primary_metric: 'acos',
|
||||
changes: []
|
||||
};
|
||||
|
||||
const result = computeDiD(experiment, salesData);
|
||||
console.log(JSON.stringify(result.metrics, null, 2));
|
||||
Reference in New Issue
Block a user