bulltrackers-module 1.0.536 → 1.0.537
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.
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* }
|
|
6
6
|
*/
|
|
7
7
|
const { normalizeName, getEarliestDataDates } = require('../utils/utils');
|
|
8
|
-
const { streamPortfolioData, streamHistoryData, getPortfolioPartRefs } = require('../utils/data_loader');
|
|
8
|
+
const { streamPortfolioData, streamHistoryData, getPortfolioPartRefs, getHistoryPartRefs } = require('../utils/data_loader');
|
|
9
9
|
const { CachedDataLoader } = require('../data/CachedDataLoader');
|
|
10
10
|
const { ContextFactory } = require('../context/ContextFactory');
|
|
11
11
|
const { commitResults } = require('../persistence/ResultCommitter');
|
|
@@ -24,15 +24,31 @@ class StandardExecutor {
|
|
|
24
24
|
const type = (c.userType || 'ALL').toUpperCase();
|
|
25
25
|
requiredUserTypes.add(type);
|
|
26
26
|
});
|
|
27
|
-
// If any calc requires 'ALL' (or has no userType), we fetch everything
|
|
28
27
|
const userTypeArray = requiredUserTypes.has('ALL') ? null : Array.from(requiredUserTypes);
|
|
29
28
|
|
|
29
|
+
// [OPTIMIZATION] Check for Target CID in manifests (On-Demand Optimization)
|
|
30
|
+
// If present, we will filter all data streams to strictly this user
|
|
31
|
+
const targetCid = calcs.find(c => c.targetCid)?.targetCid || calcs.find(c => c.manifest?.targetCid)?.manifest?.targetCid;
|
|
32
|
+
if (targetCid) {
|
|
33
|
+
logger.log('INFO', `[StandardExecutor] Running in Targeted Mode for CID: ${targetCid}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
const fullRoot = { ...rootData };
|
|
31
37
|
if (calcs.some(c => c.isHistorical)) {
|
|
32
38
|
const prev = new Date(date); prev.setUTCDate(prev.getUTCDate() - 1);
|
|
33
39
|
const prevStr = prev.toISOString().slice(0, 10);
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
|
|
41
|
+
// Fetch yesterday's refs
|
|
42
|
+
let yRefs = await getPortfolioPartRefs(config, deps, prevStr, userTypeArray);
|
|
43
|
+
|
|
44
|
+
// [OPTIMIZATION] Filter Yesterday's Refs if targetCid is set
|
|
45
|
+
if (targetCid && yRefs) {
|
|
46
|
+
const originalCount = yRefs.length;
|
|
47
|
+
yRefs = yRefs.filter(r => !r.cid || String(r.cid) === String(targetCid));
|
|
48
|
+
logger.log('INFO', `[StandardExecutor] Filtered Yesterday's Refs: ${originalCount} -> ${yRefs.length}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
fullRoot.yesterdayPortfolioRefs = yRefs;
|
|
36
52
|
}
|
|
37
53
|
|
|
38
54
|
const state = {};
|
|
@@ -46,18 +62,48 @@ class StandardExecutor {
|
|
|
46
62
|
} catch (e) { logger.log('WARN', `Failed to init ${c.name}`); }
|
|
47
63
|
}
|
|
48
64
|
|
|
49
|
-
|
|
65
|
+
// Pass targetCid to streamAndProcess
|
|
66
|
+
return await StandardExecutor.streamAndProcess(
|
|
67
|
+
dStr, state, passName, config, deps, fullRoot,
|
|
68
|
+
rootData.portfolioRefs, rootData.historyRefs,
|
|
69
|
+
fetchedDeps, previousFetchedDeps, skipStatusWrite,
|
|
70
|
+
userTypeArray, targetCid
|
|
71
|
+
);
|
|
50
72
|
}
|
|
51
73
|
|
|
52
|
-
// [UPDATED]
|
|
53
|
-
static async streamAndProcess(dateStr, state, passName, config, deps, rootData, portfolioRefs, historyRefs, fetchedDeps, previousFetchedDeps, skipStatusWrite, requiredUserTypes = null) {
|
|
74
|
+
// [UPDATED] Added targetCid param
|
|
75
|
+
static async streamAndProcess(dateStr, state, passName, config, deps, rootData, portfolioRefs, historyRefs, fetchedDeps, previousFetchedDeps, skipStatusWrite, requiredUserTypes = null, targetCid = null) {
|
|
54
76
|
const { logger } = deps;
|
|
55
77
|
const calcs = Object.values(state).filter(c => c && c.manifest);
|
|
56
78
|
const streamingCalcs = calcs.filter(c => c.manifest.rootDataDependencies.includes('portfolio') || c.manifest.rootDataDependencies.includes('history'));
|
|
57
79
|
|
|
58
80
|
if (streamingCalcs.length === 0) return { successUpdates: {}, failureReport: [] };
|
|
81
|
+
|
|
82
|
+
// --- 1. Resolve and Filter Portfolio Refs (Today) ---
|
|
83
|
+
let effectivePortfolioRefs = portfolioRefs;
|
|
84
|
+
if (!effectivePortfolioRefs) {
|
|
85
|
+
// If refs weren't provided by AvailabilityChecker, fetch them now
|
|
86
|
+
effectivePortfolioRefs = await getPortfolioPartRefs(config, deps, dateStr, requiredUserTypes);
|
|
87
|
+
}
|
|
88
|
+
if (targetCid && effectivePortfolioRefs) {
|
|
89
|
+
// Filter: Keep only refs that match the CID (or Legacy refs without CID)
|
|
90
|
+
effectivePortfolioRefs = effectivePortfolioRefs.filter(r => !r.cid || String(r.cid) === String(targetCid));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// --- 2. Resolve and Filter History Refs ---
|
|
94
|
+
let effectiveHistoryRefs = historyRefs;
|
|
95
|
+
const needsTradingHistory = streamingCalcs.some(c => c.manifest.rootDataDependencies.includes('history'));
|
|
96
|
+
|
|
97
|
+
if (needsTradingHistory) {
|
|
98
|
+
if (!effectiveHistoryRefs) {
|
|
99
|
+
effectiveHistoryRefs = await getHistoryPartRefs(config, deps, dateStr, requiredUserTypes);
|
|
100
|
+
}
|
|
101
|
+
if (targetCid && effectiveHistoryRefs) {
|
|
102
|
+
effectiveHistoryRefs = effectiveHistoryRefs.filter(r => !r.cid || String(r.cid) === String(targetCid));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
59
105
|
|
|
60
|
-
let totalReadOps = (
|
|
106
|
+
let totalReadOps = (effectivePortfolioRefs?.length || 0) + (effectiveHistoryRefs?.length || 0);
|
|
61
107
|
if (rootData.yesterdayPortfolioRefs) totalReadOps += rootData.yesterdayPortfolioRefs.length;
|
|
62
108
|
totalReadOps += 2;
|
|
63
109
|
|
|
@@ -83,6 +129,7 @@ class StandardExecutor {
|
|
|
83
129
|
const setupDuration = performance.now() - startSetup;
|
|
84
130
|
Object.keys(executionStats).forEach(name => executionStats[name].timings.setup += setupDuration);
|
|
85
131
|
|
|
132
|
+
// Yesterday's Refs are already filtered in run()
|
|
86
133
|
const prevDate = new Date(dateStr + 'T00:00:00Z'); prevDate.setUTCDate(prevDate.getUTCDate() - 1);
|
|
87
134
|
const prevDateStr = prevDate.toISOString().slice(0, 10);
|
|
88
135
|
|
|
@@ -91,17 +138,13 @@ class StandardExecutor {
|
|
|
91
138
|
earliestDates = await getEarliestDataDates(config, deps);
|
|
92
139
|
}
|
|
93
140
|
|
|
94
|
-
// [FIX]
|
|
95
|
-
|
|
96
|
-
const tP_iter = streamPortfolioData(config, deps, dateStr, portfolioRefs, requiredUserTypes);
|
|
141
|
+
// [FIX] Use effective/filtered refs
|
|
142
|
+
const tP_iter = streamPortfolioData(config, deps, dateStr, effectivePortfolioRefs, requiredUserTypes);
|
|
97
143
|
|
|
98
144
|
const needsYesterdayPortfolio = streamingCalcs.some(c => c.manifest.isHistorical);
|
|
99
|
-
// Yesterday's refs were already filtered in run(), so we pass them directly
|
|
100
145
|
const yP_iter = (needsYesterdayPortfolio && rootData.yesterdayPortfolioRefs) ? streamPortfolioData(config, deps, prevDateStr, rootData.yesterdayPortfolioRefs) : null;
|
|
101
146
|
|
|
102
|
-
const
|
|
103
|
-
// [FIX] Pass requiredUserTypes to streamHistoryData
|
|
104
|
-
const tH_iter = (needsTradingHistory) ? streamHistoryData(config, deps, dateStr, historyRefs, requiredUserTypes) : null;
|
|
147
|
+
const tH_iter = (needsTradingHistory) ? streamHistoryData(config, deps, dateStr, effectiveHistoryRefs, requiredUserTypes) : null;
|
|
105
148
|
|
|
106
149
|
let yP_chunk = {}, tH_chunk = {};
|
|
107
150
|
let usersSinceLastFlush = 0;
|
|
@@ -159,7 +202,7 @@ class StandardExecutor {
|
|
|
159
202
|
|
|
160
203
|
// ... rest of the file (flushBuffer, mergeReports, executePerUser) ...
|
|
161
204
|
static async flushBuffer(state, dateStr, passName, config, deps, shardIndexMap, executionStats, mode, skipStatusWrite, isInitialWrite = false) {
|
|
162
|
-
|
|
205
|
+
const { logger } = deps;
|
|
163
206
|
const transformedState = {};
|
|
164
207
|
for (const [name, inst] of Object.entries(state)) {
|
|
165
208
|
const rawResult = inst.results || {};
|
|
@@ -197,7 +240,6 @@ class StandardExecutor {
|
|
|
197
240
|
}
|
|
198
241
|
|
|
199
242
|
static mergeReports(successAcc, failureAcc, newResult) {
|
|
200
|
-
// ... (No changes to mergeReports)
|
|
201
243
|
if (!newResult) return;
|
|
202
244
|
for (const [name, update] of Object.entries(newResult.successUpdates)) {
|
|
203
245
|
if (!successAcc[name]) {
|
|
@@ -232,24 +274,23 @@ class StandardExecutor {
|
|
|
232
274
|
const targetUserType = metadata.userType;
|
|
233
275
|
// [NEW] Always load Global Helpers
|
|
234
276
|
const mappings = await loader.loadMappings();
|
|
235
|
-
const piMasterList = await loader.
|
|
277
|
+
const piMasterList = await loader.loadPopularInvestorMasterList(config, deps); // [NEW] Loaded globally
|
|
236
278
|
const SCHEMAS = mathLayer.SCHEMAS;
|
|
237
279
|
|
|
238
280
|
// 1. Load Root Data
|
|
239
281
|
const insights = metadata.rootDataDependencies?.includes('insights') ? { today: await loader.loadInsights(dateStr) } : null;
|
|
240
|
-
const verifications = metadata.rootDataDependencies?.includes('verification') ? await loader.
|
|
241
|
-
const rankings = metadata.rootDataDependencies?.includes('rankings') ? await loader.
|
|
282
|
+
const verifications = metadata.rootDataDependencies?.includes('verification') ? await loader.loadVerificationProfiles(config, deps) : null;
|
|
283
|
+
const rankings = metadata.rootDataDependencies?.includes('rankings') ? await loader.loadPopularInvestorRankings(config, deps, dateStr) : null;
|
|
242
284
|
|
|
243
285
|
// [FIX] Load Yesterday's Rankings if isHistorical is true
|
|
244
286
|
let yesterdayRankings = null;
|
|
245
287
|
if (metadata.rootDataDependencies?.includes('rankings') && metadata.isHistorical) {
|
|
246
288
|
const prevDate = new Date(dateStr); prevDate.setUTCDate(prevDate.getUTCDate() - 1);
|
|
247
289
|
const prevStr = prevDate.toISOString().slice(0, 10);
|
|
248
|
-
|
|
249
|
-
yesterdayRankings = await loader.loadRankings(prevStr);
|
|
290
|
+
yesterdayRankings = await loader.loadPopularInvestorRankings(config, deps, prevStr);
|
|
250
291
|
}
|
|
251
292
|
|
|
252
|
-
const socialContainer = metadata.rootDataDependencies?.includes('social') ? await loader.
|
|
293
|
+
const socialContainer = metadata.rootDataDependencies?.includes('social') ? await loader.loadDailySocialPostInsights(config, deps, dateStr) : null;
|
|
253
294
|
|
|
254
295
|
// [NEW] Load New Root Data Types for Profile Metrics
|
|
255
296
|
// [FIX] Enforce canHaveMissingRoots
|
|
@@ -258,14 +299,13 @@ class StandardExecutor {
|
|
|
258
299
|
let ratings = null;
|
|
259
300
|
if (metadata.rootDataDependencies?.includes('ratings')) {
|
|
260
301
|
try {
|
|
261
|
-
ratings = await loader.
|
|
302
|
+
ratings = await loader.loadPIRatings(config, deps, dateStr);
|
|
262
303
|
} catch (e) {
|
|
263
304
|
if (!allowMissing) {
|
|
264
305
|
throw new Error(`[StandardExecutor] Required root 'ratings' failed to load for ${metadata.name}: ${e.message}`);
|
|
265
306
|
}
|
|
266
307
|
ratings = null;
|
|
267
308
|
}
|
|
268
|
-
// Check if result is null (clean fetch but empty/missing doc)
|
|
269
309
|
if (!ratings && !allowMissing) {
|
|
270
310
|
throw new Error(`[StandardExecutor] Required root 'ratings' is missing for ${metadata.name}`);
|
|
271
311
|
}
|
|
@@ -274,7 +314,7 @@ class StandardExecutor {
|
|
|
274
314
|
let pageViews = null;
|
|
275
315
|
if (metadata.rootDataDependencies?.includes('pageViews')) {
|
|
276
316
|
try {
|
|
277
|
-
pageViews = await loader.
|
|
317
|
+
pageViews = await loader.loadPIPageViews(config, deps, dateStr);
|
|
278
318
|
} catch (e) {
|
|
279
319
|
if (!allowMissing) {
|
|
280
320
|
throw new Error(`[StandardExecutor] Required root 'pageViews' failed to load for ${metadata.name}: ${e.message}`);
|
|
@@ -289,7 +329,7 @@ class StandardExecutor {
|
|
|
289
329
|
let watchlistMembership = null;
|
|
290
330
|
if (metadata.rootDataDependencies?.includes('watchlist')) {
|
|
291
331
|
try {
|
|
292
|
-
watchlistMembership = await loader.loadWatchlistMembership(dateStr);
|
|
332
|
+
watchlistMembership = await loader.loadWatchlistMembership(config, deps, dateStr);
|
|
293
333
|
} catch (e) {
|
|
294
334
|
if (!allowMissing) {
|
|
295
335
|
throw new Error(`[StandardExecutor] Required root 'watchlist' failed to load for ${metadata.name}: ${e.message}`);
|
|
@@ -304,7 +344,7 @@ class StandardExecutor {
|
|
|
304
344
|
let alertHistory = null;
|
|
305
345
|
if (metadata.rootDataDependencies?.includes('alerts')) {
|
|
306
346
|
try {
|
|
307
|
-
alertHistory = await loader.
|
|
347
|
+
alertHistory = await loader.loadPIAlertHistory(config, deps, dateStr);
|
|
308
348
|
} catch (e) {
|
|
309
349
|
if (!allowMissing) {
|
|
310
350
|
throw new Error(`[StandardExecutor] Required root 'alerts' failed to load for ${metadata.name}: ${e.message}`);
|
|
@@ -350,7 +390,6 @@ class StandardExecutor {
|
|
|
350
390
|
|
|
351
391
|
const userVerification = verifications ? verifications[userId] : null;
|
|
352
392
|
|
|
353
|
-
// [FIX] Extract current AND yesterday's rank entry for this user
|
|
354
393
|
const userRanking = rankings ? (rankings.find(r => String(r.CustomerId) === String(userId)) || null) : null;
|
|
355
394
|
const userRankingYesterday = yesterdayRankings ? (yesterdayRankings.find(r => String(r.CustomerId) === String(userId)) || null) : null;
|
|
356
395
|
|
|
@@ -373,23 +412,19 @@ class StandardExecutor {
|
|
|
373
412
|
config, deps,
|
|
374
413
|
verification: userVerification,
|
|
375
414
|
|
|
376
|
-
// [FIX] Pass both ranking entries
|
|
377
415
|
rankings: userRanking,
|
|
378
416
|
yesterdayRankings: userRankingYesterday,
|
|
379
417
|
|
|
380
|
-
// [FIX] Pass both global lists
|
|
381
418
|
allRankings: rankings,
|
|
382
419
|
allRankingsYesterday: yesterdayRankings,
|
|
383
420
|
|
|
384
421
|
allVerifications: verifications,
|
|
385
422
|
|
|
386
|
-
// [NEW] Pass New Root Data Types for Profile Metrics
|
|
387
423
|
ratings: ratings || {},
|
|
388
424
|
pageViews: pageViews || {},
|
|
389
425
|
watchlistMembership: watchlistMembership || {},
|
|
390
426
|
alertHistory: alertHistory || {},
|
|
391
427
|
|
|
392
|
-
// [NEW] Pass Master List
|
|
393
428
|
piMasterList,
|
|
394
429
|
});
|
|
395
430
|
|