bulltrackers-module 1.0.702 → 1.0.703
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.
|
@@ -119,7 +119,8 @@ class CachedDataLoader {
|
|
|
119
119
|
// =========================================================================
|
|
120
120
|
// GENERIC LOADER HELPER
|
|
121
121
|
// =========================================================================
|
|
122
|
-
|
|
122
|
+
// [FIX] Accepts ...args to pass down filters (like requiredUserTypes)
|
|
123
|
+
async _loadGeneric(methodName, key, ...args) {
|
|
123
124
|
const def = LOADER_DEFINITIONS[methodName];
|
|
124
125
|
if (!def) throw new Error(`Unknown loader method: ${methodName}`);
|
|
125
126
|
|
|
@@ -132,7 +133,7 @@ class CachedDataLoader {
|
|
|
132
133
|
this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Loading '${def.cache}' from: ${collection}/${key}`);
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
const promise = def.fn(this.config, this.deps, key);
|
|
136
|
+
const promise = def.fn(this.config, this.deps, key, ...args);
|
|
136
137
|
cacheMap.set(key, promise);
|
|
137
138
|
return promise;
|
|
138
139
|
}
|
|
@@ -151,8 +152,8 @@ class CachedDataLoader {
|
|
|
151
152
|
async loadWatchlistMembership(dateStr) { return this._loadGeneric('loadWatchlistMembership', dateStr); }
|
|
152
153
|
async loadAlertHistory(dateStr) { return this._loadGeneric('loadAlertHistory' , dateStr); }
|
|
153
154
|
async loadPIWatchlistData(piCid) { return this._loadGeneric('loadPIWatchlistData' , String(piCid)); }
|
|
154
|
-
// <--- ADDED PORTFOLIOS ACCESSOR
|
|
155
|
-
async loadPortfolios(dateStr)
|
|
155
|
+
// <--- ADDED PORTFOLIOS ACCESSOR with extra arg
|
|
156
|
+
async loadPortfolios(dateStr, userTypes) { return this._loadGeneric('loadPortfolios', dateStr, userTypes); }
|
|
156
157
|
|
|
157
158
|
// =========================================================================
|
|
158
159
|
// SPECIALIZED LOADERS (Non-Standard Patterns)
|
|
@@ -204,13 +205,14 @@ class CachedDataLoader {
|
|
|
204
205
|
/**
|
|
205
206
|
* Optimistically loads data series using Batch Reads (db.getAll).
|
|
206
207
|
* Uses Availability Index to minimize costs.
|
|
208
|
+
* [FIX] Now accepts ...args to pass context (e.g. requiredUserTypes)
|
|
207
209
|
*/
|
|
208
|
-
async loadSeries(loaderMethod, dateStr, lookbackDays) {
|
|
210
|
+
async loadSeries(loaderMethod, dateStr, lookbackDays, ...args) {
|
|
209
211
|
const def = LOADER_DEFINITIONS[loaderMethod];
|
|
210
212
|
if (!def) throw new Error(`[CachedDataLoader] Unknown series method ${loaderMethod}`);
|
|
211
213
|
|
|
212
214
|
// Fallback to legacy loop if method isn't configured for batching (missing config/flag)
|
|
213
|
-
if (!def.configKey || !def.flag) return this._loadSeriesLegacy(loaderMethod, dateStr, lookbackDays);
|
|
215
|
+
if (!def.configKey || !def.flag) return this._loadSeriesLegacy(loaderMethod, dateStr, lookbackDays, ...args);
|
|
214
216
|
|
|
215
217
|
// 1. Calculate Date Range
|
|
216
218
|
const endDate = new Date(dateStr);
|
|
@@ -265,7 +267,7 @@ class CachedDataLoader {
|
|
|
265
267
|
});
|
|
266
268
|
} catch (err) {
|
|
267
269
|
console.warn(`[CachedDataLoader] Batch failed: ${err.message}. Legacy fallback.`);
|
|
268
|
-
return this._loadSeriesLegacy(loaderMethod, dateStr, lookbackDays);
|
|
270
|
+
return this._loadSeriesLegacy(loaderMethod, dateStr, lookbackDays, ...args);
|
|
269
271
|
}
|
|
270
272
|
}
|
|
271
273
|
|
|
@@ -277,7 +279,7 @@ class CachedDataLoader {
|
|
|
277
279
|
};
|
|
278
280
|
}
|
|
279
281
|
|
|
280
|
-
async _loadSeriesLegacy(loaderMethod, dateStr, lookbackDays) {
|
|
282
|
+
async _loadSeriesLegacy(loaderMethod, dateStr, lookbackDays, ...args) {
|
|
281
283
|
const results = {};
|
|
282
284
|
const promises = [];
|
|
283
285
|
const endDate = new Date(dateStr);
|
|
@@ -286,7 +288,8 @@ class CachedDataLoader {
|
|
|
286
288
|
const d = new Date(endDate);
|
|
287
289
|
d.setUTCDate(d.getUTCDate() - i);
|
|
288
290
|
const dStr = d.toISOString().slice(0, 10);
|
|
289
|
-
|
|
291
|
+
// [FIX] Pass args (e.g. requiredUserTypes) to the loader method
|
|
292
|
+
promises.push(this[loaderMethod](dStr, ...args).then(data => data ? results[dStr] = data : null).catch(() => null));
|
|
290
293
|
}
|
|
291
294
|
|
|
292
295
|
await Promise.all(promises);
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Executor for "Standard" (User-Level) calculations.
|
|
3
3
|
* REFACTORED: Hoisted data loading, centralized Series/Root logic.
|
|
4
|
-
* UPDATED: Added Master List Driver for PI calculations to ensure full coverage.
|
|
5
4
|
*/
|
|
6
5
|
const { normalizeName, getEarliestDataDates } = require('../utils/utils');
|
|
7
|
-
const { streamPortfolioData, streamHistoryData, getPortfolioPartRefs, getHistoryPartRefs
|
|
6
|
+
const { streamPortfolioData, streamHistoryData, getPortfolioPartRefs, getHistoryPartRefs } = require('../utils/data_loader');
|
|
8
7
|
const { CachedDataLoader } = require('../data/CachedDataLoader');
|
|
9
8
|
const { ContextFactory } = require('../context/ContextFactory');
|
|
10
9
|
const { commitResults } = require('../persistence/ResultCommitter');
|
|
@@ -81,6 +80,8 @@ class StandardExecutor {
|
|
|
81
80
|
const loader = new CachedDataLoader(config, deps);
|
|
82
81
|
|
|
83
82
|
// --- 1. PRE-LOAD GLOBAL DATA (Hoisted) ---
|
|
83
|
+
// Load all "Singleton" datasets once (Ratings, Rankings, Series, Mappings)
|
|
84
|
+
// instead of checking/loading per-user inside the loop.
|
|
84
85
|
const startSetup = performance.now();
|
|
85
86
|
|
|
86
87
|
const [
|
|
@@ -116,37 +117,8 @@ class StandardExecutor {
|
|
|
116
117
|
shardMap[normalizeName(c.manifest.name)] = 0;
|
|
117
118
|
});
|
|
118
119
|
|
|
119
|
-
// --- 3.
|
|
120
|
-
|
|
121
|
-
const isPiOnly = requiredUserTypes && requiredUserTypes.length === 1 && requiredUserTypes[0] === 'POPULAR_INVESTOR';
|
|
122
|
-
|
|
123
|
-
// STRATEGY A: MASTER LIST DRIVER (For PIs, ensuring we hit every ID in the list, even if missing today)
|
|
124
|
-
if (isPiOnly && globalRoots.piMasterList && !targetCid) {
|
|
125
|
-
logger.log('INFO', `[StandardExecutor] 🚀 Driving execution via Master List (${Object.keys(globalRoots.piMasterList).length} PIs)`);
|
|
126
|
-
|
|
127
|
-
// Load Today's Map Fully (Small enough for PIs)
|
|
128
|
-
const todayMap = await loadFullDayMap(config, deps, effPortRefs, dateStr);
|
|
129
|
-
|
|
130
|
-
// Create a generator that yields chunks from the Master List keys
|
|
131
|
-
tP_iter = (async function* () {
|
|
132
|
-
const allCids = Object.keys(globalRoots.piMasterList);
|
|
133
|
-
const batchSize = config.partRefBatchSize || 50;
|
|
134
|
-
for (let i = 0; i < allCids.length; i += batchSize) {
|
|
135
|
-
const batchCids = allCids.slice(i, i + batchSize);
|
|
136
|
-
const chunk = {};
|
|
137
|
-
batchCids.forEach(cid => {
|
|
138
|
-
// If missing today, provide a stub so the Calc can look back in History
|
|
139
|
-
chunk[cid] = todayMap[cid] || { _userType: 'POPULAR_INVESTOR', _isMissingToday: true };
|
|
140
|
-
});
|
|
141
|
-
yield chunk;
|
|
142
|
-
}
|
|
143
|
-
})();
|
|
144
|
-
}
|
|
145
|
-
// STRATEGY B: STANDARD STREAM (Iterate whatever is in Firestore/GCS for today)
|
|
146
|
-
else {
|
|
147
|
-
tP_iter = streamPortfolioData(config, deps, dateStr, effPortRefs, requiredUserTypes);
|
|
148
|
-
}
|
|
149
|
-
|
|
120
|
+
// --- 3. STREAM EXECUTION ---
|
|
121
|
+
const tP_iter = streamPortfolioData(config, deps, dateStr, effPortRefs, requiredUserTypes);
|
|
150
122
|
const yP_iter = (streamingCalcs.some(c => c.manifest.isHistorical) && rootData.yesterdayPortfolioRefs)
|
|
151
123
|
? streamPortfolioData(config, deps, new Date(new Date(dateStr).getTime() - 86400000).toISOString().slice(0, 10), rootData.yesterdayPortfolioRefs)
|
|
152
124
|
: null;
|
|
@@ -175,7 +147,7 @@ class StandardExecutor {
|
|
|
175
147
|
stats[normalizeName(calc.manifest.name)],
|
|
176
148
|
earliestDates,
|
|
177
149
|
seriesData,
|
|
178
|
-
globalRoots
|
|
150
|
+
globalRoots // <--- PASSED DOWN
|
|
179
151
|
)
|
|
180
152
|
));
|
|
181
153
|
|
|
@@ -196,8 +168,8 @@ class StandardExecutor {
|
|
|
196
168
|
}
|
|
197
169
|
}
|
|
198
170
|
} finally {
|
|
199
|
-
if (yP_iter?.return
|
|
200
|
-
if (tH_iter?.return
|
|
171
|
+
if (yP_iter?.return) await yP_iter.return();
|
|
172
|
+
if (tH_iter?.return) await tH_iter.return();
|
|
201
173
|
}
|
|
202
174
|
|
|
203
175
|
const finalRes = await StandardExecutor.flushBuffer(state, dateStr, passName, config, deps, shardMap, stats, 'FINAL', skipStatusWrite, !hasFlushed);
|
|
@@ -207,7 +179,7 @@ class StandardExecutor {
|
|
|
207
179
|
}
|
|
208
180
|
|
|
209
181
|
// =========================================================================
|
|
210
|
-
// PER-USER EXECUTION
|
|
182
|
+
// PER-USER EXECUTION (Pure Logic)
|
|
211
183
|
// =========================================================================
|
|
212
184
|
static async executePerUser(calcInstance, metadata, dateStr, portfolioData, yesterdayPortfolioData, historyData, computedDeps, prevDeps, config, deps, stats, earliestDates, seriesData, globalRoots) {
|
|
213
185
|
const { logger } = deps;
|
|
@@ -217,21 +189,18 @@ class StandardExecutor {
|
|
|
217
189
|
let success = 0, failures = 0;
|
|
218
190
|
|
|
219
191
|
for (const [userId, todayPortfolio] of Object.entries(portfolioData)) {
|
|
192
|
+
// 1. Filter User
|
|
220
193
|
if (metadata.targetCid && String(userId) !== String(metadata.targetCid)) { stats.skippedUsers++; continue; }
|
|
221
194
|
|
|
222
|
-
// 2. Determine Type
|
|
195
|
+
// 2. Determine Type
|
|
223
196
|
let actualType = todayPortfolio._userType;
|
|
224
197
|
if (!actualType) {
|
|
225
|
-
// If driving from Master List, we know they are PIs even if missing today
|
|
226
198
|
const isRanked = globalRoots.rankings && globalRoots.rankings.some(r => String(r.CustomerId) === String(userId));
|
|
227
|
-
|
|
228
|
-
actualType = 'POPULAR_INVESTOR';
|
|
229
|
-
} else {
|
|
230
|
-
actualType = isRanked ? 'POPULAR_INVESTOR' : (todayPortfolio.PublicPositions ? SCHEMAS.USER_TYPES.SPECULATOR : SCHEMAS.USER_TYPES.NORMAL);
|
|
231
|
-
}
|
|
199
|
+
actualType = isRanked ? 'POPULAR_INVESTOR' : (todayPortfolio.PublicPositions ? SCHEMAS.USER_TYPES.SPECULATOR : SCHEMAS.USER_TYPES.NORMAL);
|
|
232
200
|
}
|
|
233
201
|
if (targetUserType && targetUserType !== 'all' && targetUserType !== actualType) { stats.skippedUsers++; continue; }
|
|
234
202
|
|
|
203
|
+
// 3. Resolve User Specifics from Global Data
|
|
235
204
|
const userRank = globalRoots.rankings?.find(r => String(r.CustomerId) === String(userId)) || null;
|
|
236
205
|
const userRankYest = globalRoots.rankingsYesterday?.find(r => String(r.CustomerId) === String(userId)) || null;
|
|
237
206
|
const userVerify = globalRoots.verifications?.[userId] || null;
|
|
@@ -242,21 +211,30 @@ class StandardExecutor {
|
|
|
242
211
|
(actualType === 'SIGNED_IN_USER' ? globalRoots.social.signedIn[userId] : globalRoots.social.generic)) || {};
|
|
243
212
|
}
|
|
244
213
|
|
|
214
|
+
// 4. Build Context
|
|
245
215
|
const context = ContextFactory.buildPerUserContext({
|
|
246
|
-
todayPortfolio
|
|
216
|
+
todayPortfolio,
|
|
247
217
|
yesterdayPortfolio: yesterdayPortfolioData?.[userId] || null,
|
|
248
218
|
todayHistory: historyData?.[userId] || null,
|
|
249
219
|
userId, userType: actualType, dateStr, metadata,
|
|
220
|
+
|
|
221
|
+
// Injected Global Data
|
|
250
222
|
mappings: globalRoots.mappings,
|
|
251
223
|
piMasterList: globalRoots.piMasterList,
|
|
252
224
|
insights: globalRoots.insights,
|
|
253
225
|
socialData: social ? { today: social } : null,
|
|
226
|
+
|
|
227
|
+
// Dependency Data
|
|
254
228
|
computedDependencies: computedDeps,
|
|
255
229
|
previousComputedDependencies: prevDeps,
|
|
256
230
|
config, deps,
|
|
231
|
+
|
|
232
|
+
// Specific Lookups
|
|
257
233
|
verification: userVerify,
|
|
258
234
|
rankings: userRank,
|
|
259
235
|
yesterdayRankings: userRankYest,
|
|
236
|
+
|
|
237
|
+
// Full Access (if needed by calc)
|
|
260
238
|
allRankings: globalRoots.rankings,
|
|
261
239
|
allRankingsYesterday: globalRoots.rankingsYesterday,
|
|
262
240
|
allVerifications: globalRoots.verifications,
|
|
@@ -264,6 +242,7 @@ class StandardExecutor {
|
|
|
264
242
|
pageViews: globalRoots.pageViews || {},
|
|
265
243
|
watchlistMembership: globalRoots.watchlistMembership || {},
|
|
266
244
|
alertHistory: globalRoots.alertHistory || {},
|
|
245
|
+
|
|
267
246
|
seriesData
|
|
268
247
|
});
|
|
269
248
|
|
|
@@ -285,12 +264,13 @@ class StandardExecutor {
|
|
|
285
264
|
}
|
|
286
265
|
|
|
287
266
|
// =========================================================================
|
|
288
|
-
//
|
|
267
|
+
// HELPERS (Flush & Merge)
|
|
289
268
|
// =========================================================================
|
|
290
269
|
static async flushBuffer(state, dateStr, passName, config, deps, shardMap, stats, mode, skipStatus, isInitial) {
|
|
291
270
|
const transformedState = {};
|
|
292
271
|
for (const [name, inst] of Object.entries(state)) {
|
|
293
272
|
let data = inst.results || {};
|
|
273
|
+
// Pivot user-date structure if needed
|
|
294
274
|
const first = Object.keys(data)[0];
|
|
295
275
|
if (first && data[first] && typeof data[first] === 'object' && /^\d{4}-\d{2}-\d{2}$/.test(Object.keys(data[first])[0])) {
|
|
296
276
|
const pivoted = {};
|
|
@@ -303,7 +283,7 @@ class StandardExecutor {
|
|
|
303
283
|
data = pivoted;
|
|
304
284
|
}
|
|
305
285
|
transformedState[name] = { manifest: inst.manifest, getResult: async () => data, _executionStats: stats[name] };
|
|
306
|
-
inst.results = {};
|
|
286
|
+
inst.results = {}; // Clear buffer
|
|
307
287
|
}
|
|
308
288
|
const res = await commitResults(transformedState, dateStr, passName, config, deps, skipStatus, { flushMode: mode, shardIndexes: shardMap, isInitialWrite: isInitial });
|
|
309
289
|
if (res.shardIndexes) Object.assign(shardMap, res.shardIndexes);
|
|
@@ -330,13 +310,31 @@ class StandardExecutor {
|
|
|
330
310
|
}
|
|
331
311
|
}
|
|
332
312
|
|
|
313
|
+
// =============================================================================
|
|
314
|
+
// SHARED LOADING HELPERS
|
|
315
|
+
// =============================================================================
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Pre-loads all shared global datasets required by the active calculations.
|
|
319
|
+
* Returns a consolidated object of { ratings, rankings, insights, ... }
|
|
320
|
+
*/
|
|
333
321
|
async function loadGlobalRoots(loader, dateStr, calcs, deps) {
|
|
334
322
|
const { logger } = deps;
|
|
335
323
|
const roots = {};
|
|
324
|
+
|
|
325
|
+
// 1. Identify Requirements
|
|
336
326
|
const reqs = {
|
|
337
|
-
mappings: true,
|
|
338
|
-
|
|
339
|
-
|
|
327
|
+
mappings: true,
|
|
328
|
+
piMasterList: true,
|
|
329
|
+
rankings: false,
|
|
330
|
+
rankingsYesterday: false,
|
|
331
|
+
verifications: false,
|
|
332
|
+
insights: false,
|
|
333
|
+
social: false,
|
|
334
|
+
ratings: false,
|
|
335
|
+
pageViews: false,
|
|
336
|
+
watchlist: false,
|
|
337
|
+
alerts: false
|
|
340
338
|
};
|
|
341
339
|
|
|
342
340
|
for (const c of calcs) {
|
|
@@ -352,6 +350,7 @@ async function loadGlobalRoots(loader, dateStr, calcs, deps) {
|
|
|
352
350
|
if (deps.includes('alerts')) reqs.alerts = true;
|
|
353
351
|
}
|
|
354
352
|
|
|
353
|
+
// 2. Fetch Helper
|
|
355
354
|
const fetch = async (key, method, dateArg, optional = true) => {
|
|
356
355
|
if (!reqs[key]) return;
|
|
357
356
|
try {
|
|
@@ -363,8 +362,9 @@ async function loadGlobalRoots(loader, dateStr, calcs, deps) {
|
|
|
363
362
|
}
|
|
364
363
|
};
|
|
365
364
|
|
|
365
|
+
// 3. Execute Loads
|
|
366
366
|
await Promise.all([
|
|
367
|
-
fetch('mappings', 'loadMappings', null, false),
|
|
367
|
+
fetch('mappings', 'loadMappings', null, false), // Always required
|
|
368
368
|
fetch('piMasterList', 'loadPIMasterList', null, false),
|
|
369
369
|
fetch('rankings', 'loadRankings', dateStr),
|
|
370
370
|
fetch('verifications', 'loadVerifications', dateStr),
|
|
@@ -381,14 +381,20 @@ async function loadGlobalRoots(loader, dateStr, calcs, deps) {
|
|
|
381
381
|
await fetch('rankingsYesterday', 'loadRankings', prev);
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
+
// Map internal names to match loadGlobalRoots structure if needed
|
|
384
385
|
roots.watchlistMembership = roots.watchlist;
|
|
385
386
|
roots.alertHistory = roots.alerts;
|
|
387
|
+
|
|
386
388
|
return roots;
|
|
387
389
|
}
|
|
388
390
|
|
|
389
391
|
async function loadSeriesData(loader, dateStr, calcs, config, deps) {
|
|
390
392
|
const rootReqs = {};
|
|
391
|
-
const depReqs = {};
|
|
393
|
+
const depReqs = {}; // norm -> { days, originalName, category }
|
|
394
|
+
|
|
395
|
+
// [FIX] Calculate User Types here to pass to loadSeries
|
|
396
|
+
const requiredUserTypes = new Set(calcs.map(c => (c.userType || 'ALL').toUpperCase()));
|
|
397
|
+
const userTypeArray = requiredUserTypes.has('ALL') ? null : Array.from(requiredUserTypes);
|
|
392
398
|
|
|
393
399
|
calcs.forEach(c => {
|
|
394
400
|
if (c.manifest.rootDataSeries) {
|
|
@@ -409,21 +415,30 @@ async function loadSeriesData(loader, dateStr, calcs, config, deps) {
|
|
|
409
415
|
const rootMap = {
|
|
410
416
|
alerts: 'loadAlertHistory', insights: 'loadInsights', ratings: 'loadRatings',
|
|
411
417
|
watchlist: 'loadWatchlistMembership', rankings: 'loadRankings',
|
|
418
|
+
// <--- ADDED MAPPING
|
|
412
419
|
portfolios: 'loadPortfolios'
|
|
413
420
|
};
|
|
414
421
|
|
|
415
422
|
await Promise.all(Object.entries(rootReqs).map(async ([key, days]) => {
|
|
416
|
-
if (rootMap[key])
|
|
423
|
+
if (rootMap[key]) {
|
|
424
|
+
// [FIX] Pass userTypeArray specifically if loading portfolios
|
|
425
|
+
const extraArgs = (key === 'portfolios') ? [userTypeArray] : [];
|
|
426
|
+
series.root[key] = (await loader.loadSeries(rootMap[key], dateStr, days, ...extraArgs)).data;
|
|
427
|
+
}
|
|
417
428
|
}));
|
|
418
429
|
|
|
430
|
+
// Build lookup from ALL computations using config.calculations
|
|
419
431
|
const depEntries = Object.values(depReqs);
|
|
420
432
|
if (depEntries.length) {
|
|
421
433
|
const depOriginalNames = depEntries.map(e => e.originalName);
|
|
422
434
|
const maxDays = Math.max(...depEntries.map(e => e.days));
|
|
435
|
+
|
|
423
436
|
const allManifests = getManifest(config.activeProductLines || [], getCalculations(config), deps);
|
|
424
437
|
const lookup = Object.fromEntries(allManifests.map(m => [normalizeName(m.name), m.category]));
|
|
438
|
+
|
|
425
439
|
series.results = await fetchResultSeries(dateStr, depOriginalNames, lookup, config, deps, maxDays);
|
|
426
440
|
}
|
|
441
|
+
|
|
427
442
|
return series;
|
|
428
443
|
}
|
|
429
444
|
|
|
@@ -229,13 +229,14 @@ async function loadFullDayMap(config, deps, partRefs, dateString) {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
/** Stage 3.5: Load Daily Portfolios (Wrapper for Series Loading) */
|
|
232
|
-
async function loadDailyPortfolios(config, deps, dateString) {
|
|
232
|
+
async function loadDailyPortfolios(config, deps, dateString, requiredUserTypes = null) {
|
|
233
233
|
// 1. GCS FAST PATH
|
|
234
234
|
const cached = await tryLoadFromGCS(config, dateString, 'portfolios', deps.logger);
|
|
235
235
|
if (cached) return cached;
|
|
236
236
|
|
|
237
237
|
// 2. FIRESTORE FALLBACK
|
|
238
|
-
|
|
238
|
+
// [FIX] Now passing requiredUserTypes to prevent fetching all users (e.g. NormalUserPortfolios)
|
|
239
|
+
const partRefs = await getPortfolioPartRefs(config, deps, dateString, requiredUserTypes);
|
|
239
240
|
if (partRefs.length === 0) return {};
|
|
240
241
|
return loadDataByRefs(config, deps, partRefs);
|
|
241
242
|
}
|
|
@@ -813,7 +814,7 @@ module.exports = {
|
|
|
813
814
|
getPortfolioPartRefs,
|
|
814
815
|
loadDataByRefs,
|
|
815
816
|
loadFullDayMap,
|
|
816
|
-
loadDailyPortfolios,
|
|
817
|
+
loadDailyPortfolios,
|
|
817
818
|
loadDailyInsights,
|
|
818
819
|
loadDailySocialPostInsights,
|
|
819
820
|
getHistoryPartRefs,
|