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
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const { FieldValue } = require('@google-cloud/firestore');
|
|
7
7
|
const zlib = require('zlib');
|
|
8
8
|
const { getAlertTypeByComputation, generateAlertMessage } = require('./alert_type_registry');
|
|
9
|
-
|
|
9
|
+
// Migration helpers removed - write directly to new path
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Process alerts for a specific PI from computation results
|
|
@@ -65,39 +65,30 @@ async function processAlertForPI(db, logger, piCid, alertType, computationMetada
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
// Write to alerts collection (not notifications) - alerts are separate from system notifications
|
|
68
|
-
//
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
{
|
|
92
|
-
isCollection: true,
|
|
93
|
-
merge: false,
|
|
94
|
-
dataType: 'alerts',
|
|
95
|
-
documentId: notificationId,
|
|
96
|
-
dualWrite: false, // Disable dual write - we're fully migrated to new path
|
|
97
|
-
config,
|
|
98
|
-
collectionRegistry
|
|
99
|
-
}
|
|
100
|
-
).catch(err => {
|
|
68
|
+
// Write directly to new path: SignedInUsers/{userCid}/alerts/{alertId}
|
|
69
|
+
const alertData = {
|
|
70
|
+
alertId: notificationId,
|
|
71
|
+
piCid: Number(piCid),
|
|
72
|
+
piUsername: piUsername,
|
|
73
|
+
alertType: alertType.id,
|
|
74
|
+
alertTypeName: alertType.name,
|
|
75
|
+
message: alertMessage,
|
|
76
|
+
severity: alertType.severity,
|
|
77
|
+
watchlistId: subscription.watchlistId,
|
|
78
|
+
watchlistName: subscription.watchlistName,
|
|
79
|
+
read: false,
|
|
80
|
+
createdAt: FieldValue.serverTimestamp(),
|
|
81
|
+
computationDate: computationDate,
|
|
82
|
+
computationName: alertType.computationName,
|
|
83
|
+
...(computationMetadata || {})
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const writePromise = db.collection('SignedInUsers')
|
|
87
|
+
.doc(String(userCid))
|
|
88
|
+
.collection('alerts')
|
|
89
|
+
.doc(notificationId)
|
|
90
|
+
.set(alertData)
|
|
91
|
+
.catch(err => {
|
|
101
92
|
logger.log('ERROR', `[processAlertForPI] Failed to write alert for CID ${userCid}: ${err.message}`, err);
|
|
102
93
|
throw err; // Re-throw so we know if writes are failing
|
|
103
94
|
});
|
|
@@ -114,9 +105,21 @@ async function processAlertForPI(db, logger, piCid, alertType, computationMetada
|
|
|
114
105
|
// 6. Update global rootdata collection for computation system
|
|
115
106
|
// (Wrap in try-catch to prevent crashing the alert if metrics fail)
|
|
116
107
|
try {
|
|
117
|
-
const {
|
|
108
|
+
const { runRootDataIndexer } = require('../../root-data-indexer/index');
|
|
118
109
|
const triggeredUserCids = subscriptions.map(s => s.userCid);
|
|
119
|
-
|
|
110
|
+
|
|
111
|
+
// Update alert history root data by running root data indexer for the specific date
|
|
112
|
+
// The indexer will detect and update PIAlertHistoryData availability
|
|
113
|
+
const indexerConfig = {
|
|
114
|
+
availabilityCollection: 'root_data_availability',
|
|
115
|
+
targetDate: computationDate,
|
|
116
|
+
collections: {
|
|
117
|
+
piAlertHistory: 'PIAlertHistoryData'
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
await runRootDataIndexer(indexerConfig, { db, logger });
|
|
122
|
+
logger.log('INFO', `[processAlertForPI] Updated root data indexer for date ${computationDate}`);
|
|
120
123
|
} catch (e) {
|
|
121
124
|
logger.log('WARN', `[processAlertForPI] Failed to update history rootdata: ${e.message}`);
|
|
122
125
|
}
|
|
@@ -158,14 +161,18 @@ async function findSubscriptionsForPI(db, logger, piCid, alertTypeId, computatio
|
|
|
158
161
|
// Check for developer accounts with pretendSubscribedToAllAlerts flag enabled
|
|
159
162
|
// This allows developers to test the alert system without manually configuring subscriptions
|
|
160
163
|
try {
|
|
161
|
-
const {
|
|
162
|
-
const {
|
|
164
|
+
const { isDeveloper } = require('../../api-v2/helpers/data-fetchers/firestore.js');
|
|
165
|
+
const { fetchPopularInvestorMasterList } = require('../../api-v2/helpers/data-fetchers/firestore.js');
|
|
163
166
|
const config = dependencies.config || {};
|
|
164
|
-
const collectionRegistry = dependencies.collectionRegistry || null;
|
|
165
167
|
|
|
166
|
-
// Get
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
// Get PI username from master list
|
|
169
|
+
let piUsername = `PI-${piCid}`;
|
|
170
|
+
try {
|
|
171
|
+
const piData = await fetchPopularInvestorMasterList(db, String(piCid));
|
|
172
|
+
piUsername = piData.username || piUsername;
|
|
173
|
+
} catch (e) {
|
|
174
|
+
// PI not in master list, use fallback
|
|
175
|
+
}
|
|
169
176
|
|
|
170
177
|
// Default alert config with all alert types enabled (for dev override)
|
|
171
178
|
const allAlertsEnabledConfig = {
|
|
@@ -186,8 +193,9 @@ async function findSubscriptionsForPI(db, logger, piCid, alertTypeId, computatio
|
|
|
186
193
|
for (const devOverrideDoc of devOverridesSnapshot.docs) {
|
|
187
194
|
const devUserCid = Number(devOverrideDoc.id);
|
|
188
195
|
|
|
189
|
-
// Verify this is actually a developer account (security check)
|
|
190
|
-
|
|
196
|
+
// Verify this is actually a developer account (security check) - using api-v2 helper
|
|
197
|
+
const isDev = await isDeveloper(db, String(devUserCid));
|
|
198
|
+
if (!isDev) {
|
|
191
199
|
continue;
|
|
192
200
|
}
|
|
193
201
|
|
|
@@ -244,44 +252,26 @@ async function findSubscriptionsForPI(db, logger, piCid, alertTypeId, computatio
|
|
|
244
252
|
}
|
|
245
253
|
|
|
246
254
|
// Step 2: For each user, read their watchlists from SignedInUsers/{cid}/watchlists
|
|
247
|
-
|
|
248
|
-
// const { collectionRegistry } = dependencies; // Already destructured above
|
|
249
|
-
// const config = dependencies.config || {}; // Already defined
|
|
250
|
-
|
|
255
|
+
// Read directly from new path (no migration needed)
|
|
251
256
|
for (const userCidStr of userCids) {
|
|
252
257
|
try {
|
|
253
258
|
const userCid = Number(userCidStr);
|
|
254
259
|
|
|
255
260
|
// Read all watchlists for this user from new path
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
'
|
|
259
|
-
|
|
260
|
-
{ cid: userCid },
|
|
261
|
-
{
|
|
262
|
-
isCollection: true,
|
|
263
|
-
dataType: 'watchlists',
|
|
264
|
-
config,
|
|
265
|
-
logger,
|
|
266
|
-
collectionRegistry: collectionRegistry
|
|
267
|
-
}
|
|
268
|
-
);
|
|
261
|
+
const watchlistsSnapshot = await db.collection('SignedInUsers')
|
|
262
|
+
.doc(String(userCid))
|
|
263
|
+
.collection('watchlists')
|
|
264
|
+
.get();
|
|
269
265
|
|
|
270
|
-
if (
|
|
266
|
+
if (watchlistsSnapshot.empty) {
|
|
271
267
|
continue;
|
|
272
268
|
}
|
|
273
269
|
|
|
274
|
-
// Get watchlists from snapshot
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
...doc.data()
|
|
280
|
-
}));
|
|
281
|
-
} else if (watchlistsResult.data) {
|
|
282
|
-
// If it's a single document, wrap it
|
|
283
|
-
watchlists = [watchlistsResult.data];
|
|
284
|
-
}
|
|
270
|
+
// Get watchlists from snapshot
|
|
271
|
+
const watchlists = watchlistsSnapshot.docs.map(doc => ({
|
|
272
|
+
id: doc.id,
|
|
273
|
+
...doc.data()
|
|
274
|
+
}));
|
|
285
275
|
|
|
286
276
|
// Step 3: Check each watchlist for the PI and alert config
|
|
287
277
|
for (const watchlistData of watchlists) {
|
|
@@ -337,11 +327,13 @@ function shouldTriggerAlert(subscription, alertTypeId) {
|
|
|
337
327
|
*/
|
|
338
328
|
async function getPIUsername(db, piCid) {
|
|
339
329
|
try {
|
|
340
|
-
// Try to get from master list first (single source of truth)
|
|
341
|
-
const {
|
|
342
|
-
const
|
|
330
|
+
// Try to get from master list first (single source of truth) - using api-v2 helper
|
|
331
|
+
const { fetchPopularInvestorMasterList } = require('../../api-v2/helpers/data-fetchers/firestore.js');
|
|
332
|
+
const piData = await fetchPopularInvestorMasterList(db, String(piCid));
|
|
343
333
|
|
|
344
|
-
if (username)
|
|
334
|
+
if (piData && piData.username) {
|
|
335
|
+
return piData.username;
|
|
336
|
+
}
|
|
345
337
|
|
|
346
338
|
// Fallback: try to get from any subscription
|
|
347
339
|
const subscriptionsSnapshot = await db.collection('watchlist_subscriptions')
|
|
@@ -427,35 +427,25 @@ async function sendAllClearNotification(db, logger, userCid, piCid, piUsername,
|
|
|
427
427
|
};
|
|
428
428
|
|
|
429
429
|
// Write to alerts collection (not notifications) - alerts are separate from system notifications
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
{
|
|
450
|
-
isCollection: true,
|
|
451
|
-
merge: false,
|
|
452
|
-
dataType: 'alerts',
|
|
453
|
-
documentId: notificationId,
|
|
454
|
-
dualWrite: false, // Disable dual write - we're fully migrated to new path
|
|
455
|
-
config,
|
|
456
|
-
collectionRegistry
|
|
457
|
-
}
|
|
458
|
-
);
|
|
430
|
+
// Write directly to new path: SignedInUsers/{userCid}/alerts/{alertId}
|
|
431
|
+
const alertData = {
|
|
432
|
+
alertId: notificationId,
|
|
433
|
+
piCid: Number(piCid),
|
|
434
|
+
piUsername: piUsername,
|
|
435
|
+
alertType: 'all_clear',
|
|
436
|
+
alertTypeName: 'All Clear',
|
|
437
|
+
message: `${piUsername} was processed, all clear today!`,
|
|
438
|
+
severity: 'info',
|
|
439
|
+
read: false,
|
|
440
|
+
createdAt: FieldValue.serverTimestamp(),
|
|
441
|
+
computationDate: date
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
await db.collection('SignedInUsers')
|
|
445
|
+
.doc(String(userCid))
|
|
446
|
+
.collection('alerts')
|
|
447
|
+
.doc(notificationId)
|
|
448
|
+
.set(alertData);
|
|
459
449
|
|
|
460
450
|
logger.log('INFO', `[sendAllClearNotification] Sent all-clear notification to user ${userCid} for PI ${piCid}`);
|
|
461
451
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification helpers for task engine and computation system
|
|
3
|
+
* Replaces old-generic-api notification helpers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { FieldValue } = require('@google-cloud/firestore');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Notify task engine completion
|
|
10
|
+
* @param {Firestore} db - Firestore instance
|
|
11
|
+
* @param {Object} logger - Logger instance
|
|
12
|
+
* @param {string|number} userCid - User CID
|
|
13
|
+
* @param {string} requestId - Request ID
|
|
14
|
+
* @param {string} username - Username
|
|
15
|
+
* @param {boolean} success - Whether the task succeeded
|
|
16
|
+
* @param {string|null} errorMessage - Error message if failed
|
|
17
|
+
* @param {Object} options - Additional options (collectionRegistry, config, etc.)
|
|
18
|
+
*/
|
|
19
|
+
async function notifyTaskEngineComplete(db, logger, userCid, requestId, username, success, errorMessage, options = {}) {
|
|
20
|
+
try {
|
|
21
|
+
const notificationData = {
|
|
22
|
+
type: 'sync',
|
|
23
|
+
subType: 'complete',
|
|
24
|
+
title: success ? 'Sync Complete' : 'Sync Failed',
|
|
25
|
+
message: success
|
|
26
|
+
? `Data sync for ${username} completed successfully`
|
|
27
|
+
: `Data sync for ${username} failed: ${errorMessage || 'Unknown error'}`,
|
|
28
|
+
read: false,
|
|
29
|
+
timestamp: FieldValue.serverTimestamp(),
|
|
30
|
+
createdAt: FieldValue.serverTimestamp(),
|
|
31
|
+
metadata: {
|
|
32
|
+
requestId,
|
|
33
|
+
username,
|
|
34
|
+
success,
|
|
35
|
+
errorMessage: errorMessage || null
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
await db.collection('SignedInUsers')
|
|
40
|
+
.doc(String(userCid))
|
|
41
|
+
.collection('notifications')
|
|
42
|
+
.doc(requestId)
|
|
43
|
+
.set(notificationData);
|
|
44
|
+
|
|
45
|
+
logger?.log('INFO', `[notifyTaskEngineComplete] Notification sent for user ${userCid}, request ${requestId}`);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
logger?.log('WARN', `[notifyTaskEngineComplete] Failed to send notification: ${error.message}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Notify task engine progress
|
|
53
|
+
* @param {Firestore} db - Firestore instance
|
|
54
|
+
* @param {Object} logger - Logger instance
|
|
55
|
+
* @param {string|number} userCid - User CID
|
|
56
|
+
* @param {string} requestId - Request ID
|
|
57
|
+
* @param {string} username - Username
|
|
58
|
+
* @param {string} stage - Progress stage (e.g., 'started', 'portfolio_complete')
|
|
59
|
+
* @param {string|null} dataType - Data type (e.g., 'portfolio', 'tradeHistory')
|
|
60
|
+
* @param {Object} options - Additional options
|
|
61
|
+
*/
|
|
62
|
+
async function notifyTaskEngineProgress(db, logger, userCid, requestId, username, stage, dataType, options = {}) {
|
|
63
|
+
try {
|
|
64
|
+
const stageMessages = {
|
|
65
|
+
'started': 'Data sync started',
|
|
66
|
+
'portfolio_complete': 'Portfolio data fetched',
|
|
67
|
+
'history_complete': 'Trade history fetched',
|
|
68
|
+
'social_complete': 'Social posts fetched',
|
|
69
|
+
'indexing': 'Indexing data',
|
|
70
|
+
'computing': 'Running computations'
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const notificationData = {
|
|
74
|
+
type: 'sync',
|
|
75
|
+
subType: 'progress',
|
|
76
|
+
title: 'Sync Progress',
|
|
77
|
+
message: stageMessages[stage] || `Sync ${stage}`,
|
|
78
|
+
read: false,
|
|
79
|
+
timestamp: FieldValue.serverTimestamp(),
|
|
80
|
+
createdAt: FieldValue.serverTimestamp(),
|
|
81
|
+
metadata: {
|
|
82
|
+
requestId,
|
|
83
|
+
username,
|
|
84
|
+
stage,
|
|
85
|
+
dataType: dataType || null
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
await db.collection('SignedInUsers')
|
|
90
|
+
.doc(String(userCid))
|
|
91
|
+
.collection('notifications')
|
|
92
|
+
.doc(`${requestId}_${stage}`)
|
|
93
|
+
.set(notificationData);
|
|
94
|
+
|
|
95
|
+
logger?.log('INFO', `[notifyTaskEngineProgress] Progress notification sent for user ${userCid}, stage ${stage}`);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
logger?.log('WARN', `[notifyTaskEngineProgress] Failed to send notification: ${error.message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Notify that PI data was refreshed
|
|
103
|
+
* @param {Firestore} db - Firestore instance
|
|
104
|
+
* @param {Object} logger - Logger instance
|
|
105
|
+
* @param {Object} collectionRegistry - Collection registry
|
|
106
|
+
* @param {string|number} piCid - PI CID
|
|
107
|
+
* @param {string} username - Username
|
|
108
|
+
* @param {Object} config - Config object
|
|
109
|
+
*/
|
|
110
|
+
async function notifyPIDataRefreshed(db, logger, collectionRegistry, piCid, username, config) {
|
|
111
|
+
try {
|
|
112
|
+
// This is a non-critical notification - just log it
|
|
113
|
+
logger?.log('INFO', `[notifyPIDataRefreshed] PI ${piCid} (${username}) data refreshed`);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
logger?.log('WARN', `[notifyPIDataRefreshed] Failed: ${error.message}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Notify computation completion
|
|
121
|
+
* @param {Firestore} db - Firestore instance
|
|
122
|
+
* @param {Object} logger - Logger instance
|
|
123
|
+
* @param {string|number} userCid - User CID
|
|
124
|
+
* @param {string} requestId - Request ID
|
|
125
|
+
* @param {string} computation - Computation name
|
|
126
|
+
* @param {string} displayName - Display name for computation
|
|
127
|
+
* @param {boolean} success - Whether computation succeeded
|
|
128
|
+
* @param {string|null} errorMessage - Error message if failed
|
|
129
|
+
* @param {Object} options - Additional options
|
|
130
|
+
*/
|
|
131
|
+
async function notifyComputationComplete(db, logger, userCid, requestId, computation, displayName, success, errorMessage, options = {}) {
|
|
132
|
+
try {
|
|
133
|
+
const notificationData = {
|
|
134
|
+
type: 'computation',
|
|
135
|
+
subType: 'complete',
|
|
136
|
+
title: success ? 'Computation Complete' : 'Computation Failed',
|
|
137
|
+
message: success
|
|
138
|
+
? `${displayName} completed successfully`
|
|
139
|
+
: `${displayName} failed: ${errorMessage || 'Unknown error'}`,
|
|
140
|
+
read: false,
|
|
141
|
+
timestamp: FieldValue.serverTimestamp(),
|
|
142
|
+
createdAt: FieldValue.serverTimestamp(),
|
|
143
|
+
metadata: {
|
|
144
|
+
requestId,
|
|
145
|
+
computation,
|
|
146
|
+
displayName,
|
|
147
|
+
success,
|
|
148
|
+
errorMessage: errorMessage || null
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
await db.collection('SignedInUsers')
|
|
153
|
+
.doc(String(userCid))
|
|
154
|
+
.collection('notifications')
|
|
155
|
+
.doc(`${requestId}_${computation}`)
|
|
156
|
+
.set(notificationData);
|
|
157
|
+
|
|
158
|
+
logger?.log('INFO', `[notifyComputationComplete] Notification sent for user ${userCid}, computation ${computation}`);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
logger?.log('WARN', `[notifyComputationComplete] Failed to send notification: ${error.message}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get computation display name
|
|
166
|
+
* @param {string} computation - Computation name
|
|
167
|
+
* @returns {string} Display name
|
|
168
|
+
*/
|
|
169
|
+
function getComputationDisplayName(computation) {
|
|
170
|
+
const displayNames = {
|
|
171
|
+
'SignedInUserProfileMetrics': 'Profile Metrics',
|
|
172
|
+
'PopularInvestorProfileMetrics': 'PI Profile Metrics',
|
|
173
|
+
'SignedInUserPIPersonalizedMetrics': 'Personalized PI Metrics',
|
|
174
|
+
'SimilarInvestors': 'Similar Investors',
|
|
175
|
+
'RecommendedHedges': 'Recommended Hedges',
|
|
176
|
+
'PopularInvestorRankings': 'PI Rankings'
|
|
177
|
+
};
|
|
178
|
+
return displayNames[computation] || computation;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
module.exports = {
|
|
182
|
+
notifyTaskEngineComplete,
|
|
183
|
+
notifyTaskEngineProgress,
|
|
184
|
+
notifyPIDataRefreshed,
|
|
185
|
+
notifyComputationComplete,
|
|
186
|
+
getComputationDisplayName
|
|
187
|
+
};
|
|
@@ -232,7 +232,7 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
232
232
|
await db.doc(ledgerPath).update({ status: 'COMPLETED', completedAt: new Date() });
|
|
233
233
|
await recordRunAttempt(db, { date, computation, pass }, 'SUCCESS', null, metrics, triggerReason, resourceTier);
|
|
234
234
|
|
|
235
|
-
const { notifyComputationComplete, getComputationDisplayName } = require('../../
|
|
235
|
+
const { notifyComputationComplete, getComputationDisplayName } = require('../../api-v2/helpers/notification_helpers.js');
|
|
236
236
|
// Send notification if this was an on-demand computation
|
|
237
237
|
if (metadata?.onDemand && metadata?.requestId && metadata?.requestingUserCid) {
|
|
238
238
|
try {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
const crypto = require('crypto');
|
|
8
8
|
const { shouldTryProxy, recordProxyOutcome, getFailureCount, getMaxFailures } = require('../utils/proxy_circuit_breaker');
|
|
9
9
|
const { FieldValue } = require('@google-cloud/firestore');
|
|
10
|
-
const { notifyTaskEngineComplete, notifyTaskEngineProgress, notifyPIDataRefreshed } = require('../../
|
|
10
|
+
const { notifyTaskEngineComplete, notifyTaskEngineProgress, notifyPIDataRefreshed } = require('../../api-v2/helpers/notification_helpers');
|
|
11
11
|
const { conditionallyRunRootDataIndexer } = require('./root_data_indexer_helpers');
|
|
12
12
|
|
|
13
13
|
const {
|
|
@@ -405,7 +405,7 @@ async function finalizeOnDemandRequest(deps, config, taskData, isPI, success, to
|
|
|
405
405
|
// 2. Trigger Computations (only if root data indexer completed successfully)
|
|
406
406
|
if (indexerCompleted && pubsub && config.computationSystem) {
|
|
407
407
|
const { triggerComputationWithDependencies } = require('../../computation-system/helpers/on_demand_helpers');
|
|
408
|
-
const {
|
|
408
|
+
const { fetchPopularInvestorMasterList } = require('../../api-v2/helpers/data-fetchers/firestore.js');
|
|
409
409
|
|
|
410
410
|
// Use userType from metadata if available, otherwise fall back to isPI
|
|
411
411
|
const userType = metadata?.userType || (isPI ? 'POPULAR_INVESTOR' : 'SIGNED_IN_USER');
|
|
@@ -429,11 +429,15 @@ async function finalizeOnDemandRequest(deps, config, taskData, isPI, success, to
|
|
|
429
429
|
|
|
430
430
|
// IMPORTANT: Check if this signed-in user is also a Popular Investor
|
|
431
431
|
// If they are, we need to run PI computations as well
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
432
|
+
try {
|
|
433
|
+
const piData = await fetchPopularInvestorMasterList(db, String(cid));
|
|
434
|
+
if (piData) {
|
|
435
|
+
logger.log('INFO', `[On-Demand] Signed-in user ${cid} is also a Popular Investor. Adding PI computations.`);
|
|
436
|
+
compsSet.add('PopularInvestorProfileMetrics');
|
|
437
|
+
compsSet.add('SignedInUserPIPersonalizedMetrics');
|
|
438
|
+
}
|
|
439
|
+
} catch (e) {
|
|
440
|
+
// User is not a PI, continue with signed-in user computations only
|
|
437
441
|
}
|
|
438
442
|
} else {
|
|
439
443
|
// Fallback to isPI-based logic for backward compatibility
|
package/index.js
CHANGED
|
@@ -46,9 +46,6 @@ const { checkPassStatus } = require('./functions
|
|
|
46
46
|
const dataLoader = require('./functions/computation-system/utils/data_loader');
|
|
47
47
|
const computationUtils = require('./functions/computation-system/utils/utils');
|
|
48
48
|
|
|
49
|
-
// API
|
|
50
|
-
const { createApiApp } = require('./functions/old-generic-api/index');
|
|
51
|
-
const apiHelpers = require('./functions/old-generic-api/helpers/api_helpers');
|
|
52
49
|
|
|
53
50
|
// API v2 (CommonJS)
|
|
54
51
|
const { createApiV2App } = require('./functions/api-v2/index');
|
|
@@ -117,9 +114,7 @@ const computationSystem = {
|
|
|
117
114
|
};
|
|
118
115
|
|
|
119
116
|
const api = {
|
|
120
|
-
createApiApp,
|
|
121
117
|
createApiV2App, // New API v2 entry point
|
|
122
|
-
helpers: apiHelpers,
|
|
123
118
|
};
|
|
124
119
|
|
|
125
120
|
const maintenance = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bulltrackers-module",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.631",
|
|
4
4
|
"description": "Helper Functions for Bulltrackers.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"functions/task-engine/",
|
|
10
10
|
"functions/core/",
|
|
11
11
|
"functions/computation-system/",
|
|
12
|
-
"functions/old-generic-api/",
|
|
13
12
|
"functions/api-v2/",
|
|
14
13
|
"functions/dispatcher/",
|
|
15
14
|
"functions/invalid-speculator-handler/",
|