bulltrackers-module 1.0.732 → 1.0.734

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.
Files changed (106) hide show
  1. package/functions/computation-system-v2/README.md +152 -0
  2. package/functions/computation-system-v2/computations/PopularInvestorProfileMetrics.js +720 -0
  3. package/functions/computation-system-v2/computations/PopularInvestorRiskAssessment.js +176 -0
  4. package/functions/computation-system-v2/computations/PopularInvestorRiskMetrics.js +294 -0
  5. package/functions/computation-system-v2/computations/TestComputation.js +46 -0
  6. package/functions/computation-system-v2/computations/UserPortfolioSummary.js +172 -0
  7. package/functions/computation-system-v2/config/bulltrackers.config.js +317 -0
  8. package/functions/computation-system-v2/framework/core/Computation.js +73 -0
  9. package/functions/computation-system-v2/framework/core/Manifest.js +223 -0
  10. package/functions/computation-system-v2/framework/core/RuleInjector.js +53 -0
  11. package/functions/computation-system-v2/framework/core/Rules.js +231 -0
  12. package/functions/computation-system-v2/framework/core/RunAnalyzer.js +163 -0
  13. package/functions/computation-system-v2/framework/cost/CostTracker.js +154 -0
  14. package/functions/computation-system-v2/framework/data/DataFetcher.js +399 -0
  15. package/functions/computation-system-v2/framework/data/QueryBuilder.js +232 -0
  16. package/functions/computation-system-v2/framework/data/SchemaRegistry.js +287 -0
  17. package/functions/computation-system-v2/framework/execution/Orchestrator.js +498 -0
  18. package/functions/computation-system-v2/framework/execution/TaskRunner.js +35 -0
  19. package/functions/computation-system-v2/framework/execution/middleware/CostTrackerMiddleware.js +32 -0
  20. package/functions/computation-system-v2/framework/execution/middleware/LineageMiddleware.js +32 -0
  21. package/functions/computation-system-v2/framework/execution/middleware/Middleware.js +14 -0
  22. package/functions/computation-system-v2/framework/execution/middleware/ProfilerMiddleware.js +47 -0
  23. package/functions/computation-system-v2/framework/index.js +45 -0
  24. package/functions/computation-system-v2/framework/lineage/LineageTracker.js +147 -0
  25. package/functions/computation-system-v2/framework/monitoring/Profiler.js +80 -0
  26. package/functions/computation-system-v2/framework/resilience/Checkpointer.js +66 -0
  27. package/functions/computation-system-v2/framework/scheduling/ScheduleValidator.js +327 -0
  28. package/functions/computation-system-v2/framework/storage/StateRepository.js +286 -0
  29. package/functions/computation-system-v2/framework/storage/StorageManager.js +469 -0
  30. package/functions/computation-system-v2/framework/storage/index.js +9 -0
  31. package/functions/computation-system-v2/framework/testing/ComputationTester.js +86 -0
  32. package/functions/computation-system-v2/framework/utils/Graph.js +205 -0
  33. package/functions/computation-system-v2/handlers/dispatcher.js +109 -0
  34. package/functions/computation-system-v2/handlers/index.js +23 -0
  35. package/functions/computation-system-v2/handlers/onDemand.js +289 -0
  36. package/functions/computation-system-v2/handlers/scheduler.js +327 -0
  37. package/functions/computation-system-v2/index.js +163 -0
  38. package/functions/computation-system-v2/rules/index.js +49 -0
  39. package/functions/computation-system-v2/rules/instruments.js +465 -0
  40. package/functions/computation-system-v2/rules/metrics.js +304 -0
  41. package/functions/computation-system-v2/rules/portfolio.js +534 -0
  42. package/functions/computation-system-v2/rules/rankings.js +655 -0
  43. package/functions/computation-system-v2/rules/social.js +562 -0
  44. package/functions/computation-system-v2/rules/trades.js +545 -0
  45. package/functions/computation-system-v2/scripts/migrate-sectors.js +73 -0
  46. package/functions/computation-system-v2/test/test-dispatcher.js +317 -0
  47. package/functions/computation-system-v2/test/test-framework.js +500 -0
  48. package/functions/computation-system-v2/test/test-real-execution.js +166 -0
  49. package/functions/computation-system-v2/test/test-real-integration.js +194 -0
  50. package/functions/computation-system-v2/test/test-refactor-e2e.js +131 -0
  51. package/functions/computation-system-v2/test/test-results.json +31 -0
  52. package/functions/computation-system-v2/test/test-risk-metrics-computation.js +329 -0
  53. package/functions/computation-system-v2/test/test-scheduler.js +204 -0
  54. package/functions/computation-system-v2/test/test-storage.js +449 -0
  55. package/functions/orchestrator/index.js +24 -30
  56. package/index.js +8 -29
  57. package/package.json +3 -2
  58. package/functions/computation-system/WorkflowOrchestrator.js +0 -213
  59. package/functions/computation-system/config/monitoring_config.js +0 -31
  60. package/functions/computation-system/config/validation_overrides.js +0 -10
  61. package/functions/computation-system/context/ContextFactory.js +0 -143
  62. package/functions/computation-system/context/ManifestBuilder.js +0 -379
  63. package/functions/computation-system/data/AvailabilityChecker.js +0 -236
  64. package/functions/computation-system/data/CachedDataLoader.js +0 -325
  65. package/functions/computation-system/data/DependencyFetcher.js +0 -455
  66. package/functions/computation-system/executors/MetaExecutor.js +0 -279
  67. package/functions/computation-system/executors/PriceBatchExecutor.js +0 -108
  68. package/functions/computation-system/executors/StandardExecutor.js +0 -465
  69. package/functions/computation-system/helpers/computation_dispatcher.js +0 -750
  70. package/functions/computation-system/helpers/computation_worker.js +0 -375
  71. package/functions/computation-system/helpers/monitor.js +0 -64
  72. package/functions/computation-system/helpers/on_demand_helpers.js +0 -154
  73. package/functions/computation-system/layers/extractors.js +0 -1097
  74. package/functions/computation-system/layers/index.js +0 -40
  75. package/functions/computation-system/layers/mathematics.js +0 -522
  76. package/functions/computation-system/layers/profiling.js +0 -537
  77. package/functions/computation-system/layers/validators.js +0 -170
  78. package/functions/computation-system/legacy/AvailabilityCheckerOld.js +0 -388
  79. package/functions/computation-system/legacy/CachedDataLoaderOld.js +0 -357
  80. package/functions/computation-system/legacy/DependencyFetcherOld.js +0 -478
  81. package/functions/computation-system/legacy/MetaExecutorold.js +0 -364
  82. package/functions/computation-system/legacy/StandardExecutorold.js +0 -476
  83. package/functions/computation-system/legacy/computation_dispatcherold.js +0 -944
  84. package/functions/computation-system/logger/logger.js +0 -297
  85. package/functions/computation-system/persistence/ContractValidator.js +0 -81
  86. package/functions/computation-system/persistence/FirestoreUtils.js +0 -56
  87. package/functions/computation-system/persistence/ResultCommitter.js +0 -283
  88. package/functions/computation-system/persistence/ResultsValidator.js +0 -130
  89. package/functions/computation-system/persistence/RunRecorder.js +0 -142
  90. package/functions/computation-system/persistence/StatusRepository.js +0 -52
  91. package/functions/computation-system/reporter_epoch.js +0 -6
  92. package/functions/computation-system/scripts/UpdateContracts.js +0 -128
  93. package/functions/computation-system/services/SnapshotService.js +0 -148
  94. package/functions/computation-system/simulation/Fabricator.js +0 -285
  95. package/functions/computation-system/simulation/SeededRandom.js +0 -41
  96. package/functions/computation-system/simulation/SimRunner.js +0 -51
  97. package/functions/computation-system/system_epoch.js +0 -2
  98. package/functions/computation-system/tools/BuildReporter.js +0 -531
  99. package/functions/computation-system/tools/ContractDiscoverer.js +0 -144
  100. package/functions/computation-system/tools/DeploymentValidator.js +0 -536
  101. package/functions/computation-system/tools/FinalSweepReporter.js +0 -322
  102. package/functions/computation-system/topology/HashManager.js +0 -55
  103. package/functions/computation-system/topology/ManifestLoader.js +0 -47
  104. package/functions/computation-system/utils/data_loader.js +0 -675
  105. package/functions/computation-system/utils/schema_capture.js +0 -121
  106. 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 };