bulltrackers-module 1.0.456 → 1.0.459

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.
@@ -233,6 +233,86 @@ async function getUserSyncStatus(req, res, dependencies, config) {
233
233
  if (!requestsSnapshot.empty) {
234
234
  const latestRequest = requestsSnapshot.docs[0].data();
235
235
  let status = latestRequest.status || 'queued';
236
+ const userType = latestRequest.userType || 'SIGNED_IN_USER'; // Default to signed-in user
237
+
238
+ // If status is 'indexing' or 'computing', check if computation results are now available
239
+ if (status === 'indexing' || status === 'computing') {
240
+ const insightsCollection = config.unifiedInsightsCollection || 'unified_insights';
241
+ const resultsSub = config.resultsSubcollection || 'results';
242
+ const compsSub = config.computationsSubcollection || 'computations';
243
+
244
+ // Determine category and computation name based on user type
245
+ let category, computationName;
246
+ if (userType === 'POPULAR_INVESTOR') {
247
+ category = 'popular-investor';
248
+ computationName = 'PopularInvestorProfileMetrics';
249
+ } else {
250
+ category = 'signed_in_user';
251
+ computationName = 'SignedInUserProfileMetrics';
252
+ }
253
+
254
+ // Check today and yesterday for computation results
255
+ const checkDate = new Date();
256
+ for (let i = 0; i < 2; i++) {
257
+ const dateStr = new Date(checkDate);
258
+ dateStr.setDate(checkDate.getDate() - i);
259
+ const dateStrFormatted = dateStr.toISOString().split('T')[0];
260
+
261
+ const docRef = db.collection(insightsCollection)
262
+ .doc(dateStrFormatted)
263
+ .collection(resultsSub)
264
+ .doc(category)
265
+ .collection(compsSub)
266
+ .doc(computationName);
267
+
268
+ const doc = await docRef.get();
269
+ if (doc.exists) {
270
+ const docData = doc.data();
271
+ let mergedData = null;
272
+
273
+ // Check if data is sharded
274
+ if (docData._sharded === true && docData._shardCount) {
275
+ // Data is stored in shards - read all shards and merge
276
+ const shardsCol = docRef.collection('_shards');
277
+ const shardsSnapshot = await shardsCol.get();
278
+
279
+ if (!shardsSnapshot.empty) {
280
+ mergedData = {};
281
+ for (const shardDoc of shardsSnapshot.docs) {
282
+ const shardData = shardDoc.data();
283
+ Object.assign(mergedData, shardData);
284
+ }
285
+ }
286
+ } else {
287
+ // Data is in the main document (compressed or not)
288
+ const { tryDecompress } = require('./data_helpers');
289
+ mergedData = tryDecompress(docData);
290
+
291
+ // Handle string decompression result
292
+ if (typeof mergedData === 'string') {
293
+ try {
294
+ mergedData = JSON.parse(mergedData);
295
+ } catch (e) {
296
+ logger.log('WARN', `[getUserSyncStatus] Failed to parse decompressed string for date ${dateStrFormatted}:`, e.message);
297
+ mergedData = null;
298
+ }
299
+ }
300
+ }
301
+
302
+ if (mergedData && typeof mergedData === 'object' && mergedData[String(targetCidNum)]) {
303
+ // Computation completed! Update status
304
+ status = 'completed';
305
+ await requestsSnapshot.docs[0].ref.update({
306
+ status: 'completed',
307
+ completedAt: FieldValue.serverTimestamp(),
308
+ updatedAt: FieldValue.serverTimestamp()
309
+ });
310
+ logger.log('INFO', `[getUserSyncStatus] Computation completed for user ${targetCidNum} (${userType}). Found results in date ${dateStrFormatted}`);
311
+ break;
312
+ }
313
+ }
314
+ }
315
+ }
236
316
 
237
317
  // Check if request is stale (stuck in processing state for too long)
238
318
  const STALE_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
@@ -275,6 +355,17 @@ async function getUserSyncStatus(req, res, dependencies, config) {
275
355
  }
276
356
  }
277
357
 
358
+ // Re-fetch request if we updated it to get the latest completedAt
359
+ let completedAt = latestRequest.completedAt?.toDate?.()?.toISOString() || null;
360
+ if (status === 'completed' && !completedAt) {
361
+ // If we just updated to completed, re-fetch to get the server timestamp
362
+ const updatedDoc = await requestsSnapshot.docs[0].ref.get();
363
+ if (updatedDoc.exists) {
364
+ const updatedData = updatedDoc.data();
365
+ completedAt = updatedData.completedAt?.toDate?.()?.toISOString() || null;
366
+ }
367
+ }
368
+
278
369
  const response = {
279
370
  success: true,
280
371
  status,
@@ -282,7 +373,7 @@ async function getUserSyncStatus(req, res, dependencies, config) {
282
373
  startedAt: latestRequest.startedAt?.toDate?.()?.toISOString() || null,
283
374
  createdAt: latestRequest.createdAt?.toDate?.()?.toISOString() || null,
284
375
  dispatchedAt: latestRequest.dispatchedAt?.toDate?.()?.toISOString() || null,
285
- completedAt: latestRequest.completedAt?.toDate?.()?.toISOString() || null,
376
+ completedAt: completedAt,
286
377
  estimatedCompletion: latestRequest.dispatchedAt
287
378
  ? new Date(new Date(latestRequest.dispatchedAt.toDate()).getTime() + 5 * 60 * 1000).toISOString() // 5 min estimate
288
379
  : null
@@ -38,11 +38,16 @@ async function handlePopularInvestorUpdate(taskData, config, dependencies) {
38
38
  // If we have a requestId but no cid, try to extract from requestId or use username
39
39
  const effectiveCid = cid || (requestId ? null : null); // Will need to be handled
40
40
 
41
- const {
42
- ETORO_API_PORTFOLIO_URL,
43
- ETORO_API_POSITIONS_URL,
44
- ETORO_API_HISTORY_URL
45
- } = config;
41
+ // Validate and set API URLs with defaults and fallbacks
42
+ const ETORO_API_PORTFOLIO_URL = config.ETORO_API_PORTFOLIO_URL || process.env.ETORO_API_PORTFOLIO_URL || 'https://www.etoro.com/sapi/trade-data-real/live/public/portfolios';
43
+ const ETORO_API_POSITIONS_URL = config.ETORO_API_POSITIONS_URL || process.env.ETORO_API_POSITIONS_URL || 'https://www.etoro.com/sapi/trade-data-real/live/public/positions';
44
+ const ETORO_API_HISTORY_URL = config.ETORO_API_HISTORY_URL || process.env.ETORO_API_HISTORY_URL || 'https://www.etoro.com/sapi/trade-data-real/history/public/credit/flat';
45
+
46
+ if (!ETORO_API_PORTFOLIO_URL || ETORO_API_PORTFOLIO_URL === 'undefined') {
47
+ const errorMsg = 'ETORO_API_PORTFOLIO_URL is not configured. Check taskEngine config.';
48
+ logger.log('ERROR', `[PI Update] ${errorMsg}`);
49
+ throw new Error(errorMsg);
50
+ }
46
51
 
47
52
  logger.log('INFO', `[PI Update] Starting update for User: ${username || 'unknown'} (${cid || 'unknown'})${requestId ? ` [Request: ${requestId}]` : ''}`);
48
53
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.456",
3
+ "version": "1.0.459",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [