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 SignedInUserCopiedPIs computation ===
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
- const computationDate = await findLatestComputationDate(
98
- db,
99
- insightsCollection,
100
- resultsSub,
101
- compsSub,
102
- category,
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
- if (computationDate) {
109
- const computationRef = db.collection(insightsCollection)
110
- .doc(computationDate)
111
- .collection(resultsSub)
112
- .doc(category)
113
- .collection(compsSub)
114
- .doc(computationName);
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
- const computationDoc = await computationRef.get();
117
-
118
- if (computationDoc.exists) {
119
- const rawData = computationDoc.data();
120
- const decompressed = tryDecompress(rawData);
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
- // Check if user's data exists in computation results
123
- if (decompressed && decompressed[String(userCidNum)]) {
124
- const userData = decompressed[String(userCidNum)];
125
- // Check if PI is in current, past, or all arrays
126
- const allCopied = userData.all || [];
127
- if (allCopied.includes(piCidNum)) {
128
- return true;
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) return true;
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.459",
3
+ "version": "1.0.461",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [