bulltrackers-module 1.0.490 → 1.0.492
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.
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Profile Page Fallback Conditions
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document explains when and why the profile page uses fallback mode instead of computation data.
|
|
6
|
+
|
|
7
|
+
## Data Storage Locations
|
|
8
|
+
|
|
9
|
+
### Signed-In User Computations
|
|
10
|
+
- **Category**: `popular-investor` (NOT `signed_in_user`)
|
|
11
|
+
- **Reason**: ResultCommitter ignores category metadata when computation is in a non-core folder
|
|
12
|
+
- **Computation**: `SignedInUserProfileMetrics` is in `calculations/popular-investor/` folder
|
|
13
|
+
- **Path**: `/unified_insights/{date}/results/popular-investor/computations/SignedInUserProfileMetrics`
|
|
14
|
+
|
|
15
|
+
### Signed-In User Raw Data
|
|
16
|
+
- **Portfolio**: `signed_in_users/{blockId}/snapshots/{date}/parts/part_*`
|
|
17
|
+
- **History**: `signed_in_user_history/{blockId}/snapshots/{date}/parts/part_*` (or `signed_in_users_history` depending on config)
|
|
18
|
+
- **Social**: `signed_in_users_social/{cid}/posts/{postId}`
|
|
19
|
+
|
|
20
|
+
## Fallback Conditions
|
|
21
|
+
|
|
22
|
+
### Frontend Fallback Logic (UserProfile.tsx)
|
|
23
|
+
|
|
24
|
+
The profile page uses fallback mode when:
|
|
25
|
+
|
|
26
|
+
1. **Computation Not Found**:
|
|
27
|
+
- API call to `/user/me/computations?computation=SignedInUserProfileMetrics` returns empty `data: {}`
|
|
28
|
+
- OR the computation exists but doesn't contain data for the user's CID
|
|
29
|
+
|
|
30
|
+
2. **API Error**:
|
|
31
|
+
- Any error fetching the computation (network error, 500, etc.)
|
|
32
|
+
|
|
33
|
+
3. **Data Structure Issues**:
|
|
34
|
+
- Computation exists but `metricsResponse.data[latestDate]?.SignedInUserProfileMetrics` is undefined/null
|
|
35
|
+
|
|
36
|
+
### Backend Fallback Logic (getUserComputations)
|
|
37
|
+
|
|
38
|
+
The API returns `isFallback: false` but `data: {}` when:
|
|
39
|
+
|
|
40
|
+
1. **Wrong Category Lookup** (FIXED):
|
|
41
|
+
- Was looking in `signed_in_user` category
|
|
42
|
+
- Should look in `popular-investor` category
|
|
43
|
+
|
|
44
|
+
2. **No Data for User**:
|
|
45
|
+
- Computation document exists but doesn't contain `data[String(effectiveCid)]`
|
|
46
|
+
- This can happen if:
|
|
47
|
+
- Computation ran but user wasn't included (shouldn't happen with targetCid optimization)
|
|
48
|
+
- Data is sharded and shard doesn't contain user's data
|
|
49
|
+
- Data exists but in wrong format
|
|
50
|
+
|
|
51
|
+
3. **Date Mismatch**:
|
|
52
|
+
- Computation exists for different date than requested
|
|
53
|
+
- `mode=latest` tries to find latest date, but if none found, returns empty
|
|
54
|
+
|
|
55
|
+
## Current Issue
|
|
56
|
+
|
|
57
|
+
The user reported:
|
|
58
|
+
- API returns `"isFallback": false, "data": {}`
|
|
59
|
+
- Profile page shows fallback mode
|
|
60
|
+
- Computation exists at `/unified_insights/2025-12-29/results/popular-investor/computations/SignedInUserProfileMetrics/_shards/shard_0`
|
|
61
|
+
|
|
62
|
+
**Root Cause**: The `getUserComputations` endpoint was looking in the wrong category (`signed_in_user` instead of `popular-investor`).
|
|
63
|
+
|
|
64
|
+
**Fix Applied**: Updated `getUserComputations` to look in `popular-investor` category for `SignedInUserProfileMetrics`.
|
|
65
|
+
|
|
66
|
+
## Verification Steps
|
|
67
|
+
|
|
68
|
+
1. Check computation exists:
|
|
69
|
+
```
|
|
70
|
+
/unified_insights/2025-12-29/results/popular-investor/computations/SignedInUserProfileMetrics
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
2. Check if data is sharded:
|
|
74
|
+
- If `_sharded: true`, check `_shards` subcollection
|
|
75
|
+
- Merge all shards to get full data
|
|
76
|
+
|
|
77
|
+
3. Check if user's CID exists in data:
|
|
78
|
+
- Data structure: `{ "29312236": { ...metrics... } }`
|
|
79
|
+
- Must use `String(cid)` as key
|
|
80
|
+
|
|
81
|
+
4. Check API response:
|
|
82
|
+
- Should return `data: { "2025-12-29": { "SignedInUserProfileMetrics": { ... } } }`
|
|
83
|
+
- If empty, check logs for why lookup failed
|
|
84
|
+
|
|
85
|
+
## History Data Storage
|
|
86
|
+
|
|
87
|
+
Signed-in user trade history is stored in:
|
|
88
|
+
- Collection: `signed_in_user_history` (or `signed_in_users_history` depending on config)
|
|
89
|
+
- Path: `{collection}/{blockId}/snapshots/{date}/parts/part_*`
|
|
90
|
+
- Block ID: Typically `19M` (canary block)
|
|
91
|
+
|
|
92
|
+
The task engine stores history via:
|
|
93
|
+
```javascript
|
|
94
|
+
await batchManager.addToTradingHistoryBatch(String(cid), blockId, today, historyData, 'signed_in_user');
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This uses `getHistoryCollection('signed_in_user')` which returns `signedInHistory` collection.
|
|
98
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Signed-In User History Storage Location
|
|
2
|
+
|
|
3
|
+
## Current Issue
|
|
4
|
+
|
|
5
|
+
History data is being stored in the **wrong collection** because `FIRESTORE_COLLECTION_SIGNED_IN_HISTORY` is not defined in the task engine config, causing it to fall back to `NormalUserTradeHistory`.
|
|
6
|
+
|
|
7
|
+
## Storage Path Structure
|
|
8
|
+
|
|
9
|
+
For signed-in users, history data should be stored at:
|
|
10
|
+
```
|
|
11
|
+
{signed_in_user_history}/{blockId}/snapshots/{date}/parts/part_{shardIndex}
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Where:
|
|
15
|
+
- `signed_in_user_history` = Collection name (default: `signed_in_user_history`)
|
|
16
|
+
- `blockId` = `'19M'` (canary block ID)
|
|
17
|
+
- `date` = Date string in format `YYYY-MM-DD` (e.g., `2025-12-29`)
|
|
18
|
+
- `shardIndex` = `cid % TOTAL_SHARDS` (modulo sharding for even distribution)
|
|
19
|
+
|
|
20
|
+
## Example Path
|
|
21
|
+
|
|
22
|
+
For user CID `29312236` on date `2025-12-29`:
|
|
23
|
+
```
|
|
24
|
+
signed_in_user_history/19M/snapshots/2025-12-29/parts/part_{shardIndex}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Where `shardIndex = 29312236 % TOTAL_SHARDS` (typically 7 shards, so `29312236 % 7 = 1`)
|
|
28
|
+
|
|
29
|
+
## Fix Applied
|
|
30
|
+
|
|
31
|
+
Added `FIRESTORE_COLLECTION_SIGNED_IN_HISTORY` to `taskEngine_config.js`:
|
|
32
|
+
```javascript
|
|
33
|
+
FIRESTORE_COLLECTION_SIGNED_IN_HISTORY: getEnvVar('FIRESTORE_COLLECTION_SIGNED_IN_HISTORY', 'signed_in_user_history'),
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Before Fix
|
|
37
|
+
|
|
38
|
+
Without this config, the batch manager was using:
|
|
39
|
+
- `config.FIRESTORE_COLLECTION_SIGNED_IN_HISTORY` = `undefined`
|
|
40
|
+
- Fallback: `config.FIRESTORE_COLLECTION_NORMAL_HISTORY` = `'NormalUserTradeHistory'`
|
|
41
|
+
|
|
42
|
+
So data was being stored at:
|
|
43
|
+
```
|
|
44
|
+
NormalUserTradeHistory/19M/snapshots/2025-12-29/parts/part_{shardIndex}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## After Fix
|
|
48
|
+
|
|
49
|
+
With the config added, data will be stored at:
|
|
50
|
+
```
|
|
51
|
+
signed_in_user_history/19M/snapshots/2025-12-29/parts/part_{shardIndex}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Verification
|
|
55
|
+
|
|
56
|
+
To verify history data exists for a user:
|
|
57
|
+
1. Calculate shard index: `userCid % 7` (or whatever TOTAL_SHARDS is)
|
|
58
|
+
2. Check path: `signed_in_user_history/19M/snapshots/2025-12-29/parts/part_{shardIndex}`
|
|
59
|
+
3. Look for user's CID as a key in the document: `{ "29312236": { ...historyData... } }`
|
|
60
|
+
|
|
61
|
+
## Related Collections
|
|
62
|
+
|
|
63
|
+
- **Portfolio**: `signed_in_users/19M/snapshots/{date}/parts/part_{shardIndex}`
|
|
64
|
+
- **History**: `signed_in_user_history/19M/snapshots/{date}/parts/part_{shardIndex}` (FIXED)
|
|
65
|
+
- **Social**: `signed_in_users_social/{cid}/posts/{postId}`
|
|
66
|
+
|
|
@@ -870,8 +870,10 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
870
870
|
const resultsSub = config.resultsSubcollection || 'results';
|
|
871
871
|
const compsSub = config.computationsSubcollection || 'computations';
|
|
872
872
|
|
|
873
|
-
//
|
|
874
|
-
|
|
873
|
+
// NOTE: ResultCommitter ignores category metadata if computation is in a non-core folder
|
|
874
|
+
// SignedInUserProfileMetrics is in popular-investor folder, so it's stored in popular-investor category
|
|
875
|
+
// For signed-in users, computations are stored in the 'popular-investor' category (not 'signed_in_user')
|
|
876
|
+
const category = 'popular-investor';
|
|
875
877
|
const today = new Date().toISOString().split('T')[0];
|
|
876
878
|
|
|
877
879
|
const computationNames = computation ? computation.split(',') : [];
|
|
@@ -889,6 +891,7 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
889
891
|
|
|
890
892
|
// If mode is 'latest', try to find the latest available date for the first computation
|
|
891
893
|
// Use effectiveCid for computation lookup
|
|
894
|
+
// CRITICAL: Only look back 7 days as per requirements
|
|
892
895
|
if (mode === 'latest') {
|
|
893
896
|
const firstCompName = computationNames[0];
|
|
894
897
|
const latestDate = await findLatestComputationDate(
|
|
@@ -899,7 +902,7 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
899
902
|
category,
|
|
900
903
|
firstCompName,
|
|
901
904
|
effectiveCid,
|
|
902
|
-
|
|
905
|
+
7 // Only look back 7 days as per requirements
|
|
903
906
|
);
|
|
904
907
|
|
|
905
908
|
if (latestDate) {
|
|
@@ -908,14 +911,15 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
908
911
|
logger.log('INFO', `[getUserComputations] Using fallback date ${latestDate} for effective CID ${effectiveCid} (today: ${today})`);
|
|
909
912
|
}
|
|
910
913
|
} else {
|
|
911
|
-
// No data found
|
|
914
|
+
// No data found after 7 days - return empty (frontend will use fallback)
|
|
915
|
+
logger.log('WARN', `[getUserComputations] No computation data found for CID ${effectiveCid} in last 7 days. Frontend will use fallback.`);
|
|
912
916
|
return res.status(200).json({
|
|
913
917
|
status: 'success',
|
|
914
918
|
userCid: String(effectiveCid),
|
|
915
919
|
mode,
|
|
916
920
|
computations: computationNames,
|
|
917
921
|
data: {},
|
|
918
|
-
isFallback:
|
|
922
|
+
isFallback: true, // Mark as fallback since no data found
|
|
919
923
|
requestedDate: today,
|
|
920
924
|
isImpersonating: isImpersonating || false,
|
|
921
925
|
actualCid: Number(userCid)
|
|
@@ -953,10 +957,38 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
953
957
|
if (doc.exists) {
|
|
954
958
|
// Decompress if needed (handles byte string storage)
|
|
955
959
|
const rawData = doc.data();
|
|
956
|
-
|
|
960
|
+
let data = tryDecompress(rawData);
|
|
961
|
+
|
|
962
|
+
// Handle string decompression result
|
|
963
|
+
if (typeof data === 'string') {
|
|
964
|
+
try {
|
|
965
|
+
data = JSON.parse(data);
|
|
966
|
+
} catch (e) {
|
|
967
|
+
logger.log('WARN', `[getUserComputations] Failed to parse decompressed string for ${compName} on ${date}:`, e.message);
|
|
968
|
+
data = null;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// Check if data is sharded
|
|
973
|
+
if (data && data._sharded === true && data._shardCount) {
|
|
974
|
+
// Data is stored in shards - read all shards and merge
|
|
975
|
+
const shardsCol = docRef.collection('_shards');
|
|
976
|
+
const shardsSnapshot = await shardsCol.get();
|
|
977
|
+
|
|
978
|
+
if (!shardsSnapshot.empty) {
|
|
979
|
+
data = {};
|
|
980
|
+
for (const shardDoc of shardsSnapshot.docs) {
|
|
981
|
+
const shardData = shardDoc.data();
|
|
982
|
+
Object.assign(data, shardData);
|
|
983
|
+
}
|
|
984
|
+
} else {
|
|
985
|
+
data = null; // Sharded but no shards found
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
957
989
|
// Filter by user CID - computation results are stored as { cid: result }
|
|
958
990
|
// Use effectiveCid for lookup
|
|
959
|
-
let userResult = data[String(effectiveCid)];
|
|
991
|
+
let userResult = data && typeof data === 'object' ? data[String(effectiveCid)] : null;
|
|
960
992
|
|
|
961
993
|
// Apply dev override for computations that include copied PIs (use actual userCid for override check)
|
|
962
994
|
if (isDevOverrideActive && (compName === 'SignedInUserProfileMetrics' || compName === 'SignedInUserCopiedPIs')) {
|