bulltrackers-module 1.0.732 → 1.0.733
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/orchestrator/index.js +19 -17
- package/index.js +8 -29
- package/package.json +1 -1
- package/functions/computation-system/WorkflowOrchestrator.js +0 -213
- package/functions/computation-system/config/monitoring_config.js +0 -31
- package/functions/computation-system/config/validation_overrides.js +0 -10
- package/functions/computation-system/context/ContextFactory.js +0 -143
- package/functions/computation-system/context/ManifestBuilder.js +0 -379
- package/functions/computation-system/data/AvailabilityChecker.js +0 -236
- package/functions/computation-system/data/CachedDataLoader.js +0 -325
- package/functions/computation-system/data/DependencyFetcher.js +0 -455
- package/functions/computation-system/executors/MetaExecutor.js +0 -279
- package/functions/computation-system/executors/PriceBatchExecutor.js +0 -108
- package/functions/computation-system/executors/StandardExecutor.js +0 -465
- package/functions/computation-system/helpers/computation_dispatcher.js +0 -750
- package/functions/computation-system/helpers/computation_worker.js +0 -375
- package/functions/computation-system/helpers/monitor.js +0 -64
- package/functions/computation-system/helpers/on_demand_helpers.js +0 -154
- package/functions/computation-system/layers/extractors.js +0 -1097
- package/functions/computation-system/layers/index.js +0 -40
- package/functions/computation-system/layers/mathematics.js +0 -522
- package/functions/computation-system/layers/profiling.js +0 -537
- package/functions/computation-system/layers/validators.js +0 -170
- package/functions/computation-system/legacy/AvailabilityCheckerOld.js +0 -388
- package/functions/computation-system/legacy/CachedDataLoaderOld.js +0 -357
- package/functions/computation-system/legacy/DependencyFetcherOld.js +0 -478
- package/functions/computation-system/legacy/MetaExecutorold.js +0 -364
- package/functions/computation-system/legacy/StandardExecutorold.js +0 -476
- package/functions/computation-system/legacy/computation_dispatcherold.js +0 -944
- package/functions/computation-system/logger/logger.js +0 -297
- package/functions/computation-system/persistence/ContractValidator.js +0 -81
- package/functions/computation-system/persistence/FirestoreUtils.js +0 -56
- package/functions/computation-system/persistence/ResultCommitter.js +0 -283
- package/functions/computation-system/persistence/ResultsValidator.js +0 -130
- package/functions/computation-system/persistence/RunRecorder.js +0 -142
- package/functions/computation-system/persistence/StatusRepository.js +0 -52
- package/functions/computation-system/reporter_epoch.js +0 -6
- package/functions/computation-system/scripts/UpdateContracts.js +0 -128
- package/functions/computation-system/services/SnapshotService.js +0 -148
- package/functions/computation-system/simulation/Fabricator.js +0 -285
- package/functions/computation-system/simulation/SeededRandom.js +0 -41
- package/functions/computation-system/simulation/SimRunner.js +0 -51
- package/functions/computation-system/system_epoch.js +0 -2
- package/functions/computation-system/tools/BuildReporter.js +0 -531
- package/functions/computation-system/tools/ContractDiscoverer.js +0 -144
- package/functions/computation-system/tools/DeploymentValidator.js +0 -536
- package/functions/computation-system/tools/FinalSweepReporter.js +0 -322
- package/functions/computation-system/topology/HashManager.js +0 -55
- package/functions/computation-system/topology/ManifestLoader.js +0 -47
- package/functions/computation-system/utils/data_loader.js +0 -675
- package/functions/computation-system/utils/schema_capture.js +0 -121
- package/functions/computation-system/utils/utils.js +0 -188
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Executor for "Standard" (User-Level) calculations.
|
|
3
|
-
* REFACTORED: Hoisted data loading, centralized Series/Root logic.
|
|
4
|
-
* UPDATED: Calls getResult() on computations to trigger summary logging.
|
|
5
|
-
* FIXED: Removed redundant shard index increment that caused shard gaps (0, 2, 4...).
|
|
6
|
-
*/
|
|
7
|
-
const { normalizeName, getEarliestDataDates } = require('../utils/utils');
|
|
8
|
-
const { streamPortfolioData, streamHistoryData, getPortfolioPartRefs, getHistoryPartRefs } = require('../utils/data_loader');
|
|
9
|
-
const { CachedDataLoader } = require('../data/CachedDataLoader');
|
|
10
|
-
const { ContextFactory } = require('../context/ContextFactory');
|
|
11
|
-
const { commitResults } = require('../persistence/ResultCommitter');
|
|
12
|
-
const { fetchResultSeries } = require('../data/DependencyFetcher');
|
|
13
|
-
const { getManifest } = require('../topology/ManifestLoader');
|
|
14
|
-
const mathLayer = require('../layers/index');
|
|
15
|
-
const { performance } = require('perf_hooks');
|
|
16
|
-
const v8 = require('v8');
|
|
17
|
-
|
|
18
|
-
// Helper to get calculations - prefer config.calculations, fallback to direct require
|
|
19
|
-
function getCalculations(config) {
|
|
20
|
-
if (config && config.calculations) return config.calculations;
|
|
21
|
-
try { return require('aiden-shared-calculations-unified').calculations; }
|
|
22
|
-
catch (e) { return {}; }
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
class StandardExecutor {
|
|
26
|
-
|
|
27
|
-
// =========================================================================
|
|
28
|
-
// ENTRY POINT
|
|
29
|
-
// =========================================================================
|
|
30
|
-
static async run(date, calcs, passName, config, deps, rootData, fetchedDeps, previousFetchedDeps, skipStatusWrite = false) {
|
|
31
|
-
const dStr = date.toISOString().slice(0, 10);
|
|
32
|
-
const { logger } = deps;
|
|
33
|
-
|
|
34
|
-
// 1. Setup Scope (User Types & Target CID)
|
|
35
|
-
const requiredUserTypes = new Set(calcs.map(c => (c.userType || 'ALL').toUpperCase()));
|
|
36
|
-
const userTypeArray = requiredUserTypes.has('ALL') ? null : Array.from(requiredUserTypes);
|
|
37
|
-
|
|
38
|
-
const targetCid = calcs.find(c => c.targetCid)?.targetCid || calcs.find(c => c.manifest?.targetCid)?.manifest?.targetCid;
|
|
39
|
-
if (targetCid) logger.log('INFO', `[StandardExecutor] 🎯 Targeted Mode: CID ${targetCid}`);
|
|
40
|
-
|
|
41
|
-
// 2. Prepare History Context (Refs)
|
|
42
|
-
const fullRoot = { ...rootData };
|
|
43
|
-
if (calcs.some(c => c.isHistorical)) {
|
|
44
|
-
const prevStr = new Date(date.getTime() - 86400000).toISOString().slice(0, 10);
|
|
45
|
-
let yRefs = await getPortfolioPartRefs(config, deps, prevStr, userTypeArray);
|
|
46
|
-
|
|
47
|
-
if (targetCid && yRefs) {
|
|
48
|
-
yRefs = yRefs.filter(r => !r.cid || String(r.cid) === String(targetCid));
|
|
49
|
-
}
|
|
50
|
-
fullRoot.yesterdayPortfolioRefs = yRefs;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// 3. Initialize Instances
|
|
54
|
-
const state = {};
|
|
55
|
-
for (const c of calcs) {
|
|
56
|
-
try {
|
|
57
|
-
const inst = new c.class();
|
|
58
|
-
inst.manifest = c;
|
|
59
|
-
inst.results = {};
|
|
60
|
-
state[normalizeName(c.name)] = inst;
|
|
61
|
-
} catch (e) { logger.log('WARN', `Failed to init ${c.name}`); }
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return await StandardExecutor.streamAndProcess(
|
|
65
|
-
dStr, state, passName, config, deps, fullRoot,
|
|
66
|
-
rootData.portfolioRefs, rootData.historyRefs,
|
|
67
|
-
fetchedDeps, previousFetchedDeps, skipStatusWrite,
|
|
68
|
-
userTypeArray, targetCid
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// =========================================================================
|
|
73
|
-
// STREAMING LOOP
|
|
74
|
-
// =========================================================================
|
|
75
|
-
static async streamAndProcess(dateStr, state, passName, config, deps, rootData, portfolioRefs, historyRefs, fetchedDeps, previousFetchedDeps, skipStatusWrite, requiredUserTypes = null, targetCid = null) {
|
|
76
|
-
const { logger } = deps;
|
|
77
|
-
const calcs = Object.values(state).filter(c => c?.manifest);
|
|
78
|
-
const streamingCalcs = calcs.filter(c => c.manifest.rootDataDependencies.includes('portfolio') || c.manifest.rootDataDependencies.includes('history'));
|
|
79
|
-
|
|
80
|
-
if (!streamingCalcs.length) return { successUpdates: {}, failureReport: [] };
|
|
81
|
-
|
|
82
|
-
const loader = new CachedDataLoader(config, deps);
|
|
83
|
-
|
|
84
|
-
// --- 1. PRE-LOAD GLOBAL DATA (Hoisted) ---
|
|
85
|
-
const startSetup = performance.now();
|
|
86
|
-
|
|
87
|
-
const [
|
|
88
|
-
globalRoots,
|
|
89
|
-
seriesData,
|
|
90
|
-
earliestDates
|
|
91
|
-
] = await Promise.all([
|
|
92
|
-
loadGlobalRoots(loader, dateStr, streamingCalcs, deps),
|
|
93
|
-
loadSeriesData(loader, dateStr, streamingCalcs, config, deps),
|
|
94
|
-
streamingCalcs.some(c => c.manifest.requiresEarliestDataDate) ? getEarliestDataDates(config, deps) : null
|
|
95
|
-
]);
|
|
96
|
-
|
|
97
|
-
const setupDuration = performance.now() - startSetup;
|
|
98
|
-
|
|
99
|
-
// --- 2. PREPARE REFS & STATS ---
|
|
100
|
-
let effPortRefs = portfolioRefs || await getPortfolioPartRefs(config, deps, dateStr, requiredUserTypes);
|
|
101
|
-
let effHistRefs = historyRefs;
|
|
102
|
-
|
|
103
|
-
if (targetCid) effPortRefs = effPortRefs?.filter(r => !r.cid || String(r.cid) === String(targetCid));
|
|
104
|
-
|
|
105
|
-
const needsHistory = streamingCalcs.some(c => c.manifest.rootDataDependencies.includes('history'));
|
|
106
|
-
if (needsHistory) {
|
|
107
|
-
effHistRefs = effHistRefs || await getHistoryPartRefs(config, deps, dateStr, requiredUserTypes);
|
|
108
|
-
if (targetCid) effHistRefs = effHistRefs?.filter(r => !r.cid || String(r.cid) === String(targetCid));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const stats = {};
|
|
112
|
-
const shardMap = {};
|
|
113
|
-
calcs.forEach(c => {
|
|
114
|
-
stats[normalizeName(c.manifest.name)] = {
|
|
115
|
-
processedUsers: 0, skippedUsers: 0, timings: { setup: setupDuration, stream: 0, processing: 0 }
|
|
116
|
-
};
|
|
117
|
-
shardMap[normalizeName(c.manifest.name)] = 0;
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// --- 3. STREAM EXECUTION ---
|
|
121
|
-
const tP_iter = streamPortfolioData(config, deps, dateStr, effPortRefs, requiredUserTypes);
|
|
122
|
-
const yP_iter = (streamingCalcs.some(c => c.manifest.isHistorical) && rootData.yesterdayPortfolioRefs)
|
|
123
|
-
? streamPortfolioData(config, deps, new Date(new Date(dateStr).getTime() - 86400000).toISOString().slice(0, 10), rootData.yesterdayPortfolioRefs)
|
|
124
|
-
: null;
|
|
125
|
-
const tH_iter = needsHistory ? streamHistoryData(config, deps, dateStr, effHistRefs, requiredUserTypes) : null;
|
|
126
|
-
|
|
127
|
-
let yP_chunk = {}, tH_chunk = {};
|
|
128
|
-
let usersSinceFlush = 0;
|
|
129
|
-
let hasFlushed = false;
|
|
130
|
-
const aggregatedSuccess = {};
|
|
131
|
-
const aggregatedFailures = [];
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
for await (const tP_chunk of tP_iter) {
|
|
135
|
-
const t0 = performance.now();
|
|
136
|
-
if (yP_iter) yP_chunk = (await yP_iter.next()).value || {};
|
|
137
|
-
if (tH_iter) tH_chunk = (await tH_iter.next()).value || {};
|
|
138
|
-
const tStream = performance.now() - t0;
|
|
139
|
-
|
|
140
|
-
const t1 = performance.now();
|
|
141
|
-
|
|
142
|
-
// Parallelize Calc Execution for this Chunk
|
|
143
|
-
await Promise.all(streamingCalcs.map(calc =>
|
|
144
|
-
StandardExecutor.executePerUser(
|
|
145
|
-
calc, calc.manifest, dateStr, tP_chunk, yP_chunk, tH_chunk,
|
|
146
|
-
fetchedDeps, previousFetchedDeps, config, deps,
|
|
147
|
-
stats[normalizeName(calc.manifest.name)],
|
|
148
|
-
earliestDates,
|
|
149
|
-
seriesData,
|
|
150
|
-
globalRoots // <--- PASSED DOWN
|
|
151
|
-
)
|
|
152
|
-
));
|
|
153
|
-
|
|
154
|
-
const tProc = performance.now() - t1;
|
|
155
|
-
streamingCalcs.forEach(c => {
|
|
156
|
-
const s = stats[normalizeName(c.manifest.name)];
|
|
157
|
-
s.timings.stream += tStream;
|
|
158
|
-
s.timings.processing += tProc;
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
usersSinceFlush += Object.keys(tP_chunk).length;
|
|
162
|
-
const heap = v8.getHeapStatistics();
|
|
163
|
-
if (usersSinceFlush >= 500 || (heap.used_heap_size / heap.heap_size_limit) > 0.7) {
|
|
164
|
-
const res = await StandardExecutor.flushBuffer(state, dateStr, passName, config, deps, shardMap, stats, 'INTERMEDIATE', true, !hasFlushed);
|
|
165
|
-
StandardExecutor.mergeReports(aggregatedSuccess, aggregatedFailures, res);
|
|
166
|
-
hasFlushed = true;
|
|
167
|
-
usersSinceFlush = 0;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
} finally {
|
|
171
|
-
if (yP_iter?.return) await yP_iter.return();
|
|
172
|
-
if (tH_iter?.return) await tH_iter.return();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const finalRes = await StandardExecutor.flushBuffer(state, dateStr, passName, config, deps, shardMap, stats, 'FINAL', skipStatusWrite, !hasFlushed);
|
|
176
|
-
StandardExecutor.mergeReports(aggregatedSuccess, aggregatedFailures, finalRes);
|
|
177
|
-
|
|
178
|
-
return { successUpdates: aggregatedSuccess, failureReport: aggregatedFailures };
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// =========================================================================
|
|
182
|
-
// PER-USER EXECUTION (Pure Logic)
|
|
183
|
-
// =========================================================================
|
|
184
|
-
static async executePerUser(calcInstance, metadata, dateStr, portfolioData, yesterdayPortfolioData, historyData, computedDeps, prevDeps, config, deps, stats, earliestDates, seriesData, globalRoots) {
|
|
185
|
-
const { logger } = deps;
|
|
186
|
-
const targetUserType = metadata.userType;
|
|
187
|
-
const SCHEMAS = mathLayer.SCHEMAS;
|
|
188
|
-
|
|
189
|
-
let success = 0, failures = 0;
|
|
190
|
-
|
|
191
|
-
for (const [userId, todayPortfolio] of Object.entries(portfolioData)) {
|
|
192
|
-
// 1. Filter User
|
|
193
|
-
if (metadata.targetCid && String(userId) !== String(metadata.targetCid)) { stats.skippedUsers++; continue; }
|
|
194
|
-
|
|
195
|
-
// 2. Determine Type
|
|
196
|
-
let actualType = todayPortfolio._userType;
|
|
197
|
-
if (!actualType) {
|
|
198
|
-
const isRanked = globalRoots.rankings && globalRoots.rankings.some(r => String(r.CustomerId) === String(userId));
|
|
199
|
-
actualType = isRanked ? 'POPULAR_INVESTOR' : (todayPortfolio.PublicPositions ? SCHEMAS.USER_TYPES.SPECULATOR : SCHEMAS.USER_TYPES.NORMAL);
|
|
200
|
-
}
|
|
201
|
-
if (targetUserType && targetUserType !== 'all' && targetUserType !== actualType) { stats.skippedUsers++; continue; }
|
|
202
|
-
|
|
203
|
-
// 3. Resolve User Specifics from Global Data
|
|
204
|
-
const userRank = globalRoots.rankings?.find(r => String(r.CustomerId) === String(userId)) || null;
|
|
205
|
-
const userRankYest = globalRoots.rankingsYesterday?.find(r => String(r.CustomerId) === String(userId)) || null;
|
|
206
|
-
const userVerify = globalRoots.verifications?.[userId] || null;
|
|
207
|
-
|
|
208
|
-
let social = null;
|
|
209
|
-
if (globalRoots.social) {
|
|
210
|
-
// Use optional chaining to handle missing sub-objects (pi, signedIn, generic)
|
|
211
|
-
social = (actualType === 'POPULAR_INVESTOR' ? globalRoots.social.pi?.[userId] :
|
|
212
|
-
(actualType === 'SIGNED_IN_USER' ? globalRoots.social.signedIn?.[userId] : globalRoots.social.generic)) || {};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// 4. Build Context
|
|
216
|
-
const context = ContextFactory.buildPerUserContext({
|
|
217
|
-
todayPortfolio,
|
|
218
|
-
yesterdayPortfolio: yesterdayPortfolioData?.[userId] || null,
|
|
219
|
-
todayHistory: historyData?.[userId] || null,
|
|
220
|
-
userId, userType: actualType, dateStr, metadata,
|
|
221
|
-
|
|
222
|
-
// Injected Global Data
|
|
223
|
-
mappings: globalRoots.mappings,
|
|
224
|
-
piMasterList: globalRoots.piMasterList,
|
|
225
|
-
insights: globalRoots.insights,
|
|
226
|
-
socialData: social ? { today: social } : null,
|
|
227
|
-
|
|
228
|
-
// Dependency Data
|
|
229
|
-
computedDependencies: computedDeps,
|
|
230
|
-
previousComputedDependencies: prevDeps,
|
|
231
|
-
config, deps,
|
|
232
|
-
|
|
233
|
-
// Specific Lookups
|
|
234
|
-
verification: userVerify,
|
|
235
|
-
rankings: userRank,
|
|
236
|
-
yesterdayRankings: userRankYest,
|
|
237
|
-
|
|
238
|
-
// Full Access (if needed by calc)
|
|
239
|
-
allRankings: globalRoots.rankings,
|
|
240
|
-
allRankingsYesterday: globalRoots.rankingsYesterday,
|
|
241
|
-
allVerifications: globalRoots.verifications,
|
|
242
|
-
ratings: globalRoots.ratings || {},
|
|
243
|
-
pageViews: globalRoots.pageViews || {},
|
|
244
|
-
watchlistMembership: globalRoots.watchlistMembership || {},
|
|
245
|
-
alertHistory: globalRoots.alertHistory || {},
|
|
246
|
-
|
|
247
|
-
seriesData
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
if (metadata.requiresEarliestDataDate && earliestDates) {
|
|
251
|
-
if (!context.system) context.system = {};
|
|
252
|
-
context.system.earliestHistoryDate = earliestDates.history;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
await calcInstance.process(context);
|
|
257
|
-
stats.processedUsers++;
|
|
258
|
-
success++;
|
|
259
|
-
} catch (e) {
|
|
260
|
-
logger.log('WARN', `Calc ${metadata.name} failed for user ${userId}: ${e.message}`);
|
|
261
|
-
failures++;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return { success, failures };
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// =========================================================================
|
|
268
|
-
// HELPERS (Flush & Merge)
|
|
269
|
-
// =========================================================================
|
|
270
|
-
static async flushBuffer(state, dateStr, passName, config, deps, shardMap, stats, mode, skipStatus, isInitial) {
|
|
271
|
-
const transformedState = {};
|
|
272
|
-
const activeCalcs = [];
|
|
273
|
-
|
|
274
|
-
for (const [name, inst] of Object.entries(state)) {
|
|
275
|
-
// [FIX 1] Allow computations to finalize/log summary stats via getResult()
|
|
276
|
-
let data;
|
|
277
|
-
if (typeof inst.getResult === 'function') {
|
|
278
|
-
data = await inst.getResult();
|
|
279
|
-
} else {
|
|
280
|
-
data = inst.results || {};
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Track active calculations (for debugging/logging only now)
|
|
284
|
-
if (Object.keys(data).length > 0) {
|
|
285
|
-
activeCalcs.push(name);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Pivot user-date structure if needed
|
|
289
|
-
const first = Object.keys(data)[0];
|
|
290
|
-
if (first && data[first] && typeof data[first] === 'object' && /^\d{4}-\d{2}-\d{2}$/.test(Object.keys(data[first])[0])) {
|
|
291
|
-
const pivoted = {};
|
|
292
|
-
for (const [uid, dMap] of Object.entries(data)) {
|
|
293
|
-
for (const [dKey, val] of Object.entries(dMap)) {
|
|
294
|
-
if (!pivoted[dKey]) pivoted[dKey] = {};
|
|
295
|
-
pivoted[dKey][uid] = val;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
data = pivoted;
|
|
299
|
-
}
|
|
300
|
-
transformedState[name] = { manifest: inst.manifest, getResult: async () => data, _executionStats: stats[name] };
|
|
301
|
-
inst.results = {}; // Clear buffer
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const res = await commitResults(transformedState, dateStr, passName, config, deps, skipStatus, { flushMode: mode, shardIndexes: shardMap, isInitialWrite: isInitial });
|
|
305
|
-
|
|
306
|
-
// Update shardMap from result.
|
|
307
|
-
// ResultCommitter now returns the CORRECT nextShardIndex (e.g. if it wrote shard_0, it returns 1).
|
|
308
|
-
if (res.shardIndexes) Object.assign(shardMap, res.shardIndexes);
|
|
309
|
-
|
|
310
|
-
return res;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
static mergeReports(success, failure, result) {
|
|
314
|
-
if (!result) return;
|
|
315
|
-
if (result.failureReport) failure.push(...result.failureReport);
|
|
316
|
-
for (const [name, update] of Object.entries(result.successUpdates)) {
|
|
317
|
-
if (!success[name]) success[name] = update;
|
|
318
|
-
else {
|
|
319
|
-
const m = success[name].metrics;
|
|
320
|
-
const u = update.metrics;
|
|
321
|
-
m.storage.sizeBytes += u.storage.sizeBytes;
|
|
322
|
-
m.storage.keys += u.storage.keys;
|
|
323
|
-
m.storage.shardCount = Math.max(m.storage.shardCount, u.storage.shardCount);
|
|
324
|
-
if (u.execution) {
|
|
325
|
-
m.execution.timings.stream += u.execution.timings.stream;
|
|
326
|
-
m.execution.timings.processing += u.execution.timings.processing;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// =============================================================================
|
|
334
|
-
// SHARED LOADING HELPERS
|
|
335
|
-
// =============================================================================
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Pre-loads all shared global datasets required by the active calculations.
|
|
339
|
-
* Returns a consolidated object of { ratings, rankings, insights, ... }
|
|
340
|
-
*/
|
|
341
|
-
async function loadGlobalRoots(loader, dateStr, calcs, deps) {
|
|
342
|
-
const { logger } = deps;
|
|
343
|
-
const roots = {};
|
|
344
|
-
|
|
345
|
-
// 1. Identify Requirements
|
|
346
|
-
const reqs = {
|
|
347
|
-
mappings: true,
|
|
348
|
-
piMasterList: true,
|
|
349
|
-
rankings: false,
|
|
350
|
-
rankingsYesterday: false,
|
|
351
|
-
verifications: false,
|
|
352
|
-
insights: false,
|
|
353
|
-
social: false,
|
|
354
|
-
ratings: false,
|
|
355
|
-
pageViews: false,
|
|
356
|
-
watchlist: false,
|
|
357
|
-
alerts: false
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
for (const c of calcs) {
|
|
361
|
-
const deps = c.manifest.rootDataDependencies || [];
|
|
362
|
-
if (deps.includes('rankings')) reqs.rankings = true;
|
|
363
|
-
if (deps.includes('rankings') && c.manifest.isHistorical) reqs.rankingsYesterday = true;
|
|
364
|
-
if (deps.includes('verification')) reqs.verifications = true;
|
|
365
|
-
if (deps.includes('insights')) reqs.insights = true;
|
|
366
|
-
if (deps.includes('social')) reqs.social = true;
|
|
367
|
-
if (deps.includes('ratings')) reqs.ratings = true;
|
|
368
|
-
if (deps.includes('pageViews')) reqs.pageViews = true;
|
|
369
|
-
if (deps.includes('watchlist')) reqs.watchlist = true;
|
|
370
|
-
if (deps.includes('alerts')) reqs.alerts = true;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// 2. Fetch Helper
|
|
374
|
-
const fetch = async (key, method, dateArg, optional = true) => {
|
|
375
|
-
if (!reqs[key]) return;
|
|
376
|
-
try {
|
|
377
|
-
roots[key] = await loader[method](dateArg);
|
|
378
|
-
} catch (e) {
|
|
379
|
-
if (!optional) throw e;
|
|
380
|
-
logger.log('WARN', `[StandardExecutor] Optional root '${key}' failed to load: ${e.message}`);
|
|
381
|
-
roots[key] = null;
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
// 3. Execute Loads
|
|
386
|
-
await Promise.all([
|
|
387
|
-
fetch('mappings', 'loadMappings', null, false), // Always required
|
|
388
|
-
fetch('piMasterList', 'loadPIMasterList', null, false),
|
|
389
|
-
fetch('rankings', 'loadRankings', dateStr),
|
|
390
|
-
fetch('verifications', 'loadVerifications', dateStr),
|
|
391
|
-
fetch('insights', 'loadInsights', dateStr),
|
|
392
|
-
fetch('social', 'loadSocial', dateStr),
|
|
393
|
-
fetch('ratings', 'loadRatings', dateStr),
|
|
394
|
-
fetch('pageViews', 'loadPageViews', dateStr),
|
|
395
|
-
fetch('watchlist', 'loadWatchlistMembership', dateStr),
|
|
396
|
-
fetch('alerts', 'loadAlertHistory', dateStr)
|
|
397
|
-
]);
|
|
398
|
-
|
|
399
|
-
if (reqs.rankingsYesterday) {
|
|
400
|
-
const prev = new Date(new Date(dateStr).getTime() - 86400000).toISOString().slice(0, 10);
|
|
401
|
-
await fetch('rankingsYesterday', 'loadRankings', prev);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Map internal names to match loadGlobalRoots structure if needed
|
|
405
|
-
roots.watchlistMembership = roots.watchlist;
|
|
406
|
-
roots.alertHistory = roots.alerts;
|
|
407
|
-
|
|
408
|
-
return roots;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
async function loadSeriesData(loader, dateStr, calcs, config, deps) {
|
|
412
|
-
const rootReqs = {};
|
|
413
|
-
const depReqs = {}; // norm -> { days, originalName, category }
|
|
414
|
-
|
|
415
|
-
// [FIX] Calculate User Types here to pass to loadSeries
|
|
416
|
-
const requiredUserTypes = new Set(calcs.map(c => (c.userType || 'ALL').toUpperCase()));
|
|
417
|
-
const userTypeArray = requiredUserTypes.has('ALL') ? null : Array.from(requiredUserTypes);
|
|
418
|
-
|
|
419
|
-
calcs.forEach(c => {
|
|
420
|
-
if (c.manifest.rootDataSeries) {
|
|
421
|
-
Object.entries(c.manifest.rootDataSeries).forEach(([k, v]) => rootReqs[k] = Math.max(rootReqs[k]||0, v.lookback||v));
|
|
422
|
-
}
|
|
423
|
-
if (c.manifest.dependencySeries) {
|
|
424
|
-
Object.entries(c.manifest.dependencySeries).forEach(([k, v]) => {
|
|
425
|
-
const norm = normalizeName(k);
|
|
426
|
-
const days = typeof v === 'object' ? v.lookback : v;
|
|
427
|
-
if (!depReqs[norm] || depReqs[norm].days < days) {
|
|
428
|
-
depReqs[norm] = { days, originalName: k };
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
const series = { root: {}, results: {} };
|
|
435
|
-
const rootMap = {
|
|
436
|
-
alerts: 'loadAlertHistory', insights: 'loadInsights', ratings: 'loadRatings',
|
|
437
|
-
watchlist: 'loadWatchlistMembership', rankings: 'loadRankings',
|
|
438
|
-
// <--- ADDED MAPPING
|
|
439
|
-
portfolios: 'loadPortfolios'
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
await Promise.all(Object.entries(rootReqs).map(async ([key, days]) => {
|
|
443
|
-
if (rootMap[key]) {
|
|
444
|
-
// [FIX] Pass userTypeArray specifically if loading portfolios
|
|
445
|
-
const extraArgs = (key === 'portfolios') ? [userTypeArray] : [];
|
|
446
|
-
series.root[key] = (await loader.loadSeries(rootMap[key], dateStr, days, ...extraArgs)).data;
|
|
447
|
-
}
|
|
448
|
-
}));
|
|
449
|
-
|
|
450
|
-
// Build lookup from ALL computations using config.calculations
|
|
451
|
-
const depEntries = Object.values(depReqs);
|
|
452
|
-
if (depEntries.length) {
|
|
453
|
-
const depOriginalNames = depEntries.map(e => e.originalName);
|
|
454
|
-
const maxDays = Math.max(...depEntries.map(e => e.days));
|
|
455
|
-
|
|
456
|
-
const allManifests = getManifest(config.activeProductLines || [], getCalculations(config), deps);
|
|
457
|
-
const lookup = Object.fromEntries(allManifests.map(m => [normalizeName(m.name), m.category]));
|
|
458
|
-
|
|
459
|
-
series.results = await fetchResultSeries(dateStr, depOriginalNames, lookup, config, deps, maxDays);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
return series;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
module.exports = { StandardExecutor };
|