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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|
|
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) {
|