bulltrackers-module 1.0.459 → 1.0.461
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.
|
@@ -62,9 +62,11 @@ async function findLatestComputationDate(db, insightsCollection, resultsSub, com
|
|
|
62
62
|
|
|
63
63
|
const doc = await computationRef.get();
|
|
64
64
|
if (doc.exists) {
|
|
65
|
+
console.log(`[findLatestComputationDate] Found computation ${computationName} in category ${category} for date ${dateStr}`);
|
|
65
66
|
return dateStr;
|
|
66
67
|
}
|
|
67
68
|
}
|
|
69
|
+
console.log(`[findLatestComputationDate] No computation found for ${computationName} in category ${category} within ${maxDays} days`);
|
|
68
70
|
return null;
|
|
69
71
|
}
|
|
70
72
|
|
|
@@ -87,45 +89,110 @@ async function hasUserCopied(db, userCid, piCid, config) {
|
|
|
87
89
|
}
|
|
88
90
|
// devResult is null, meaning no dev override, continue with normal logic
|
|
89
91
|
|
|
90
|
-
// === PRIMARY: Try to fetch from
|
|
92
|
+
// === PRIMARY: Try to fetch from computation results ===
|
|
91
93
|
const insightsCollection = config.unifiedInsightsCollection || 'unified_insights';
|
|
92
|
-
const category = 'signed_in_user';
|
|
93
|
-
const computationName = 'SignedInUserCopiedPIs';
|
|
94
94
|
const resultsSub = config.resultsSubcollection || 'results';
|
|
95
95
|
const compsSub = config.computationsSubcollection || 'computations';
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
computationName,
|
|
104
|
-
userCidNum,
|
|
105
|
-
30
|
|
106
|
-
);
|
|
97
|
+
// Try both computation names and categories
|
|
98
|
+
const computationConfigs = [
|
|
99
|
+
{ category: 'signed_in_user', name: 'SignedInUserCopiedPIs', format: 'object' }, // Returns { current: [], past: [], all: [] }
|
|
100
|
+
{ category: 'signed_in_user', name: 'SignedInUserCopiedList', format: 'array' }, // Returns array directly
|
|
101
|
+
{ category: 'popular-investor', name: 'SignedInUserCopiedList', format: 'array' }, // Alternative location
|
|
102
|
+
];
|
|
107
103
|
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
.
|
|
104
|
+
for (const configItem of computationConfigs) {
|
|
105
|
+
const computationDate = await findLatestComputationDate(
|
|
106
|
+
db,
|
|
107
|
+
insightsCollection,
|
|
108
|
+
resultsSub,
|
|
109
|
+
compsSub,
|
|
110
|
+
configItem.category,
|
|
111
|
+
configItem.name,
|
|
112
|
+
userCidNum,
|
|
113
|
+
30
|
|
114
|
+
);
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
if (computationDate) {
|
|
117
|
+
const computationRef = db.collection(insightsCollection)
|
|
118
|
+
.doc(computationDate)
|
|
119
|
+
.collection(resultsSub)
|
|
120
|
+
.doc(configItem.category)
|
|
121
|
+
.collection(compsSub)
|
|
122
|
+
.doc(configItem.name);
|
|
123
|
+
|
|
124
|
+
const computationDoc = await computationRef.get();
|
|
121
125
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
|
|
126
|
+
if (computationDoc.exists) {
|
|
127
|
+
const rawData = computationDoc.data();
|
|
128
|
+
|
|
129
|
+
// Handle sharded data
|
|
130
|
+
let mergedData = null;
|
|
131
|
+
if (rawData._sharded === true && rawData._shardCount) {
|
|
132
|
+
// Data is in shards - read all shards and merge
|
|
133
|
+
const shardsCol = computationRef.collection('_shards');
|
|
134
|
+
const shardsSnapshot = await shardsCol.get();
|
|
135
|
+
|
|
136
|
+
if (!shardsSnapshot.empty) {
|
|
137
|
+
mergedData = {};
|
|
138
|
+
for (const shardDoc of shardsSnapshot.docs) {
|
|
139
|
+
const shardData = shardDoc.data();
|
|
140
|
+
// Merge shard data - each shard may contain different user IDs
|
|
141
|
+
for (const [key, value] of Object.entries(shardData)) {
|
|
142
|
+
if (mergedData[key]) {
|
|
143
|
+
// If key exists, merge arrays or objects
|
|
144
|
+
if (Array.isArray(mergedData[key]) && Array.isArray(value)) {
|
|
145
|
+
mergedData[key] = [...mergedData[key], ...value];
|
|
146
|
+
} else if (typeof mergedData[key] === 'object' && typeof value === 'object') {
|
|
147
|
+
mergedData[key] = { ...mergedData[key], ...value };
|
|
148
|
+
} else {
|
|
149
|
+
mergedData[key] = value;
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
mergedData[key] = value;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
// Data is in main document - decompress if needed
|
|
159
|
+
const { tryDecompress } = require('./data_helpers');
|
|
160
|
+
mergedData = tryDecompress(rawData);
|
|
161
|
+
|
|
162
|
+
// Handle string decompression result
|
|
163
|
+
if (typeof mergedData === 'string') {
|
|
164
|
+
try {
|
|
165
|
+
mergedData = JSON.parse(mergedData);
|
|
166
|
+
} catch (e) {
|
|
167
|
+
console.error(`[hasUserCopied] Failed to parse decompressed string:`, e.message);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!mergedData) continue;
|
|
174
|
+
|
|
175
|
+
// Extract user data based on format
|
|
176
|
+
const userData = mergedData[String(userCidNum)];
|
|
177
|
+
if (!userData) continue;
|
|
178
|
+
|
|
179
|
+
if (configItem.format === 'object') {
|
|
180
|
+
// SignedInUserCopiedPIs format: { userId: { current: [], past: [], all: [] } }
|
|
181
|
+
if (userData.all && Array.isArray(userData.all)) {
|
|
182
|
+
if (userData.all.includes(piCidNum)) {
|
|
183
|
+
console.log(`[hasUserCopied] Found PI ${piCidNum} in SignedInUserCopiedPIs.all for user ${userCid} (date: ${computationDate}, category: ${configItem.category})`);
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} else if (configItem.format === 'array') {
|
|
188
|
+
// SignedInUserCopiedList format: { userId: [{ cid: X, ... }, ...] }
|
|
189
|
+
if (Array.isArray(userData)) {
|
|
190
|
+
const hasCopied = userData.some(item => Number(item.cid) === piCidNum);
|
|
191
|
+
if (hasCopied) {
|
|
192
|
+
console.log(`[hasUserCopied] Found PI ${piCidNum} in SignedInUserCopiedList for user ${userCid} (date: ${computationDate}, category: ${configItem.category})`);
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
129
196
|
}
|
|
130
197
|
}
|
|
131
198
|
}
|
|
@@ -135,12 +202,34 @@ async function hasUserCopied(db, userCid, piCid, config) {
|
|
|
135
202
|
const userDoc = await db.collection(signedInUsersCollection).doc(String(userCid)).get();
|
|
136
203
|
if (userDoc.exists) {
|
|
137
204
|
const data = userDoc.data();
|
|
205
|
+
|
|
206
|
+
// Check AggregatedMirrors (current copies)
|
|
138
207
|
if (data.AggregatedMirrors && Array.isArray(data.AggregatedMirrors)) {
|
|
139
208
|
const isCopying = data.AggregatedMirrors.some(m => Number(m.ParentCID) === piCidNum);
|
|
140
|
-
if (isCopying)
|
|
209
|
+
if (isCopying) {
|
|
210
|
+
console.log(`[hasUserCopied] Found PI ${piCidNum} in AggregatedMirrors for user ${userCid}`);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Also check historical mirrors if available
|
|
216
|
+
if (data.snapshots && typeof data.snapshots === 'object') {
|
|
217
|
+
// Check recent snapshots for any copy history
|
|
218
|
+
const snapshotDates = Object.keys(data.snapshots).sort().reverse().slice(0, 30); // Last 30 days
|
|
219
|
+
for (const date of snapshotDates) {
|
|
220
|
+
const snapshot = data.snapshots[date];
|
|
221
|
+
if (snapshot && snapshot.AggregatedMirrors && Array.isArray(snapshot.AggregatedMirrors)) {
|
|
222
|
+
const wasCopying = snapshot.AggregatedMirrors.some(m => Number(m.ParentCID) === piCidNum);
|
|
223
|
+
if (wasCopying) {
|
|
224
|
+
console.log(`[hasUserCopied] Found PI ${piCidNum} in historical snapshot ${date} for user ${userCid}`);
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
141
229
|
}
|
|
142
230
|
}
|
|
143
231
|
|
|
232
|
+
console.log(`[hasUserCopied] User ${userCid} has not copied PI ${piCidNum} (checked computation and portfolio)`);
|
|
144
233
|
return false;
|
|
145
234
|
} catch (error) {
|
|
146
235
|
console.error('[hasUserCopied] Error checking copy status:', error);
|
|
@@ -435,6 +524,8 @@ async function checkReviewEligibility(req, res, dependencies, config) {
|
|
|
435
524
|
|
|
436
525
|
const canReview = await hasUserCopied(db, effectiveCid, piCid, config);
|
|
437
526
|
|
|
527
|
+
logger.log('INFO', `[checkReviewEligibility] User ${effectiveCid} eligibility for PI ${piCid}: ${canReview}`);
|
|
528
|
+
|
|
438
529
|
return res.status(200).json({
|
|
439
530
|
piCid: Number(piCid),
|
|
440
531
|
eligible: canReview,
|