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
- // For signed-in users, computations are stored in the 'signed_in_user' category
874
- const category = 'signed_in_user';
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
- 30
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, return empty
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: false,
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
- const data = tryDecompress(rawData);
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')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.490",
3
+ "version": "1.0.492",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [