bulltrackers-module 1.0.248 → 1.0.250
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.
|
@@ -11,7 +11,7 @@ const { checkRootDataAvailability } = require('../data/AvailabilityChecker');
|
|
|
11
11
|
const { FieldValue } = require('@google-cloud/firestore');
|
|
12
12
|
const pLimit = require('p-limit');
|
|
13
13
|
const path = require('path');
|
|
14
|
-
const packageJson = require(path.join(__dirname, '..', '..', 'package.json'));
|
|
14
|
+
const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
|
|
15
15
|
const packageVersion = packageJson.version;
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Root Data Indexer
|
|
3
|
+
* Runs daily to index exactly what data is available for every date.
|
|
4
|
+
* Creates a "Source of Truth" map in Firestore for the Dispatcher/Computation System.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { FieldValue } = require('@google-cloud/firestore');
|
|
8
|
+
const pLimit = require('p-limit');
|
|
9
|
+
|
|
10
|
+
// Hardcoded verification blocks as per logic requirements
|
|
11
|
+
const CANARY_BLOCK_ID = '19M';
|
|
12
|
+
const CANARY_PART_ID = 'part_0';
|
|
13
|
+
const PRICE_SHARD_ID = 'shard_0';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Main pipe: pipe.maintenance.runRootDataIndexer
|
|
17
|
+
*/
|
|
18
|
+
exports.runRootDataIndexer = async (config, dependencies) => {
|
|
19
|
+
const { db, logger } = dependencies;
|
|
20
|
+
const {
|
|
21
|
+
availabilityCollection,
|
|
22
|
+
earliestDate,
|
|
23
|
+
collections
|
|
24
|
+
} = config;
|
|
25
|
+
|
|
26
|
+
logger.log('INFO', '[RootDataIndexer] Starting Root Data Availability Scan...');
|
|
27
|
+
|
|
28
|
+
// 1. Pre-fetch Price Data Availability (Optimization)
|
|
29
|
+
// Instead of reading the shard N times, we read it once and map available dates.
|
|
30
|
+
const priceAvailabilitySet = new Set();
|
|
31
|
+
try {
|
|
32
|
+
const priceShardRef = db.collection(collections.prices).doc(PRICE_SHARD_ID);
|
|
33
|
+
const priceSnap = await priceShardRef.get();
|
|
34
|
+
if (priceSnap.exists) {
|
|
35
|
+
const data = priceSnap.data();
|
|
36
|
+
// Iterate over all instruments in this shard to find any available dates
|
|
37
|
+
Object.values(data).forEach(instrument => {
|
|
38
|
+
if (instrument.prices) {
|
|
39
|
+
Object.keys(instrument.prices).forEach(dateKey => {
|
|
40
|
+
// Validate format YYYY-MM-DD
|
|
41
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(dateKey)) {
|
|
42
|
+
priceAvailabilitySet.add(dateKey);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
logger.log('INFO', `[RootDataIndexer] Loaded price availability map. Found prices for ${priceAvailabilitySet.size} unique dates.`);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
logger.log('ERROR', `[RootDataIndexer] Failed to load price shard ${PRICE_SHARD_ID}. Price availability will be false.`, { error: e.message });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 2. Determine Date Range (Earliest -> Tomorrow)
|
|
54
|
+
const start = new Date(earliestDate || '2023-01-01');
|
|
55
|
+
const end = new Date();
|
|
56
|
+
end.setDate(end.getDate() + 1); // Look ahead 1 day
|
|
57
|
+
|
|
58
|
+
const datesToScan = [];
|
|
59
|
+
for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
|
|
60
|
+
datesToScan.push(d.toISOString().slice(0, 10));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
logger.log('INFO', `[RootDataIndexer] Scanning ${datesToScan.length} dates from ${datesToScan[0]} to ${datesToScan[datesToScan.length-1]}`);
|
|
64
|
+
|
|
65
|
+
// 3. Scan in Parallel
|
|
66
|
+
const limit = pLimit(20); // Concurrent date checks
|
|
67
|
+
let updatesCount = 0;
|
|
68
|
+
|
|
69
|
+
const promises = datesToScan.map(dateStr => limit(async () => {
|
|
70
|
+
try {
|
|
71
|
+
const availability = {
|
|
72
|
+
date: dateStr,
|
|
73
|
+
lastUpdated: FieldValue.serverTimestamp(),
|
|
74
|
+
|
|
75
|
+
// Defaults
|
|
76
|
+
hasPortfolio: false,
|
|
77
|
+
hasHistory: false,
|
|
78
|
+
hasSocial: false,
|
|
79
|
+
hasInsights: false,
|
|
80
|
+
hasPrices: false,
|
|
81
|
+
|
|
82
|
+
// Detailed breakdown
|
|
83
|
+
details: {
|
|
84
|
+
normalPortfolio: false,
|
|
85
|
+
speculatorPortfolio: false,
|
|
86
|
+
normalHistory: false,
|
|
87
|
+
speculatorHistory: false
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// A. User Portfolios (Normal) - /NormalUserPortfolios/19M/snapshots/{date}/parts/part_0
|
|
92
|
+
const normPortRef = db.collection(collections.normalPortfolios)
|
|
93
|
+
.doc(CANARY_BLOCK_ID)
|
|
94
|
+
.collection('snapshots')
|
|
95
|
+
.doc(dateStr)
|
|
96
|
+
.collection('parts')
|
|
97
|
+
.doc(CANARY_PART_ID);
|
|
98
|
+
|
|
99
|
+
// B. Speculator Portfolios - /SpeculatorPortfolios/19M/snapshots/{date}/parts/part_0
|
|
100
|
+
const specPortRef = db.collection(collections.speculatorPortfolios)
|
|
101
|
+
.doc(CANARY_BLOCK_ID)
|
|
102
|
+
.collection('snapshots')
|
|
103
|
+
.doc(dateStr)
|
|
104
|
+
.collection('parts')
|
|
105
|
+
.doc(CANARY_PART_ID);
|
|
106
|
+
|
|
107
|
+
// C. User Trade History - /NormalUserTradeHistory/19M/snapshots/{date}/parts/part_0
|
|
108
|
+
const normHistRef = db.collection(collections.normalHistory)
|
|
109
|
+
.doc(CANARY_BLOCK_ID)
|
|
110
|
+
.collection('snapshots')
|
|
111
|
+
.doc(dateStr)
|
|
112
|
+
.collection('parts')
|
|
113
|
+
.doc(CANARY_PART_ID);
|
|
114
|
+
|
|
115
|
+
// D. Speculator Trade History - /SpeculatorTradeHistory/19M/snapshots/{date}/parts/part_0
|
|
116
|
+
const specHistRef = db.collection(collections.speculatorHistory)
|
|
117
|
+
.doc(CANARY_BLOCK_ID)
|
|
118
|
+
.collection('snapshots')
|
|
119
|
+
.doc(dateStr)
|
|
120
|
+
.collection('parts')
|
|
121
|
+
.doc(CANARY_PART_ID);
|
|
122
|
+
|
|
123
|
+
// E. Insights Data - /daily_instrument_insights/{date}
|
|
124
|
+
const insightsRef = db.collection(collections.insights).doc(dateStr);
|
|
125
|
+
|
|
126
|
+
// F. Social Data - /daily_social_insights/{date}/posts (Check for ANY doc)
|
|
127
|
+
const socialPostsRef = db.collection(collections.social).doc(dateStr).collection('posts');
|
|
128
|
+
|
|
129
|
+
// --- EXECUTE CHECKS ---
|
|
130
|
+
const [
|
|
131
|
+
normPortSnap, specPortSnap,
|
|
132
|
+
normHistSnap, specHistSnap,
|
|
133
|
+
insightsSnap, socialQuerySnap
|
|
134
|
+
] = await Promise.all([
|
|
135
|
+
normPortRef.get(), specPortRef.get(),
|
|
136
|
+
normHistRef.get(), specHistRef.get(),
|
|
137
|
+
insightsRef.get(),
|
|
138
|
+
socialPostsRef.limit(1).get()
|
|
139
|
+
]);
|
|
140
|
+
|
|
141
|
+
// Evaluate Findings
|
|
142
|
+
availability.details.normalPortfolio = normPortSnap.exists;
|
|
143
|
+
availability.details.speculatorPortfolio = specPortSnap.exists;
|
|
144
|
+
availability.hasPortfolio = normPortSnap.exists || specPortSnap.exists;
|
|
145
|
+
|
|
146
|
+
availability.details.normalHistory = normHistSnap.exists;
|
|
147
|
+
availability.details.speculatorHistory = specHistSnap.exists;
|
|
148
|
+
availability.hasHistory = normHistSnap.exists || specHistSnap.exists;
|
|
149
|
+
|
|
150
|
+
availability.hasInsights = insightsSnap.exists;
|
|
151
|
+
availability.hasSocial = !socialQuerySnap.empty;
|
|
152
|
+
|
|
153
|
+
// G. Prices (From pre-loaded set)
|
|
154
|
+
availability.hasPrices = priceAvailabilitySet.has(dateStr);
|
|
155
|
+
|
|
156
|
+
// Write to Index
|
|
157
|
+
await db.collection(availabilityCollection).doc(dateStr).set(availability);
|
|
158
|
+
updatesCount++;
|
|
159
|
+
|
|
160
|
+
} catch (e) {
|
|
161
|
+
logger.log('ERROR', `[RootDataIndexer] Failed to index ${dateStr}`, { error: e.message });
|
|
162
|
+
}
|
|
163
|
+
}));
|
|
164
|
+
|
|
165
|
+
await Promise.all(promises);
|
|
166
|
+
logger.log('SUCCESS', `[RootDataIndexer] Indexing complete. Updated ${updatesCount} dates.`);
|
|
167
|
+
return { success: true, count: updatesCount };
|
|
168
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bulltrackers-module",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.250",
|
|
4
4
|
"description": "Helper Functions for Bulltrackers.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"functions/appscript-api/",
|
|
19
19
|
"functions/social-orchestrator/",
|
|
20
20
|
"functions/social-task-handler/",
|
|
21
|
-
"functions/price-backfill/"
|
|
21
|
+
"functions/price-backfill/",
|
|
22
|
+
"functions/root-data-indexer/"
|
|
22
23
|
],
|
|
23
24
|
"keywords": [
|
|
24
25
|
"bulltrackers",
|