bulltrackers-module 1.0.439 → 1.0.441
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/generic-api/user-api/helpers/data_helpers.js +1 -0
- package/functions/generic-api/user-api/helpers/on_demand_fetch_helpers.js +44 -3
- package/functions/task-engine/handler_creator.js +18 -4
- package/functions/task-engine/helpers/popular_investor_helpers.js +74 -5
- package/package.json +1 -1
|
@@ -2483,6 +2483,7 @@ module.exports = {
|
|
|
2483
2483
|
checkIfUserIsPopularInvestor,
|
|
2484
2484
|
checkIfUserIsPI, // Export for use in review_helpers
|
|
2485
2485
|
findLatestRankingsDate, // Export for use in on_demand_fetch_helpers
|
|
2486
|
+
tryDecompress, // Export for use in on_demand_fetch_helpers
|
|
2486
2487
|
trackProfileView,
|
|
2487
2488
|
getSignedInUserPIPersonalizedMetrics
|
|
2488
2489
|
};
|
|
@@ -275,17 +275,52 @@ async function getPiFetchStatus(req, res, dependencies, config) {
|
|
|
275
275
|
|
|
276
276
|
if (!requestsSnapshot.empty) {
|
|
277
277
|
const latestRequest = requestsSnapshot.docs[0].data();
|
|
278
|
-
|
|
278
|
+
let status = latestRequest.status || 'queued';
|
|
279
|
+
|
|
280
|
+
// If status is 'indexing' or 'computing', check if computation results are now available
|
|
281
|
+
if (status === 'indexing' || status === 'computing') {
|
|
282
|
+
// Re-check if computation results exist
|
|
283
|
+
const checkDate = new Date();
|
|
284
|
+
for (let i = 0; i < 2; i++) { // Check today and yesterday
|
|
285
|
+
const dateStr = new Date(checkDate);
|
|
286
|
+
dateStr.setDate(checkDate.getDate() - i);
|
|
287
|
+
const dateStrFormatted = dateStr.toISOString().split('T')[0];
|
|
288
|
+
|
|
289
|
+
const docRef = db.collection(insightsCollection)
|
|
290
|
+
.doc(dateStrFormatted)
|
|
291
|
+
.collection(resultsSub)
|
|
292
|
+
.doc('popular-investor')
|
|
293
|
+
.collection(compsSub)
|
|
294
|
+
.doc('PopularInvestorProfileMetrics');
|
|
295
|
+
|
|
296
|
+
const doc = await docRef.get();
|
|
297
|
+
if (doc.exists) {
|
|
298
|
+
const { tryDecompress } = require('./data_helpers');
|
|
299
|
+
const data = tryDecompress(doc.data());
|
|
300
|
+
|
|
301
|
+
if (data && data[String(piCidNum)]) {
|
|
302
|
+
// Computation completed! Update status
|
|
303
|
+
status = 'completed';
|
|
304
|
+
await requestsSnapshot.docs[0].ref.update({
|
|
305
|
+
status: 'completed',
|
|
306
|
+
completedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp(),
|
|
307
|
+
updatedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp()
|
|
308
|
+
});
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
279
314
|
|
|
280
315
|
const response = {
|
|
281
316
|
success: true,
|
|
282
|
-
dataAvailable:
|
|
317
|
+
dataAvailable: status === 'completed',
|
|
283
318
|
status,
|
|
284
319
|
requestId: latestRequest.requestId,
|
|
285
320
|
startedAt: latestRequest.startedAt?.toDate?.()?.toISOString() || null,
|
|
286
321
|
createdAt: latestRequest.createdAt?.toDate?.()?.toISOString() || null,
|
|
287
322
|
estimatedCompletion: latestRequest.startedAt
|
|
288
|
-
? new Date(new Date(latestRequest.startedAt.toDate()).getTime() +
|
|
323
|
+
? new Date(new Date(latestRequest.startedAt.toDate()).getTime() + 10 * 60 * 1000).toISOString() // 10 min for computation
|
|
289
324
|
: null
|
|
290
325
|
};
|
|
291
326
|
|
|
@@ -295,6 +330,12 @@ async function getPiFetchStatus(req, res, dependencies, config) {
|
|
|
295
330
|
response.failedAt = latestRequest.failedAt?.toDate?.()?.toISOString() || null;
|
|
296
331
|
}
|
|
297
332
|
|
|
333
|
+
// Include raw data status if computing
|
|
334
|
+
if (status === 'computing') {
|
|
335
|
+
response.rawDataStoredAt = latestRequest.rawDataStoredAt?.toDate?.()?.toISOString() || null;
|
|
336
|
+
response.message = 'Raw data stored, computation in progress...';
|
|
337
|
+
}
|
|
338
|
+
|
|
298
339
|
return res.status(200).json(response);
|
|
299
340
|
}
|
|
300
341
|
|
|
@@ -154,12 +154,26 @@ async function handleRequest(message, context, config, dependencies) {
|
|
|
154
154
|
await handleUpdate(data, 'single-update', dependencies, config);
|
|
155
155
|
break;
|
|
156
156
|
case 'POPULAR_INVESTOR_UPDATE':
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
// For POPULAR_INVESTOR_UPDATE, the entire payload IS the task data
|
|
158
|
+
// (not wrapped in a 'data' field like other task types)
|
|
159
|
+
// Extract task data from payload, excluding 'type'
|
|
160
|
+
const taskData = data || {
|
|
161
|
+
cid: payload.cid,
|
|
162
|
+
username: payload.username,
|
|
163
|
+
requestId: payload.requestId,
|
|
164
|
+
source: payload.source,
|
|
165
|
+
requestedBy: payload.requestedBy,
|
|
166
|
+
actualRequestedBy: payload.actualRequestedBy,
|
|
167
|
+
metadata: payload.metadata,
|
|
168
|
+
priority: payload.priority
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
if (!taskData || (!taskData.cid && !taskData.username)) {
|
|
172
|
+
logger.log('ERROR', '[TaskEngine] POPULAR_INVESTOR_UPDATE missing required fields (cid or username)', { payload, taskData });
|
|
160
173
|
return;
|
|
161
174
|
}
|
|
162
|
-
|
|
175
|
+
|
|
176
|
+
await handlePopularInvestorUpdate(taskData, config, dependencies);
|
|
163
177
|
break;
|
|
164
178
|
case 'ON_DEMAND_USER_UPDATE':
|
|
165
179
|
const onDemandData = data || payload;
|
|
@@ -19,7 +19,7 @@ const { shouldTryProxy, recordProxyOutcome, getFailureCount, getMaxFailures } =
|
|
|
19
19
|
* @param {object} dependencies - db, logger, proxyManager, batchManager, headerManager.
|
|
20
20
|
*/
|
|
21
21
|
async function handlePopularInvestorUpdate(taskData, config, dependencies) {
|
|
22
|
-
const { logger, proxyManager, batchManager, headerManager, db } = dependencies;
|
|
22
|
+
const { logger, proxyManager, batchManager, headerManager, db, pubsub } = dependencies;
|
|
23
23
|
|
|
24
24
|
// Validate taskData exists and has required fields
|
|
25
25
|
if (!taskData) {
|
|
@@ -299,7 +299,7 @@ async function handlePopularInvestorUpdate(taskData, config, dependencies) {
|
|
|
299
299
|
|
|
300
300
|
logger.log('SUCCESS', `[PI Update] Completed full update for ${username}`);
|
|
301
301
|
|
|
302
|
-
// Update request status
|
|
302
|
+
// Update request status and trigger computation if this is an on-demand request
|
|
303
303
|
if (requestId && source === 'on_demand' && db) {
|
|
304
304
|
try {
|
|
305
305
|
const requestRef = db.collection('pi_fetch_requests')
|
|
@@ -307,13 +307,82 @@ async function handlePopularInvestorUpdate(taskData, config, dependencies) {
|
|
|
307
307
|
.collection('requests')
|
|
308
308
|
.doc(requestId);
|
|
309
309
|
|
|
310
|
+
// Update status to indicate raw data is stored, indexing and computation will be triggered
|
|
310
311
|
await requestRef.update({
|
|
311
|
-
status: '
|
|
312
|
-
|
|
312
|
+
status: 'indexing',
|
|
313
|
+
rawDataStoredAt: require('@google-cloud/firestore').FieldValue.serverTimestamp(),
|
|
313
314
|
updatedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp()
|
|
314
315
|
});
|
|
316
|
+
|
|
317
|
+
// CRITICAL: Trigger root data indexer FIRST so computation system knows data exists
|
|
318
|
+
try {
|
|
319
|
+
const { runRootDataIndexer } = require('../../root-data-indexer/index');
|
|
320
|
+
const indexerConfig = {
|
|
321
|
+
availabilityCollection: config.rootDataIndexer?.availabilityCollection || 'system_root_data_index',
|
|
322
|
+
earliestDate: config.rootDataIndexer?.earliestDate || '2025-08-01',
|
|
323
|
+
collections: config.rootDataIndexer?.collections || {},
|
|
324
|
+
targetDate: today // Index only today's date for speed
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
logger.log('INFO', `[PI Update] Triggering root data indexer for date ${today} before computation...`);
|
|
328
|
+
await runRootDataIndexer(indexerConfig, dependencies);
|
|
329
|
+
logger.log('INFO', `[PI Update] Root data indexer completed for date ${today}`);
|
|
330
|
+
|
|
331
|
+
// Update status to indicate indexing is done, computation is being triggered
|
|
332
|
+
await requestRef.update({
|
|
333
|
+
status: 'computing',
|
|
334
|
+
indexedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp(),
|
|
335
|
+
updatedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp()
|
|
336
|
+
});
|
|
337
|
+
} catch (indexerError) {
|
|
338
|
+
logger.log('ERROR', `[PI Update] Failed to run root data indexer for ${today}`, indexerError);
|
|
339
|
+
// Continue anyway - computation might still work if index already exists
|
|
340
|
+
// But update status to indicate we tried
|
|
341
|
+
await requestRef.update({
|
|
342
|
+
status: 'computing',
|
|
343
|
+
indexerError: indexerError.message,
|
|
344
|
+
updatedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp()
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Trigger computation for PopularInvestorProfileMetrics
|
|
349
|
+
const { pubsub } = dependencies;
|
|
350
|
+
if (pubsub) {
|
|
351
|
+
const computationTopic = config.computationSystem?.computationTopicStandard || 'computation-tasks';
|
|
352
|
+
const topic = pubsub.topic(computationTopic);
|
|
353
|
+
const crypto = require('crypto');
|
|
354
|
+
|
|
355
|
+
const computationMessage = {
|
|
356
|
+
action: 'RUN_COMPUTATION_DATE',
|
|
357
|
+
computation: 'PopularInvestorProfileMetrics',
|
|
358
|
+
date: today,
|
|
359
|
+
pass: '1', // Standard pass
|
|
360
|
+
dispatchId: crypto.randomUUID(),
|
|
361
|
+
triggerReason: 'on_demand_pi_fetch',
|
|
362
|
+
resources: 'standard',
|
|
363
|
+
metadata: {
|
|
364
|
+
onDemand: true,
|
|
365
|
+
requestId: requestId,
|
|
366
|
+
piCid: cid,
|
|
367
|
+
piUsername: username
|
|
368
|
+
},
|
|
369
|
+
traceContext: {
|
|
370
|
+
traceId: crypto.randomBytes(16).toString('hex'),
|
|
371
|
+
spanId: crypto.randomBytes(8).toString('hex'),
|
|
372
|
+
sampled: true
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
await topic.publishMessage({
|
|
377
|
+
data: Buffer.from(JSON.stringify(computationMessage))
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
logger.log('INFO', `[PI Update] Triggered computation PopularInvestorProfileMetrics for PI ${cid} (${username}) for date ${today}`);
|
|
381
|
+
} else {
|
|
382
|
+
logger.log('WARN', `[PI Update] PubSub not available, cannot trigger computation`);
|
|
383
|
+
}
|
|
315
384
|
} catch (err) {
|
|
316
|
-
logger.log('WARN', `[PI Update] Failed to update request status
|
|
385
|
+
logger.log('WARN', `[PI Update] Failed to update request status or trigger computation for ${requestId}`, err);
|
|
317
386
|
}
|
|
318
387
|
}
|
|
319
388
|
|