Fix previous period data

This commit is contained in:
2024-12-21 16:24:39 -05:00
parent c6467087c1
commit 9896355017
3 changed files with 130 additions and 74 deletions

View File

@@ -1,6 +1,7 @@
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import { TimeManager } from '../utils/time.utils.js'; import { TimeManager } from '../utils/time.utils.js';
import { RedisService } from './redis.service.js'; import { RedisService } from './redis.service.js';
import _ from 'lodash';
const METRIC_IDS = { const METRIC_IDS = {
PLACED_ORDER: 'Y8cqcF', PLACED_ORDER: 'Y8cqcF',
@@ -225,12 +226,24 @@ export class EventsService {
metricId: METRIC_IDS.CANCELED_ORDER metricId: METRIC_IDS.CANCELED_ORDER
}), }),
this.getEvents({ this.getEvents({
// Only pass through non-date related params for previous period
..._.omit(params, ['timeRange', 'startDate', 'endDate']),
metricId: METRIC_IDS.PLACED_ORDER, metricId: METRIC_IDS.PLACED_ORDER,
startDate: prevPeriodStart.toISO(), startDate: prevPeriodStart.toISO(),
endDate: prevPeriodEnd.toISO() endDate: prevPeriodEnd.toISO()
}) })
]); ]);
// Add debug logging
console.log('[EventsService] Previous period request:', {
params: _.omit(params, ['timeRange', 'startDate', 'endDate']),
dates: {
start: prevPeriodStart.toISO(),
end: prevPeriodEnd.toISO()
},
responseLength: prevPeriodData?.data?.length
});
// Transform all data // Transform all data
const transformedOrders = this._transformEvents(orderData.data); const transformedOrders = this._transformEvents(orderData.data);
const transformedShipped = this._transformEvents(shippedData.data); const transformedShipped = this._transformEvents(shippedData.data);
@@ -1489,15 +1502,51 @@ export class EventsService {
...params, ...params,
startDate: periodStart.toISO(), startDate: periodStart.toISO(),
endDate: periodEnd.toISO(), endDate: periodEnd.toISO(),
metricId: METRIC_IDS.PLACED_ORDER metricId: METRIC_IDS.PLACED_ORDER,
customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] :
params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] :
params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined
}), }),
this.getEvents({ this.getEvents({
metricId: METRIC_IDS.PLACED_ORDER, ..._.omit(params, ['timeRange', 'startDate', 'endDate']),
startDate: prevPeriodStart.toISO(), startDate: prevPeriodStart.toISO(),
endDate: prevPeriodEnd.toISO() endDate: prevPeriodEnd.toISO(),
metricId: METRIC_IDS.PLACED_ORDER,
timeRange: undefined,
isPreviousPeriod: true,
cacheKey: `prev_${prevPeriodStart.toISO()}_${prevPeriodEnd.toISO()}`,
customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] :
params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] :
params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined
}) })
]); ]);
// Add debug logging for request params and filters
console.log('[EventsService] Request details with filters:', {
current: {
params: {
...params,
startDate: periodStart.toISO(),
endDate: periodEnd.toISO(),
customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] :
params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] :
params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined
},
responseLength: currentResponse?.data?.length
},
previous: {
params: {
..._.omit(params, ['timeRange', 'startDate', 'endDate']),
startDate: prevPeriodStart.toISO(),
endDate: prevPeriodEnd.toISO(),
customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] :
params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] :
params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined
},
responseLength: prevResponse?.data?.length
}
});
// Transform events // Transform events
const currentEvents = this._transformEvents(currentResponse.data || []); const currentEvents = this._transformEvents(currentResponse.data || []);
const prevEvents = this._transformEvents(prevResponse.data || []); const prevEvents = this._transformEvents(prevResponse.data || []);
@@ -1561,6 +1610,7 @@ export class EventsService {
const dateKey = prevDate.toFormat('yyyy-MM-dd'); const dateKey = prevDate.toFormat('yyyy-MM-dd');
prevDailyStats.set(dateKey, { prevDailyStats.set(dateKey, {
date: prevDate.toISO(), date: prevDate.toISO(),
timestamp: dateKey,
revenue: 0, revenue: 0,
orders: 0, orders: 0,
itemCount: 0 itemCount: 0
@@ -1584,31 +1634,43 @@ export class EventsService {
dayStats.revenue += totalAmount; dayStats.revenue += totalAmount;
dayStats.orders++; dayStats.orders++;
dayStats.itemCount += items.length; dayStats.itemCount += items.length;
prevDailyStats.set(dateKey, dayStats);
} }
// Map previous period data to current period days based on relative position // Map previous period data to current period days
const prevPeriodDays = Array.from(prevDailyStats.values()); const prevPeriodDays = Array.from(prevDailyStats.values()).sort((a, b) => a.date.localeCompare(b.date));
const currentPeriodDays = Array.from(dailyStats.values()); const currentPeriodDays = Array.from(dailyStats.values()).sort((a, b) => a.date.localeCompare(b.date));
const daysInPeriod = currentPeriodDays.length;
for (let i = 0; i < daysInPeriod; i++) { // Add debug logging for data before mapping
console.log('[EventsService] Data before mapping:', {
currentPeriod: currentPeriodDays.slice(0, 3),
previousPeriod: prevPeriodDays.slice(0, 3),
currentLength: currentPeriodDays.length,
prevLength: prevPeriodDays.length
});
// Map the data using array indices
for (let i = 0; i < currentPeriodDays.length && i < prevPeriodDays.length; i++) {
const currentDayStats = currentPeriodDays[i]; const currentDayStats = currentPeriodDays[i];
const prevDayStats = prevPeriodDays[i]; const prevDayStats = prevPeriodDays[i];
if (prevDayStats) { if (prevDayStats && currentDayStats) {
const dayStats = dailyStats.get(currentDayStats.timestamp); const dayStats = dailyStats.get(currentDayStats.timestamp);
dayStats.prevRevenue = prevDayStats.revenue; if (dayStats) {
dayStats.prevOrders = prevDayStats.orders; dayStats.prevRevenue = prevDayStats.revenue;
dayStats.prevItemCount = prevDayStats.itemCount; dayStats.prevOrders = prevDayStats.orders;
dayStats.prevAvgOrderValue = prevDayStats.orders > 0 ? prevDayStats.revenue / prevDayStats.orders : 0; dayStats.prevItemCount = prevDayStats.itemCount;
dayStats.prevAvgOrderValue = prevDayStats.orders > 0 ? prevDayStats.revenue / prevDayStats.orders : 0;
dailyStats.set(currentDayStats.timestamp, dayStats);
}
} }
} }
// Log the final daily stats before returning // Add debug logging for mapped data
console.log('[EventsService] Final daily stats sample:', { console.log('[EventsService] Sample of mapped data:', {
totalDays: dailyStats.size, firstDay: dailyStats.get(currentPeriodDays[0]?.timestamp),
firstDay: Array.from(dailyStats.values())[0], lastDay: dailyStats.get(currentPeriodDays[currentPeriodDays.length - 1]?.timestamp),
lastDay: Array.from(dailyStats.values())[dailyStats.size - 1] totalDays: dailyStats.size
}); });
// Convert to array and sort by date // Convert to array and sort by date

View File

@@ -131,24 +131,47 @@ export class RedisService {
} }
} }
// Helper to generate cache keys // Helper to generate cache keys
_getCacheKey(type, params = {}) { _getCacheKey(type, params = {}) {
const { timeRange, startDate, endDate, metricId, metric, daily } = params; const {
let key = `klaviyo:${type}`; timeRange,
startDate,
endDate,
metricId,
metric,
daily,
cacheKey,
isPreviousPeriod
} = params;
if (type === 'stats:details') { let key = `klaviyo:${type}`;
key += `:${metric}${daily ? ':daily' : ''}`;
}
if (timeRange) { // Handle "stats:details" for daily or metric-based keys
key += `:${timeRange}${metricId ? `:${metricId}` : ''}`; if (type === 'stats:details') {
} else if (startDate && endDate) { key += `:${metric}${daily ? ':daily' : ''}`;
key += `:custom:${startDate}:${endDate}${metricId ? `:${metricId}` : ''}`;
}
return key;
} }
// If a specific cache key is provided, use it (highest priority)
if (cacheKey) {
key += `:${cacheKey}`;
}
// Otherwise, build a default cache key
else if (timeRange) {
key += `:${timeRange}${metricId ? `:${metricId}` : ''}`;
if (isPreviousPeriod) {
key += ':prev';
}
} else if (startDate && endDate) {
key += `:custom:${startDate}:${endDate}${metricId ? `:${metricId}` : ''}`;
if (isPreviousPeriod) {
key += ':prev';
}
}
return key;
}
// Get TTL based on time range // Get TTL based on time range
_getTTL(timeRange) { _getTTL(timeRange) {
const TTL_MAP = { const TTL_MAP = {

View File

@@ -523,53 +523,24 @@ const SalesChart = ({
setLoading(true); setLoading(true);
setError(null); setError(null);
// Get previous period params // Fetch data
const prevPeriodParams = calculatePreviousPeriodDates( const response = await axios.get('/api/klaviyo/events/stats/details', {
params.timeRange, params: {
params.startDate, ...params,
params.endDate metric: 'revenue',
); daily: true
}
});
// Fetch both current and previous period data if (!response.data) {
const [currentResponse, prevResponse] = await Promise.all([
axios.get('/api/klaviyo/events/stats/details', {
params: {
...params,
metric: 'revenue',
daily: true
}
}),
axios.get('/api/klaviyo/events/stats/details', {
params: {
metric: 'revenue',
daily: true,
...(prevPeriodParams || {})
}
})
]);
if (!currentResponse.data) {
throw new Error('Invalid response format'); throw new Error('Invalid response format');
} }
// Process the data // Process the data
const currentStats = Array.isArray(currentResponse.data) ? currentResponse.data : currentResponse.data.stats || []; const currentStats = Array.isArray(response.data) ? response.data : response.data.stats || [];
const prevStats = Array.isArray(prevResponse.data) ? prevResponse.data : (prevResponse.data?.stats || []);
// Map previous period data to current period dates // Process the data directly without remapping
const processedStats = currentStats.map((day, index) => { const processedData = processData(currentStats);
// Find the corresponding previous period day
const prevDay = prevStats[index] || {};
return {
...day,
prevRevenue: Number(prevDay.revenue || 0),
prevOrders: Number(prevDay.orders || 0),
prevAvgOrderValue: Number(prevDay.averageOrderValue || (prevDay.orders > 0 ? prevDay.revenue / prevDay.orders : 0))
};
});
const processedData = processData(processedStats);
const stats = calculateSummaryStats(processedData); const stats = calculateSummaryStats(processedData);
setData(processedData); setData(processedData);