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.
- package/functions/alert-system/helpers/alert_helpers.js +69 -77
- package/functions/alert-system/index.js +19 -29
- package/functions/api-v2/helpers/notification_helpers.js +187 -0
- package/functions/computation-system/helpers/computation_worker.js +1 -1
- package/functions/task-engine/helpers/popular_investor_helpers.js +11 -7
- package/index.js +0 -5
- package/package.json +1 -2
- package/functions/old-generic-api/admin-api/index.js +0 -895
- package/functions/old-generic-api/helpers/api_helpers.js +0 -457
- package/functions/old-generic-api/index.js +0 -204
- package/functions/old-generic-api/user-api/helpers/alerts/alert_helpers.js +0 -355
- package/functions/old-generic-api/user-api/helpers/alerts/subscription_helpers.js +0 -327
- package/functions/old-generic-api/user-api/helpers/alerts/test_alert_helpers.js +0 -212
- package/functions/old-generic-api/user-api/helpers/collection_helpers.js +0 -193
- package/functions/old-generic-api/user-api/helpers/core/compression_helpers.js +0 -68
- package/functions/old-generic-api/user-api/helpers/core/data_lookup_helpers.js +0 -256
- package/functions/old-generic-api/user-api/helpers/core/path_resolution_helpers.js +0 -640
- package/functions/old-generic-api/user-api/helpers/core/user_status_helpers.js +0 -195
- package/functions/old-generic-api/user-api/helpers/data/computation_helpers.js +0 -503
- package/functions/old-generic-api/user-api/helpers/data/instrument_helpers.js +0 -55
- package/functions/old-generic-api/user-api/helpers/data/portfolio_helpers.js +0 -245
- package/functions/old-generic-api/user-api/helpers/data/social_helpers.js +0 -174
- package/functions/old-generic-api/user-api/helpers/data_helpers.js +0 -87
- package/functions/old-generic-api/user-api/helpers/dev/dev_helpers.js +0 -336
- package/functions/old-generic-api/user-api/helpers/fetch/on_demand_fetch_helpers.js +0 -615
- package/functions/old-generic-api/user-api/helpers/metrics/personalized_metrics_helpers.js +0 -231
- package/functions/old-generic-api/user-api/helpers/notifications/notification_helpers.js +0 -641
- package/functions/old-generic-api/user-api/helpers/profile/pi_profile_helpers.js +0 -182
- package/functions/old-generic-api/user-api/helpers/profile/profile_view_helpers.js +0 -137
- package/functions/old-generic-api/user-api/helpers/profile/user_profile_helpers.js +0 -190
- package/functions/old-generic-api/user-api/helpers/recommendations/recommendation_helpers.js +0 -66
- package/functions/old-generic-api/user-api/helpers/reviews/review_helpers.js +0 -550
- package/functions/old-generic-api/user-api/helpers/rootdata/rootdata_aggregation_helpers.js +0 -378
- package/functions/old-generic-api/user-api/helpers/search/pi_request_helpers.js +0 -295
- package/functions/old-generic-api/user-api/helpers/search/pi_search_helpers.js +0 -162
- package/functions/old-generic-api/user-api/helpers/sync/user_sync_helpers.js +0 -677
- package/functions/old-generic-api/user-api/helpers/verification/verification_helpers.js +0 -323
- package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_analytics_helpers.js +0 -96
- package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_data_helpers.js +0 -141
- package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_generation_helpers.js +0 -310
- package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_management_helpers.js +0 -829
- package/functions/old-generic-api/user-api/index.js +0 -109
package/functions/old-generic-api/user-api/helpers/watchlist/watchlist_generation_helpers.js
DELETED
|
@@ -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
|
-
|