bulltrackers-module 1.0.562 → 1.0.564

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.
@@ -6,6 +6,7 @@
6
6
  /**
7
7
  * Find the latest available date for signed-in user portfolio data
8
8
  * Searches backwards from today up to maxDaysBack days
9
+ * Checks both new root data path and legacy path for compatibility
9
10
  * @param {Firestore} db - Firestore instance
10
11
  * @param {string} signedInUsersCollection - Collection name (legacy)
11
12
  * @param {string|number} userCid - User CID
@@ -13,9 +14,51 @@
13
14
  * @returns {Promise<string|null>} - Date string (YYYY-MM-DD) or null if not found
14
15
  */
15
16
  async function findLatestPortfolioDate(db, signedInUsersCollection, userCid, maxDaysBack = 30) {
16
- const CANARY_BLOCK_ID = '19M';
17
17
  const today = new Date();
18
+ const userCidStr = String(userCid);
19
+
20
+ // First, check the user-centric latest snapshot (fastest check)
21
+ try {
22
+ const latestRef = db.collection('SignedInUsers')
23
+ .doc(userCidStr)
24
+ .collection('portfolio')
25
+ .doc('latest');
26
+
27
+ const latestDoc = await latestRef.get();
28
+ if (latestDoc.exists) {
29
+ const data = latestDoc.data();
30
+ if (data && data.date) {
31
+ return data.date; // Found in latest snapshot
32
+ }
33
+ }
34
+ } catch (error) {
35
+ // Continue to other checks if this fails
36
+ }
37
+
38
+ // Second, check the new root data path: SignedInUserPortfolioData/{date}/{cid}/{cid}
39
+ for (let i = 0; i < maxDaysBack; i++) {
40
+ const checkDate = new Date(today);
41
+ checkDate.setDate(checkDate.getDate() - i);
42
+ const dateStr = checkDate.toISOString().split('T')[0];
43
+
44
+ try {
45
+ const rootDataRef = db.collection('SignedInUserPortfolioData')
46
+ .doc(dateStr)
47
+ .collection(userCidStr)
48
+ .doc(userCidStr);
49
+
50
+ const rootDataDoc = await rootDataRef.get();
51
+ if (rootDataDoc.exists) {
52
+ return dateStr; // Found data in root data collection
53
+ }
54
+ } catch (error) {
55
+ // Continue to next date if error
56
+ continue;
57
+ }
58
+ }
18
59
 
60
+ // Third, check legacy path for backward compatibility: {signedInUsersCollection}/19M/snapshots/{date}/parts
61
+ const CANARY_BLOCK_ID = '19M';
19
62
  for (let i = 0; i < maxDaysBack; i++) {
20
63
  const checkDate = new Date(today);
21
64
  checkDate.setDate(checkDate.getDate() - i);
@@ -33,8 +76,8 @@ async function findLatestPortfolioDate(db, signedInUsersCollection, userCid, max
33
76
  // Check if user's CID exists in any part document
34
77
  for (const partDoc of partsSnapshot.docs) {
35
78
  const partData = partDoc.data();
36
- if (partData && partData[String(userCid)]) {
37
- return dateStr; // Found data for this date
79
+ if (partData && partData[userCidStr]) {
80
+ return dateStr; // Found data for this date in legacy path
38
81
  }
39
82
  }
40
83
  } catch (error) {
@@ -3,9 +3,10 @@
3
3
  * Handles user portfolio data endpoints with migration support
4
4
  */
5
5
 
6
- const { findLatestPortfolioDate } = require('../core/data_lookup_helpers');
6
+ const { findLatestPortfolioDate, findLatestComputationDate } = require('../core/data_lookup_helpers');
7
7
  const { checkIfUserIsPI } = require('../core/user_status_helpers');
8
8
  const { getEffectiveCid, getDevOverride } = require('../dev/dev_helpers');
9
+ const { checkPiInComputationDate } = require('./computation_helpers');
9
10
 
10
11
  /**
11
12
  * GET /user/me/portfolio
@@ -119,7 +120,9 @@ async function getUserPortfolio(req, res, dependencies, config) {
119
120
 
120
121
  /**
121
122
  * GET /user/me/data-status
122
- * Checks if signed-in user's portfolio data has been fetched and stored.
123
+ * Checks if signed-in user's profile page computation is available.
124
+ * The profile page uses SignedInUserProfileMetrics computation, not raw portfolio/history data.
125
+ * If computation exists, the profile page can be rendered. If not, user should sync.
123
126
  */
124
127
  async function getUserDataStatus(req, res, dependencies, config) {
125
128
  const { db, logger } = dependencies;
@@ -130,95 +133,80 @@ async function getUserDataStatus(req, res, dependencies, config) {
130
133
  }
131
134
 
132
135
  try {
133
- const { signedInUsersCollection, signedInHistoryCollection } = config;
134
- const CANARY_BLOCK_ID = '19M';
135
-
136
136
  const today = new Date().toISOString().split('T')[0];
137
137
 
138
- logger.log('INFO', `[getUserDataStatus] Checking data for CID: ${userCid}, Date: ${today}, Collection: ${signedInUsersCollection}`);
139
-
140
- // Try to find latest available date for portfolio (with fallback)
141
- const portfolioDate = await findLatestPortfolioDate(db, signedInUsersCollection, userCid, 30);
142
- const portfolioExists = !!portfolioDate;
143
- const isPortfolioFallback = portfolioDate && portfolioDate !== today;
144
-
145
- if (portfolioDate && isPortfolioFallback) {
146
- logger.log('INFO', `[getUserDataStatus] Using fallback portfolio date ${portfolioDate} for user ${userCid} (today: ${today})`);
147
- }
148
-
149
- // Check history data with fallback
150
- let historyExists = false;
151
- let historyDate = null;
152
- let isHistoryFallback = false;
153
-
154
- const historyCollection = signedInHistoryCollection || 'signed_in_user_history';
138
+ logger.log('INFO', `[getUserDataStatus] Checking computation for CID: ${userCid}, Date: ${today}`);
155
139
 
156
- // Check today first
157
- const todayHistoryRef = db.collection(historyCollection)
158
- .doc(CANARY_BLOCK_ID)
159
- .collection('snapshots')
160
- .doc(today)
161
- .collection('parts');
162
-
163
- const todayHistorySnapshot = await todayHistoryRef.get();
164
-
165
- if (!todayHistorySnapshot.empty) {
166
- for (const partDoc of todayHistorySnapshot.docs) {
167
- const partData = partDoc.data();
168
- if (partData && partData[String(userCid)]) {
169
- historyExists = true;
170
- historyDate = today;
171
- break;
172
- }
173
- }
174
- }
140
+ // Check for dev override impersonation
141
+ const effectiveCid = await getEffectiveCid(db, userCid, config, logger);
175
142
 
176
- // If not found today, search backwards
177
- if (!historyExists) {
178
- for (let i = 1; i <= 30; i++) {
179
- const checkDate = new Date();
180
- checkDate.setDate(checkDate.getDate() - i);
181
- const dateStr = checkDate.toISOString().split('T')[0];
143
+ // Primary check: Does SignedInUserProfileMetrics computation exist for this user?
144
+ // This is what the profile page actually uses to render
145
+ const insightsCollection = config.unifiedInsightsCollection || 'unified_insights';
146
+ const resultsSub = config.resultsSubcollection || 'results';
147
+ const compsSub = config.computationsSubcollection || 'computations';
148
+ const category = 'popular-investor';
149
+ const computationName = 'SignedInUserProfileMetrics';
150
+
151
+ // Find latest computation date
152
+ const latestComputationDate = await findLatestComputationDate(
153
+ db,
154
+ insightsCollection,
155
+ resultsSub,
156
+ compsSub,
157
+ category,
158
+ computationName,
159
+ effectiveCid,
160
+ 30
161
+ );
162
+
163
+ let computationAvailable = false;
164
+ let computationDate = null;
165
+ let isComputationFallback = false;
166
+
167
+ if (latestComputationDate) {
168
+ // Check if this specific user exists in the computation
169
+ const { found } = await checkPiInComputationDate(
170
+ db,
171
+ insightsCollection,
172
+ resultsSub,
173
+ compsSub,
174
+ category,
175
+ computationName,
176
+ latestComputationDate,
177
+ String(effectiveCid),
178
+ logger
179
+ );
180
+
181
+ if (found) {
182
+ computationAvailable = true;
183
+ computationDate = latestComputationDate;
184
+ isComputationFallback = latestComputationDate !== today;
182
185
 
183
- try {
184
- const historyPartsRef = db.collection(historyCollection)
185
- .doc(CANARY_BLOCK_ID)
186
- .collection('snapshots')
187
- .doc(dateStr)
188
- .collection('parts');
189
-
190
- const historyPartsSnapshot = await historyPartsRef.get();
191
-
192
- if (!historyPartsSnapshot.empty) {
193
- for (const partDoc of historyPartsSnapshot.docs) {
194
- const partData = partDoc.data();
195
- if (partData && partData[String(userCid)]) {
196
- historyExists = true;
197
- historyDate = dateStr;
198
- isHistoryFallback = true;
199
- logger.log('INFO', `[getUserDataStatus] Found history data in fallback date ${dateStr}`);
200
- break;
201
- }
202
- }
203
- }
204
-
205
- if (historyExists) break;
206
- } catch (error) {
207
- continue;
186
+ if (isComputationFallback) {
187
+ logger.log('INFO', `[getUserDataStatus] Using fallback computation date ${computationDate} for user ${userCid} (today: ${today})`);
188
+ } else {
189
+ logger.log('INFO', `[getUserDataStatus] Found computation for user ${userCid} on today's date`);
208
190
  }
191
+ } else {
192
+ logger.log('INFO', `[getUserDataStatus] Computation exists for date ${latestComputationDate} but user ${userCid} not found in it`);
209
193
  }
194
+ } else {
195
+ logger.log('INFO', `[getUserDataStatus] No computation found for user ${userCid} in last 30 days`);
210
196
  }
211
197
 
198
+ // For backward compatibility, keep portfolioAvailable and historyAvailable
199
+ // but they should match computationAvailable (computation is the source of truth)
212
200
  const result = {
213
- portfolioAvailable: portfolioExists,
214
- historyAvailable: historyExists,
215
- date: portfolioDate || today,
216
- portfolioDate: portfolioDate,
217
- historyDate: historyDate,
218
- isPortfolioFallback: isPortfolioFallback,
219
- isHistoryFallback: isHistoryFallback,
201
+ portfolioAvailable: computationAvailable, // Profile page uses computation, not raw portfolio
202
+ historyAvailable: computationAvailable, // Profile page uses computation, not raw history
203
+ computationAvailable: computationAvailable, // Explicit computation check
204
+ date: computationDate || today,
205
+ computationDate: computationDate,
206
+ isComputationFallback: isComputationFallback,
220
207
  requestedDate: today,
221
- userCid: String(userCid)
208
+ userCid: String(userCid),
209
+ effectiveCid: String(effectiveCid)
222
210
  };
223
211
 
224
212
  logger.log('INFO', `[getUserDataStatus] Result for CID ${userCid}:`, result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.562",
3
+ "version": "1.0.564",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [