bulltrackers-module 1.0.470 → 1.0.471

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.
@@ -235,16 +235,38 @@ exports.runRootDataIndexer = async (config, dependencies) => {
235
235
  // Path: {verifications} (Limit 1) - Checks if collection is non-empty generally
236
236
  const verificationsRef = db.collection(collections.verifications || 'user_verifications');
237
237
 
238
- // 4. Universal Social Check via Collection Group
239
- // Path: Collection Group 'posts' where 'fetchedAt' is within the day
240
- // This queries ALL 'posts' subcollections across:
241
- // - signed_in_users_social/{userId}/posts/{postId}
242
- // - pi_social_posts/{userId}/posts/{postId}
243
- // - daily_social_insights/{date}/posts/{postId}
244
- const universalSocialQuery = db.collectionGroup('posts')
245
- .where('fetchedAt', '>=', dayStart)
246
- .where('fetchedAt', '<=', dayEnd)
247
- .limit(100); // Increased limit to ensure we catch all posts
238
+ // 4. Social Data Checks - Use date tracking documents
239
+ // Single tracking documents at root level:
240
+ // - pi_social_posts/_dates -> fetchedDates.{date}
241
+ // - signed_in_users_social/_dates -> fetchedDates.{date}
242
+ // For generic social (daily_social_insights), check the collection directly
243
+ const [piSocialTrackingDoc, signedInSocialTrackingDoc, genericSocialSnap] = await Promise.all([
244
+ db.collection(PI_SOCIAL_COLL_NAME).doc('_dates').get(),
245
+ db.collection(SIGNED_IN_SOCIAL_COLL_NAME).doc('_dates').get(),
246
+ db.collectionGroup('posts')
247
+ .where('fetchedAt', '>=', dayStart)
248
+ .where('fetchedAt', '<=', dayEnd)
249
+ .limit(1)
250
+ .get()
251
+ ]);
252
+
253
+ // Check if date exists in tracking documents
254
+ let foundPISocial = false;
255
+ let foundSignedInSocial = false;
256
+
257
+ if (piSocialTrackingDoc.exists) {
258
+ const data = piSocialTrackingDoc.data();
259
+ if (data.fetchedDates && data.fetchedDates[dateStr] === true) {
260
+ foundPISocial = true;
261
+ }
262
+ }
263
+
264
+ if (signedInSocialTrackingDoc.exists) {
265
+ const data = signedInSocialTrackingDoc.data();
266
+ if (data.fetchedDates && data.fetchedDates[dateStr] === true) {
267
+ foundSignedInSocial = true;
268
+ }
269
+ }
248
270
 
249
271
  // --- Execute Checks ---
250
272
  const [
@@ -256,8 +278,7 @@ exports.runRootDataIndexer = async (config, dependencies) => {
256
278
  piDeepExists,
257
279
  piHistExists,
258
280
  signedInPortExists, signedInHistExists,
259
- verificationsQuery,
260
- universalSocialSnap
281
+ verificationsQuery
261
282
  ] = await Promise.all([
262
283
  checkAnyPartExists(normPortPartsRef),
263
284
  checkAnyPartExists(specPortPartsRef),
@@ -271,73 +292,12 @@ exports.runRootDataIndexer = async (config, dependencies) => {
271
292
  checkAnyPartExists(piHistoryPartsRef),
272
293
  checkAnyPartExists(signedInPortPartsRef),
273
294
  checkAnyPartExists(signedInHistPartsRef),
274
- verificationsRef.limit(1).get(),
275
- universalSocialQuery.get()
295
+ verificationsRef.limit(1).get()
276
296
  ]);
277
297
 
278
- // --- Evaluate Social Sources ---
279
- let foundPISocial = false;
280
- let foundSignedInSocial = false;
281
-
282
- logger.log('INFO', `[RootDataIndexer/${dateStr}] Checking social data availability (dayStart: ${dayStart.toISOString()}, dayEnd: ${dayEnd.toISOString()})`);
283
- logger.log('INFO', `[RootDataIndexer/${dateStr}] Looking for PI social in: "${PI_SOCIAL_COLL_NAME}", Signed-in social in: "${SIGNED_IN_SOCIAL_COLL_NAME}"`);
284
-
285
- if (!universalSocialSnap.empty) {
286
- logger.log('INFO', `[RootDataIndexer/${dateStr}] Collection group query returned ${universalSocialSnap.docs.length} posts`);
287
-
288
- // Track all paths found for debugging
289
- const allPaths = [];
290
- const piPaths = [];
291
- const signedInPaths = [];
292
- const otherPaths = [];
293
-
294
- universalSocialSnap.docs.forEach(doc => {
295
- const path = doc.ref.path;
296
- const data = doc.data();
297
- const fetchedAt = data.fetchedAt;
298
- allPaths.push(path);
299
-
300
- // Convert fetchedAt to string for logging
301
- let fetchedAtStr = 'missing';
302
- if (fetchedAt) {
303
- if (fetchedAt.toDate) {
304
- fetchedAtStr = fetchedAt.toDate().toISOString();
305
- } else if (fetchedAt.toMillis) {
306
- fetchedAtStr = new Date(fetchedAt.toMillis()).toISOString();
307
- } else {
308
- fetchedAtStr = String(fetchedAt);
309
- }
310
- }
311
-
312
- // Use includes() to match collection name anywhere in path
313
- // Path format: {collectionName}/{userId}/posts/{postId}
314
- const piMatchPattern = `${PI_SOCIAL_COLL_NAME}/`;
315
- const signedInMatchPattern = `${SIGNED_IN_SOCIAL_COLL_NAME}/`;
316
-
317
- if (path.includes(piMatchPattern)) {
318
- foundPISocial = true;
319
- piPaths.push({ path, fetchedAt: fetchedAtStr });
320
- logger.log('INFO', `[RootDataIndexer/${dateStr}] ✓ PI social: ${path} (fetchedAt: ${fetchedAtStr})`);
321
- } else if (path.includes(signedInMatchPattern)) {
322
- foundSignedInSocial = true;
323
- signedInPaths.push({ path, fetchedAt: fetchedAtStr });
324
- logger.log('INFO', `[RootDataIndexer/${dateStr}] ✓ Signed-in social: ${path} (fetchedAt: ${fetchedAtStr})`);
325
- } else {
326
- otherPaths.push({ path, fetchedAt: fetchedAtStr });
327
- logger.log('INFO', `[RootDataIndexer/${dateStr}] ? Other social: ${path} (fetchedAt: ${fetchedAtStr})`);
328
- }
329
- });
330
-
331
- logger.log('INFO', `[RootDataIndexer/${dateStr}] Summary - Total: ${allPaths.length}, PI: ${piPaths.length}, Signed-in: ${signedInPaths.length}, Other: ${otherPaths.length}`);
332
-
333
- if (signedInPaths.length === 0 && !foundSignedInSocial) {
334
- logger.log('WARN', `[RootDataIndexer/${dateStr}] ⚠️ No signed-in social posts found! Expected pattern: "${signedInMatchPattern}"`);
335
- logger.log('WARN', `[RootDataIndexer/${dateStr}] All paths found: ${allPaths.slice(0, 10).join(', ')}${allPaths.length > 10 ? '...' : ''}`);
336
- }
337
- } else {
338
- logger.log('WARN', `[RootDataIndexer/${dateStr}] ⚠️ Collection group query returned NO posts! (dayStart: ${dayStart.toISOString()}, dayEnd: ${dayEnd.toISOString()})`);
339
- logger.log('WARN', `[RootDataIndexer/${dateStr}] This might indicate: 1) No posts with fetchedAt in this range, 2) Missing Firestore index, or 3) Query error`);
340
- }
298
+ // Social data checks are done above using tracking documents
299
+ // foundPISocial and foundSignedInSocial are already set from the tracking document checks
300
+ logger.log('INFO', `[RootDataIndexer/${dateStr}] Social check results - PI: ${foundPISocial}, Signed-in: ${foundSignedInSocial}`);
341
301
 
342
302
  // --- Assign to Availability ---
343
303
  availability.details.normalPortfolio = normPortExists;
@@ -351,6 +351,30 @@ exports.handleSocialTask = async (message, context, config, dependencies) => {
351
351
  offset += take;
352
352
  }
353
353
 
354
+ // Write date tracking document after successful fetch
355
+ // Single document at root level: /signed_in_users_social/_dates and /pi_social_posts/_dates
356
+ // Contains a map of all dates that have been fetched
357
+ if (totalSaved > 0 && (type === 'SIGNED_IN_USER' || type === 'POPULAR_INVESTOR')) {
358
+ const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
359
+ let trackingCollectionPath;
360
+
361
+ if (type === 'SIGNED_IN_USER') {
362
+ trackingCollectionPath = config.signedInUserSocialCollection || 'signed_in_users_social';
363
+ } else {
364
+ trackingCollectionPath = config.piSocialCollectionName || 'pi_social_posts';
365
+ }
366
+
367
+ // Write to a single tracking document at the root of the collection
368
+ const trackingDocRef = db.collection(trackingCollectionPath).doc('_dates');
369
+ const trackingData = {
370
+ [`fetchedDates.${today}`]: true,
371
+ lastUpdated: FieldValue.serverTimestamp()
372
+ };
373
+
374
+ await trackingDocRef.set(trackingData, { merge: true });
375
+ logger.log('INFO', `[SocialTask/${taskId}] Updated date tracking for ${type}: ${today}`);
376
+ }
377
+
354
378
  logger.log('SUCCESS', `[SocialTask/${taskId}] Process complete for ${cid}. Total stored: ${totalSaved}/${MAX_POSTS_TO_STORE}`);
355
379
 
356
380
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.470",
3
+ "version": "1.0.471",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [