bulltrackers-module 1.0.504 → 1.0.506

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 (49) hide show
  1. package/functions/generic-api/user-api/ADDING_LEGACY_ROUTES_GUIDE.md +345 -0
  2. package/functions/generic-api/user-api/CODE_REORGANIZATION_PLAN.md +320 -0
  3. package/functions/generic-api/user-api/COMPLETE_REFACTORING_PLAN.md +116 -0
  4. package/functions/generic-api/user-api/FIRESTORE_PATHS_INVENTORY.md +171 -0
  5. package/functions/generic-api/user-api/FIRESTORE_PATH_MIGRATION_REFERENCE.md +710 -0
  6. package/functions/generic-api/user-api/FIRESTORE_PATH_VALIDATION.md +109 -0
  7. package/functions/generic-api/user-api/MIGRATION_PLAN.md +499 -0
  8. package/functions/generic-api/user-api/README_MIGRATION.md +152 -0
  9. package/functions/generic-api/user-api/REFACTORING_COMPLETE.md +106 -0
  10. package/functions/generic-api/user-api/REFACTORING_STATUS.md +85 -0
  11. package/functions/generic-api/user-api/VERIFICATION_MIGRATION_NOTES.md +206 -0
  12. package/functions/generic-api/user-api/helpers/ORGANIZATION_COMPLETE.md +126 -0
  13. package/functions/generic-api/user-api/helpers/alerts/subscription_helpers.js +327 -0
  14. package/functions/generic-api/user-api/helpers/{test_alert_helpers.js → alerts/test_alert_helpers.js} +1 -1
  15. package/functions/generic-api/user-api/helpers/collection_helpers.js +23 -45
  16. package/functions/generic-api/user-api/helpers/core/compression_helpers.js +68 -0
  17. package/functions/generic-api/user-api/helpers/core/data_lookup_helpers.js +213 -0
  18. package/functions/generic-api/user-api/helpers/core/path_resolution_helpers.js +486 -0
  19. package/functions/generic-api/user-api/helpers/core/user_status_helpers.js +77 -0
  20. package/functions/generic-api/user-api/helpers/data/computation_helpers.js +299 -0
  21. package/functions/generic-api/user-api/helpers/data/instrument_helpers.js +55 -0
  22. package/functions/generic-api/user-api/helpers/data/portfolio_helpers.js +238 -0
  23. package/functions/generic-api/user-api/helpers/data/social_helpers.js +55 -0
  24. package/functions/generic-api/user-api/helpers/data_helpers.js +85 -2750
  25. package/functions/generic-api/user-api/helpers/{dev_helpers.js → dev/dev_helpers.js} +0 -1
  26. package/functions/generic-api/user-api/helpers/{on_demand_fetch_helpers.js → fetch/on_demand_fetch_helpers.js} +33 -115
  27. package/functions/generic-api/user-api/helpers/metrics/personalized_metrics_helpers.js +360 -0
  28. package/functions/generic-api/user-api/helpers/{notification_helpers.js → notifications/notification_helpers.js} +0 -1
  29. package/functions/generic-api/user-api/helpers/profile/pi_profile_helpers.js +200 -0
  30. package/functions/generic-api/user-api/helpers/profile/profile_view_helpers.js +125 -0
  31. package/functions/generic-api/user-api/helpers/profile/user_profile_helpers.js +178 -0
  32. package/functions/generic-api/user-api/helpers/recommendations/recommendation_helpers.js +65 -0
  33. package/functions/generic-api/user-api/helpers/{review_helpers.js → reviews/review_helpers.js} +23 -107
  34. package/functions/generic-api/user-api/helpers/search/pi_request_helpers.js +177 -0
  35. package/functions/generic-api/user-api/helpers/search/pi_search_helpers.js +70 -0
  36. package/functions/generic-api/user-api/helpers/{user_sync_helpers.js → sync/user_sync_helpers.js} +54 -127
  37. package/functions/generic-api/user-api/helpers/{verification_helpers.js → verification/verification_helpers.js} +4 -43
  38. package/functions/generic-api/user-api/helpers/watchlist/watchlist_analytics_helpers.js +95 -0
  39. package/functions/generic-api/user-api/helpers/watchlist/watchlist_data_helpers.js +139 -0
  40. package/functions/generic-api/user-api/helpers/watchlist/watchlist_generation_helpers.js +306 -0
  41. package/functions/generic-api/user-api/helpers/{watchlist_helpers.js → watchlist/watchlist_management_helpers.js} +62 -213
  42. package/functions/generic-api/user-api/index.js +9 -9
  43. package/functions/task-engine/handler_creator.js +7 -6
  44. package/package.json +1 -1
  45. package/functions/generic-api/API_MIGRATION_PLAN.md +0 -436
  46. package/functions/generic-api/user-api/helpers/FALLBACK_CONDITIONS.md +0 -98
  47. package/functions/generic-api/user-api/helpers/HISTORY_STORAGE_LOCATION.md +0 -66
  48. package/functions/generic-api/user-api/helpers/subscription_helpers.js +0 -512
  49. /package/functions/generic-api/user-api/helpers/{alert_helpers.js → alerts/alert_helpers.js} +0 -0
@@ -0,0 +1,345 @@
1
+ # Adding New Legacy Routes for Auto-Migration
2
+
3
+ ## How Easy Is It?
4
+
5
+ **Answer: Very Easy!** Adding a new legacy route requires updating **just 1 location** in most cases!
6
+
7
+ **Even Easier:** The system now automatically reads legacy paths from the collection registry, so you typically only need to add one line to the registry!
8
+
9
+ ---
10
+
11
+ ## Quick Overview
12
+
13
+ The auto-migration system works by:
14
+ 1. **Trying new path first** - Reads from the new CID-based path
15
+ 2. **Falling back to legacy** - If not found, tries legacy path(s) from registry
16
+ 3. **Auto-migrating** - If found in legacy, automatically migrates to new path
17
+
18
+ To add a new legacy route, you typically only need to:
19
+ 1. **`collection_registry.js`** - Add to `legacyPaths` array (that's it!)
20
+
21
+ **Only if you need complex conditional logic:**
22
+ 2. **`path_resolution_helpers.js`** - Add to `legacyPathMap` object (fallback)
23
+
24
+ ---
25
+
26
+ ## Step-by-Step Guide
27
+
28
+ ### ✅ EASIEST WAY: Just Add to Collection Registry (Recommended)
29
+
30
+ **File:** `Backend/Entrypoints/BullTrackers/Backend/Core/config/collection_registry.js`
31
+
32
+ **Location:** Find your collection definition and add to `legacyPaths` array
33
+
34
+ **Example:**
35
+ ```javascript
36
+ notifications: {
37
+ path: 'SignedInUsers/{cid}/notifications',
38
+ description: 'User notifications',
39
+ dynamicSegments: ['{cid}'],
40
+ status: 'active',
41
+ legacyPaths: [
42
+ 'user_notifications/{firebaseUid}/notifications', // Existing
43
+ 'user_notifications/{userCid}/notifications', // Existing
44
+ 'old_notifications/{userId}/items' // NEW - Just add here!
45
+ ]
46
+ }
47
+ ```
48
+
49
+ **That's it!** The system automatically uses the first legacy path from the registry. No other changes needed!
50
+
51
+ **How it works:**
52
+ - `readWithMigration()` automatically passes `category` and `subcategory` to `getLegacyPath()`
53
+ - `getLegacyPath()` first tries to get legacy paths from the registry
54
+ - If found, it automatically resolves dynamic segments and returns the path
55
+ - No need to update `path_resolution_helpers.js`!
56
+
57
+ ---
58
+
59
+ ### Alternative: Add to Legacy Path Map (For Complex Logic)
60
+
61
+ **Only needed if:** You need conditional logic or special handling that can't be expressed in a simple path template.
62
+
63
+ **File:** `Backend/Entrypoints/BullTrackers/Backend/Core/bulltrackers-module/functions/generic-api/user-api/helpers/core/path_resolution_helpers.js`
64
+
65
+ **Location:** Inside `getLegacyPath()` function, add to `legacyPathMap` object
66
+
67
+ **Example:**
68
+ ```javascript
69
+ function getLegacyPath(dataType, userCid, config = {}, params = {}) {
70
+ // ... registry lookup happens first ...
71
+
72
+ // Fallback to hardcoded map (only if registry doesn't have it)
73
+ const legacyPathMap = {
74
+ // ... existing mappings ...
75
+
76
+ // NEW - Add your new data type here (only if you need complex logic)
77
+ newDataType: params.someCondition
78
+ ? `old_collection/${userId}/items/${params.someParam}`
79
+ : `old_collection/${userId}/items`
80
+ };
81
+
82
+ return legacyPathMap[dataType] || null;
83
+ }
84
+ ```
85
+
86
+ **Note:** This is only needed for complex conditional logic. For simple paths, just use the registry!
87
+
88
+ ---
89
+
90
+ ## Real-World Example
91
+
92
+ Let's say you want to add support for a legacy "user preferences" collection:
93
+
94
+ ### Step 1: Update Collection Registry
95
+
96
+ ```javascript
97
+ // In collection_registry.js
98
+ signedInUsers: {
99
+ // ... other collections ...
100
+
101
+ preferences: {
102
+ path: 'SignedInUsers/{cid}/preferences/data',
103
+ description: 'User preferences (single document)',
104
+ dynamicSegments: ['{cid}'],
105
+ status: 'active',
106
+ legacyPaths: [
107
+ 'user_preferences/{userCid}', // Add this
108
+ 'old_prefs/{userId}/settings' // And this if multiple legacy formats exist
109
+ ]
110
+ }
111
+ }
112
+ ```
113
+
114
+ ### Step 2: (Optional) Update Path Resolution Helper
115
+
116
+ **Only needed if:** You need conditional logic that can't be expressed in a simple path template.
117
+
118
+ ```javascript
119
+ // In path_resolution_helpers.js
120
+ // This is only needed for complex conditional logic
121
+ // The registry lookup happens first, so this is just a fallback
122
+ ```
123
+
124
+ ### Step 3: Use It in Your API
125
+
126
+ ```javascript
127
+ // In your API helper file
128
+ const { readWithMigration } = require('./helpers/core/path_resolution_helpers');
129
+
130
+ async function getUserPreferences(req, res, dependencies, config) {
131
+ const { db, logger } = dependencies;
132
+ const { userCid } = req.query;
133
+
134
+ const result = await readWithMigration(
135
+ db,
136
+ 'signedInUsers', // category
137
+ 'preferences', // subcategory
138
+ { cid: userCid }, // params
139
+ {
140
+ isCollection: false, // It's a document
141
+ dataType: 'preferences', // This triggers legacy path lookup
142
+ config: config,
143
+ logger: logger
144
+ }
145
+ );
146
+
147
+ if (!result) {
148
+ return res.status(404).json({ error: "Preferences not found" });
149
+ }
150
+
151
+ // result.data contains the preferences
152
+ // result.source tells you if it came from 'new' or 'legacy'
153
+ // result.migrated tells you if it was auto-migrated
154
+
155
+ return res.status(200).json({
156
+ preferences: result.data,
157
+ source: result.source,
158
+ migrated: result.migrated || false
159
+ });
160
+ }
161
+ ```
162
+
163
+ **That's it!** The auto-migration happens automatically.
164
+
165
+ ---
166
+
167
+ ## Advanced: Multiple Legacy Formats
168
+
169
+ If you have multiple legacy formats for the same data type:
170
+
171
+ ### Option 1: Try Multiple Legacy Paths (Recommended)
172
+
173
+ Update `getLegacyPath()` to return an array or try multiple paths:
174
+
175
+ ```javascript
176
+ function getLegacyPath(dataType, userCid, config = {}, params = {}) {
177
+ const cid = String(userCid);
178
+
179
+ const legacyPathMap = {
180
+ // ... existing ...
181
+
182
+ preferences: [
183
+ `user_preferences/${cid}`, // Try first
184
+ `old_prefs/${cid}/settings`, // Try second
185
+ `legacy_preferences/${cid}/data` // Try third
186
+ ]
187
+ };
188
+
189
+ const path = legacyPathMap[dataType];
190
+ return Array.isArray(path) ? path[0] : path; // Return first for now
191
+ }
192
+ ```
193
+
194
+ Then update `readWithMigration()` to try all paths in the array (this would require a small code change).
195
+
196
+ ### Option 2: Use Conditional Logic
197
+
198
+ ```javascript
199
+ const legacyPathMap = {
200
+ preferences: params.useOldFormat
201
+ ? `old_prefs/${userId}/settings`
202
+ : `user_preferences/${cid}`
203
+ };
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Dynamic Segment Resolution
209
+
210
+ The system automatically resolves common dynamic segments:
211
+
212
+ - `{cid}` → User CID
213
+ - `{userCid}` → User CID
214
+ - `{firebaseUid}` → Firebase UID (from params)
215
+ - `{username}` → Username (from params)
216
+ - `{date}` → Date (from params)
217
+ - `{requestId}` → Request ID (from params or documentId)
218
+ - `{piCid}` → PI CID (from params)
219
+ - `{postId}` → Post ID (from params or documentId)
220
+ - `{watchlistId}` → Watchlist ID (from params)
221
+ - `{version}` → Version (from params)
222
+ - `{viewId}` → View ID (from params or documentId)
223
+ - `{reviewId}` → Review ID (from params or documentId)
224
+ - `{timestamp}` → Timestamp (from params or auto-generated)
225
+
226
+ **Just use these placeholders in your legacy path string!**
227
+
228
+ ---
229
+
230
+ ## Testing Your New Legacy Route
231
+
232
+ 1. **Create test data in legacy path:**
233
+ ```javascript
234
+ await db.collection('user_preferences').doc('29312236').set({
235
+ theme: 'dark',
236
+ notifications: true
237
+ });
238
+ ```
239
+
240
+ 2. **Call your API:**
241
+ ```javascript
242
+ GET /user/me/preferences?userCid=29312236
243
+ ```
244
+
245
+ 3. **Check the response:**
246
+ ```json
247
+ {
248
+ "preferences": { "theme": "dark", "notifications": true },
249
+ "source": "legacy",
250
+ "migrated": true
251
+ }
252
+ ```
253
+
254
+ 4. **Verify migration:**
255
+ ```javascript
256
+ const newDoc = await db.collection('SignedInUsers/29312236/preferences/data').get();
257
+ // Should exist with _migratedAt and _migratedFrom fields
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Common Patterns
263
+
264
+ ### Pattern 1: Simple CID-based Legacy Path
265
+ ```javascript
266
+ legacyPaths: ['old_collection/{userCid}']
267
+ legacyPathMap: { newDataType: `old_collection/${cid}` }
268
+ ```
269
+
270
+ ### Pattern 2: Firebase UID-based Legacy Path
271
+ ```javascript
272
+ legacyPaths: ['old_collection/{firebaseUid}']
273
+ legacyPathMap: {
274
+ newDataType: firebaseUid
275
+ ? `old_collection/${firebaseUid}`
276
+ : `old_collection/${cid}`
277
+ }
278
+ ```
279
+
280
+ ### Pattern 3: Nested Legacy Path
281
+ ```javascript
282
+ legacyPaths: ['old_collection/{userCid}/subcollection/{itemId}']
283
+ legacyPathMap: {
284
+ newDataType: `old_collection/${cid}/subcollection/${params.itemId || '{itemId}'}`
285
+ }
286
+ ```
287
+
288
+ ### Pattern 4: Date-based Legacy Path
289
+ ```javascript
290
+ legacyPaths: ['old_collection/{date}/{userCid}']
291
+ legacyPathMap: {
292
+ newDataType: `old_collection/${params.date || '{date}'}/${cid}`
293
+ }
294
+ ```
295
+
296
+ ---
297
+
298
+ ## Troubleshooting
299
+
300
+ ### Legacy Path Not Found?
301
+
302
+ 1. **Check `dataType` parameter** - Must match key in `legacyPathMap`
303
+ 2. **Check `params`** - Ensure all required dynamic segments are provided
304
+ 3. **Check path resolution** - Use logger to see what path is being tried
305
+
306
+ ### Migration Not Happening?
307
+
308
+ 1. **Check if new path exists** - Migration only happens if new path is available
309
+ 2. **Check logger** - Look for migration success/error messages
310
+ 3. **Check permissions** - Ensure write permissions to new path
311
+
312
+ ### Multiple Legacy Formats?
313
+
314
+ 1. **Add all to `legacyPaths` array** in registry
315
+ 2. **Use conditional logic** in `getLegacyPath()` to choose which to try first
316
+ 3. **Or modify `readWithMigration()`** to try multiple legacy paths sequentially
317
+
318
+ ---
319
+
320
+ ## Summary
321
+
322
+ **Adding a new legacy route is as simple as:**
323
+
324
+ ### ✅ EASIEST (Recommended):
325
+ 1. ✅ Add one line to `legacyPaths` array in `collection_registry.js`
326
+ 2. ✅ Use `readWithMigration()` in your API code (pass `category` and `subcategory`)
327
+
328
+ **Total time: ~30 seconds per legacy route!**
329
+
330
+ ### Alternative (For Complex Logic):
331
+ 1. ✅ Add one line to `legacyPaths` array in `collection_registry.js` (optional, for documentation)
332
+ 2. ✅ Add one line to `legacyPathMap` in `path_resolution_helpers.js` (if you need conditional logic)
333
+ 3. ✅ Use `readWithMigration()` in your API code
334
+
335
+ **Total time: ~2 minutes per legacy route!**
336
+
337
+ The system handles:
338
+ - ✅ Path resolution
339
+ - ✅ Fallback logic
340
+ - ✅ Auto-migration
341
+ - ✅ Error handling
342
+ - ✅ Logging
343
+
344
+ You just need to define the paths! 🎉
345
+
@@ -0,0 +1,320 @@
1
+ # User API Code Reorganization Plan
2
+
3
+ ## Current State Analysis
4
+
5
+ ### File Sizes and Complexity
6
+ - **data_helpers.js**: 2573 lines, 29 functions - **TOO LARGE**
7
+ - **review_helpers.js**: 545 lines, 4 functions - OK
8
+ - **watchlist_helpers.js**: 697 lines, 8 functions - OK
9
+ - **on_demand_fetch_helpers.js**: 554 lines, 3 functions - OK
10
+ - **user_sync_helpers.js**: 593 lines, 2 functions - OK
11
+ - **alert_helpers.js**: 355 lines, 7 functions - OK
12
+ - **subscription_helpers.js**: 328 lines, 5 functions - OK
13
+ - **verification_helpers.js**: 231 lines, 2 functions - OK
14
+ - **notification_helpers.js**: 131 lines, 3 functions - OK
15
+ - **dev_helpers.js**: 316 lines, 6 functions - OK
16
+ - **collection_helpers.js**: 216 lines, 8 functions - OK
17
+
18
+ ### Functions in data_helpers.js (29 total)
19
+ 1. `tryDecompress` - Utility
20
+ 2. `findLatestPortfolioDate` - Data lookup utility
21
+ 3. `findLatestComputationDate` - Data lookup utility
22
+ 4. `checkIfUserIsPI` - User status check
23
+ 5. `findLatestRankingsDate` - Data lookup utility
24
+ 6. `findLatestPiPortfolioDate` - Data lookup utility
25
+ 7. `findLatestPiHistoryDate` - Data lookup utility
26
+ 8. `getPiAnalytics` - PI analytics endpoint
27
+ 9. `getUserRecommendations` - User recommendations endpoint
28
+ 10. `getUserDataStatus` - User data status endpoint
29
+ 11. `getWatchlist` - Watchlist endpoint (legacy)
30
+ 12. `updateWatchlist` - Watchlist endpoint (legacy)
31
+ 13. `getUserPortfolio` - User portfolio endpoint
32
+ 14. `getUserSocialPosts` - User social posts endpoint
33
+ 15. `getInstrumentMappings` - Instrument mappings endpoint
34
+ 16. `getUserVerification` - User verification endpoint
35
+ 17. `getUserComputations` - User computations endpoint
36
+ 18. `autoGenerateWatchlist` - Watchlist generation endpoint
37
+ 19. `searchPopularInvestors` - PI search endpoint
38
+ 20. `requestPiAddition` - PI request endpoint
39
+ 21. `getWatchlistTriggerCounts` - Watchlist analytics endpoint
40
+ 22. `checkPisInRankings` - PI rankings check endpoint
41
+ 23. `checkPiInComputationDate` - Computation check utility
42
+ 24. `getPiProfile` - PI profile endpoint
43
+ 25. `checkIfUserIsPopularInvestor` - User status endpoint
44
+ 26. `trackProfileView` - Profile view tracking endpoint
45
+ 27. `generateSamplePIPersonalizedMetrics` - Sample data generator
46
+ 28. `getSignedInUserPIPersonalizedMetrics` - Personalized metrics endpoint
47
+
48
+ ## Proposed Reorganization
49
+
50
+ ### New File Structure
51
+
52
+ ```
53
+ user-api/
54
+ ├── index.js (router - unchanged)
55
+ ├── helpers/
56
+ │ ├── core/
57
+ │ │ ├── compression_helpers.js (tryDecompress)
58
+ │ │ ├── data_lookup_helpers.js (findLatest* functions)
59
+ │ │ ├── user_status_helpers.js (checkIfUserIsPI, checkIfUserIsPopularInvestor)
60
+ │ │ └── path_resolution_helpers.js (enhanced collection_helpers)
61
+ │ ├── data/
62
+ │ │ ├── portfolio_helpers.js (getUserPortfolio, getUserDataStatus)
63
+ │ │ ├── social_helpers.js (getUserSocialPosts)
64
+ │ │ ├── computation_helpers.js (getUserComputations, checkPiInComputationDate)
65
+ │ │ └── instrument_helpers.js (getInstrumentMappings)
66
+ │ ├── profile/
67
+ │ │ ├── pi_profile_helpers.js (getPiProfile, getPiAnalytics)
68
+ │ │ ├── user_profile_helpers.js (getUserVerification, checkIfUserIsPopularInvestor)
69
+ │ │ └── profile_view_helpers.js (trackProfileView)
70
+ │ ├── watchlist/
71
+ │ │ ├── watchlist_data_helpers.js (getWatchlist, updateWatchlist - legacy)
72
+ │ │ ├── watchlist_generation_helpers.js (autoGenerateWatchlist)
73
+ │ │ └── watchlist_analytics_helpers.js (getWatchlistTriggerCounts)
74
+ │ ├── recommendations/
75
+ │ │ └── recommendation_helpers.js (getUserRecommendations)
76
+ │ ├── search/
77
+ │ │ ├── pi_search_helpers.js (searchPopularInvestors)
78
+ │ │ └── pi_request_helpers.js (requestPiAddition, checkPisInRankings)
79
+ │ ├── metrics/
80
+ │ │ └── personalized_metrics_helpers.js (getSignedInUserPIPersonalizedMetrics, generateSamplePIPersonalizedMetrics)
81
+ │ ├── notifications/
82
+ │ │ └── notification_helpers.js (unchanged)
83
+ │ ├── alerts/
84
+ │ │ └── alert_helpers.js (unchanged)
85
+ │ ├── watchlists/
86
+ │ │ └── watchlist_helpers.js (unchanged)
87
+ │ ├── subscriptions/
88
+ │ │ └── subscription_helpers.js (unchanged)
89
+ │ ├── verification/
90
+ │ │ └── verification_helpers.js (unchanged)
91
+ │ ├── reviews/
92
+ │ │ └── review_helpers.js (unchanged)
93
+ │ ├── sync/
94
+ │ │ └── user_sync_helpers.js (unchanged)
95
+ │ ├── fetch/
96
+ │ │ └── on_demand_fetch_helpers.js (unchanged)
97
+ │ ├── dev/
98
+ │ │ └── dev_helpers.js (unchanged)
99
+ │ └── collection_helpers.js (keep for backward compatibility, re-export from core/)
100
+ ```
101
+
102
+ ## Detailed Breakdown
103
+
104
+ ### 1. core/compression_helpers.js
105
+ **Purpose:** Compression/decompression utilities
106
+ **Functions:**
107
+ - `tryDecompress(data)` - Decompress computation results
108
+
109
+ **Dependencies:** `zlib`
110
+
111
+ ### 2. core/data_lookup_helpers.js
112
+ **Purpose:** Find latest available data dates
113
+ **Functions:**
114
+ - `findLatestPortfolioDate(db, signedInUsersCollection, userCid, maxDaysBack)`
115
+ - `findLatestComputationDate(db, insightsCollection, resultsSub, compsSub, category, computationName, userCid, maxDaysBack)`
116
+ - `findLatestRankingsDate(db, rankingsCollection, maxDaysBack)`
117
+ - `findLatestPiPortfolioDate(db, piPortfoliosCollection, userCid, maxDaysBack)`
118
+ - `findLatestPiHistoryDate(db, piHistoryCollection, userCid, maxDaysBack)`
119
+
120
+ **Dependencies:** `@google-cloud/firestore`
121
+
122
+ ### 3. core/user_status_helpers.js
123
+ **Purpose:** Check user status (PI, signed-in, etc.)
124
+ **Functions:**
125
+ - `checkIfUserIsPI(db, userCid, config, logger)`
126
+ - `checkIfUserIsPopularInvestor(req, res, dependencies, config)` (endpoint handler)
127
+
128
+ **Dependencies:** `dev_helpers`, `data_lookup_helpers`
129
+
130
+ ### 4. core/path_resolution_helpers.js
131
+ **Purpose:** Enhanced path resolution with migration support
132
+ **Functions:**
133
+ - `getCidFromFirebaseUid(db, firebaseUid)` - from collection_helpers
134
+ - `getCollectionPath(collectionRegistry, category, subcategory, params)` - enhanced
135
+ - `readWithMigration(db, newPath, legacyPath, options)` - new, with auto-migration
136
+ - `writeDual(db, newPath, legacyPath, data, options)` - enhanced
137
+ - `migrateUserData(db, userCid, dataType, logger)` - new, batch migration
138
+
139
+ **Dependencies:** `collection_registry`, `@google-cloud/firestore`
140
+
141
+ ### 5. data/portfolio_helpers.js
142
+ **Purpose:** User portfolio data endpoints
143
+ **Functions:**
144
+ - `getUserPortfolio(req, res, dependencies, config)`
145
+ - `getUserDataStatus(req, res, dependencies, config)`
146
+
147
+ **Dependencies:** `core/data_lookup_helpers`, `core/path_resolution_helpers`, `core/compression_helpers`
148
+
149
+ ### 6. data/social_helpers.js
150
+ **Purpose:** User social posts endpoints
151
+ **Functions:**
152
+ - `getUserSocialPosts(req, res, dependencies, config)`
153
+
154
+ **Dependencies:** `core/data_lookup_helpers`, `core/path_resolution_helpers`, `core/compression_helpers`
155
+
156
+ ### 7. data/computation_helpers.js
157
+ **Purpose:** Computation result endpoints
158
+ **Functions:**
159
+ - `getUserComputations(req, res, dependencies, config)`
160
+ - `checkPiInComputationDate(db, insightsCollection, resultsSub, compsSub, category, computationName, dateStr, cidStr, logger)`
161
+
162
+ **Dependencies:** `core/data_lookup_helpers`, `core/compression_helpers`, `core/path_resolution_helpers`
163
+
164
+ ### 8. data/instrument_helpers.js
165
+ **Purpose:** Instrument mappings endpoint
166
+ **Functions:**
167
+ - `getInstrumentMappings(req, res, dependencies, config)`
168
+
169
+ **Dependencies:** `@google-cloud/firestore`
170
+
171
+ ### 9. profile/pi_profile_helpers.js
172
+ **Purpose:** Popular Investor profile endpoints
173
+ **Functions:**
174
+ - `getPiProfile(req, res, dependencies, config)`
175
+ - `getPiAnalytics(req, res, dependencies, config)`
176
+
177
+ **Dependencies:** `core/data_lookup_helpers`, `core/compression_helpers`, `review_helpers`, `data/computation_helpers`
178
+
179
+ ### 10. profile/user_profile_helpers.js
180
+ **Purpose:** Signed-in user profile endpoints
181
+ **Functions:**
182
+ - `getUserVerification(req, res, dependencies, config)`
183
+ - `checkIfUserIsPopularInvestor(req, res, dependencies, config)` - moved from data_helpers
184
+
185
+ **Dependencies:** `core/user_status_helpers`, `verification_helpers`
186
+
187
+ ### 11. profile/profile_view_helpers.js
188
+ **Purpose:** Profile view tracking
189
+ **Functions:**
190
+ - `trackProfileView(req, res, dependencies, config)`
191
+
192
+ **Dependencies:** `@google-cloud/firestore`
193
+
194
+ ### 12. watchlist/watchlist_data_helpers.js
195
+ **Purpose:** Legacy watchlist endpoints (for backward compatibility)
196
+ **Functions:**
197
+ - `getWatchlist(req, res, dependencies, config)` - legacy single watchlist
198
+ - `updateWatchlist(req, res, dependencies, config)` - legacy single watchlist
199
+
200
+ **Dependencies:** `watchlist_helpers` (new multi-watchlist system)
201
+
202
+ ### 13. watchlist/watchlist_generation_helpers.js
203
+ **Purpose:** Watchlist auto-generation
204
+ **Functions:**
205
+ - `autoGenerateWatchlist(req, res, dependencies, config)`
206
+
207
+ **Dependencies:** `core/data_lookup_helpers`, `core/compression_helpers`, `watchlist_helpers`
208
+
209
+ ### 14. watchlist/watchlist_analytics_helpers.js
210
+ **Purpose:** Watchlist analytics
211
+ **Functions:**
212
+ - `getWatchlistTriggerCounts(req, res, dependencies, config)`
213
+
214
+ **Dependencies:** `core/data_lookup_helpers`, `core/compression_helpers`, `watchlist_helpers`
215
+
216
+ ### 15. recommendations/recommendation_helpers.js
217
+ **Purpose:** User recommendations
218
+ **Functions:**
219
+ - `getUserRecommendations(req, res, dependencies, config, type)`
220
+
221
+ **Dependencies:** `@google-cloud/firestore`
222
+
223
+ ### 16. search/pi_search_helpers.js
224
+ **Purpose:** Popular Investor search
225
+ **Functions:**
226
+ - `searchPopularInvestors(req, res, dependencies, config)`
227
+
228
+ **Dependencies:** `core/data_lookup_helpers`, `@google-cloud/firestore`
229
+
230
+ ### 17. search/pi_request_helpers.js
231
+ **Purpose:** PI addition requests and rankings checks
232
+ **Functions:**
233
+ - `requestPiAddition(req, res, dependencies, config)`
234
+ - `checkPisInRankings(req, res, dependencies, config)`
235
+
236
+ **Dependencies:** `core/data_lookup_helpers`, `@google-cloud/firestore`
237
+
238
+ ### 18. metrics/personalized_metrics_helpers.js
239
+ **Purpose:** Personalized metrics for signed-in users
240
+ **Functions:**
241
+ - `getSignedInUserPIPersonalizedMetrics(req, res, dependencies, config)`
242
+ - `generateSamplePIPersonalizedMetrics(userCid, rankEntry)` - internal helper
243
+
244
+ **Dependencies:** `core/data_lookup_helpers`, `core/compression_helpers`, `core/user_status_helpers`, `review_helpers`
245
+
246
+ ## Migration Strategy
247
+
248
+ ### Phase 1: Create Core Helpers
249
+ 1. Create `helpers/core/` directory
250
+ 2. Extract compression utilities → `compression_helpers.js`
251
+ 3. Extract data lookup utilities → `data_lookup_helpers.js`
252
+ 4. Extract user status checks → `user_status_helpers.js`
253
+ 5. Enhance path resolution → `path_resolution_helpers.js`
254
+
255
+ ### Phase 2: Create Data Helpers
256
+ 1. Create `helpers/data/` directory
257
+ 2. Extract portfolio functions → `portfolio_helpers.js`
258
+ 3. Extract social functions → `social_helpers.js`
259
+ 4. Extract computation functions → `computation_helpers.js`
260
+ 5. Extract instrument functions → `instrument_helpers.js`
261
+
262
+ ### Phase 3: Create Profile Helpers
263
+ 1. Create `helpers/profile/` directory
264
+ 2. Extract PI profile functions → `pi_profile_helpers.js`
265
+ 3. Extract user profile functions → `user_profile_helpers.js`
266
+ 4. Extract profile view functions → `profile_view_helpers.js`
267
+
268
+ ### Phase 4: Create Feature-Specific Helpers
269
+ 1. Create `helpers/watchlist/` directory
270
+ 2. Create `helpers/recommendations/` directory
271
+ 3. Create `helpers/search/` directory
272
+ 4. Create `helpers/metrics/` directory
273
+ 5. Extract respective functions
274
+
275
+ ### Phase 5: Update Imports
276
+ 1. Update `index.js` to import from new locations
277
+ 2. Update all helper files to use new core utilities
278
+ 3. Keep `collection_helpers.js` for backward compatibility (re-export from core)
279
+
280
+ ### Phase 6: Cleanup
281
+ 1. Delete old `data_helpers.js`
282
+ 2. Update any external references
283
+ 3. Run tests to ensure everything works
284
+
285
+ ## Benefits
286
+
287
+ 1. **Modularity**: Each file has a clear, single responsibility
288
+ 2. **Maintainability**: Easier to find and update specific functionality
289
+ 3. **Testability**: Smaller files are easier to test
290
+ 4. **Reusability**: Core utilities can be shared across modules
291
+ 5. **Readability**: Clear organization makes code easier to understand
292
+ 6. **Scalability**: Easy to add new features without bloating existing files
293
+
294
+ ## File Size Targets
295
+
296
+ - **Core helpers**: 100-300 lines each
297
+ - **Data helpers**: 200-400 lines each
298
+ - **Profile helpers**: 200-500 lines each
299
+ - **Feature helpers**: 200-400 lines each
300
+ - **Total**: Better organized, easier to navigate
301
+
302
+ ## Implementation Order
303
+
304
+ 1. ✅ Create migration plan (done)
305
+ 2. ⏳ Create core helpers (compression, data lookup, user status, path resolution)
306
+ 3. ⏳ Create data helpers (portfolio, social, computation, instrument)
307
+ 4. ⏳ Create profile helpers (PI profile, user profile, profile view)
308
+ 5. ⏳ Create feature helpers (watchlist, recommendations, search, metrics)
309
+ 6. ⏳ Update index.js imports
310
+ 7. ⏳ Update all helper files to use new structure
311
+ 8. ⏳ Test all endpoints
312
+ 9. ⏳ Remove old data_helpers.js
313
+
314
+ ## Notes
315
+
316
+ - Keep backward compatibility during migration
317
+ - Use re-exports where needed to avoid breaking changes
318
+ - Update tests as we go
319
+ - Document new structure in README
320
+