fix: resolve LINE level experiments to individual ASINs using salesData so calculation works for product lines

This commit is contained in:
Christian Vidal Wolf
2026-02-21 19:26:45 +01:00
parent c2d516dbfc
commit 249dc0c749
4 changed files with 63 additions and 36 deletions
+54 -29
View File
@@ -10,6 +10,34 @@ import { CombinedKPIs } from '../types';
const API_BASE = '/api/experiments';
// ============ Helper Functions ============
export const getExperimentAsins = (experimentAsins: string[], salesData: CombinedKPIs[]): Set<string> => {
const explicitAsins = new Set<string>();
const lines = new Set<string>();
(experimentAsins || []).forEach(a => {
const val = (a || '').trim().toUpperCase();
if (val.startsWith('LINE:')) {
lines.add(val.substring(5).trim());
} else if (val) {
explicitAsins.add(val);
}
});
const asinSet = new Set<string>(explicitAsins);
if (lines.size > 0 && salesData) {
salesData.forEach(r => {
const line = (r.line || '').trim().toUpperCase();
if (line && lines.has(line)) {
if (r.asin) asinSet.add(r.asin.toUpperCase());
}
});
}
return asinSet;
};
// ============ CRUD Operations ============
export const createExperiment = async (input: ExperimentCreateInput): Promise<Experiment> => {
@@ -135,13 +163,10 @@ export const listExperiments = async (
});
};
export const getActiveExperimentsForASINs = async (
asins: string[]
export const getActiveExperiments = async (
salesData: CombinedKPIs[]
): Promise<Map<string, ActiveExperiment[]>> => {
const params = new URLSearchParams();
asins.forEach(asin => params.append('asin', asin));
const response = await fetch(`${API_BASE}?${params.toString()}&status=active`);
const response = await fetch(`${API_BASE}?status=active`);
if (!response.ok) {
const error = await response.json();
@@ -160,30 +185,30 @@ export const getActiveExperimentsForASINs = async (
const map = new Map<string, ActiveExperiment[]>();
for (const asin of asins) {
const experiments = activeExperiments
.filter((exp: any) => exp.asins?.includes(asin))
.map((exp: any) => {
const endDate = exp.end_date ? new Date(exp.end_date) : null;
const daysRemaining = endDate
? Math.ceil((endDate.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24))
: undefined;
for (const exp of activeExperiments) {
const expAsins = getExperimentAsins(exp.asins || [], salesData);
return {
asin,
experiment_id: exp.id,
experiment_name: exp.name,
type: exp.type,
status: exp.status,
start_date: exp.start_date,
end_date: exp.end_date,
days_remaining: daysRemaining,
} as ActiveExperiment;
});
const endDate = exp.end_date ? new Date(exp.end_date) : null;
const daysRemaining = endDate
? Math.ceil((endDate.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24))
: undefined;
if (experiments.length > 0) {
map.set(asin, experiments);
}
const activeExp: ActiveExperiment = {
asin: '', // placeholder
experiment_id: exp.id,
experiment_name: exp.name,
type: exp.type,
status: exp.status,
start_date: exp.start_date,
end_date: exp.end_date,
days_remaining: daysRemaining,
};
expAsins.forEach(asin => {
const asinList = map.get(asin) || [];
asinList.push({ ...activeExp, asin });
map.set(asin, asinList);
});
}
return map;
@@ -216,7 +241,7 @@ export const calculateExperimentPerformance = async (
const baselineEnd = startDate;
// Filter sales data for experiment ASINs
const asinSet = new Set(experiment.asins.map(a => a.toUpperCase()));
const asinSet = getExperimentAsins(experiment.asins, salesData);
const baselineData = salesData.filter(r => {
const recordDate = new Date(r.year, 0, 1 + (r.week - 1) * 7);