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[
|
|
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
|
|
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
|
|
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
|
|
157
|
-
const
|
|
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
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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:
|
|
214
|
-
historyAvailable:
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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);
|