bulltrackers-module 1.0.454 → 1.0.455

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.
@@ -735,30 +735,42 @@ async function getUserSocialPosts(req, res, dependencies, config) {
735
735
 
736
736
  /**
737
737
  * GET /user/me/instrument-mappings
738
- * Fetches instrument ID to ticker mapping for the frontend
738
+ * Fetches instrument ID to ticker and sector mappings for the frontend
739
739
  */
740
740
  async function getInstrumentMappings(req, res, dependencies, config) {
741
741
  const { db, logger } = dependencies;
742
742
 
743
743
  try {
744
744
  // Fetch from Firestore (same source as computation system)
745
- const tickerToIdDoc = await db.collection('instrument_mappings').doc('etoro_to_ticker').get();
745
+ const [tickerToIdDoc, tickerToSectorDoc] = await Promise.all([
746
+ db.collection('instrument_mappings').doc('etoro_to_ticker').get(),
747
+ db.collection('instrument_sector_mappings').doc('sector_mappings').get()
748
+ ]);
746
749
 
747
750
  if (!tickerToIdDoc.exists) {
748
751
  return res.status(404).json({ error: "Instrument mappings not found" });
749
752
  }
750
753
 
751
754
  const tickerToId = tickerToIdDoc.data();
755
+ const tickerToSector = tickerToSectorDoc.exists ? tickerToSectorDoc.data() : {};
752
756
 
753
757
  // Convert to ID -> Ticker mapping (reverse the mapping)
754
758
  const idToTicker = {};
759
+ const idToSector = {};
760
+
755
761
  for (const [id, ticker] of Object.entries(tickerToId)) {
756
762
  idToTicker[String(id)] = ticker;
763
+ // Map ID -> Sector via ticker
764
+ if (tickerToSector[ticker]) {
765
+ idToSector[String(id)] = tickerToSector[ticker];
766
+ }
757
767
  }
758
768
 
759
769
  return res.status(200).json({
760
770
  instrumentToTicker: idToTicker,
761
- count: Object.keys(idToTicker).length
771
+ instrumentToSector: idToSector,
772
+ count: Object.keys(idToTicker).length,
773
+ sectorCount: Object.keys(idToSector).length
762
774
  });
763
775
 
764
776
  } catch (error) {
@@ -1847,11 +1859,16 @@ async function getPiProfile(req, res, dependencies, config) {
1847
1859
  });
1848
1860
  }
1849
1861
 
1862
+ // Get username from rankings
1863
+ const { getPiUsername } = require('./on_demand_fetch_helpers');
1864
+ const username = await getPiUsername(db, cid, config, logger);
1865
+
1850
1866
  logger.log('SUCCESS', `[getPiProfile] Returning profile data for CID ${cid} from date ${foundDate} (requested: ${today}, latest available: ${latestDate})`);
1851
1867
 
1852
1868
  return res.status(200).json({
1853
1869
  status: 'success',
1854
1870
  cid: cidStr,
1871
+ username: username || null,
1855
1872
  data: profileData,
1856
1873
  isFallback: foundDate !== latestDate || foundDate !== today,
1857
1874
  dataDate: foundDate,
@@ -646,7 +646,7 @@ async function handleOnDemandUserUpdate(taskData, config, dependencies) {
646
646
 
647
647
  logger.log('SUCCESS', `[On-Demand Update] Complete for ${username}`);
648
648
 
649
- // Update request status to completed if this is a sync request
649
+ // Update request status and trigger computation if this is a sync request
650
650
  if (requestId && source === 'on_demand_sync' && db) {
651
651
  try {
652
652
  const requestRef = db.collection('user_sync_requests')
@@ -654,14 +654,87 @@ async function handleOnDemandUserUpdate(taskData, config, dependencies) {
654
654
  .collection('requests')
655
655
  .doc(requestId);
656
656
 
657
- await requestRef.update({
658
- status: 'completed',
659
- completedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp(),
660
- updatedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp()
661
- });
662
- logger.log('INFO', `[On-Demand Update] Updated sync request ${requestId} to completed`);
657
+ // CRITICAL: Trigger root data indexer FIRST so computation system knows data exists
658
+ try {
659
+ const { runRootDataIndexer } = require('../../root-data-indexer/index');
660
+
661
+ // Get rootDataIndexer config from injected config object
662
+ const rootDataIndexerConfig = config.rootDataIndexer || {};
663
+
664
+ // Build indexer config with targetDate for single-date indexing
665
+ const indexerConfig = {
666
+ ...rootDataIndexerConfig, // Use injected config (has all collection names)
667
+ targetDate: today // Index only today's date for speed
668
+ };
669
+
670
+ logger.log('INFO', `[On-Demand Update] Triggering root data indexer for date ${today} before computation...`);
671
+ await runRootDataIndexer(indexerConfig, dependencies);
672
+ logger.log('INFO', `[On-Demand Update] Root data indexer completed for date ${today}`);
673
+
674
+ // Update status to indicate indexing is done, computation is being triggered
675
+ await requestRef.update({
676
+ status: 'computing',
677
+ indexedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp(),
678
+ updatedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp()
679
+ });
680
+ } catch (indexerError) {
681
+ logger.log('ERROR', `[On-Demand Update] Failed to run root data indexer for ${today}`, indexerError);
682
+ // Continue anyway - computation might still work if index already exists
683
+ // But update status to indicate we tried
684
+ await requestRef.update({
685
+ status: 'computing',
686
+ indexerError: indexerError.message,
687
+ updatedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp()
688
+ });
689
+ }
690
+
691
+ // Trigger computation for SignedInUserProfileMetrics
692
+ const { pubsub } = dependencies;
693
+ if (pubsub) {
694
+ const computationTopic = config.computationSystem?.computationTopicStandard || 'computation-tasks';
695
+ const topic = pubsub.topic(computationTopic);
696
+ const crypto = require('crypto');
697
+
698
+ const computationMessage = {
699
+ action: 'RUN_COMPUTATION_DATE',
700
+ computation: 'SignedInUserProfileMetrics',
701
+ date: today,
702
+ pass: '1', // Standard pass
703
+ dispatchId: crypto.randomUUID(),
704
+ triggerReason: 'on_demand_sync',
705
+ resources: 'standard',
706
+ metadata: {
707
+ onDemand: true,
708
+ requestId: requestId,
709
+ userCid: cid,
710
+ username: username
711
+ },
712
+ traceContext: {
713
+ traceId: crypto.randomBytes(16).toString('hex'),
714
+ spanId: crypto.randomBytes(8).toString('hex'),
715
+ sampled: true
716
+ }
717
+ };
718
+
719
+ await topic.publishMessage({
720
+ data: Buffer.from(JSON.stringify(computationMessage))
721
+ });
722
+
723
+ logger.log('INFO', `[On-Demand Update] Triggered computation SignedInUserProfileMetrics for user ${cid} (${username}) for date ${today}`);
724
+
725
+ // Don't mark as completed yet - wait for computation to finish
726
+ // The status will remain 'computing' until computation completes
727
+ } else {
728
+ logger.log('WARN', `[On-Demand Update] PubSub not available, cannot trigger computation`);
729
+ // Mark as completed if we can't trigger computation
730
+ await requestRef.update({
731
+ status: 'completed',
732
+ completedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp(),
733
+ updatedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp()
734
+ });
735
+ }
663
736
  } catch (err) {
664
- logger.log('WARN', `[On-Demand Update] Failed to update request status to completed for ${requestId}`, err);
737
+ logger.log('WARN', `[On-Demand Update] Failed to update request status or trigger computation for ${requestId}`, err);
665
738
  }
666
739
  }
667
740
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.454",
3
+ "version": "1.0.455",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [