bulltrackers-module 1.0.592 → 1.0.593

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.
Files changed (36) hide show
  1. package/functions/old-generic-api/admin-api/index.js +895 -0
  2. package/functions/old-generic-api/helpers/api_helpers.js +457 -0
  3. package/functions/old-generic-api/index.js +204 -0
  4. package/functions/old-generic-api/user-api/helpers/alerts/alert_helpers.js +355 -0
  5. package/functions/old-generic-api/user-api/helpers/alerts/subscription_helpers.js +327 -0
  6. package/functions/old-generic-api/user-api/helpers/alerts/test_alert_helpers.js +212 -0
  7. package/functions/old-generic-api/user-api/helpers/collection_helpers.js +193 -0
  8. package/functions/old-generic-api/user-api/helpers/core/compression_helpers.js +68 -0
  9. package/functions/old-generic-api/user-api/helpers/core/data_lookup_helpers.js +256 -0
  10. package/functions/old-generic-api/user-api/helpers/core/path_resolution_helpers.js +640 -0
  11. package/functions/old-generic-api/user-api/helpers/core/user_status_helpers.js +195 -0
  12. package/functions/old-generic-api/user-api/helpers/data/computation_helpers.js +503 -0
  13. package/functions/old-generic-api/user-api/helpers/data/instrument_helpers.js +55 -0
  14. package/functions/old-generic-api/user-api/helpers/data/portfolio_helpers.js +245 -0
  15. package/functions/old-generic-api/user-api/helpers/data/social_helpers.js +174 -0
  16. package/functions/old-generic-api/user-api/helpers/data_helpers.js +87 -0
  17. package/functions/old-generic-api/user-api/helpers/dev/dev_helpers.js +336 -0
  18. package/functions/old-generic-api/user-api/helpers/fetch/on_demand_fetch_helpers.js +615 -0
  19. package/functions/old-generic-api/user-api/helpers/metrics/personalized_metrics_helpers.js +231 -0
  20. package/functions/old-generic-api/user-api/helpers/notifications/notification_helpers.js +641 -0
  21. package/functions/old-generic-api/user-api/helpers/profile/pi_profile_helpers.js +182 -0
  22. package/functions/old-generic-api/user-api/helpers/profile/profile_view_helpers.js +137 -0
  23. package/functions/old-generic-api/user-api/helpers/profile/user_profile_helpers.js +190 -0
  24. package/functions/old-generic-api/user-api/helpers/recommendations/recommendation_helpers.js +66 -0
  25. package/functions/old-generic-api/user-api/helpers/reviews/review_helpers.js +550 -0
  26. package/functions/old-generic-api/user-api/helpers/rootdata/rootdata_aggregation_helpers.js +378 -0
  27. package/functions/old-generic-api/user-api/helpers/search/pi_request_helpers.js +295 -0
  28. package/functions/old-generic-api/user-api/helpers/search/pi_search_helpers.js +162 -0
  29. package/functions/old-generic-api/user-api/helpers/sync/user_sync_helpers.js +677 -0
  30. package/functions/old-generic-api/user-api/helpers/verification/verification_helpers.js +323 -0
  31. package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_analytics_helpers.js +96 -0
  32. package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_data_helpers.js +141 -0
  33. package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_generation_helpers.js +310 -0
  34. package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_management_helpers.js +829 -0
  35. package/functions/old-generic-api/user-api/index.js +109 -0
  36. package/package.json +2 -2
@@ -0,0 +1,162 @@
1
+ /**
2
+ * @fileoverview Popular Investor Search Helpers
3
+ * Handles PI search endpoints
4
+ */
5
+
6
+ const { getPIMasterList } = require('../core/user_status_helpers');
7
+ const { tryDecompress } = require('../core/compression_helpers');
8
+
9
+ /**
10
+ * GET /user/search/pis
11
+ * Search for Popular Investors by username
12
+ */
13
+ async function searchPopularInvestors(req, res, dependencies, config) {
14
+ const { db, logger } = dependencies;
15
+ const { query, limit = 20 } = req.query;
16
+
17
+ if (!query || query.trim().length < 2) {
18
+ return res.status(400).json({ error: "Query must be at least 2 characters" });
19
+ }
20
+
21
+ try {
22
+ // Use master list instead of rankings (single source of truth)
23
+ const collectionRegistry = dependencies.collectionRegistry || null;
24
+ const investors = await getPIMasterList(db, collectionRegistry, logger);
25
+
26
+ if (Object.keys(investors).length === 0) {
27
+ return res.status(404).json({ error: "Popular investor data not available" });
28
+ }
29
+
30
+ // Search by username (case-insensitive, partial match)
31
+ const searchQuery = query.toLowerCase().trim();
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
+ };
101
+
102
+ for (const [cid, investor] of Object.entries(investors)) {
103
+ if (investor.username) {
104
+ const username = investor.username.toLowerCase();
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
+
131
+ matches.push({
132
+ cid: Number(cid),
133
+ username: investor.username,
134
+ aum: aum,
135
+ riskScore: riskScore,
136
+ gain: gain,
137
+ copiers: copiers
138
+ });
139
+
140
+ if (matches.length >= parseInt(limit)) {
141
+ break;
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ return res.status(200).json({
148
+ results: matches,
149
+ count: matches.length,
150
+ query: query
151
+ });
152
+
153
+ } catch (error) {
154
+ logger.log('ERROR', `[searchPopularInvestors] Error searching PIs`, error);
155
+ return res.status(500).json({ error: error.message });
156
+ }
157
+ }
158
+
159
+ module.exports = {
160
+ searchPopularInvestors
161
+ };
162
+