bulltrackers-module 1.0.574 → 1.0.576
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.
|
@@ -9,6 +9,8 @@ const { getEffectiveCid, getDevOverride } = require('../dev/dev_helpers');
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Check if a PI exists in a computation date
|
|
12
|
+
* For PopularInvestorProfileMetrics and SignedInUserProfileMetrics: reads from pages subcollection
|
|
13
|
+
* For other computations: reads from main computation document
|
|
12
14
|
* Returns { found: boolean, profileData: object | null, computationData: object | null }
|
|
13
15
|
* @param {object} db - Firestore instance
|
|
14
16
|
* @param {string} insightsCollection - Insights collection name
|
|
@@ -23,73 +25,121 @@ const { getEffectiveCid, getDevOverride } = require('../dev/dev_helpers');
|
|
|
23
25
|
*/
|
|
24
26
|
async function checkPiInComputationDate(db, insightsCollection, resultsSub, compsSub, category, computationName, dateStr, cidStr, logger) {
|
|
25
27
|
try {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.collection(resultsSub)
|
|
29
|
-
.doc(category)
|
|
30
|
-
.collection(compsSub)
|
|
31
|
-
.doc(computationName);
|
|
28
|
+
// Check if this computation uses the pages subcollection structure
|
|
29
|
+
const usesPagesStructure = computationName === 'PopularInvestorProfileMetrics' || computationName === 'SignedInUserProfileMetrics';
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (rawData._sharded === true && rawData._shardCount) {
|
|
44
|
-
const shardsCol = computationRef.collection('_shards');
|
|
45
|
-
const shardCount = rawData._shardCount;
|
|
31
|
+
if (usesPagesStructure) {
|
|
32
|
+
// New path format: /unified_insights/YYYY-MM-DD/results/popular-investor/computations/{computationName}/pages/{cid}
|
|
33
|
+
const pageRef = db.collection(insightsCollection)
|
|
34
|
+
.doc(dateStr)
|
|
35
|
+
.collection(resultsSub)
|
|
36
|
+
.doc(category)
|
|
37
|
+
.collection(compsSub)
|
|
38
|
+
.doc(computationName)
|
|
39
|
+
.collection('pages')
|
|
40
|
+
.doc(cidStr);
|
|
46
41
|
|
|
47
|
-
|
|
42
|
+
const pageDoc = await pageRef.get();
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
for (let i = 0; i < shardCount; i++) {
|
|
53
|
-
const shardDoc = await shardsCol.doc(`shard_${i}`).get();
|
|
54
|
-
if (shardDoc.exists) {
|
|
55
|
-
const shardData = shardDoc.data();
|
|
56
|
-
Object.assign(computationData, shardData);
|
|
57
|
-
} else {
|
|
58
|
-
logger.log('WARN', `[checkPiInComputationDate] Shard shard_${i} missing for date ${dateStr}`);
|
|
59
|
-
}
|
|
44
|
+
if (!pageDoc.exists) {
|
|
45
|
+
logger.log('INFO', `[checkPiInComputationDate] Page document not found for CID ${cidStr} in date ${dateStr}`);
|
|
46
|
+
return { found: false, profileData: null, computationData: null };
|
|
60
47
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
48
|
+
|
|
49
|
+
const rawData = pageDoc.data();
|
|
50
|
+
let profileData = null;
|
|
51
|
+
|
|
52
|
+
// Decompress if needed
|
|
53
|
+
profileData = tryDecompress(rawData);
|
|
64
54
|
|
|
65
55
|
// Handle string decompression result
|
|
66
|
-
if (typeof
|
|
56
|
+
if (typeof profileData === 'string') {
|
|
67
57
|
try {
|
|
68
|
-
|
|
58
|
+
profileData = JSON.parse(profileData);
|
|
69
59
|
} catch (e) {
|
|
70
|
-
logger.log('WARN', `[checkPiInComputationDate] Failed to parse decompressed string for date ${dateStr}:`, e.message);
|
|
60
|
+
logger.log('WARN', `[checkPiInComputationDate] Failed to parse decompressed string for CID ${cidStr} on date ${dateStr}:`, e.message);
|
|
71
61
|
return { found: false, profileData: null, computationData: null };
|
|
72
62
|
}
|
|
73
63
|
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Check if CID exists in the computation data
|
|
77
|
-
if (computationData && typeof computationData === 'object' && !Array.isArray(computationData)) {
|
|
78
|
-
// Filter out metadata keys that start with underscore
|
|
79
|
-
const cids = Object.keys(computationData).filter(key => !key.startsWith('_'));
|
|
80
64
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return { found: true, profileData, computationData };
|
|
65
|
+
if (profileData && typeof profileData === 'object') {
|
|
66
|
+
logger.log('INFO', `[checkPiInComputationDate] Found profile data for CID ${cidStr} in date ${dateStr}`);
|
|
67
|
+
// Return the profile data - computationData is set to null since we're reading individual pages
|
|
68
|
+
return { found: true, profileData, computationData: null };
|
|
86
69
|
} else {
|
|
87
|
-
logger.log('
|
|
88
|
-
return { found: false, profileData: null, computationData };
|
|
70
|
+
logger.log('WARN', `[checkPiInComputationDate] Profile data is not an object for CID ${cidStr} on date ${dateStr}`);
|
|
71
|
+
return { found: false, profileData: null, computationData: null };
|
|
89
72
|
}
|
|
90
73
|
} else {
|
|
91
|
-
|
|
92
|
-
|
|
74
|
+
// Standard path for other computations: read from main computation document
|
|
75
|
+
const computationRef = db.collection(insightsCollection)
|
|
76
|
+
.doc(dateStr)
|
|
77
|
+
.collection(resultsSub)
|
|
78
|
+
.doc(category)
|
|
79
|
+
.collection(compsSub)
|
|
80
|
+
.doc(computationName);
|
|
81
|
+
|
|
82
|
+
const computationDoc = await computationRef.get();
|
|
83
|
+
|
|
84
|
+
if (!computationDoc.exists) {
|
|
85
|
+
return { found: false, profileData: null, computationData: null };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const rawData = computationDoc.data();
|
|
89
|
+
let computationData = null;
|
|
90
|
+
|
|
91
|
+
// Check if data is sharded
|
|
92
|
+
if (rawData._sharded === true && rawData._shardCount) {
|
|
93
|
+
const shardsCol = computationRef.collection('_shards');
|
|
94
|
+
const shardCount = rawData._shardCount;
|
|
95
|
+
|
|
96
|
+
logger.log('INFO', `[checkPiInComputationDate] Reading ${shardCount} shards for date ${dateStr}`);
|
|
97
|
+
|
|
98
|
+
computationData = {};
|
|
99
|
+
|
|
100
|
+
// Read all shards (shard_0, shard_1, ..., shard_N-1)
|
|
101
|
+
for (let i = 0; i < shardCount; i++) {
|
|
102
|
+
const shardDoc = await shardsCol.doc(`shard_${i}`).get();
|
|
103
|
+
if (shardDoc.exists) {
|
|
104
|
+
const shardData = shardDoc.data();
|
|
105
|
+
Object.assign(computationData, shardData);
|
|
106
|
+
} else {
|
|
107
|
+
logger.log('WARN', `[checkPiInComputationDate] Shard shard_${i} missing for date ${dateStr}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
// Data is in the main document (compressed or raw)
|
|
112
|
+
computationData = tryDecompress(rawData);
|
|
113
|
+
|
|
114
|
+
// Handle string decompression result
|
|
115
|
+
if (typeof computationData === 'string') {
|
|
116
|
+
try {
|
|
117
|
+
computationData = JSON.parse(computationData);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
logger.log('WARN', `[checkPiInComputationDate] Failed to parse decompressed string for date ${dateStr}:`, e.message);
|
|
120
|
+
return { found: false, profileData: null, computationData: null };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Check if CID exists in the computation data
|
|
126
|
+
if (computationData && typeof computationData === 'object' && !Array.isArray(computationData)) {
|
|
127
|
+
// Filter out metadata keys that start with underscore
|
|
128
|
+
const cids = Object.keys(computationData).filter(key => !key.startsWith('_'));
|
|
129
|
+
|
|
130
|
+
// Check if the requested CID exists
|
|
131
|
+
const profileData = computationData[cidStr];
|
|
132
|
+
if (profileData) {
|
|
133
|
+
logger.log('INFO', `[checkPiInComputationDate] Found CID ${cidStr} in date ${dateStr} (total CIDs: ${cids.length})`);
|
|
134
|
+
return { found: true, profileData, computationData };
|
|
135
|
+
} else {
|
|
136
|
+
logger.log('INFO', `[checkPiInComputationDate] CID ${cidStr} not found in date ${dateStr} (total CIDs: ${cids.length})`);
|
|
137
|
+
return { found: false, profileData: null, computationData };
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
logger.log('WARN', `[checkPiInComputationDate] Computation data is not an object for date ${dateStr}`);
|
|
141
|
+
return { found: false, profileData: null, computationData: null };
|
|
142
|
+
}
|
|
93
143
|
}
|
|
94
144
|
} catch (error) {
|
|
95
145
|
logger.log('ERROR', `[checkPiInComputationDate] Error checking PI in computation:`, error);
|
|
@@ -174,39 +224,29 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
174
224
|
const dateStr = checkDate.toISOString().split('T')[0];
|
|
175
225
|
|
|
176
226
|
try {
|
|
177
|
-
// Check if computation document exists for this date
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
227
|
+
// Check if page/computation document exists for this user and date
|
|
228
|
+
// For computations using pages subcollection, checkPiInComputationDate handles it
|
|
229
|
+
// For other computations, check the main document first
|
|
230
|
+
const { found } = await checkPiInComputationDate(
|
|
231
|
+
db,
|
|
232
|
+
insightsCollection,
|
|
233
|
+
resultsSub,
|
|
234
|
+
compsSub,
|
|
235
|
+
category,
|
|
236
|
+
firstCompName,
|
|
237
|
+
dateStr,
|
|
238
|
+
String(effectiveCid),
|
|
239
|
+
logger
|
|
240
|
+
);
|
|
184
241
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
insightsCollection,
|
|
192
|
-
resultsSub,
|
|
193
|
-
compsSub,
|
|
194
|
-
category,
|
|
195
|
-
firstCompName,
|
|
196
|
-
dateStr,
|
|
197
|
-
String(effectiveCid),
|
|
198
|
-
logger
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
if (found) {
|
|
202
|
-
foundDate = dateStr;
|
|
203
|
-
if (dateStr !== today) {
|
|
204
|
-
logger.log('INFO', `[getUserComputations] Using fallback date ${foundDate} for effective CID ${effectiveCid} (today: ${today})`);
|
|
205
|
-
} else {
|
|
206
|
-
logger.log('INFO', `[getUserComputations] Found computation for effective CID ${effectiveCid} on today's date`);
|
|
207
|
-
}
|
|
208
|
-
break; // Found user, stop searching
|
|
242
|
+
if (found) {
|
|
243
|
+
foundDate = dateStr;
|
|
244
|
+
if (dateStr !== today) {
|
|
245
|
+
logger.log('INFO', `[getUserComputations] Using fallback date ${foundDate} for effective CID ${effectiveCid} (today: ${today})`);
|
|
246
|
+
} else {
|
|
247
|
+
logger.log('INFO', `[getUserComputations] Found computation for effective CID ${effectiveCid} on today's date`);
|
|
209
248
|
}
|
|
249
|
+
break; // Found user, stop searching
|
|
210
250
|
}
|
|
211
251
|
} catch (error) {
|
|
212
252
|
// Continue to next date if error
|
|
@@ -249,94 +289,129 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
249
289
|
|
|
250
290
|
for (const compName of computationNames) {
|
|
251
291
|
try {
|
|
252
|
-
|
|
253
|
-
.doc(date)
|
|
254
|
-
.collection(resultsSub)
|
|
255
|
-
.doc(category)
|
|
256
|
-
.collection(compsSub)
|
|
257
|
-
.doc(compName);
|
|
258
|
-
|
|
259
|
-
const doc = await docRef.get();
|
|
292
|
+
let userResult = null;
|
|
260
293
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
294
|
+
// Special handling for computations that use pages subcollection structure
|
|
295
|
+
// Both PopularInvestorProfileMetrics and SignedInUserProfileMetrics use pages subcollection
|
|
296
|
+
if (compName === 'PopularInvestorProfileMetrics' || compName === 'SignedInUserProfileMetrics') {
|
|
297
|
+
const pageRef = db.collection(insightsCollection)
|
|
298
|
+
.doc(date)
|
|
299
|
+
.collection(resultsSub)
|
|
300
|
+
.doc(category)
|
|
301
|
+
.collection(compsSub)
|
|
302
|
+
.doc(compName)
|
|
303
|
+
.collection('pages')
|
|
304
|
+
.doc(String(effectiveCid));
|
|
264
305
|
|
|
265
|
-
|
|
266
|
-
try {
|
|
267
|
-
data = JSON.parse(data);
|
|
268
|
-
} catch (e) {
|
|
269
|
-
logger.log('WARN', `[getUserComputations] Failed to parse decompressed string for ${compName} on ${date}:`, e.message);
|
|
270
|
-
data = null;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
306
|
+
const pageDoc = await pageRef.get();
|
|
273
307
|
|
|
274
|
-
if (
|
|
275
|
-
const
|
|
276
|
-
|
|
308
|
+
if (pageDoc.exists) {
|
|
309
|
+
const rawData = pageDoc.data();
|
|
310
|
+
let data = tryDecompress(rawData);
|
|
277
311
|
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
312
|
+
if (typeof data === 'string') {
|
|
313
|
+
try {
|
|
314
|
+
data = JSON.parse(data);
|
|
315
|
+
} catch (e) {
|
|
316
|
+
logger.log('WARN', `[getUserComputations] Failed to parse decompressed string for ${compName} page on ${date}:`, e.message);
|
|
317
|
+
data = null;
|
|
283
318
|
}
|
|
284
|
-
} else {
|
|
285
|
-
data = null;
|
|
286
319
|
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// [FIX] Handle meta computations (global results) vs standard computations (user-specific)
|
|
290
|
-
// Use metadata from schema collection to determine computation type
|
|
291
|
-
let userResult = null;
|
|
292
|
-
if (data && typeof data === 'object') {
|
|
293
|
-
const metadata = computationMetadata[compName];
|
|
294
|
-
const isMetaComputation = metadata && metadata.type === 'meta';
|
|
295
320
|
|
|
296
|
-
if (
|
|
297
|
-
// Meta computation: return entire data object (global results)
|
|
321
|
+
if (data && typeof data === 'object') {
|
|
298
322
|
userResult = data;
|
|
299
|
-
} else {
|
|
300
|
-
// Standard computation: extract user-specific result
|
|
301
|
-
const userCidKey = String(effectiveCid);
|
|
302
|
-
userResult = data.hasOwnProperty(userCidKey) ? data[userCidKey] : null;
|
|
303
323
|
}
|
|
304
324
|
}
|
|
325
|
+
} else {
|
|
326
|
+
// Standard path for other computations
|
|
327
|
+
const docRef = db.collection(insightsCollection)
|
|
328
|
+
.doc(date)
|
|
329
|
+
.collection(resultsSub)
|
|
330
|
+
.doc(category)
|
|
331
|
+
.collection(compsSub)
|
|
332
|
+
.doc(compName);
|
|
305
333
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
334
|
+
const doc = await docRef.get();
|
|
335
|
+
|
|
336
|
+
if (doc.exists) {
|
|
337
|
+
const rawData = doc.data();
|
|
338
|
+
let data = tryDecompress(rawData);
|
|
339
|
+
|
|
340
|
+
if (typeof data === 'string') {
|
|
341
|
+
try {
|
|
342
|
+
data = JSON.parse(data);
|
|
343
|
+
} catch (e) {
|
|
344
|
+
logger.log('WARN', `[getUserComputations] Failed to parse decompressed string for ${compName} on ${date}:`, e.message);
|
|
345
|
+
data = null;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (data && data._sharded === true && data._shardCount) {
|
|
350
|
+
const shardsCol = docRef.collection('_shards');
|
|
351
|
+
const shardsSnapshot = await shardsCol.get();
|
|
324
352
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
data
|
|
353
|
+
if (!shardsSnapshot.empty) {
|
|
354
|
+
data = {};
|
|
355
|
+
for (const shardDoc of shardsSnapshot.docs) {
|
|
356
|
+
const shardData = shardDoc.data();
|
|
357
|
+
Object.assign(data, shardData);
|
|
330
358
|
}
|
|
331
|
-
}
|
|
332
|
-
|
|
359
|
+
} else {
|
|
360
|
+
data = null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// [FIX] Handle meta computations (global results) vs standard computations (user-specific)
|
|
365
|
+
// Use metadata from schema collection to determine computation type
|
|
366
|
+
if (data && typeof data === 'object') {
|
|
367
|
+
const metadata = computationMetadata[compName];
|
|
368
|
+
const isMetaComputation = metadata && metadata.type === 'meta';
|
|
369
|
+
|
|
370
|
+
if (isMetaComputation) {
|
|
371
|
+
// Meta computation: return entire data object (global results)
|
|
372
|
+
userResult = data;
|
|
373
|
+
} else {
|
|
374
|
+
// Standard computation: extract user-specific result
|
|
375
|
+
const userCidKey = String(effectiveCid);
|
|
376
|
+
userResult = data.hasOwnProperty(userCidKey) ? data[userCidKey] : null;
|
|
377
|
+
}
|
|
333
378
|
}
|
|
334
379
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (isDevOverrideActive && (compName === 'SignedInUserProfileMetrics' || compName === 'SignedInUserCopiedPIs')) {
|
|
383
|
+
if (compName === 'SignedInUserCopiedPIs') {
|
|
384
|
+
userResult = {
|
|
385
|
+
current: devOverride.fakeCopiedPIs,
|
|
386
|
+
past: [],
|
|
387
|
+
all: devOverride.fakeCopiedPIs
|
|
388
|
+
};
|
|
389
|
+
logger.log('INFO', `[getUserComputations] Applied DEV OVERRIDE to SignedInUserCopiedPIs for user ${userCid}`);
|
|
390
|
+
} else if (compName === 'SignedInUserProfileMetrics' && userResult && userResult.copiedPIs) {
|
|
391
|
+
const fakeMirrors = devOverride.fakeCopiedPIs.map(cid => ({
|
|
392
|
+
cid: Number(cid),
|
|
393
|
+
username: `PI-${cid}`,
|
|
394
|
+
invested: 0,
|
|
395
|
+
netProfit: 0,
|
|
396
|
+
value: 0,
|
|
397
|
+
pendingClosure: false,
|
|
398
|
+
isRanked: false
|
|
399
|
+
}));
|
|
400
|
+
|
|
401
|
+
userResult = {
|
|
402
|
+
...userResult,
|
|
403
|
+
copiedPIs: {
|
|
404
|
+
chartType: 'cards',
|
|
405
|
+
data: fakeMirrors
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
logger.log('INFO', `[getUserComputations] Applied DEV OVERRIDE to SignedInUserProfileMetrics.copiedPIs for user ${userCid}`);
|
|
338
409
|
}
|
|
339
410
|
}
|
|
411
|
+
|
|
412
|
+
if (userResult) {
|
|
413
|
+
results[date][compName] = userResult;
|
|
414
|
+
}
|
|
340
415
|
} catch (err) {
|
|
341
416
|
logger.log('WARN', `[getUserComputations] Error fetching ${compName} for ${date}`, err);
|
|
342
417
|
}
|
|
@@ -163,45 +163,33 @@ async function getUserDataStatus(req, res, dependencies, config) {
|
|
|
163
163
|
const dateStr = checkDate.toISOString().split('T')[0];
|
|
164
164
|
|
|
165
165
|
try {
|
|
166
|
-
// Check if
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
166
|
+
// Check if page document exists for this user and date (new pages subcollection structure)
|
|
167
|
+
const { found } = await checkPiInComputationDate(
|
|
168
|
+
db,
|
|
169
|
+
insightsCollection,
|
|
170
|
+
resultsSub,
|
|
171
|
+
compsSub,
|
|
172
|
+
category,
|
|
173
|
+
computationName,
|
|
174
|
+
dateStr,
|
|
175
|
+
String(effectiveCid),
|
|
176
|
+
logger
|
|
177
|
+
);
|
|
173
178
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
db,
|
|
180
|
-
insightsCollection,
|
|
181
|
-
resultsSub,
|
|
182
|
-
compsSub,
|
|
183
|
-
category,
|
|
184
|
-
computationName,
|
|
185
|
-
dateStr,
|
|
186
|
-
String(effectiveCid),
|
|
187
|
-
logger
|
|
188
|
-
);
|
|
179
|
+
if (found) {
|
|
180
|
+
foundDate = dateStr;
|
|
181
|
+
computationAvailable = true;
|
|
182
|
+
computationDate = dateStr;
|
|
183
|
+
isComputationFallback = daysBack > 0;
|
|
189
184
|
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
computationAvailable = true;
|
|
193
|
-
computationDate = dateStr;
|
|
194
|
-
isComputationFallback = daysBack > 0;
|
|
195
|
-
|
|
196
|
-
if (isComputationFallback) {
|
|
197
|
-
logger.log('INFO', `[getUserDataStatus] Found computation for user ${userCid} on date ${computationDate} (${daysBack} days back from today: ${today})`);
|
|
198
|
-
} else {
|
|
199
|
-
logger.log('INFO', `[getUserDataStatus] Found computation for user ${userCid} on today's date`);
|
|
200
|
-
}
|
|
201
|
-
break; // Found user, stop searching
|
|
185
|
+
if (isComputationFallback) {
|
|
186
|
+
logger.log('INFO', `[getUserDataStatus] Found computation for user ${userCid} on date ${computationDate} (${daysBack} days back from today: ${today})`);
|
|
202
187
|
} else {
|
|
203
|
-
logger.log('
|
|
188
|
+
logger.log('INFO', `[getUserDataStatus] Found computation for user ${userCid} on today's date`);
|
|
204
189
|
}
|
|
190
|
+
break; // Found user, stop searching
|
|
191
|
+
} else {
|
|
192
|
+
logger.log('DEBUG', `[getUserDataStatus] Page document not found for user ${userCid} on date ${dateStr}, continuing search...`);
|
|
205
193
|
}
|
|
206
194
|
} catch (error) {
|
|
207
195
|
// Continue to next date if error
|
|
@@ -138,33 +138,14 @@ async function getPiProfile(req, res, dependencies, config) {
|
|
|
138
138
|
if (!foundDate || !profileData) {
|
|
139
139
|
logger.log('WARN', `[getPiProfile] CID ${cid} not found in any checked dates: ${checkedDates.join(', ')}`);
|
|
140
140
|
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
db,
|
|
144
|
-
insightsCollection,
|
|
145
|
-
resultsSub,
|
|
146
|
-
compsSub,
|
|
147
|
-
category,
|
|
148
|
-
computationName,
|
|
149
|
-
latestDate,
|
|
150
|
-
cidStr,
|
|
151
|
-
logger
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
const allAvailableCids = latestResult.computationData && typeof latestResult.computationData === 'object' && !Array.isArray(latestResult.computationData)
|
|
155
|
-
? Object.keys(latestResult.computationData)
|
|
156
|
-
.filter(key => !key.startsWith('_'))
|
|
157
|
-
.sort()
|
|
158
|
-
: [];
|
|
159
|
-
|
|
141
|
+
// Note: With the new pages subcollection structure, we can't enumerate all available CIDs
|
|
142
|
+
// from a single document, so we skip that debug information
|
|
160
143
|
return res.status(404).json({
|
|
161
144
|
error: "Profile data not found",
|
|
162
145
|
message: `Popular Investor ${cid} does not exist in computation results for the last ${maxDaysBackForPi + 1} days. This PI may not have been processed recently.`,
|
|
163
146
|
debug: {
|
|
164
147
|
searchedCid: cidStr,
|
|
165
148
|
checkedDates: checkedDates,
|
|
166
|
-
totalCidsInLatestDocument: allAvailableCids.length,
|
|
167
|
-
sampleAvailableCids: allAvailableCids.slice(0, 20),
|
|
168
149
|
latestDate: latestDate
|
|
169
150
|
}
|
|
170
151
|
});
|