bulltrackers-module 1.0.658 → 1.0.660
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/functions/computation-system/data/AvailabilityChecker.js +163 -317
- package/functions/computation-system/data/CachedDataLoader.js +158 -222
- package/functions/computation-system/data/DependencyFetcher.js +201 -406
- package/functions/computation-system/executors/MetaExecutor.js +176 -280
- package/functions/computation-system/executors/StandardExecutor.js +325 -383
- package/functions/computation-system/helpers/computation_dispatcher.js +306 -701
- package/functions/computation-system/helpers/computation_worker.js +3 -2
- package/functions/computation-system/legacy/AvailabilityCheckerOld.js +382 -0
- package/functions/computation-system/legacy/CachedDataLoaderOld.js +357 -0
- package/functions/computation-system/legacy/DependencyFetcherOld.js +478 -0
- package/functions/computation-system/legacy/MetaExecutorold.js +364 -0
- package/functions/computation-system/legacy/StandardExecutorold.js +476 -0
- package/functions/computation-system/legacy/computation_dispatcherold.js +944 -0
- package/functions/computation-system/persistence/ResultCommitter.js +137 -188
- package/functions/computation-system/services/SnapshotService.js +129 -0
- package/functions/computation-system/tools/BuildReporter.js +12 -7
- package/functions/computation-system/utils/data_loader.js +213 -238
- package/package.json +3 -2
- package/functions/computation-system/workflows/bulltrackers_pipeline.yaml +0 -163
- package/functions/computation-system/workflows/data_feeder_pipeline.yaml +0 -115
- package/functions/computation-system/workflows/datafeederpipelineinstructions.md +0 -30
- package/functions/computation-system/workflows/morning_prep_pipeline.yaml +0 -55
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Executor for "Meta" (global) calculations.
|
|
3
|
+
* UPDATED: Uses CachedDataLoader for all data access.
|
|
4
|
+
* UPDATED: Tracks processed shard/item counts.
|
|
5
|
+
* UPDATED: Sends 'isInitialWrite: true' for robust cleanup.
|
|
6
|
+
* UPDATED: Support for historical rankings in Meta Context.
|
|
7
|
+
* UPDATED: Added support for loading Series Data (Root & Results) for lookbacks.
|
|
8
|
+
* UPDATED: Builds Manifest Lookup for DependencyFetcher.
|
|
9
|
+
*/
|
|
10
|
+
const { normalizeName } = require('../utils/utils');
|
|
11
|
+
const { CachedDataLoader } = require('../data/CachedDataLoader');
|
|
12
|
+
const { ContextFactory } = require('../context/ContextFactory');
|
|
13
|
+
const { commitResults } = require('../persistence/ResultCommitter');
|
|
14
|
+
const { fetchResultSeries, fetchDependencies } = require('../data/DependencyFetcher');
|
|
15
|
+
const { getManifest } = require('../topology/ManifestLoader');
|
|
16
|
+
|
|
17
|
+
class MetaExecutor {
|
|
18
|
+
static async run(date, calcs, passName, config, deps, rootData, fetchedDeps, previousFetchedDeps) {
|
|
19
|
+
const dStr = date.toISOString().slice(0, 10);
|
|
20
|
+
const { logger, db } = deps;
|
|
21
|
+
const loader = new CachedDataLoader(config, deps);
|
|
22
|
+
|
|
23
|
+
// --- [FIXED] Build Global Manifest Lookup using getManifest ---
|
|
24
|
+
// getManifest is typically cached, so this is cheap.
|
|
25
|
+
const allManifests = getManifest(config.productLines, config.calculationsDirectory, deps);
|
|
26
|
+
const manifestLookup = {};
|
|
27
|
+
allManifests.forEach(m => { manifestLookup[normalizeName(m.name)] = m.category; });
|
|
28
|
+
|
|
29
|
+
// [FIX] Check if any meta calculation needs history
|
|
30
|
+
const needsHistory = calcs.some(c => c.isHistorical);
|
|
31
|
+
let rankingsYesterday = null;
|
|
32
|
+
|
|
33
|
+
if (needsHistory) {
|
|
34
|
+
const prevDate = new Date(date);
|
|
35
|
+
prevDate.setUTCDate(prevDate.getUTCDate() - 1);
|
|
36
|
+
const prevStr = prevDate.toISOString().slice(0, 10);
|
|
37
|
+
logger.log('INFO', `[MetaExecutor] Loading historical rankings for ${prevStr}`);
|
|
38
|
+
rankingsYesterday = await loader.loadRankings(prevStr);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 1. Load Global Dependencies
|
|
42
|
+
const [mappings, rankings, verifications] = await Promise.all([
|
|
43
|
+
loader.loadMappings(),
|
|
44
|
+
loader.loadRankings(dStr),
|
|
45
|
+
loader.loadVerifications()
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
// [NEW] Load New Root Data Types for Profile Metrics (if any calc needs them)
|
|
49
|
+
const needsNewRootData = calcs.some(c => {
|
|
50
|
+
const deps = c.rootDataDependencies || [];
|
|
51
|
+
return deps.includes('ratings') || deps.includes('pageViews') ||
|
|
52
|
+
deps.includes('watchlist') || deps.includes('alerts');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
let ratings = null, pageViews = null, watchlistMembership = null, alertHistory = null;
|
|
56
|
+
if (needsNewRootData) {
|
|
57
|
+
const loadPromises = [];
|
|
58
|
+
|
|
59
|
+
// Ratings
|
|
60
|
+
if (calcs.some(c => c.rootDataDependencies?.includes('ratings'))) {
|
|
61
|
+
loadPromises.push(loader.loadRatings(dStr).then(r => { ratings = r; }).catch(e => {
|
|
62
|
+
const allAllowMissing = calcs.every(c => {
|
|
63
|
+
const needsRatings = c.rootDataDependencies?.includes('ratings');
|
|
64
|
+
return !needsRatings || c.canHaveMissingRoots === true;
|
|
65
|
+
});
|
|
66
|
+
if (allAllowMissing) { ratings = null; } else { throw e; }
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// PageViews
|
|
71
|
+
if (calcs.some(c => c.rootDataDependencies?.includes('pageViews'))) {
|
|
72
|
+
loadPromises.push(loader.loadPageViews(dStr).then(pv => { pageViews = pv; }).catch(e => {
|
|
73
|
+
const allAllowMissing = calcs.every(c => {
|
|
74
|
+
const needsPageViews = c.rootDataDependencies?.includes('pageViews');
|
|
75
|
+
return !needsPageViews || c.canHaveMissingRoots === true;
|
|
76
|
+
});
|
|
77
|
+
if (allAllowMissing) { pageViews = null; } else { throw e; }
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Watchlist
|
|
82
|
+
if (calcs.some(c => c.rootDataDependencies?.includes('watchlist'))) {
|
|
83
|
+
loadPromises.push(loader.loadWatchlistMembership(dStr).then(w => { watchlistMembership = w; }).catch(e => {
|
|
84
|
+
const allAllowMissing = calcs.every(c => {
|
|
85
|
+
const needsWatchlist = c.rootDataDependencies?.includes('watchlist');
|
|
86
|
+
return !needsWatchlist || c.canHaveMissingRoots === true;
|
|
87
|
+
});
|
|
88
|
+
if (allAllowMissing) { watchlistMembership = null; } else { throw e; }
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Alerts
|
|
93
|
+
if (calcs.some(c => c.rootDataDependencies?.includes('alerts'))) {
|
|
94
|
+
loadPromises.push(loader.loadAlertHistory(dStr).then(a => { alertHistory = a; }).catch(e => {
|
|
95
|
+
const allAllowMissing = calcs.every(c => {
|
|
96
|
+
const needsAlerts = c.rootDataDependencies?.includes('alerts');
|
|
97
|
+
return !needsAlerts || c.canHaveMissingRoots === true;
|
|
98
|
+
});
|
|
99
|
+
if (allAllowMissing) { alertHistory = null; } else { throw e; }
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await Promise.all(loadPromises);
|
|
104
|
+
|
|
105
|
+
// [FIX] Enforce canHaveMissingRoots - validate after loading
|
|
106
|
+
for (const c of calcs) {
|
|
107
|
+
const deps = c.rootDataDependencies || [];
|
|
108
|
+
const canSkip = c.canHaveMissingRoots === true;
|
|
109
|
+
const isMissing = (key, val) => deps.includes(key) && (val === null || val === undefined);
|
|
110
|
+
|
|
111
|
+
if (!canSkip) {
|
|
112
|
+
if (isMissing('ratings', ratings)) throw new Error(`[MetaExecutor] Missing required root 'ratings' for ${c.name}`);
|
|
113
|
+
if (isMissing('pageViews', pageViews)) throw new Error(`[MetaExecutor] Missing required root 'pageViews' for ${c.name}`);
|
|
114
|
+
if (isMissing('watchlist', watchlistMembership)) throw new Error(`[MetaExecutor] Missing required root 'watchlist' for ${c.name}`);
|
|
115
|
+
if (isMissing('alerts', alertHistory)) throw new Error(`[MetaExecutor] Missing required root 'alerts' for ${c.name}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// --- [NEW] Series / Lookback Loading Logic ---
|
|
121
|
+
const rootSeriesRequests = {};
|
|
122
|
+
const dependencySeriesRequests = {};
|
|
123
|
+
|
|
124
|
+
// 1. Identify requirements from manifests
|
|
125
|
+
calcs.forEach(c => {
|
|
126
|
+
if (c.rootDataSeries) {
|
|
127
|
+
Object.entries(c.rootDataSeries).forEach(([type, conf]) => {
|
|
128
|
+
const days = typeof conf === 'object' ? conf.lookback : conf;
|
|
129
|
+
if (!rootSeriesRequests[type] || days > rootSeriesRequests[type]) {
|
|
130
|
+
rootSeriesRequests[type] = days;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (c.dependencySeries) {
|
|
135
|
+
Object.entries(c.dependencySeries).forEach(([depName, conf]) => {
|
|
136
|
+
const days = typeof conf === 'object' ? conf.lookback : conf;
|
|
137
|
+
const normalized = normalizeName(depName);
|
|
138
|
+
if (!dependencySeriesRequests[normalized] || days > dependencySeriesRequests[normalized]) {
|
|
139
|
+
dependencySeriesRequests[normalized] = days;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const seriesData = { root: {}, results: {} };
|
|
146
|
+
|
|
147
|
+
// 2. Load Root Series
|
|
148
|
+
for (const [type, days] of Object.entries(rootSeriesRequests)) {
|
|
149
|
+
let loaderMethod = null;
|
|
150
|
+
if (type === 'alerts') loaderMethod = 'loadAlertHistory';
|
|
151
|
+
else if (type === 'insights') loaderMethod = 'loadInsights';
|
|
152
|
+
else if (type === 'ratings') loaderMethod = 'loadRatings';
|
|
153
|
+
else if (type === 'watchlist') loaderMethod = 'loadWatchlistMembership';
|
|
154
|
+
// [CRITICAL UPDATE] Add rankings support for Meta lookbacks
|
|
155
|
+
else if (type === 'rankings') loaderMethod = 'loadRankings';
|
|
156
|
+
|
|
157
|
+
if (loaderMethod) {
|
|
158
|
+
logger.log('INFO', `[MetaExecutor] Loading ${days}-day series for Root Data '${type}'...`);
|
|
159
|
+
const series = await loader.loadSeries(loaderMethod, dStr, days);
|
|
160
|
+
seriesData.root[type] = series.data;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 3. Load Computation Result Series
|
|
165
|
+
const calcNamesToFetch = Object.keys(dependencySeriesRequests);
|
|
166
|
+
if (calcNamesToFetch.length > 0) {
|
|
167
|
+
const maxDays = Math.max(...Object.values(dependencySeriesRequests));
|
|
168
|
+
logger.log('INFO', `[MetaExecutor] Loading up to ${maxDays}-day series for Dependencies: ${calcNamesToFetch.join(', ')}`);
|
|
169
|
+
|
|
170
|
+
// Pass manifestLookup to fetcher
|
|
171
|
+
const resultsSeries = await fetchResultSeries(dStr, calcNamesToFetch, manifestLookup, config, deps, maxDays);
|
|
172
|
+
seriesData.results = resultsSeries;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const state = {};
|
|
176
|
+
for (const c of calcs) {
|
|
177
|
+
const inst = new c.class();
|
|
178
|
+
inst.manifest = c;
|
|
179
|
+
|
|
180
|
+
// DEBUG: Log what dependencies were fetched
|
|
181
|
+
const fetchedDepKeys = Object.keys(fetchedDeps || {});
|
|
182
|
+
logger.log('INFO', `[MetaExecutor] 📦 Fetched dependencies available: ${fetchedDepKeys.length > 0 ? fetchedDepKeys.join(', ') : 'NONE'}`);
|
|
183
|
+
|
|
184
|
+
const context = ContextFactory.buildMetaContext({
|
|
185
|
+
dateStr: dStr,
|
|
186
|
+
metadata: c,
|
|
187
|
+
mappings,
|
|
188
|
+
insights: { today: rootData.todayInsights },
|
|
189
|
+
socialData: { today: rootData.todaySocialPostInsights },
|
|
190
|
+
computedDependencies: fetchedDeps,
|
|
191
|
+
previousComputedDependencies: previousFetchedDeps,
|
|
192
|
+
config, deps,
|
|
193
|
+
allRankings: rankings,
|
|
194
|
+
allRankingsYesterday: rankingsYesterday,
|
|
195
|
+
allVerifications: verifications,
|
|
196
|
+
ratings: ratings || {},
|
|
197
|
+
pageViews: pageViews || {},
|
|
198
|
+
watchlistMembership: watchlistMembership || {},
|
|
199
|
+
alertHistory: alertHistory || {},
|
|
200
|
+
seriesData
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// DEBUG: Log dependency availability
|
|
204
|
+
// CRITICAL: Use class.getDependencies() to get original case-sensitive names
|
|
205
|
+
const depNames = (c.class && typeof c.class.getDependencies === 'function')
|
|
206
|
+
? c.class.getDependencies()
|
|
207
|
+
: (c.getDependencies ? c.getDependencies() : (c.dependencies || []));
|
|
208
|
+
depNames.forEach(depName => {
|
|
209
|
+
const depData = context.computed[depName];
|
|
210
|
+
if (depData) {
|
|
211
|
+
const keys = Object.keys(depData);
|
|
212
|
+
logger.log('INFO', `[MetaExecutor] ✅ Dependency '${depName}' available for ${c.name}. Keys: ${keys.length} (sample: ${keys.slice(0, 5).join(', ')})`);
|
|
213
|
+
} else {
|
|
214
|
+
logger.log('ERROR', `[MetaExecutor] ❌ Dependency '${depName}' MISSING for ${c.name} in context.computed`);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const result = await inst.process(context);
|
|
220
|
+
|
|
221
|
+
// DEBUG: Log result before saving
|
|
222
|
+
if (result && typeof result === 'object') {
|
|
223
|
+
const resultKeys = Object.keys(result);
|
|
224
|
+
logger.log('INFO', `[MetaExecutor] ✅ ${c.name} computed result. Keys: ${resultKeys.length} (sample: ${resultKeys.slice(0, 10).join(', ')})`);
|
|
225
|
+
if (resultKeys.length === 0) {
|
|
226
|
+
logger.log('WARN', `[MetaExecutor] ⚠️ ${c.name} returned EMPTY result object!`);
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
logger.log('WARN', `[MetaExecutor] ⚠️ ${c.name} returned non-object result: ${typeof result} - ${result}`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// CRITICAL FIX: Do NOT overwrite this.results - the computation already sets it correctly
|
|
233
|
+
// The computation's process() method sets this.results, and getResult() returns it
|
|
234
|
+
// We only use the return value for logging/debugging
|
|
235
|
+
// inst.results should already be set by the computation's process() method
|
|
236
|
+
if (!inst.results) {
|
|
237
|
+
logger.log('WARN', `[MetaExecutor] ⚠️ ${c.name} did not set this.results - using return value as fallback`);
|
|
238
|
+
inst.results = result;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// DEBUG: Verify what getResult() will return
|
|
242
|
+
const finalResult = await inst.getResult();
|
|
243
|
+
if (finalResult && typeof finalResult === 'object') {
|
|
244
|
+
const finalKeys = Object.keys(finalResult);
|
|
245
|
+
logger.log('INFO', `[MetaExecutor] ✅ ${c.name} getResult() will return ${finalKeys.length} keys`);
|
|
246
|
+
} else {
|
|
247
|
+
logger.log('WARN', `[MetaExecutor] ⚠️ ${c.name} getResult() returns: ${typeof finalResult}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
state[c.name] = inst;
|
|
251
|
+
} catch (e) {
|
|
252
|
+
logger.log('ERROR', `Meta calc ${c.name} failed: ${e.message}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// CRITICAL FIX: Pass 'isInitialWrite: true' to ensure proper cleanup of old meta data
|
|
257
|
+
return await commitResults(state, dStr, passName, config, deps, false, { isInitialWrite: true });
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
static async executeOncePerDay(calcInstance, metadata, dateStr, computedDeps, prevDeps, config, deps, loader) {
|
|
261
|
+
const mappings = await loader.loadMappings();
|
|
262
|
+
const { logger } = deps;
|
|
263
|
+
const stats = { processedShards: 0, processedItems: 0 };
|
|
264
|
+
|
|
265
|
+
const insights = metadata.rootDataDependencies?.includes('insights') ? { today: await loader.loadInsights(dateStr) } : null;
|
|
266
|
+
const social = metadata.rootDataDependencies?.includes('social') ? { today: await loader.loadSocial(dateStr) } : null;
|
|
267
|
+
|
|
268
|
+
let rankingsYesterday = null;
|
|
269
|
+
if (metadata.isHistorical) {
|
|
270
|
+
const prevDate = new Date(dateStr);
|
|
271
|
+
prevDate.setUTCDate(prevDate.getUTCDate() - 1);
|
|
272
|
+
const prevStr = prevDate.toISOString().slice(0, 10);
|
|
273
|
+
rankingsYesterday = await loader.loadRankings(prevStr);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const rankings = await loader.loadRankings(dateStr);
|
|
277
|
+
const allowMissing = metadata.canHaveMissingRoots === true;
|
|
278
|
+
|
|
279
|
+
let ratings = null;
|
|
280
|
+
if (metadata.rootDataDependencies?.includes('ratings')) {
|
|
281
|
+
try { ratings = await loader.loadRatings(dateStr); }
|
|
282
|
+
catch (e) { if (!allowMissing) throw e; ratings = null; }
|
|
283
|
+
if (!ratings && !allowMissing) throw new Error(`Missing ratings`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
let pageViews = null;
|
|
287
|
+
if (metadata.rootDataDependencies?.includes('pageViews')) {
|
|
288
|
+
try { pageViews = await loader.loadPageViews(dateStr); }
|
|
289
|
+
catch (e) { if (!allowMissing) throw e; pageViews = null; }
|
|
290
|
+
if (!pageViews && !allowMissing) throw new Error(`Missing pageViews`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
let watchlistMembership = null;
|
|
294
|
+
if (metadata.rootDataDependencies?.includes('watchlist')) {
|
|
295
|
+
try { watchlistMembership = await loader.loadWatchlistMembership(dateStr); }
|
|
296
|
+
catch (e) { if (!allowMissing) throw e; watchlistMembership = null; }
|
|
297
|
+
if (!watchlistMembership && !allowMissing) throw new Error(`Missing watchlist`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
let alertHistory = null;
|
|
301
|
+
if (metadata.rootDataDependencies?.includes('alerts')) {
|
|
302
|
+
try { alertHistory = await loader.loadAlertHistory(dateStr); }
|
|
303
|
+
catch (e) { if (!allowMissing) throw e; alertHistory = null; }
|
|
304
|
+
if (!alertHistory && !allowMissing) throw new Error(`Missing alerts`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const seriesData = { root: {}, results: {} };
|
|
308
|
+
|
|
309
|
+
if (metadata.rootDataDependencies?.includes('price')) {
|
|
310
|
+
logger.log('INFO', `[Executor] Running Batched/Sharded Execution for ${metadata.name}`);
|
|
311
|
+
const shardRefs = await loader.getPriceShardReferences();
|
|
312
|
+
if (shardRefs.length === 0) { logger.log('WARN', '[Executor] No price shards found.'); return {}; }
|
|
313
|
+
|
|
314
|
+
let processedCount = 0;
|
|
315
|
+
for (const ref of shardRefs) {
|
|
316
|
+
const shardData = await loader.loadPriceShard(ref);
|
|
317
|
+
const partialContext = ContextFactory.buildMetaContext({
|
|
318
|
+
dateStr, metadata, mappings, insights, socialData: social,
|
|
319
|
+
prices: { history: shardData }, computedDependencies: computedDeps,
|
|
320
|
+
previousComputedDependencies: prevDeps, config, deps,
|
|
321
|
+
allRankings: rankings,
|
|
322
|
+
allRankingsYesterday: rankingsYesterday,
|
|
323
|
+
ratings: ratings || {},
|
|
324
|
+
pageViews: pageViews || {},
|
|
325
|
+
watchlistMembership: watchlistMembership || {},
|
|
326
|
+
alertHistory: alertHistory || {},
|
|
327
|
+
seriesData
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
await calcInstance.process(partialContext);
|
|
331
|
+
partialContext.prices = null;
|
|
332
|
+
processedCount++;
|
|
333
|
+
|
|
334
|
+
stats.processedShards++;
|
|
335
|
+
stats.processedItems += Object.keys(shardData).length;
|
|
336
|
+
}
|
|
337
|
+
logger.log('INFO', `[Executor] Finished Batched Execution for ${metadata.name} (${processedCount} shards).`);
|
|
338
|
+
|
|
339
|
+
calcInstance._executionStats = stats;
|
|
340
|
+
return calcInstance.getResult ? await calcInstance.getResult() : {};
|
|
341
|
+
} else {
|
|
342
|
+
const context = ContextFactory.buildMetaContext({
|
|
343
|
+
dateStr, metadata, mappings, insights, socialData: social,
|
|
344
|
+
prices: {}, computedDependencies: computedDeps,
|
|
345
|
+
previousComputedDependencies: prevDeps, config, deps,
|
|
346
|
+
allRankings: rankings,
|
|
347
|
+
allRankingsYesterday: rankingsYesterday,
|
|
348
|
+
ratings: ratings || {},
|
|
349
|
+
pageViews: pageViews || {},
|
|
350
|
+
watchlistMembership: watchlistMembership || {},
|
|
351
|
+
alertHistory: alertHistory || {},
|
|
352
|
+
seriesData
|
|
353
|
+
});
|
|
354
|
+
const res = await calcInstance.process(context);
|
|
355
|
+
|
|
356
|
+
stats.processedItems = 1;
|
|
357
|
+
calcInstance._executionStats = stats;
|
|
358
|
+
|
|
359
|
+
return res;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
module.exports = { MetaExecutor };
|