-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathfetcher.js
152 lines (134 loc) · 4.82 KB
/
fetcher.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
const axios = require('axios');
const cheerio = require('cheerio');
const mongoose = require('mongoose');
const moment = require('moment');
require('dotenv').config();
// Function to extract users from HTML
function getUsers($) {
const users = {};
try {
$('tr').slice(1).each((_, row) => {
const columns = $(row).find('td');
if (columns.length === 4) {
const userName = $(columns[1]).text().trim();
const solvedTasks = parseInt($(columns[2]).text().trim());
if (userName && !isNaN(solvedTasks)) {
users[userName] = solvedTasks;
}
}
});
} catch (error) {
console.error('Error parsing users:', error);
}
return users;
}
// Function to fetch CSES data
async function fetchCSESData() {
const cookies = {
PHPSESSID: process.env.PHPSESSID
};
const headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.9',
'Cookie': `PHPSESSID=${cookies.PHPSESSID}`,
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
};
const users = {};
try {
for (let page of [1, 2]) {
const response = await axios.get(`https://cses.fi/problemset/stats/friends/p/${page}`, {
headers,
timeout: 10000,
validateStatus: status => status === 200
});
if (response.data) {
const $ = cheerio.load(response.data);
Object.assign(users, getUsers($));
}
}
return Object.keys(users).length > 0 ? users : null;
} catch (error) {
console.error('Error fetching CSES data:', error.message);
return null;
}
}
// Function to update MongoDB
async function updateMongoDB(users) {
if (!users || Object.keys(users).length === 0) {
console.log('No user data to update');
return false;
}
const session = await mongoose.startSession();
try {
session.startTransaction();
const collection = mongoose.connection.collection('CSES');
const todayDate = moment().format('DD/MM/YYYY');
const yesterdayDate = moment().subtract(1, 'days').format('DD/MM/YYYY');
const operations = [];
for (const [user, tasks] of Object.entries(users)) {
const document = await collection.findOne({ username: user }, { session });
if (document) {
const prevSolved = document.solved?.[yesterdayDate] || 0;
const currentStreak = document.streak || 0;
let newStreak = 0;
if (tasks > prevSolved) {
newStreak = currentStreak + 1;
}
operations.push({
updateOne: {
filter: { username: user },
update: {
$set: {
[`solved.${todayDate}`]: tasks,
streak: newStreak,
questionSolved: tasks,
lastUpdated: new Date()
}
}
}
});
} else {
operations.push({
insertOne: {
document: {
username: user,
solved: { [todayDate]: tasks },
streak: 0,
questionSolved: tasks,
lastUpdated: new Date()
}
}
});
}
}
if (operations.length > 0) {
await collection.bulkWrite(operations, { session });
console.log(`Updated ${operations.length} users`);
}
await session.commitTransaction();
return true;
} catch (error) {
await session.abortTransaction();
console.error('Error updating MongoDB:', error);
throw error;
} finally {
await session.endSession();
}
}
// Main function to fetch and update data
async function updateLeaderboard() {
try {
console.log('Starting leaderboard update...');
const users = await fetchCSESData();
if (!users || Object.keys(users).length === 0) {
throw new Error('No user data received from CSES');
}
await updateMongoDB(users);
console.log('Leaderboard update completed successfully');
return true;
} catch (error) {
console.error('Error updating leaderboard:', error);
throw error;
}
}
module.exports = { updateLeaderboard };