bulltrackers-module 1.0.610 → 1.0.611

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.
@@ -2212,6 +2212,50 @@ const checkDataStatus = async (db, userId) => {
2212
2212
  };
2213
2213
  };
2214
2214
 
2215
+ /**
2216
+ * Check Popular Investor data status - finds the latest computation date for a PI
2217
+ * Similar to checkDataStatus but specifically for PopularInvestorProfileMetrics
2218
+ */
2219
+ const checkPopularInvestorDataStatus = async (db, piId) => {
2220
+ const lookbackDays = 7;
2221
+ const today = new Date();
2222
+ let computationDate = null;
2223
+
2224
+ const computationName = 'PopularInvestorProfileMetrics';
2225
+
2226
+ // Check for computation results in the last 7 days
2227
+ for (let i = 0; i < lookbackDays; i++) {
2228
+ const checkDate = new Date(today);
2229
+ checkDate.setDate(checkDate.getDate() - i);
2230
+ const dateStr = checkDate.toISOString().split('T')[0];
2231
+
2232
+ try {
2233
+ const pageRef = db.collection('unified_insights')
2234
+ .doc(dateStr)
2235
+ .collection('results')
2236
+ .doc('popular-investor')
2237
+ .collection('computations')
2238
+ .doc(computationName)
2239
+ .collection('pages')
2240
+ .doc(String(piId));
2241
+
2242
+ const pageSnap = await pageRef.get();
2243
+ if (pageSnap.exists) {
2244
+ computationDate = dateStr;
2245
+ break;
2246
+ }
2247
+ } catch (error) {
2248
+ // Continue checking other dates
2249
+ console.error(`Error checking computation ${computationName} for ${dateStr}:`, error);
2250
+ }
2251
+ }
2252
+
2253
+ return {
2254
+ computationDate: computationDate,
2255
+ available: computationDate !== null
2256
+ };
2257
+ };
2258
+
2215
2259
  const sendTestAlert = async (db, userId, payload) => {
2216
2260
  // Create a fake notification
2217
2261
  const id = `test_${Date.now()}`;
@@ -2539,6 +2583,7 @@ module.exports = {
2539
2583
  getSyncStatus,
2540
2584
  autoGenerateWatchlist,
2541
2585
  checkDataStatus,
2586
+ checkPopularInvestorDataStatus,
2542
2587
  sendTestAlert,
2543
2588
  isSignedInUser,
2544
2589
  getUserUsername,
@@ -147,11 +147,16 @@ router.get('/:piId/profile', async (req, res) => {
147
147
  const computationName = 'PopularInvestorProfileMetrics';
148
148
  const profileData = await pageCollection(db, targetDate, computationName, piId, parseInt(lookback));
149
149
 
150
+ // Extract computationDate from the latest data entry (first item in array, sorted by date descending)
151
+ // The profileData array contains { date, data } objects, sorted with latest first
152
+ const computationDate = profileData && profileData.length > 0 ? profileData[0].date : null;
153
+
150
154
  res.json({
151
155
  success: true,
152
156
  computation: computationName,
153
157
  piId: piId,
154
158
  data: profileData,
159
+ computationDate: computationDate, // Latest computation date for up-to-date validation
155
160
  profileType: 'public' // Indicates this is a public PI profile
156
161
  });
157
162
  } catch (error) {
@@ -6,7 +6,9 @@ const {
6
6
  isDeveloper,
7
7
  isSignedInUser,
8
8
  fetchPopularInvestorMasterList,
9
- getUserUsername
9
+ getUserUsername,
10
+ checkDataStatus,
11
+ checkPopularInvestorDataStatus
10
12
  } = require('../helpers/data-fetchers/firestore.js');
11
13
 
12
14
  const router = express.Router();
@@ -25,20 +27,56 @@ const handleSyncRequest = async (req, res) => {
25
27
  const limit = await checkSyncRateLimits(db, targetId, req.targetUserId, isDev);
26
28
  if (!limit.allowed) return res.status(429).json({ error: limit.message });
27
29
 
28
- // 2. Detect User Types
30
+ // 2. Detect User Types (needed for validation)
29
31
  const [isSignedIn, isPI] = await Promise.all([
30
32
  isSignedInUser(db, targetId),
31
33
  fetchPopularInvestorMasterList(db, String(targetId)).then(() => true).catch(() => false)
32
34
  ]);
33
35
 
36
+ // 3. Check if data is already up-to-date (prevent unnecessary syncs)
37
+ // Developers can bypass this check
38
+ if (!isDev) {
39
+ try {
40
+ let computationDate = null;
41
+
42
+ // Check Popular Investor data status if target is a PI (anyone can sync a PI, so always check)
43
+ if (isPI) {
44
+ const piDataStatus = await checkPopularInvestorDataStatus(db, String(targetId));
45
+ computationDate = piDataStatus.computationDate;
46
+ }
47
+
48
+ // Check signed-in user data status if target is a signed-in user syncing themselves
49
+ // (Only check if not already found from PI check, and only if syncing themselves)
50
+ if (isSignedIn && targetId === req.targetUserId && !computationDate) {
51
+ const userDataStatus = await checkDataStatus(db, String(targetId));
52
+ computationDate = userDataStatus.computationDate;
53
+ }
54
+
55
+ // If we found a computation date and it's today, block the sync
56
+ if (computationDate) {
57
+ const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD format
58
+ if (computationDate === today) {
59
+ return res.status(400).json({
60
+ error: "Data is already up-to-date for today. Sync is not needed.",
61
+ code: "DATA_UP_TO_DATE",
62
+ computationDate: computationDate
63
+ });
64
+ }
65
+ }
66
+ } catch (error) {
67
+ // If data status check fails, log but don't block sync (fail open for reliability)
68
+ console.warn(`[handleSyncRequest] Failed to check data status for ${targetId}:`, error.message);
69
+ }
70
+ }
71
+
34
72
  if (!isSignedIn && !isPI) {
35
73
  return res.status(404).json({ error: "User not found in SignedInUsers or Popular Investor master list" });
36
74
  }
37
75
 
38
- // 3. Get username
76
+ // 4. Get username
39
77
  const username = await getUserUsername(db, targetId) || String(targetId);
40
78
 
41
- // 4. Create request IDs and dispatch tasks
79
+ // 5. Create request IDs and dispatch tasks
42
80
  const requestIds = [];
43
81
  const tasks = [];
44
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.610",
3
+ "version": "1.0.611",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [