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,310 +0,0 @@
1
- /**
2
- * @fileoverview Watchlist Generation Helpers
3
- * Handles auto-generation of watchlists from copied PIs
4
- */
5
-
6
- const { FieldValue } = require('@google-cloud/firestore');
7
- const { findLatestPortfolioDate, findLatestComputationDate } = require('../core/data_lookup_helpers');
8
- const { tryDecompress } = require('../core/compression_helpers');
9
- const { getEffectiveCid, getCopiedPIsWithDevOverride } = require('../dev/dev_helpers');
10
- const { writeWithMigration } = require('../core/path_resolution_helpers');
11
- const { getPIMasterList, getPIUsernameFromMasterList } = require('../core/user_status_helpers');
12
-
13
- /**
14
- * POST /user/me/watchlist/auto-generate
15
- * Auto-generates watchlist based on copied PIs
16
- * Primary: Uses SignedInUserCopiedPIs computation (cheaper/faster)
17
- * Fallback: Reads portfolio AggregatedMirrors directly if computation not available
18
- */
19
- async function autoGenerateWatchlist(req, res, dependencies, config) {
20
- const { db, logger } = dependencies;
21
- const { userCid } = req.body;
22
-
23
- if (!userCid) {
24
- return res.status(400).json({ error: "Missing userCid" });
25
- }
26
-
27
- try {
28
- const effectiveCid = await getEffectiveCid(db, userCid, config, logger);
29
-
30
- let copiedPIs = [];
31
- let dataSource = 'unknown';
32
- let isDevOverride = false;
33
- const today = new Date().toISOString().split('T')[0];
34
-
35
- // === DEV OVERRIDE CHECK ===
36
- const devResult = await getCopiedPIsWithDevOverride(db, userCid, config, logger);
37
- if (devResult.isDevOverride) {
38
- copiedPIs = devResult.copiedPIs;
39
- dataSource = devResult.dataSource;
40
- isDevOverride = true;
41
- logger.log('INFO', `[autoGenerateWatchlist] Using DEV OVERRIDE for user ${userCid}, found ${copiedPIs.length} fake copied PIs`);
42
- }
43
-
44
- // === PRIMARY: Try to fetch from computation ===
45
- if (!isDevOverride) {
46
- const insightsCollection = config.unifiedInsightsCollection || 'unified_insights';
47
- const category = 'signed_in_user';
48
- const computationName = 'SignedInUserCopiedPIs';
49
- const resultsSub = config.resultsSubcollection || 'results';
50
- const compsSub = config.computationsSubcollection || 'computations';
51
-
52
- const computationDate = await findLatestComputationDate(
53
- db,
54
- insightsCollection,
55
- resultsSub,
56
- compsSub,
57
- category,
58
- computationName,
59
- effectiveCid,
60
- 30
61
- );
62
-
63
- if (computationDate) {
64
- const computationRef = db.collection(insightsCollection)
65
- .doc(computationDate)
66
- .collection('results')
67
- .doc(category)
68
- .collection('computations')
69
- .doc(computationName);
70
-
71
- const computationDoc = await computationRef.get();
72
-
73
- if (computationDoc.exists) {
74
- const rawData = computationDoc.data();
75
- const computationData = tryDecompress(rawData);
76
- const userResult = computationData && typeof computationData === 'object' ? computationData[String(effectiveCid)] : null;
77
-
78
- if (userResult && userResult.current && userResult.current.length > 0) {
79
- copiedPIs = userResult.current.map(cid => ({
80
- cid: Number(cid),
81
- username: 'Unknown'
82
- }));
83
- dataSource = 'computation';
84
- logger.log('INFO', `[autoGenerateWatchlist] Using computation data (date: ${computationDate}) for user ${userCid}, found ${copiedPIs.length} copied PIs`);
85
- }
86
- }
87
- }
88
- }
89
-
90
- // === FALLBACK: Read portfolio data directly ===
91
- if (!isDevOverride && copiedPIs.length === 0) {
92
- logger.log('INFO', `[autoGenerateWatchlist] Computation not available, falling back to direct portfolio read for user ${userCid}`);
93
-
94
- const { signedInUsersCollection } = config;
95
- const CANARY_BLOCK_ID = '19M';
96
- const portfolioDate = await findLatestPortfolioDate(db, signedInUsersCollection, userCid, 30);
97
-
98
- if (!portfolioDate) {
99
- return res.status(200).json({
100
- success: true,
101
- generated: 0,
102
- totalCopied: 0,
103
- dataSource: 'none',
104
- message: "No portfolio data found for this user"
105
- });
106
- }
107
-
108
- const partsRef = db.collection(signedInUsersCollection)
109
- .doc(CANARY_BLOCK_ID)
110
- .collection('snapshots')
111
- .doc(portfolioDate)
112
- .collection('parts');
113
-
114
- const partsSnapshot = await partsRef.get();
115
- let portfolioData = null;
116
-
117
- for (const partDoc of partsSnapshot.docs) {
118
- const partData = partDoc.data();
119
- if (partData && partData[String(userCid)]) {
120
- portfolioData = partData[String(userCid)];
121
- break;
122
- }
123
- }
124
-
125
- if (portfolioData) {
126
- const aggregatedMirrors = portfolioData.AggregatedMirrors || [];
127
- for (const mirror of aggregatedMirrors) {
128
- const parentCID = mirror.ParentCID;
129
- if (parentCID && parentCID > 0) {
130
- copiedPIs.push({
131
- cid: parentCID,
132
- username: mirror.ParentUsername || 'Unknown'
133
- });
134
- }
135
- }
136
- dataSource = 'portfolio';
137
- }
138
- }
139
-
140
- if (copiedPIs.length === 0) {
141
- return res.status(200).json({
142
- success: true,
143
- generated: 0,
144
- totalCopied: 0,
145
- dataSource: dataSource,
146
- message: "User is not currently copying any PIs"
147
- });
148
- }
149
-
150
- // Get usernames from master list (single source of truth)
151
- const collectionRegistry = dependencies.collectionRegistry || null;
152
- const investors = await getPIMasterList(db, collectionRegistry, logger);
153
-
154
- // Create watchlist items
155
- let matchedCount = 0;
156
- const watchlistItems = [];
157
-
158
- for (const copiedPI of copiedPIs) {
159
- const cidStr = String(copiedPI.cid);
160
- // Try to get username from master list
161
- const username = await getPIUsernameFromMasterList(db, cidStr, collectionRegistry, logger)
162
- || copiedPI.username
163
- || 'Unknown';
164
-
165
- // Check if PI exists in master list
166
- if (investors[cidStr]) {
167
- matchedCount++;
168
- }
169
-
170
- watchlistItems.push({
171
- cid: Number(cidStr),
172
- username: username,
173
- addedAt: new Date(),
174
- alertConfig: {
175
- newPositions: true,
176
- volatilityChanges: true,
177
- increasedRisk: true,
178
- newSector: true,
179
- increasedPositionSize: true,
180
- newSocialPost: true
181
- }
182
- });
183
- }
184
-
185
- // Create or update auto-generated watchlist using new path with migration
186
- const crypto = require('crypto');
187
- let watchlistId = `watchlist_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
188
-
189
- // Check if auto-generated watchlist exists
190
- const { readWithMigration } = require('../core/path_resolution_helpers');
191
- const existingResult = await readWithMigration(
192
- db,
193
- 'signedInUsers',
194
- 'watchlists',
195
- { cid: userCid },
196
- {
197
- isCollection: true,
198
- dataType: 'watchlists',
199
- config,
200
- logger,
201
- collectionRegistry: dependencies.collectionRegistry
202
- }
203
- );
204
-
205
- if (existingResult && existingResult.snapshot) {
206
- for (const doc of existingResult.snapshot.docs) {
207
- const data = doc.data();
208
- if (data.isAutoGenerated) {
209
- watchlistId = doc.id;
210
- // Update existing
211
- await writeWithMigration(
212
- db,
213
- 'signedInUsers',
214
- 'watchlists',
215
- { cid: userCid },
216
- {
217
- ...data,
218
- items: watchlistItems,
219
- updatedAt: FieldValue.serverTimestamp()
220
- },
221
- {
222
- isCollection: true,
223
- merge: true,
224
- dataType: 'watchlists',
225
- config,
226
- documentId: watchlistId,
227
- dualWrite: true,
228
- collectionRegistry: dependencies.collectionRegistry
229
- }
230
- );
231
- logger.log('SUCCESS', `[autoGenerateWatchlist] Updated auto-generated watchlist ${watchlistId} for user ${userCid}`);
232
- return res.status(200).json({
233
- success: true,
234
- generated: watchlistItems.length,
235
- totalCopied: copiedPIs.length,
236
- matchedInRankings: matchedCount,
237
- dataSource: dataSource,
238
- watchlistId: watchlistId
239
- });
240
- }
241
- }
242
- }
243
-
244
- // Create new watchlist
245
- const watchlistData = {
246
- id: watchlistId,
247
- name: 'My Copy Watchlist',
248
- type: 'static',
249
- visibility: 'private',
250
- createdBy: Number(userCid),
251
- createdAt: FieldValue.serverTimestamp(),
252
- updatedAt: FieldValue.serverTimestamp(),
253
- isAutoGenerated: true,
254
- copyCount: 0,
255
- items: watchlistItems
256
- };
257
-
258
- await writeWithMigration(
259
- db,
260
- 'signedInUsers',
261
- 'watchlists',
262
- { cid: userCid },
263
- watchlistData,
264
- {
265
- isCollection: true,
266
- merge: false,
267
- dataType: 'watchlists',
268
- config,
269
- collectionRegistry: dependencies.collectionRegistry,
270
- documentId: watchlistId,
271
- dualWrite: true
272
- }
273
- );
274
-
275
- // Update global rootdata collections for computation system
276
- if (watchlistItems && watchlistItems.length > 0) {
277
- const { updateWatchlistMembershipRootData, updatePIWatchlistData } = require('../rootdata/rootdata_aggregation_helpers');
278
- const today = new Date().toISOString().split('T')[0];
279
- const isPublic = false; // Auto-generated watchlists are always private
280
-
281
- for (const item of watchlistItems) {
282
- const piCid = String(item.cid);
283
- // Update global WatchlistMembershipData/{date} document
284
- await updateWatchlistMembershipRootData(db, logger, piCid, userCid, isPublic, today, 'add');
285
- // Update PI-centric PopularInvestors/{piCid}/watchlistData
286
- await updatePIWatchlistData(db, logger, piCid, userCid, today, 'add');
287
- }
288
- }
289
-
290
- logger.log('SUCCESS', `[autoGenerateWatchlist] Created auto-generated watchlist ${watchlistId} with ${watchlistItems.length} items for user ${userCid}`);
291
-
292
- return res.status(200).json({
293
- success: true,
294
- generated: watchlistItems.length,
295
- totalCopied: copiedPIs.length,
296
- matchedInRankings: matchedCount,
297
- dataSource: dataSource,
298
- watchlistId: watchlistId
299
- });
300
-
301
- } catch (error) {
302
- logger.log('ERROR', `[autoGenerateWatchlist] Error auto-generating watchlist for ${userCid}`, error);
303
- return res.status(500).json({ error: error.message });
304
- }
305
- }
306
-
307
- module.exports = {
308
- autoGenerateWatchlist
309
- };
310
-