bulltrackers-module 1.0.540 → 1.0.541

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.
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  const { getPIMasterList } = require('../core/user_status_helpers');
7
+ const { tryDecompress } = require('../core/compression_helpers');
7
8
 
8
9
  /**
9
10
  * GET /user/search/pis
@@ -29,16 +30,111 @@ async function searchPopularInvestors(req, res, dependencies, config) {
29
30
  // Search by username (case-insensitive, partial match)
30
31
  const searchQuery = query.toLowerCase().trim();
31
32
  const matches = [];
33
+ const rankingsCollection = config.popularInvestorRankingsCollection || process.env.FIRESTORE_COLLECTION_PI_RANKINGS || 'popular_investor_rankings';
34
+
35
+ // Cache rankings data by date to avoid fetching the same date multiple times
36
+ const rankingsCache = new Map();
37
+
38
+ /**
39
+ * Helper to get rankings data for a specific date
40
+ */
41
+ const getRankingsForDate = async (dateStr) => {
42
+ if (rankingsCache.has(dateStr)) {
43
+ return rankingsCache.get(dateStr);
44
+ }
45
+
46
+ try {
47
+ const rankingsRef = db.collection(rankingsCollection).doc(dateStr);
48
+ const rankingsDoc = await rankingsRef.get();
49
+
50
+ if (!rankingsDoc.exists) {
51
+ rankingsCache.set(dateStr, null);
52
+ return null;
53
+ }
54
+
55
+ const rawRankingsData = rankingsDoc.data();
56
+ const rankingsData = tryDecompress(rawRankingsData);
57
+ const rankingsItems = rankingsData.Items || [];
58
+
59
+ // Create a map for quick lookup by CID
60
+ const rankingsMap = new Map();
61
+ for (const item of rankingsItems) {
62
+ if (item.CustomerId) {
63
+ rankingsMap.set(String(item.CustomerId), item);
64
+ }
65
+ }
66
+
67
+ rankingsCache.set(dateStr, rankingsMap);
68
+ return rankingsMap;
69
+ } catch (error) {
70
+ if (logger) {
71
+ logger.log('WARN', `[searchPopularInvestors] Failed to fetch rankings for date ${dateStr}: ${error.message}`);
72
+ }
73
+ rankingsCache.set(dateStr, null);
74
+ return null;
75
+ }
76
+ };
77
+
78
+ /**
79
+ * Convert timestamp to date string (YYYY-MM-DD)
80
+ */
81
+ const timestampToDateStr = (timestamp) => {
82
+ if (!timestamp) return null;
83
+
84
+ // Handle Firestore Timestamp
85
+ if (timestamp.toDate && typeof timestamp.toDate === 'function') {
86
+ return timestamp.toDate().toISOString().split('T')[0];
87
+ }
88
+
89
+ // Handle Date object
90
+ if (timestamp instanceof Date) {
91
+ return timestamp.toISOString().split('T')[0];
92
+ }
93
+
94
+ // Handle number (milliseconds since epoch)
95
+ if (typeof timestamp === 'number') {
96
+ return new Date(timestamp).toISOString().split('T')[0];
97
+ }
98
+
99
+ return null;
100
+ };
32
101
 
33
102
  for (const [cid, investor] of Object.entries(investors)) {
34
103
  if (investor.username) {
35
104
  const username = investor.username.toLowerCase();
36
105
  if (username.includes(searchQuery)) {
106
+ // Get ranking metrics from rankings collection using lastSeenAt
107
+ let aum = null;
108
+ let riskScore = null;
109
+ let gain = null;
110
+ let copiers = null;
111
+
112
+ if (investor.lastSeenAt) {
113
+ const rankingsDate = timestampToDateStr(investor.lastSeenAt);
114
+
115
+ if (rankingsDate) {
116
+ const rankingsMap = await getRankingsForDate(rankingsDate);
117
+
118
+ if (rankingsMap) {
119
+ const rankingEntry = rankingsMap.get(cid);
120
+
121
+ if (rankingEntry) {
122
+ aum = rankingEntry.AUMValue || null;
123
+ riskScore = rankingEntry.RiskScore || null;
124
+ gain = rankingEntry.Gain || null;
125
+ copiers = rankingEntry.Copiers || null;
126
+ }
127
+ }
128
+ }
129
+ }
130
+
37
131
  matches.push({
38
132
  cid: Number(cid),
39
- username: investor.username
40
- // Note: AUM, riskScore, gain, copiers are not in master list
41
- // If needed, they should be fetched from rankings collection separately
133
+ username: investor.username,
134
+ aum: aum,
135
+ riskScore: riskScore,
136
+ gain: gain,
137
+ copiers: copiers
42
138
  });
43
139
 
44
140
  if (matches.length >= parseInt(limit)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.540",
3
+ "version": "1.0.541",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [