bulltrackers-module 1.0.629 → 1.0.631

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 (42) hide show
  1. package/functions/alert-system/helpers/alert_helpers.js +69 -77
  2. package/functions/alert-system/index.js +19 -29
  3. package/functions/api-v2/helpers/notification_helpers.js +187 -0
  4. package/functions/computation-system/helpers/computation_worker.js +1 -1
  5. package/functions/task-engine/helpers/popular_investor_helpers.js +11 -7
  6. package/index.js +0 -5
  7. package/package.json +1 -2
  8. package/functions/old-generic-api/admin-api/index.js +0 -895
  9. package/functions/old-generic-api/helpers/api_helpers.js +0 -457
  10. package/functions/old-generic-api/index.js +0 -204
  11. package/functions/old-generic-api/user-api/helpers/alerts/alert_helpers.js +0 -355
  12. package/functions/old-generic-api/user-api/helpers/alerts/subscription_helpers.js +0 -327
  13. package/functions/old-generic-api/user-api/helpers/alerts/test_alert_helpers.js +0 -212
  14. package/functions/old-generic-api/user-api/helpers/collection_helpers.js +0 -193
  15. package/functions/old-generic-api/user-api/helpers/core/compression_helpers.js +0 -68
  16. package/functions/old-generic-api/user-api/helpers/core/data_lookup_helpers.js +0 -256
  17. package/functions/old-generic-api/user-api/helpers/core/path_resolution_helpers.js +0 -640
  18. package/functions/old-generic-api/user-api/helpers/core/user_status_helpers.js +0 -195
  19. package/functions/old-generic-api/user-api/helpers/data/computation_helpers.js +0 -503
  20. package/functions/old-generic-api/user-api/helpers/data/instrument_helpers.js +0 -55
  21. package/functions/old-generic-api/user-api/helpers/data/portfolio_helpers.js +0 -245
  22. package/functions/old-generic-api/user-api/helpers/data/social_helpers.js +0 -174
  23. package/functions/old-generic-api/user-api/helpers/data_helpers.js +0 -87
  24. package/functions/old-generic-api/user-api/helpers/dev/dev_helpers.js +0 -336
  25. package/functions/old-generic-api/user-api/helpers/fetch/on_demand_fetch_helpers.js +0 -615
  26. package/functions/old-generic-api/user-api/helpers/metrics/personalized_metrics_helpers.js +0 -231
  27. package/functions/old-generic-api/user-api/helpers/notifications/notification_helpers.js +0 -641
  28. package/functions/old-generic-api/user-api/helpers/profile/pi_profile_helpers.js +0 -182
  29. package/functions/old-generic-api/user-api/helpers/profile/profile_view_helpers.js +0 -137
  30. package/functions/old-generic-api/user-api/helpers/profile/user_profile_helpers.js +0 -190
  31. package/functions/old-generic-api/user-api/helpers/recommendations/recommendation_helpers.js +0 -66
  32. package/functions/old-generic-api/user-api/helpers/reviews/review_helpers.js +0 -550
  33. package/functions/old-generic-api/user-api/helpers/rootdata/rootdata_aggregation_helpers.js +0 -378
  34. package/functions/old-generic-api/user-api/helpers/search/pi_request_helpers.js +0 -295
  35. package/functions/old-generic-api/user-api/helpers/search/pi_search_helpers.js +0 -162
  36. package/functions/old-generic-api/user-api/helpers/sync/user_sync_helpers.js +0 -677
  37. package/functions/old-generic-api/user-api/helpers/verification/verification_helpers.js +0 -323
  38. package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_analytics_helpers.js +0 -96
  39. package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_data_helpers.js +0 -141
  40. package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_generation_helpers.js +0 -310
  41. package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_management_helpers.js +0 -829
  42. package/functions/old-generic-api/user-api/index.js +0 -109
@@ -1,245 +0,0 @@
1
- /**
2
- * @fileoverview Portfolio Data Helpers
3
- * Handles user portfolio data endpoints with migration support
4
- */
5
-
6
- const { findLatestPortfolioDate, findLatestComputationDate } = require('../core/data_lookup_helpers');
7
- const { checkIfUserIsPI } = require('../core/user_status_helpers');
8
- const { getEffectiveCid, getDevOverride } = require('../dev/dev_helpers');
9
- const { checkPiInComputationDate } = require('./computation_helpers');
10
-
11
- /**
12
- * GET /user/me/portfolio
13
- * Fetches the signed-in user's portfolio data
14
- */
15
- async function getUserPortfolio(req, res, dependencies, config) {
16
- const { db, logger } = dependencies;
17
- const { userCid } = req.query;
18
-
19
- if (!userCid) {
20
- return res.status(400).json({ error: "Missing userCid" });
21
- }
22
-
23
- try {
24
- // Check for dev override impersonation
25
- const effectiveCid = await getEffectiveCid(db, userCid, config, logger);
26
- const devOverride = await getDevOverride(db, userCid, config, logger);
27
- const isImpersonating = devOverride && devOverride.enabled && devOverride.impersonateCid && effectiveCid !== Number(userCid);
28
-
29
- // If impersonating, check if the effective CID is a Popular Investor
30
- if (isImpersonating) {
31
- const rankEntry = await checkIfUserIsPI(db, effectiveCid, config, logger);
32
- if (rankEntry) {
33
- return res.status(404).json({
34
- error: "Popular Investor account",
35
- message: `CID ${effectiveCid} is a Popular Investor. Use /user/me/pi-personalized-metrics or /user/pi/${effectiveCid}/profile instead.`,
36
- effectiveCid: effectiveCid,
37
- isImpersonating: true,
38
- isPopularInvestor: true,
39
- suggestedEndpoint: `/user/me/pi-personalized-metrics?userCid=${userCid}`
40
- });
41
- }
42
- }
43
-
44
- const { signedInUsersCollection } = config;
45
- const CANARY_BLOCK_ID = '19M';
46
- const today = new Date().toISOString().split('T')[0];
47
-
48
- // Use effective CID for portfolio lookup
49
- const dataDate = await findLatestPortfolioDate(db, signedInUsersCollection, effectiveCid, 30);
50
-
51
- if (!dataDate) {
52
- return res.status(404).json({
53
- error: "Portfolio data not found for this user (checked last 30 days)",
54
- effectiveCid: effectiveCid,
55
- isImpersonating: isImpersonating || false
56
- });
57
- }
58
-
59
- const isFallback = dataDate !== today;
60
- if (isFallback) {
61
- logger.log('INFO', `[getUserPortfolio] Using fallback date ${dataDate} for effective CID ${effectiveCid} (today: ${today})`);
62
- }
63
-
64
- // Fetch portfolio from signed_in_users/19M/snapshots/{date}/parts/part_X
65
- const partsRef = db.collection(signedInUsersCollection)
66
- .doc(CANARY_BLOCK_ID)
67
- .collection('snapshots')
68
- .doc(dataDate)
69
- .collection('parts');
70
-
71
- const partsSnapshot = await partsRef.get();
72
-
73
- let portfolioData = null;
74
-
75
- // Search through all parts to find the user's portfolio (use effective CID)
76
- for (const partDoc of partsSnapshot.docs) {
77
- const partData = partDoc.data();
78
- if (partData && partData[String(effectiveCid)]) {
79
- portfolioData = partData[String(effectiveCid)];
80
- break;
81
- }
82
- }
83
-
84
- if (!portfolioData) {
85
- return res.status(404).json({
86
- error: "Portfolio data not found in any part document",
87
- effectiveCid: effectiveCid,
88
- isImpersonating: isImpersonating || false
89
- });
90
- }
91
-
92
- // Apply dev override to AggregatedMirrors if dev override is active
93
- if (devOverride && devOverride.enabled && devOverride.fakeCopiedPIs.length > 0 && portfolioData.AggregatedMirrors) {
94
- logger.log('INFO', `[getUserPortfolio] Applying DEV OVERRIDE to AggregatedMirrors for user ${userCid}`);
95
-
96
- portfolioData.AggregatedMirrors = devOverride.fakeCopiedPIs.map(cid => ({
97
- ParentCID: Number(cid),
98
- ParentUsername: `PI-${cid}`,
99
- Invested: 0,
100
- NetProfit: 0,
101
- Value: 0,
102
- PendingForClosure: false
103
- }));
104
- }
105
-
106
- return res.status(200).json({
107
- portfolio: portfolioData,
108
- date: dataDate,
109
- isFallback: isFallback,
110
- requestedDate: today,
111
- userCid: String(userCid),
112
- devOverrideActive: devOverride && devOverride.enabled
113
- });
114
-
115
- } catch (error) {
116
- logger.log('ERROR', `[getUserPortfolio] Error fetching portfolio for ${userCid}`, error);
117
- return res.status(500).json({ error: error.message });
118
- }
119
- }
120
-
121
- /**
122
- * GET /user/me/data-status
123
- * Checks if signed-in user's profile page computation is available.
124
- * The profile page uses SignedInUserProfileMetrics computation, not raw portfolio/history data.
125
- * If computation exists, the profile page can be rendered. If not, user should sync.
126
- */
127
- async function getUserDataStatus(req, res, dependencies, config) {
128
- const { db, logger } = dependencies;
129
- const { userCid } = req.query;
130
-
131
- if (!userCid) {
132
- return res.status(400).json({ error: "Missing userCid" });
133
- }
134
-
135
- try {
136
- const today = new Date().toISOString().split('T')[0];
137
-
138
- logger.log('INFO', `[getUserDataStatus] Checking computation for CID: ${userCid}, Date: ${today}`);
139
-
140
- // Check for dev override impersonation
141
- const effectiveCid = await getEffectiveCid(db, userCid, config, logger);
142
-
143
- // Primary check: Does SignedInUserProfileMetrics computation exist for this user?
144
- // This is what the profile page actually uses to render
145
- const insightsCollection = config.unifiedInsightsCollection || 'unified_insights';
146
- const resultsSub = config.resultsSubcollection || 'results';
147
- const compsSub = config.computationsSubcollection || 'computations';
148
- const category = 'popular-investor';
149
- const computationName = 'SignedInUserProfileMetrics';
150
-
151
- let computationAvailable = false;
152
- let computationDate = null;
153
- let isComputationFallback = false;
154
-
155
- // Search backwards from today to find the latest date where user exists in computation
156
- // Priority: Today (T) -> Yesterday (T-1) -> Continue backwards up to 30 days
157
- const maxDaysBack = 30;
158
- let foundDate = null;
159
-
160
- for (let daysBack = 0; daysBack < maxDaysBack; daysBack++) {
161
- const checkDate = new Date();
162
- checkDate.setDate(checkDate.getDate() - daysBack);
163
- const dateStr = checkDate.toISOString().split('T')[0];
164
-
165
- try {
166
- // Check if page document exists for this user and date (new pages subcollection structure)
167
- const { found } = await checkPiInComputationDate(
168
- db,
169
- insightsCollection,
170
- resultsSub,
171
- compsSub,
172
- category,
173
- computationName,
174
- dateStr,
175
- String(effectiveCid),
176
- logger
177
- );
178
-
179
- if (found) {
180
- foundDate = dateStr;
181
- computationAvailable = true;
182
- computationDate = dateStr;
183
- isComputationFallback = daysBack > 0;
184
-
185
- if (isComputationFallback) {
186
- logger.log('INFO', `[getUserDataStatus] Found computation for user ${userCid} on date ${computationDate} (${daysBack} days back from today: ${today})`);
187
- } else {
188
- logger.log('INFO', `[getUserDataStatus] Found computation for user ${userCid} on today's date`);
189
- }
190
- break; // Found user, stop searching
191
- } else {
192
- logger.log('DEBUG', `[getUserDataStatus] Page document not found for user ${userCid} on date ${dateStr}, continuing search...`);
193
- }
194
- } catch (error) {
195
- // Continue to next date if error
196
- logger.log('DEBUG', `[getUserDataStatus] Error checking date ${dateStr}:`, error.message);
197
- continue;
198
- }
199
- }
200
-
201
- // Check if we've exhausted the fallback window
202
- const fallbackWindowExhausted = !foundDate;
203
-
204
- if (!foundDate) {
205
- logger.log('INFO', `[getUserDataStatus] No computation found for user ${userCid} in last ${maxDaysBack} days - fallback window exhausted`);
206
- }
207
-
208
- // For backward compatibility, keep portfolioAvailable and historyAvailable
209
- // but they should match computationAvailable (computation is the source of truth)
210
- const result = {
211
- portfolioAvailable: computationAvailable, // Profile page uses computation, not raw portfolio
212
- historyAvailable: computationAvailable, // Profile page uses computation, not raw history
213
- computationAvailable: computationAvailable, // Explicit computation check
214
- date: computationDate || today, // Use found date or today as fallback
215
- computationDate: computationDate, // The actual date where computation was found
216
- isComputationFallback: isComputationFallback, // True if using T-1 or older date
217
- requestedDate: today, // What date was requested (today)
218
- fallbackWindowExhausted: fallbackWindowExhausted, // True if checked all dates in fallback window and found nothing
219
- userCid: String(userCid),
220
- effectiveCid: String(effectiveCid)
221
- };
222
-
223
- if (computationAvailable && isComputationFallback) {
224
- logger.log('INFO', `[getUserDataStatus] Using fallback date ${computationDate} for CID ${userCid} (requested: ${today})`);
225
- } else if (computationAvailable) {
226
- logger.log('INFO', `[getUserDataStatus] Using today's date ${computationDate} for CID ${userCid}`);
227
- } else {
228
- logger.log('WARN', `[getUserDataStatus] No computation found for CID ${userCid} in last ${maxDaysBack} days`);
229
- }
230
-
231
- logger.log('INFO', `[getUserDataStatus] Result for CID ${userCid}:`, result);
232
-
233
- return res.status(200).json(result);
234
-
235
- } catch (error) {
236
- logger.log('ERROR', `[getUserDataStatus] Error checking data status`, error);
237
- return res.status(500).json({ error: error.message });
238
- }
239
- }
240
-
241
- module.exports = {
242
- getUserPortfolio,
243
- getUserDataStatus
244
- };
245
-
@@ -1,174 +0,0 @@
1
- /**
2
- * @fileoverview Social Posts Data Helpers
3
- * Handles user social posts endpoints
4
- * UPDATED: Uses collection registry to read from root data collection
5
- */
6
-
7
- const { findLatestRankingsDate } = require('../core/data_lookup_helpers');
8
- const { getEffectiveCid, getDevOverride } = require('../dev/dev_helpers');
9
-
10
- /**
11
- * Find the latest available date for signed-in user social posts
12
- * Searches backwards from today up to maxDaysBack days
13
- * @param {Firestore} db - Firestore instance
14
- * @param {string|number} userCid - User CID
15
- * @param {object} collectionRegistry - Collection registry (injected at runtime)
16
- * @param {number} maxDaysBack - Maximum days to search backwards (default: 30)
17
- * @returns {Promise<string|null>} - Date string (YYYY-MM-DD) or null if not found
18
- */
19
- async function findLatestSocialDate(db, userCid, collectionRegistry, maxDaysBack = 30) {
20
- const today = new Date();
21
-
22
- // Get collection name from registry
23
- let collectionName = 'SignedInUserSocialPostData'; // Default fallback
24
- if (collectionRegistry && collectionRegistry.getCollectionPath) {
25
- try {
26
- // Extract collection name from registry path template
27
- const samplePath = collectionRegistry.getCollectionPath('rootData', 'signedInUserSocial', {
28
- date: '2025-01-01',
29
- cid: '123'
30
- });
31
- collectionName = samplePath.split('/')[0]; // Extract collection name
32
- } catch (e) {
33
- // Use default collection name
34
- }
35
- }
36
-
37
- for (let i = 0; i < maxDaysBack; i++) {
38
- const checkDate = new Date(today);
39
- checkDate.setDate(checkDate.getDate() - i);
40
- const dateStr = checkDate.toISOString().split('T')[0];
41
-
42
- try {
43
- // Construct path: SignedInUserSocialPostData/{date}/{cid}/{cid}
44
- const socialDocRef = db.collection(collectionName)
45
- .doc(dateStr)
46
- .collection(String(userCid))
47
- .doc(String(userCid));
48
-
49
- const socialDoc = await socialDocRef.get();
50
-
51
- if (socialDoc.exists) {
52
- const data = socialDoc.data();
53
- if (data.posts && Object.keys(data.posts).length > 0) {
54
- return dateStr; // Found social posts for this date
55
- }
56
- }
57
- } catch (error) {
58
- // Continue to next date if error
59
- continue;
60
- }
61
- }
62
-
63
- return null; // No social posts found in the last maxDaysBack days
64
- }
65
-
66
- /**
67
- * GET /user/me/social-posts
68
- * Fetches the signed-in user's social posts from root data collection
69
- */
70
- async function getUserSocialPosts(req, res, dependencies, config) {
71
- const { db, logger, collectionRegistry } = dependencies;
72
- const { userCid, date } = req.query;
73
-
74
- if (!userCid) {
75
- return res.status(400).json({ error: "Missing userCid" });
76
- }
77
-
78
- try {
79
- // Check for dev override impersonation
80
- const effectiveCid = await getEffectiveCid(db, userCid, config, logger);
81
- const devOverride = await getDevOverride(db, userCid, config, logger);
82
- const isImpersonating = devOverride && devOverride.enabled && devOverride.impersonateCid && effectiveCid !== Number(userCid);
83
-
84
- const today = new Date().toISOString().split('T')[0];
85
-
86
- // Determine which date to use
87
- let dataDate = date || today;
88
- let isFallback = false;
89
-
90
- // If no specific date requested, find latest available date
91
- if (!date) {
92
- dataDate = await findLatestSocialDate(db, effectiveCid, collectionRegistry, 30);
93
- if (!dataDate) {
94
- return res.status(404).json({
95
- error: "Social posts not found for this user (checked last 30 days)",
96
- effectiveCid: effectiveCid,
97
- isImpersonating: isImpersonating || false
98
- });
99
- }
100
- isFallback = dataDate !== today;
101
- if (isFallback) {
102
- logger.log('INFO', `[getUserSocialPosts] Using fallback date ${dataDate} for effective CID ${effectiveCid} (today: ${today})`);
103
- }
104
- }
105
-
106
- // Get collection name from registry
107
- let collectionName = 'SignedInUserSocialPostData'; // Default fallback
108
- if (collectionRegistry && collectionRegistry.getCollectionPath) {
109
- try {
110
- // Extract collection name from registry path template
111
- const samplePath = collectionRegistry.getCollectionPath('rootData', 'signedInUserSocial', {
112
- date: '2025-01-01',
113
- cid: '123'
114
- });
115
- collectionName = samplePath.split('/')[0]; // Extract collection name
116
- } catch (e) {
117
- logger.log('WARN', `[getUserSocialPosts] Failed to get collection name from registry, using default: ${e.message}`);
118
- }
119
- }
120
-
121
- // Construct path: SignedInUserSocialPostData/{date}/{cid}/{cid}
122
- const socialDocRef = db.collection(collectionName)
123
- .doc(dataDate)
124
- .collection(String(effectiveCid))
125
- .doc(String(effectiveCid));
126
-
127
- const socialDoc = await socialDocRef.get();
128
-
129
- if (!socialDoc.exists) {
130
- return res.status(404).json({
131
- error: "Social posts not found",
132
- date: dataDate,
133
- effectiveCid: effectiveCid,
134
- isImpersonating: isImpersonating || false
135
- });
136
- }
137
-
138
- const socialData = socialDoc.data();
139
- const postsMap = socialData.posts || {};
140
-
141
- // Convert posts map to array and sort by createdAt (descending)
142
- const posts = Object.entries(postsMap)
143
- .map(([postId, postData]) => ({
144
- id: postId,
145
- ...postData
146
- }))
147
- .sort((a, b) => {
148
- const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
149
- const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
150
- return dateB - dateA; // Descending order
151
- })
152
- .slice(0, 50); // Limit to 50 most recent posts
153
-
154
- return res.status(200).json({
155
- posts,
156
- count: posts.length,
157
- date: dataDate,
158
- isFallback: isFallback,
159
- requestedDate: date || today,
160
- userCid: String(userCid),
161
- effectiveCid: effectiveCid,
162
- isImpersonating: isImpersonating || false
163
- });
164
-
165
- } catch (error) {
166
- logger.log('ERROR', `[getUserSocialPosts] Error fetching social posts for ${userCid}`, error);
167
- return res.status(500).json({ error: error.message });
168
- }
169
- }
170
-
171
- module.exports = {
172
- getUserSocialPosts
173
- };
174
-
@@ -1,87 +0,0 @@
1
- /**
2
- * @fileoverview Data Helpers - Re-export Hub
3
- * This file re-exports all helper functions from organized modules for backward compatibility.
4
- * All functions have been refactored into organized modules with migration support.
5
- *
6
- * New code should import directly from the organized modules:
7
- * - core/ - Core utilities (compression, data lookup, user status, path resolution)
8
- * - data/ - Data endpoints (portfolio, social, computation, instrument)
9
- * - profile/ - Profile endpoints (PI profile, user profile, profile views)
10
- * - watchlist/ - Watchlist endpoints (legacy, generation, analytics)
11
- * - search/ - Search endpoints (PI search, PI requests)
12
- * - recommendations/ - Recommendation endpoints
13
- * - metrics/ - Metrics endpoints
14
- */
15
-
16
- // Import from organized modules
17
- const portfolioHelpers = require('./data/portfolio_helpers');
18
- const socialHelpers = require('./data/social_helpers');
19
- const computationHelpers = require('./data/computation_helpers');
20
- const instrumentHelpers = require('./data/instrument_helpers');
21
- const piProfileHelpers = require('./profile/pi_profile_helpers');
22
- const userProfileHelpers = require('./profile/user_profile_helpers');
23
- const profileViewHelpers = require('./profile/profile_view_helpers');
24
- const watchlistDataHelpers = require('./watchlist/watchlist_data_helpers');
25
- const watchlistGenerationHelpers = require('./watchlist/watchlist_generation_helpers');
26
- const watchlistAnalyticsHelpers = require('./watchlist/watchlist_analytics_helpers');
27
- const piSearchHelpers = require('./search/pi_search_helpers');
28
- const piRequestHelpers = require('./search/pi_request_helpers');
29
- const recommendationHelpers = require('./recommendations/recommendation_helpers');
30
- const metricsHelpers = require('./metrics/personalized_metrics_helpers');
31
-
32
- // Import core helpers
33
- const { tryDecompress } = require('./core/compression_helpers');
34
- const {
35
- findLatestRankingsDate,
36
- findLatestPortfolioDate,
37
- findLatestComputationDate,
38
- findLatestPiPortfolioDate,
39
- findLatestPiHistoryDate
40
- } = require('./core/data_lookup_helpers');
41
- const { checkIfUserIsPI } = require('./core/user_status_helpers');
42
- const { checkPiInComputationDate } = require('./data/computation_helpers');
43
-
44
- // Re-export all functions for backward compatibility
45
- module.exports = {
46
- // Data helpers
47
- getUserPortfolio: portfolioHelpers.getUserPortfolio,
48
- getUserDataStatus: portfolioHelpers.getUserDataStatus,
49
- getUserSocialPosts: socialHelpers.getUserSocialPosts,
50
- getUserComputations: computationHelpers.getUserComputations,
51
- getInstrumentMappings: instrumentHelpers.getInstrumentMappings,
52
- checkPiInComputationDate,
53
-
54
- // Profile helpers
55
- getPiAnalytics: piProfileHelpers.getPiAnalytics,
56
- getPiProfile: piProfileHelpers.getPiProfile,
57
- getUserVerification: userProfileHelpers.getUserVerification,
58
- checkIfUserIsPopularInvestor: userProfileHelpers.checkIfUserIsPopularInvestor,
59
- trackProfileView: profileViewHelpers.trackProfileView,
60
-
61
- // Watchlist helpers
62
- getWatchlist: watchlistDataHelpers.getWatchlist,
63
- updateWatchlist: watchlistDataHelpers.updateWatchlist,
64
- autoGenerateWatchlist: watchlistGenerationHelpers.autoGenerateWatchlist,
65
- getWatchlistTriggerCounts: watchlistAnalyticsHelpers.getWatchlistTriggerCounts,
66
-
67
- // Search helpers
68
- searchPopularInvestors: piSearchHelpers.searchPopularInvestors,
69
- requestPiAddition: piRequestHelpers.requestPiAddition,
70
- checkPisInRankings: piRequestHelpers.checkPisInRankings,
71
-
72
- // Recommendations helpers
73
- getUserRecommendations: recommendationHelpers.getUserRecommendations,
74
-
75
- // Metrics helpers
76
- getSignedInUserPIPersonalizedMetrics: metricsHelpers.getSignedInUserPIPersonalizedMetrics,
77
- generateSamplePIPersonalizedMetrics: metricsHelpers.generateSamplePIPersonalizedMetrics,
78
-
79
- // Core utilities (for use in other helpers)
80
- tryDecompress,
81
- findLatestRankingsDate,
82
- findLatestPortfolioDate,
83
- findLatestComputationDate,
84
- findLatestPiPortfolioDate,
85
- findLatestPiHistoryDate,
86
- checkIfUserIsPI
87
- };