bulltrackers-module 1.0.701 → 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.
@@ -17,7 +17,8 @@ const {
17
17
  loadWatchlistMembership: loadWatchlistMembershipData,
18
18
  loadPIAlertHistory,
19
19
  loadPIWatchlistData,
20
- loadPopularInvestorMasterList
20
+ loadPopularInvestorMasterList,
21
+ loadDailyPortfolios // <--- IMPORTED
21
22
  } = require('../utils/data_loader');
22
23
  const { getAvailabilityWindow } = require('./AvailabilityChecker');
23
24
  const zlib = require('zlib');
@@ -81,6 +82,12 @@ const LOADER_DEFINITIONS = {
81
82
  // No collection key needed for direct implementation, but handled by fn
82
83
  fn: loadPIWatchlistData,
83
84
  isIdBased: true // Uses ID instead of Date
85
+ },
86
+ // <--- ADDED SUPPORT FOR PORTFOLIO SERIES
87
+ loadPortfolios: {
88
+ cache: 'portfolios',
89
+ fn: loadDailyPortfolios
90
+ // No configKey/flag ensures legacy loop (required for complex multi-collection fetch)
84
91
  }
85
92
  };
86
93
 
@@ -112,7 +119,8 @@ class CachedDataLoader {
112
119
  // =========================================================================
113
120
  // GENERIC LOADER HELPER
114
121
  // =========================================================================
115
- async _loadGeneric(methodName, key) {
122
+ // [FIX] Accepts ...args to pass down filters (like requiredUserTypes)
123
+ async _loadGeneric(methodName, key, ...args) {
116
124
  const def = LOADER_DEFINITIONS[methodName];
117
125
  if (!def) throw new Error(`Unknown loader method: ${methodName}`);
118
126
 
@@ -125,7 +133,7 @@ class CachedDataLoader {
125
133
  this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Loading '${def.cache}' from: ${collection}/${key}`);
126
134
  }
127
135
 
128
- const promise = def.fn(this.config, this.deps, key);
136
+ const promise = def.fn(this.config, this.deps, key, ...args);
129
137
  cacheMap.set(key, promise);
130
138
  return promise;
131
139
  }
@@ -144,6 +152,8 @@ class CachedDataLoader {
144
152
  async loadWatchlistMembership(dateStr) { return this._loadGeneric('loadWatchlistMembership', dateStr); }
145
153
  async loadAlertHistory(dateStr) { return this._loadGeneric('loadAlertHistory' , dateStr); }
146
154
  async loadPIWatchlistData(piCid) { return this._loadGeneric('loadPIWatchlistData' , String(piCid)); }
155
+ // <--- ADDED PORTFOLIOS ACCESSOR with extra arg
156
+ async loadPortfolios(dateStr, userTypes) { return this._loadGeneric('loadPortfolios', dateStr, userTypes); }
147
157
 
148
158
  // =========================================================================
149
159
  // SPECIALIZED LOADERS (Non-Standard Patterns)
@@ -195,13 +205,14 @@ class CachedDataLoader {
195
205
  /**
196
206
  * Optimistically loads data series using Batch Reads (db.getAll).
197
207
  * Uses Availability Index to minimize costs.
208
+ * [FIX] Now accepts ...args to pass context (e.g. requiredUserTypes)
198
209
  */
199
- async loadSeries(loaderMethod, dateStr, lookbackDays) {
210
+ async loadSeries(loaderMethod, dateStr, lookbackDays, ...args) {
200
211
  const def = LOADER_DEFINITIONS[loaderMethod];
201
212
  if (!def) throw new Error(`[CachedDataLoader] Unknown series method ${loaderMethod}`);
202
213
 
203
214
  // Fallback to legacy loop if method isn't configured for batching (missing config/flag)
204
- if (!def.configKey || !def.flag) return this._loadSeriesLegacy(loaderMethod, dateStr, lookbackDays);
215
+ if (!def.configKey || !def.flag) return this._loadSeriesLegacy(loaderMethod, dateStr, lookbackDays, ...args);
205
216
 
206
217
  // 1. Calculate Date Range
207
218
  const endDate = new Date(dateStr);
@@ -256,7 +267,7 @@ class CachedDataLoader {
256
267
  });
257
268
  } catch (err) {
258
269
  console.warn(`[CachedDataLoader] Batch failed: ${err.message}. Legacy fallback.`);
259
- return this._loadSeriesLegacy(loaderMethod, dateStr, lookbackDays);
270
+ return this._loadSeriesLegacy(loaderMethod, dateStr, lookbackDays, ...args);
260
271
  }
261
272
  }
262
273
 
@@ -268,7 +279,7 @@ class CachedDataLoader {
268
279
  };
269
280
  }
270
281
 
271
- async _loadSeriesLegacy(loaderMethod, dateStr, lookbackDays) {
282
+ async _loadSeriesLegacy(loaderMethod, dateStr, lookbackDays, ...args) {
272
283
  const results = {};
273
284
  const promises = [];
274
285
  const endDate = new Date(dateStr);
@@ -277,7 +288,8 @@ class CachedDataLoader {
277
288
  const d = new Date(endDate);
278
289
  d.setUTCDate(d.getUTCDate() - i);
279
290
  const dStr = d.toISOString().slice(0, 10);
280
- promises.push(this[loaderMethod](dStr).then(data => data ? results[dStr] = data : null).catch(() => null));
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));
281
293
  }
282
294
 
283
295
  await Promise.all(promises);
@@ -392,6 +392,10 @@ async function loadSeriesData(loader, dateStr, calcs, config, deps) {
392
392
  const rootReqs = {};
393
393
  const depReqs = {}; // norm -> { days, originalName, category }
394
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);
398
+
395
399
  calcs.forEach(c => {
396
400
  if (c.manifest.rootDataSeries) {
397
401
  Object.entries(c.manifest.rootDataSeries).forEach(([k, v]) => rootReqs[k] = Math.max(rootReqs[k]||0, v.lookback||v));
@@ -410,11 +414,17 @@ async function loadSeriesData(loader, dateStr, calcs, config, deps) {
410
414
  const series = { root: {}, results: {} };
411
415
  const rootMap = {
412
416
  alerts: 'loadAlertHistory', insights: 'loadInsights', ratings: 'loadRatings',
413
- watchlist: 'loadWatchlistMembership', rankings: 'loadRankings'
417
+ watchlist: 'loadWatchlistMembership', rankings: 'loadRankings',
418
+ // <--- ADDED MAPPING
419
+ portfolios: 'loadPortfolios'
414
420
  };
415
421
 
416
422
  await Promise.all(Object.entries(rootReqs).map(async ([key, days]) => {
417
- if (rootMap[key]) series.root[key] = (await loader.loadSeries(rootMap[key], dateStr, days)).data;
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
+ }
418
428
  }));
419
429
 
420
430
  // Build lookup from ALL computations using config.calculations
@@ -228,6 +228,19 @@ async function loadFullDayMap(config, deps, partRefs, dateString) {
228
228
  return fullMap;
229
229
  }
230
230
 
231
+ /** Stage 3.5: Load Daily Portfolios (Wrapper for Series Loading) */
232
+ async function loadDailyPortfolios(config, deps, dateString, requiredUserTypes = null) {
233
+ // 1. GCS FAST PATH
234
+ const cached = await tryLoadFromGCS(config, dateString, 'portfolios', deps.logger);
235
+ if (cached) return cached;
236
+
237
+ // 2. FIRESTORE FALLBACK
238
+ // [FIX] Now passing requiredUserTypes to prevent fetching all users (e.g. NormalUserPortfolios)
239
+ const partRefs = await getPortfolioPartRefs(config, deps, dateString, requiredUserTypes);
240
+ if (partRefs.length === 0) return {};
241
+ return loadDataByRefs(config, deps, partRefs);
242
+ }
243
+
231
244
  /** Stage 4: Load daily instrument insights */
232
245
  async function loadDailyInsights(config, deps, dateString) {
233
246
  const { db, logger, calculationUtils } = deps;
@@ -800,7 +813,8 @@ async function loadPopularInvestorMasterList(config, deps, dateString = null) {
800
813
  module.exports = {
801
814
  getPortfolioPartRefs,
802
815
  loadDataByRefs,
803
- loadFullDayMap,
816
+ loadFullDayMap,
817
+ loadDailyPortfolios,
804
818
  loadDailyInsights,
805
819
  loadDailySocialPostInsights,
806
820
  getHistoryPartRefs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.701",
3
+ "version": "1.0.703",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [