bulltrackers-module 1.0.535 → 1.0.536

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.
@@ -27,7 +27,8 @@ class ContextFactory {
27
27
  allRankings, allRankingsYesterday, // Global rank lists
28
28
  allVerifications,
29
29
  // [NEW] New Root Data Types for Profile Metrics
30
- ratings, pageViews, watchlistMembership, alertHistory
30
+ ratings, pageViews, watchlistMembership, alertHistory,
31
+ piMasterList, // [NEW]
31
32
  } = options;
32
33
 
33
34
  return {
@@ -56,7 +57,8 @@ class ContextFactory {
56
57
  ratings: ratings || {},
57
58
  pageViews: pageViews || {},
58
59
  watchlistMembership: watchlistMembership || {},
59
- alertHistory: alertHistory || {}
60
+ alertHistory: alertHistory || {},
61
+ piMasterList: piMasterList || {}, // [NEW]
60
62
  }
61
63
  };
62
64
  }
@@ -229,7 +229,10 @@ async function checkRootDataAvailability(dateStr, config, dependencies, earliest
229
229
  piRatings: !!details.piRatings,
230
230
  piPageViews: !!details.piPageViews,
231
231
  watchlistMembership: !!details.watchlistMembership,
232
- piAlertHistory: !!details.piAlertHistory
232
+ piAlertHistory: !!details.piAlertHistory,
233
+
234
+ // [NEW] Global Helper Data
235
+ piMasterList: !!details.piMasterList
233
236
  },
234
237
  portfolioRefs: null,
235
238
  historyRefs: null,
@@ -14,7 +14,8 @@ const {
14
14
  loadPIRatings, // [NEW]
15
15
  loadPIPageViews, // [NEW]
16
16
  loadWatchlistMembership: loadWatchlistMembershipData, // [NEW] Renamed to avoid conflict
17
- loadPIAlertHistory // [NEW]
17
+ loadPIAlertHistory, // [NEW]
18
+ loadPopularInvestorMasterList // [NEW]
18
19
  } = require('../utils/data_loader');
19
20
  const zlib = require('zlib');
20
21
 
@@ -31,7 +32,8 @@ class CachedDataLoader {
31
32
  ratings: new Map(), // [NEW]
32
33
  pageViews: new Map(), // [NEW]
33
34
  watchlistMembership: new Map(), // [NEW]
34
- alertHistory: new Map() // [NEW]
35
+ alertHistory: new Map(),// [NEW]
36
+ piMasterList: null // [NEW] Singleton cache (not date dependent)
35
37
  };
36
38
  }
37
39
 
@@ -141,6 +143,13 @@ class CachedDataLoader {
141
143
  return {};
142
144
  }
143
145
  }
146
+ // [NEW] Load PI Master List (Global, Cached)
147
+ async loadPIMasterList() {
148
+ if (this.cache.piMasterList) return this.cache.piMasterList;
149
+ const data = await loadPopularInvestorMasterList(this.config, this.deps);
150
+ this.cache.piMasterList = data;
151
+ return data;
152
+ }
144
153
  }
145
154
 
146
155
  module.exports = { CachedDataLoader };
@@ -230,7 +230,9 @@ class StandardExecutor {
230
230
  static async executePerUser(calcInstance, metadata, dateStr, portfolioData, yesterdayPortfolioData, historyData, computedDeps, prevDeps, config, deps, loader, stats, earliestDates) {
231
231
  const { logger } = deps;
232
232
  const targetUserType = metadata.userType;
233
+ // [NEW] Always load Global Helpers
233
234
  const mappings = await loader.loadMappings();
235
+ const piMasterList = await loader.loadPIMasterList(); // [NEW] Loaded globally
234
236
  const SCHEMAS = mathLayer.SCHEMAS;
235
237
 
236
238
  // 1. Load Root Data
@@ -385,7 +387,10 @@ class StandardExecutor {
385
387
  ratings: ratings || {},
386
388
  pageViews: pageViews || {},
387
389
  watchlistMembership: watchlistMembership || {},
388
- alertHistory: alertHistory || {}
390
+ alertHistory: alertHistory || {},
391
+
392
+ // [NEW] Pass Master List
393
+ piMasterList,
389
394
  });
390
395
 
391
396
  if (metadata.requiresEarliestDataDate && earliestDates) {
@@ -947,6 +947,39 @@ class AlertHistoryExtractor {
947
947
  }
948
948
  }
949
949
 
950
+ /**
951
+ * [NEW] Extractor for Popular Investor Master List (Global Backup)
952
+ * Access via: context.globalData.piMasterList
953
+ */
954
+ class PIMasterListExtractor {
955
+ /**
956
+ * Get the master record for a PI
957
+ * @param {Object} masterList - context.globalData.piMasterList
958
+ * @param {string} cid - The User CID
959
+ */
960
+ static getRecord(masterList, cid) {
961
+ if (!masterList || !cid) return null;
962
+ return masterList[String(cid)] || null;
963
+ }
964
+
965
+ /**
966
+ * Resolve Username from Master List
967
+ * @param {Object} masterList
968
+ * @param {string} cid
969
+ */
970
+ static getUsername(masterList, cid) {
971
+ const record = this.getRecord(masterList, cid);
972
+ return record ? record.username : null;
973
+ }
974
+
975
+ /**
976
+ * Check if CID exists in master list
977
+ */
978
+ static exists(masterList, cid) {
979
+ return !!this.getRecord(masterList, cid);
980
+ }
981
+ }
982
+
950
983
  module.exports = {
951
984
  DataExtractor,
952
985
  priceExtractor,
@@ -962,5 +995,6 @@ module.exports = {
962
995
  RatingsExtractor,
963
996
  PageViewsExtractor,
964
997
  WatchlistMembershipExtractor,
965
- AlertHistoryExtractor
998
+ AlertHistoryExtractor,
999
+ PIMasterListExtractor
966
1000
  };
@@ -774,6 +774,37 @@ async function loadPIAlertHistory(config, deps, dateString) {
774
774
  }
775
775
  }
776
776
 
777
+ // [NEW] Load Popular Investor Master List
778
+ async function loadPopularInvestorMasterList(config, deps) {
779
+ const { db, logger, calculationUtils } = deps;
780
+ const { withRetry } = calculationUtils;
781
+
782
+ // Default to 'system_state' collection, 'popular_investor_master_list' doc
783
+ const collectionName = config.piMasterListCollection || 'system_state';
784
+ const docId = config.piMasterListDocId || 'popular_investor_master_list';
785
+
786
+ logger.log('INFO', `Loading Popular Investor Master List from ${collectionName}/${docId}`);
787
+
788
+ try {
789
+ const docRef = db.collection(collectionName).doc(docId);
790
+ const docSnap = await withRetry(() => docRef.get(), 'getPIMasterList');
791
+
792
+ if (!docSnap.exists) {
793
+ logger.log('WARN', 'Popular Investor Master List not found.');
794
+ return {};
795
+ }
796
+
797
+ const data = tryDecompress(docSnap.data());
798
+ // Structure is { investors: { cid: { username, ... } } } or direct map { cid: { ... } }
799
+ // Based on user input, it looks like a direct map of CIDs or a field holding the map.
800
+ // We return the raw object which acts as the map.
801
+ return data.investors || data;
802
+ } catch (error) {
803
+ logger.log('ERROR', `Failed to load PI Master List: ${error.message}`);
804
+ return {};
805
+ }
806
+ }
807
+
777
808
  module.exports = {
778
809
  getPortfolioPartRefs,
779
810
  loadDataByRefs,
@@ -791,5 +822,6 @@ module.exports = {
791
822
  loadPIRatings,
792
823
  loadPIPageViews,
793
824
  loadWatchlistMembership,
794
- loadPIAlertHistory
825
+ loadPIAlertHistory,
826
+ loadPopularInvestorMasterList // [NEW]
795
827
  };
@@ -93,6 +93,7 @@ exports.runRootDataIndexer = async (config, dependencies) => {
93
93
  piPageViews: collections.piPageViews || 'PIPageViewsData',
94
94
  watchlistMembership: collections.watchlistMembership || 'WatchlistMembershipData',
95
95
  piAlertHistory: collections.piAlertHistory || 'PIAlertHistoryData',
96
+ piMasterList: collections.piMasterList || 'system_state', // [NEW] Collection for master list
96
97
  ...collections // Allow overrides
97
98
  };
98
99
 
@@ -237,7 +238,8 @@ exports.runRootDataIndexer = async (config, dependencies) => {
237
238
  piRatings: false,
238
239
  piPageViews: false,
239
240
  watchlistMembership: false,
240
- piAlertHistory: false
241
+ piAlertHistory: false,
242
+ piMasterList: false // [NEW] Flag for Master List
241
243
  }
242
244
  };
243
245
 
@@ -312,6 +314,10 @@ exports.runRootDataIndexer = async (config, dependencies) => {
312
314
  // Path: PIAlertHistoryData/{YYYY-MM-DD}
313
315
  const piAlertHistoryRef = db.collection(safeCollections.piAlertHistory).doc(dateStr);
314
316
 
317
+ // [NEW] Master List Ref (Single Global Document)
318
+ // Path: system_state/popular_investor_master_list
319
+ const piMasterListRef = db.collection(safeCollections.piMasterList).doc('popular_investor_master_list');
320
+
315
321
  // 4. Social Data Checks - Use date tracking documents (NEW STRUCTURE)
316
322
  // Single tracking documents at root level:
317
323
  // - PopularInvestorSocialPostData/_dates -> fetchedDates.{date}
@@ -387,7 +393,8 @@ exports.runRootDataIndexer = async (config, dependencies) => {
387
393
  piRatingsSnap,
388
394
  piPageViewsSnap,
389
395
  watchlistMembershipSnap,
390
- piAlertHistorySnap
396
+ piAlertHistorySnap,
397
+ piMasterListSnap // [NEW]
391
398
  ] = await Promise.all([
392
399
  checkAnyPartExists(normPortPartsRef),
393
400
  checkAnyPartExists(specPortPartsRef),
@@ -396,6 +403,7 @@ exports.runRootDataIndexer = async (config, dependencies) => {
396
403
  insightsRef.get(),
397
404
  Promise.resolve(!genericSocialSnap.empty),
398
405
  piRankingsRef.get(),
406
+ piMasterListRef.get(), // [NEW]
399
407
  // Check new structure first, fallback to legacy
400
408
  checkDateCollectionHasDocs(piPortfoliosCollectionRef).then(exists => exists || checkAnyPartExists(piPortfoliosPartsRef)),
401
409
  checkAnyPartExists(piDeepPartsRef), // Legacy only
@@ -459,6 +467,9 @@ exports.runRootDataIndexer = async (config, dependencies) => {
459
467
  availability.hasHistory = normHistExists || specHistExists || piHistExists || signedInHistExists;
460
468
  availability.hasInsights = insightsSnap.exists;
461
469
  availability.hasSocial = foundPISocial || foundSignedInSocial || genericSocialExists;
470
+
471
+ // [NEW] Assign Master List Availability
472
+ availability.details.piMasterList = piMasterListSnap.exists;
462
473
 
463
474
  // Price Check
464
475
  // Check if the target date exists in the price availability set
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.535",
3
+ "version": "1.0.536",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [