From 69ec0fa869f1259451c4224def503ba6ca643178 Mon Sep 17 00:00:00 2001 From: KanishkRanjan <68316017+KanishkRanjan@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:30:39 +0530 Subject: [PATCH 1/2] Fix some bug and improved fetcher --- fetcher.js | 113 +++++++++++++------------- index.js | 140 ++++++++++++++++---------------- public/images/profile.jpg | Bin 0 -> 4981 bytes public/script.js | 36 +++++++++ public/styles.css | 165 ++++++++++++++++++++++++++++++++++++++ vercel.json | 16 +--- views/profile.ejs | 99 +++++++++++++++++++++++ 7 files changed, 429 insertions(+), 140 deletions(-) create mode 100644 public/images/profile.jpg create mode 100644 public/script.js create mode 100644 public/styles.css create mode 100644 views/profile.ejs diff --git a/fetcher.js b/fetcher.js index 73ac28a..72fe660 100644 --- a/fetcher.js +++ b/fetcher.js @@ -2,6 +2,7 @@ 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 @@ -60,92 +61,92 @@ async function fetchCSESData() { // Function to update MongoDB async function updateMongoDB(users) { - if (!users || Object.keys(users).length === 0) { + if (!users) { console.log('No user data to update'); return false; } - const session = await mongoose.startSession(); + let client; try { - session.startTransaction(); - + const uri = process.env.MONGODB_URI; + if (!uri) { + throw new Error('MongoDB URI not found in environment variables'); + } + + if (!mongoose.connection.readyState) { + await mongoose.connect(uri, { + useNewUrlParser: true, + useUnifiedTopology: true + }); + } + 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 }); + const document = await collection.findOne({ username: user }); if (document) { - const prevSolved = document.solved?.[yesterdayDate] || 0; - const currentStreak = document.streak || 0; - - let newStreak = 0; - if (tasks > prevSolved) { - newStreak = currentStreak + 1; + let streak = parseInt(document.streak || 0); + const prevSolved = document.solved?.[yesterdayDate]; + let currSolved = prevSolved; + + if (prevSolved !== undefined && prevSolved < tasks) { + streak = document.prevStreak + 1; + currSolved = tasks; + } else { + streak = 0; } - 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, + document.solved = document.solved || {}; + document.solved[todayDate] = currSolved; + + await collection.updateOne( + { username: user }, + { + $set: { + solved: document.solved, + streak: streak, questionSolved: tasks, - lastUpdated: new Date() + prevStreak: document.streak || 0 } } - }); + ); + } else { + const data = { + username: user, + solved: { [todayDate]: tasks }, + streak: 0, + questionSolved: tasks, + prevStreak: 0 + }; + await collection.insertOne(data); } } - - 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(); + console.error('Error updating MongoDB:', error.message); + return false; } } // Main function to fetch and update data async function updateLeaderboard() { + console.log('Starting leaderboard update:', new Date().toISOString()); 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'); + if (users) { + const success = await updateMongoDB(users); + console.log('Update completed:', success ? 'successful' : 'failed'); + return success; + } else { + console.log('No user data fetched'); + return false; } - - await updateMongoDB(users); - console.log('Leaderboard update completed successfully'); - return true; } catch (error) { - console.error('Error updating leaderboard:', error); - throw error; + console.error('Error in updateLeaderboard:', error.message); + return false; } } diff --git a/index.js b/index.js index 0736056..47fe0bf 100644 --- a/index.js +++ b/index.js @@ -2,13 +2,15 @@ const express = require('express'); const mongoose = require("mongoose"); const moment = require('moment'); const path = require('path'); +const cron = require('node-cron'); const { updateLeaderboard } = require('./fetcher'); + moment().format(); require('dotenv').config(); const app = express(); -const port = process.env.PORT || 3000; +const port = 5000; // Middleware app.set('views', path.join(__dirname, 'views')); @@ -20,34 +22,19 @@ app.use(express.json()); console.log('Current directory:', __dirname); console.log('Views directory:', path.join(__dirname, 'views')); -const mongoURI = process.env.MONGODB_URI; -if (!mongoURI) { - console.error('MONGODB_URI environment variable is not set'); - process.exit(1); -} +const mongoURI = process.env.MONGODB_URI ; -// Connect to MongoDB with connection pooling +// Connect to MongoDB const connectDB = async () => { try { - if (mongoose.connection.readyState === 1) { - console.log('MongoDB already connected'); - return mongoose.connection; - } - - if (!global.mongoConnection) { - global.mongoConnection = await mongoose.connect(mongoURI, { - useNewUrlParser: true, - useUnifiedTopology: true, - serverSelectionTimeoutMS: 5000, - bufferCommands: false, - maxPoolSize: 10 - }); - console.log("Connected to MongoDB!"); - } - return global.mongoConnection; + await mongoose.connect(mongoURI, { + useNewUrlParser: true, + useUnifiedTopology: true + }); + console.log("Connected to MongoDB!"); } catch (error) { console.error("MongoDB connection error:", error); - throw error; + process.exit(1); } }; @@ -56,15 +43,13 @@ const User = mongoose.model("User", new mongoose.Schema({ username: String, solved: Object, streak: Number, - questionSolved: Number, - lastUpdated: Date + questionSolved: Number }), "CSES"); // Routes -app.get("/", async (req, res, next) => { +app.get("/", async (req, res) => { try { - await connectDB(); - const users = await User.find().lean(); + const users = await User.find(); const usersData = users.map(userData => { const timeline = Array(7).fill(false); const noOfDaysInWeek = 7; @@ -72,16 +57,15 @@ app.get("/", async (req, res, next) => { for (let index = 0; index < noOfDaysInWeek; index++) { const reqDate = moment().subtract(index, 'days').format('DD/MM/YYYY'); const prevDate = moment().subtract(index + 1, 'days').format('DD/MM/YYYY'); - - timeline[noOfDaysInWeek - index - 1] = parseInt(userData.solved?.[reqDate] || 0) > parseInt(userData.solved?.[prevDate] || 0); + if (userData.solved[prevDate]) + timeline[noOfDaysInWeek - index - 1] = parseInt(userData.solved[reqDate] || 0) > parseInt(userData.solved[prevDate]); } return { name: userData.username, timeline: timeline, streak: userData.streak || 0, - questionSolved: userData.questionSolved || 0, - lastUpdated: userData.lastUpdated + questionSolved: userData.questionSolved || 0 }; }); @@ -90,54 +74,72 @@ app.get("/", async (req, res, next) => { res.render("index", { data: usersData }); } catch (error) { - next(error); + console.error("Error fetching data:", error); + res.status(500).json({ error: "Error fetching data", details: error.message }); } }); -// Manual update endpoint (protected) -app.post("/update", async (req, res, next) => { + +app.get("/profile" , async (req , res) => { try { - const apiKey = req.headers['x-api-key']; - if (apiKey !== process.env.API_KEY) { - return res.status(401).json({ error: "Unauthorized" }); - } + res.render("profile") - await connectDB(); + } + catch (error) { + console.error("Error while loading page:", error); + res.status(500).json({ error: "Error while loading data", details: error.message }); + + } +}) + + +// Manual update endpoint (protected) +app.post("/update", async (req, res) => { + const apiKey = req.headers['x-api-key']; + if (apiKey !== process.env.API_KEY) { + return res.status(401).json({ error: "Unauthorized" }); + } + + try { await updateLeaderboard(); res.json({ status: "success" }); } catch (error) { - next(error); + console.error("Error in manual update:", error); + res.status(500).json({ error: "Update failed" }); } }); -// Health check endpoint -app.get("/health", (req, res) => { - res.json({ status: "healthy" }); -}); - // Error handling middleware app.use((err, req, res, next) => { - console.error('Error:', err); - const statusCode = err.statusCode || 500; - const message = err.message || 'Internal Server Error'; - res.status(statusCode).json({ error: message }); + console.error(err.stack); + res.status(500).send('Something broke!'); }); -// For local development -if (process.env.NODE_ENV !== 'production') { - const startServer = async () => { - try { - await connectDB(); - app.listen(port, () => { - console.log(`Server is running on port ${port}`); - }); - } catch (error) { - console.error('Failed to start server:', error); - process.exit(1); - } - }; - - startServer(); -} - -module.exports = app; +// Schedule the update every 3 hours +cron.schedule('0 */3 * * *', async () => { + console.log('Running scheduled update'); + try { + await updateLeaderboard(); + } catch (error) { + console.error('Scheduled update failed:', error); + } +}); + +// Start server +const startServer = async () => { + await connectDB(); + + // Initial update on server start + try { + await updateLeaderboard(); + console.log('Initial update completed'); + } catch (error) { + console.error('Initial update failed:', error); + } + + app.listen(port, () => { + console.log(`Server is running on port ${port}`); + }); +}; +// setInterval(updateLeaderboard, 3600000); +startServer(); diff --git a/public/images/profile.jpg b/public/images/profile.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f99f0952b45b3808ec20119ff4a8c382b761ce20 GIT binary patch literal 4981 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 { + graphContainer.appendChild(dayElement.cloneNode(true)); + }); + + currentDate.setDate(currentDate.getDate() + 1); + } + \ No newline at end of file diff --git a/public/styles.css b/public/styles.css new file mode 100644 index 0000000..0884ee2 --- /dev/null +++ b/public/styles.css @@ -0,0 +1,165 @@ +/* Typography */ +.rubik-p { + font-family: "Rubik", sans-serif; + font-optical-sizing: auto; + font-weight: 300; + font-style: normal; + color: rgba(255, 255, 255, 0.8); /* Softer text for readability */ + } + + .rubik-h, .rubik-hmain, .rubik-hmain2, .rubik-hmain3 { + font-family: "Rubik", sans-serif; + font-optical-sizing: auto; + font-weight: 600; + font-style: normal; + } + + .rubik-hmain { + font-size: xxx-large; + background: linear-gradient(90deg, #dfbac9, #cdc3ee); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + } + + .rubik-hmain2 { + font-size: xx-large; + background: linear-gradient(90deg, #dfbac9, #cdc3ee); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + } + + .rubik-hmain3 { + font-size: x-large; + background: linear-gradient(90deg, #dfbac9, #cdc3ee); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + margin-bottom: 15px; + } + + /* Background */ + body { + background-color: black; + margin: 0; + font-family: "Rubik", sans-serif; + } + + /* Profile Card */ + .profile_card { + background-color: rgb(22, 22, 22); + padding: 30px; + width: 300px; + border-radius: 35px; + border: 1px solid rgb(48, 48, 51); + color: #fff; + margin-left: 30px; + position: fixed; + left: 30px; + top: 50%; + transform: translateY(-50%); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); + } + + .avatar-img { + width: 100%; + border-radius: 35px; + border: 1px solid rgb(48, 48, 51); + } + + /* Profile Bottom Section */ + .profile_bot { + background: linear-gradient(to right, #e1b9c5, #d0c2e9); + border-radius: 25px; + width: 85%; + padding: 20px; + margin-top: 15px; + text-align: center; + color: #000; + margin-bottom: -60px; + } + + /* Main Content */ + .main { + margin-top: 40px; + margin-left: 460px; + color: rgba(255, 255, 255, 0.75); + } + + /* Links */ + a { + color: white; + text-decoration: none; + } + + a:hover { + color: rgb(223, 185, 201); + } + + a:active { + color: rgb(208, 193, 231); + } + + /* Icons */ + .icons { + display: inline; + height: 30px; + width: 30px; + margin-right: 5px; + } + + .graph-container { + display: grid; + grid-template-rows: repeat(7, 15px); /* Days go from top to bottom */ + grid-auto-flow: column; /* Columns represent weeks */ + gap: 2px; + } + .day { + width: 15px; + height: 15px; + background-color: rgb(48,48,51); + border-radius: 2px; + position: relative; + } + .day[data-count="1"] { background-color: #0d4429 } + .day[data-count="2"] { background-color: #016c31 } + .day[data-count="3"] { background-color: #26a641 } + .day[data-count="4"] { background-color: #39d353; } + .tooltip { + display: none; + position: absolute; + background: #000; + color: #fff; + padding: 5px; + border-radius: 3px; + font-size: 10px; + white-space: nowrap; + z-index: 10; + pointer-events: none; + } + .day:hover .tooltip { + display: block; + top: -25px; + left: 50%; + transform: translateX(-50%); + } + .month-labels { + display: flex; + justify-content: space-between; + padding: 0 5px; + } + .day-labels { + display: grid; + grid-template-rows: repeat(7, 15px); + gap: 2px; + } + .day-label { + font-size: 10px; + color: #666; + text-align: right; + margin-right: 5px; + } + .months { + margin-top: 20px; + } \ No newline at end of file diff --git a/vercel.json b/vercel.json index 04f32b6..213cb53 100644 --- a/vercel.json +++ b/vercel.json @@ -7,14 +7,6 @@ } ], "routes": [ - { - "src": "/health", - "dest": "index.js" - }, - { - "src": "/update", - "dest": "index.js" - }, { "src": "/(.*)", "dest": "index.js" @@ -25,11 +17,5 @@ "path": "/update", "schedule": "0 */3 * * *" } - ], - "functions": { - "index.js": { - "memory": 1024, - "maxDuration": 10 - } - } + ] } diff --git a/views/profile.ejs b/views/profile.ejs new file mode 100644 index 0000000..9f5cf51 --- /dev/null +++ b/views/profile.ejs @@ -0,0 +1,99 @@ + + + + + + + + + Portfolio + + + + +
+

Neel Verma

+ +

Role:

+

Executive

+

Skills:

+

Python, C++, Java

+ + + + + + + + + +
+

print("hello world!")

+
+
+ +
+
+ +
+

CodeChef

+
+
+ +
+
+ +
+

Codeforces

+
+
+ +
+
+ +
+

CSES

+
+
+ +
+
+
+
+ + + + + From 03b62f38bfe342472c39dd357ba1d76a8456ca08 Mon Sep 17 00:00:00 2001 From: KanishkRanjan <68316017+KanishkRanjan@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:04:03 +0530 Subject: [PATCH 2/2] Fixed null error and introduced profile page --- fetcher.js | 6 +- images/profile.jpg | Bin 0 -> 4981 bytes index.ejs | 52 ++++++++++++++ index.js | 2 +- profile.ejs | 99 +++++++++++++++++++++++++++ script.js | 36 ++++++++++ styles.css | 165 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 356 insertions(+), 4 deletions(-) create mode 100644 images/profile.jpg create mode 100644 index.ejs create mode 100644 profile.ejs create mode 100644 script.js create mode 100644 styles.css diff --git a/fetcher.js b/fetcher.js index 72fe660..c5f3b57 100644 --- a/fetcher.js +++ b/fetcher.js @@ -90,7 +90,7 @@ async function updateMongoDB(users) { if (document) { let streak = parseInt(document.streak || 0); const prevSolved = document.solved?.[yesterdayDate]; - let currSolved = prevSolved; + let currSolved = prevSolved ; if (prevSolved !== undefined && prevSolved < tasks) { streak = document.prevStreak + 1; @@ -100,8 +100,8 @@ async function updateMongoDB(users) { } document.solved = document.solved || {}; - document.solved[todayDate] = currSolved; - + document.solved[todayDate] = currSolved ?? tasks; + await collection.updateOne( { username: user }, { diff --git a/images/profile.jpg b/images/profile.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f99f0952b45b3808ec20119ff4a8c382b761ce20 GIT binary patch literal 4981 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 + + + + + + + + Leaderboard + + + +
+

Leaderboard

+ + + + + + + + + + + + + + <% for (let index = 0; index < data.length; index++) { %> + + + + + + + + + <% } %> + +
RankNameActivityStreakQuestion Solved
#<%= index +1 %><%= data[index].name %> +
+ <% data[index].timeline.forEach(element => { %> + <% if(element) { %> +
+ <% } else { %> +
+ <% } %> + <% }); %> +
+
<%= data[index].streak %><%= data[index].questionSolved %>
+
+ + \ No newline at end of file diff --git a/index.js b/index.js index 47fe0bf..1837778 100644 --- a/index.js +++ b/index.js @@ -138,7 +138,7 @@ const startServer = async () => { } app.listen(port, () => { - console.log(`Server is running on port ${port}`); + console.log(`Server is 2 running on port ${port}`); }); }; // setInterval(updateLeaderboard, 3600000); diff --git a/profile.ejs b/profile.ejs new file mode 100644 index 0000000..9f5cf51 --- /dev/null +++ b/profile.ejs @@ -0,0 +1,99 @@ + + + + + + + + + Portfolio + + + + +
+

Neel Verma

+ +

Role:

+

Executive

+

Skills:

+

Python, C++, Java

+ + + + + + + + + +
+

print("hello world!")

+
+
+ +
+
+ +
+

CodeChef

+
+
+ +
+
+ +
+

Codeforces

+
+
+ +
+
+ +
+

CSES

+
+
+ +
+
+
+
+ + + + + diff --git a/script.js b/script.js new file mode 100644 index 0000000..9cbc50f --- /dev/null +++ b/script.js @@ -0,0 +1,36 @@ +// Get all the graph containers for CodeChef, Codeforces, and CSES +const graphContainers = [ + document.getElementById('codechef-graph-container'), + document.getElementById('codeforces-graph-container'), + document.getElementById('cses-graph-container') + ]; + + const startDate = new Date('2025-01-01'); + const endDate = new Date('2025-12-31'); + + let currentDate = startDate; + + while (currentDate <= endDate) { + const dayElement = document.createElement('div'); + dayElement.className = 'day'; + // Data regarding the day + const activityLevel = Math.floor(Math.random() * 5); + + + + + dayElement.setAttribute('data-count', activityLevel); + + const tooltip = document.createElement('div'); + tooltip.className = 'tooltip'; + tooltip.innerText = currentDate.toDateString(); + dayElement.appendChild(tooltip); + + // Add the created day element to all graph containers + graphContainers.forEach(graphContainer => { + graphContainer.appendChild(dayElement.cloneNode(true)); + }); + + currentDate.setDate(currentDate.getDate() + 1); + } + \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..0884ee2 --- /dev/null +++ b/styles.css @@ -0,0 +1,165 @@ +/* Typography */ +.rubik-p { + font-family: "Rubik", sans-serif; + font-optical-sizing: auto; + font-weight: 300; + font-style: normal; + color: rgba(255, 255, 255, 0.8); /* Softer text for readability */ + } + + .rubik-h, .rubik-hmain, .rubik-hmain2, .rubik-hmain3 { + font-family: "Rubik", sans-serif; + font-optical-sizing: auto; + font-weight: 600; + font-style: normal; + } + + .rubik-hmain { + font-size: xxx-large; + background: linear-gradient(90deg, #dfbac9, #cdc3ee); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + } + + .rubik-hmain2 { + font-size: xx-large; + background: linear-gradient(90deg, #dfbac9, #cdc3ee); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + } + + .rubik-hmain3 { + font-size: x-large; + background: linear-gradient(90deg, #dfbac9, #cdc3ee); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + margin-bottom: 15px; + } + + /* Background */ + body { + background-color: black; + margin: 0; + font-family: "Rubik", sans-serif; + } + + /* Profile Card */ + .profile_card { + background-color: rgb(22, 22, 22); + padding: 30px; + width: 300px; + border-radius: 35px; + border: 1px solid rgb(48, 48, 51); + color: #fff; + margin-left: 30px; + position: fixed; + left: 30px; + top: 50%; + transform: translateY(-50%); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); + } + + .avatar-img { + width: 100%; + border-radius: 35px; + border: 1px solid rgb(48, 48, 51); + } + + /* Profile Bottom Section */ + .profile_bot { + background: linear-gradient(to right, #e1b9c5, #d0c2e9); + border-radius: 25px; + width: 85%; + padding: 20px; + margin-top: 15px; + text-align: center; + color: #000; + margin-bottom: -60px; + } + + /* Main Content */ + .main { + margin-top: 40px; + margin-left: 460px; + color: rgba(255, 255, 255, 0.75); + } + + /* Links */ + a { + color: white; + text-decoration: none; + } + + a:hover { + color: rgb(223, 185, 201); + } + + a:active { + color: rgb(208, 193, 231); + } + + /* Icons */ + .icons { + display: inline; + height: 30px; + width: 30px; + margin-right: 5px; + } + + .graph-container { + display: grid; + grid-template-rows: repeat(7, 15px); /* Days go from top to bottom */ + grid-auto-flow: column; /* Columns represent weeks */ + gap: 2px; + } + .day { + width: 15px; + height: 15px; + background-color: rgb(48,48,51); + border-radius: 2px; + position: relative; + } + .day[data-count="1"] { background-color: #0d4429 } + .day[data-count="2"] { background-color: #016c31 } + .day[data-count="3"] { background-color: #26a641 } + .day[data-count="4"] { background-color: #39d353; } + .tooltip { + display: none; + position: absolute; + background: #000; + color: #fff; + padding: 5px; + border-radius: 3px; + font-size: 10px; + white-space: nowrap; + z-index: 10; + pointer-events: none; + } + .day:hover .tooltip { + display: block; + top: -25px; + left: 50%; + transform: translateX(-50%); + } + .month-labels { + display: flex; + justify-content: space-between; + padding: 0 5px; + } + .day-labels { + display: grid; + grid-template-rows: repeat(7, 15px); + gap: 2px; + } + .day-label { + font-size: 10px; + color: #666; + text-align: right; + margin-right: 5px; + } + .months { + margin-top: 20px; + } \ No newline at end of file