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
- // [FIX] Pass userTypeArray to filter yesterday's data loading
35
- fullRoot.yesterdayPortfolioRefs = await getPortfolioPartRefs(config, deps, prevStr, userTypeArray);
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
- return await StandardExecutor.streamAndProcess(dStr, state, passName, config, deps, fullRoot, rootData.portfolioRefs, rootData.historyRefs, fetchedDeps, previousFetchedDeps, skipStatusWrite, userTypeArray);
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] Accepts requiredUserTypes param
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 = (portfolioRefs?.length || 0) + (historyRefs?.length || 0);
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] Pass requiredUserTypes to streamPortfolioData
95
- // Note: portfolioRefs (for today) might be null if not pre-fetched, allowing streamPortfolioData to fetch filtered refs
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 needsTradingHistory = streamingCalcs.some(c => c.manifest.rootDataDependencies.includes('history'));
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
- // ... (No changes to flushBuffer)
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.loadPIMasterList(); // [NEW] Loaded globally
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.loadVerifications() : null;
241
- const rankings = metadata.rootDataDependencies?.includes('rankings') ? await loader.loadRankings(dateStr) : null;
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
- // Assuming CachedDataLoader handles caching for efficiency
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.loadSocial(dateStr) : null;
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.loadRatings(dateStr);
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.loadPageViews(dateStr);
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.loadAlertHistory(dateStr);
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.536",
3
+ "version": "1.0.537",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [