Skip to content

Commit 7931c99

Browse files
feat: add student history API endpoint (#77)
* feat: add student history API endpoint * chore: update files * refactor: move student history logic to separate module * style: format student history module * changing the file name and removing comments * style: apply prettier formatting * message * improve user history API response time * Change main entry point from index.js to server.js * Add caching for student API endpoint Implement caching for student data retrieval to improve performance. --------- Co-authored-by: Jagdish Prajapati <jagadishdrp@gmail.com>
1 parent 9ea6bfc commit 7931c99

5 files changed

Lines changed: 146 additions & 30 deletions

File tree

CODE_OF_CONDUCT.md

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
1717
Examples of behavior that contributes to a positive environment for our
1818
community include:
1919

20-
* Demonstrating empathy and kindness toward other people
21-
* Being respectful of differing opinions, viewpoints, and experiences
22-
* Giving and gracefully accepting constructive feedback
23-
* Accepting responsibility and apologizing to those affected by our mistakes,
20+
- Demonstrating empathy and kindness toward other people
21+
- Being respectful of differing opinions, viewpoints, and experiences
22+
- Giving and gracefully accepting constructive feedback
23+
- Accepting responsibility and apologizing to those affected by our mistakes,
2424
and learning from the experience
25-
* Focusing on what is best not just for us as individuals, but for the
25+
- Focusing on what is best not just for us as individuals, but for the
2626
overall community
2727

2828
Examples of unacceptable behavior include:
2929

30-
* The use of sexualized language or imagery, and sexual attention or
30+
- The use of sexualized language or imagery, and sexual attention or
3131
advances of any kind
32-
* Trolling, insulting or derogatory comments, and personal or political attacks
33-
* Public or private harassment
34-
* Publishing others' private information, such as a physical or email
32+
- Trolling, insulting or derogatory comments, and personal or political attacks
33+
- Public or private harassment
34+
- Publishing others' private information, such as a physical or email
3535
address, without their explicit permission
36-
* Other conduct which could reasonably be considered inappropriate in a
36+
- Other conduct which could reasonably be considered inappropriate in a
3737
professional setting
3838

3939
## Enforcement Responsibilities
@@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
106106
### 4. Permanent Ban
107107

108108
**Community Impact**: Demonstrating a pattern of violation of community
109-
standards, including sustained inappropriate behavior, harassment of an
109+
standards, including sustained inappropriate behavior, harassment of an
110110
individual, or aggression toward or disparagement of classes of individuals.
111111

112112
**Consequence**: A permanent ban from any sort of public interaction within
@@ -126,4 +126,3 @@ enforcement ladder](https://github.com/mozilla/diversity).
126126
For answers to common questions about this code of conduct, see the FAQ at
127127
https://www.contributor-covenant.org/faq. Translations are available at
128128
https://www.contributor-covenant.org/translations.
129-

README.md

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ It allows users to register with their LeetCode username and automatically fetch
1010

1111
The goal of this project is to:
1212

13-
- Encourage consistent problem-solving among students
14-
- Create a competitive yet motivating environment
15-
- Provide visibility into individual coding progress
13+
- Encourage consistent problem-solving among students
14+
- Create a competitive yet motivating environment
15+
- Provide visibility into individual coding progress
1616

1717
---
1818

19-
20-
2119
## Screenshots
20+
2221
A quick preview of the platform UI. The appearance may evolve as the project develops.
22+
2323
### Home Page
2424

2525
![Home Page](assets/home-page.png)
@@ -28,13 +28,10 @@ A quick preview of the platform UI. The appearance may evolve as the project dev
2828

2929
![Registration](assets/registration-page.png)
3030

31-
3231
### Leaderboard
3332

3433
![Leaderboard](assets/leaderboard.png)
3534

36-
37-
3835
## Related Repositories
3936

4037
- [leetcode-ranking-data](https://github.com/codepvg/leetcode-ranking-data) – The database repository where raw JSON data and historical stats are stored
@@ -65,6 +62,7 @@ leetcode-ranking/
6562
### 1. Fork and clone the repository
6663

6764
First, fork the repository to your GitHub account. Then clone it locally:
65+
6866
```bash
6967
git clone https://github.com/YOUR-USERNAME/leetcode-ranking.git
7068
cd leetcode-ranking
@@ -82,9 +80,9 @@ or
8280

8381
## Usage
8482

85-
1. Open the registration page
86-
2. Enter your name and LeetCode username
87-
3. Submit the form
83+
1. Open the registration page
84+
2. Enter your name and LeetCode username
85+
3. Submit the form
8886
4. View your ranking on the leaderboard after the next sync
8987

9088
---
@@ -93,7 +91,7 @@ or
9391

9492
Contributions are welcome.
9593

96-
- Fork the repository
97-
- Create a new branch
98-
- Make your changes
99-
- Submit a Pull Request
94+
- Fork the repository
95+
- Create a new branch
96+
- Make your changes
97+
- Submit a Pull Request

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
"name": "leetcode-ranking",
33
"version": "1.0.0",
44
"description": "",
5-
"main": "index.js",
5+
"main": "server.js",
66
"scripts": {
7-
"dev": "node ./server.js"
7+
"start": "node server.js",
8+
"dev": "nodemon server.js"
89
},
910
"repository": {
1011
"type": "git",

scripts/fetch-student-info.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
function getFileName(daysAgo) {
2+
const now = new Date();
3+
now.setDate(now.getDate() - daysAgo);
4+
5+
const year = now.getFullYear();
6+
const month = String(now.getMonth() + 1).padStart(2, "0");
7+
const date = String(now.getDate()).padStart(2, "0");
8+
let day = now.getDay();
9+
day = day === 0 ? 7 : day;
10+
11+
return `${year}-${month}-${date}-${day}.json`;
12+
}
13+
14+
async function fetchStudentHistory(username) {
15+
console.log("Fetching history for:", username);
16+
17+
let history = [];
18+
let missingFilesCount = 0;
19+
const maxDays = 365;
20+
const chunkSize = 100;
21+
22+
let done = false;
23+
24+
for (let chunkStart = 0; chunkStart < maxDays; chunkStart += chunkSize) {
25+
if (done) break;
26+
27+
const fetchPromises = [];
28+
const chunkEnd = Math.min(chunkStart + chunkSize, maxDays);
29+
30+
for (let daysAgo = chunkStart; daysAgo < chunkEnd; daysAgo++) {
31+
const fileName = getFileName(daysAgo);
32+
const rawUrl = `https://raw.githubusercontent.com/codepvg/leetcode-ranking-data/main/daily/${fileName}`;
33+
34+
const p = fetch(rawUrl)
35+
.then(async (res) => {
36+
if (!res.ok) {
37+
return { daysAgo, fileName, ok: false };
38+
}
39+
const data = await res.json();
40+
return { daysAgo, fileName, ok: true, data };
41+
})
42+
.catch((err) => {
43+
return { daysAgo, fileName, ok: false, error: err };
44+
});
45+
46+
fetchPromises.push(p);
47+
}
48+
49+
const results = await Promise.all(fetchPromises);
50+
51+
for (const result of results) {
52+
if (!result.ok) {
53+
missingFilesCount++;
54+
if (missingFilesCount >= 7) {
55+
done = true;
56+
break;
57+
}
58+
continue;
59+
}
60+
61+
missingFilesCount = 0;
62+
63+
const user = result.data.find((u) => u.id === username);
64+
65+
if (user) {
66+
const dateStr = result.fileName.split("-").slice(0, 3).join("-");
67+
68+
history.push({
69+
date: dateStr,
70+
easy: user.data.easySolved,
71+
medium: user.data.mediumSolved,
72+
hard: user.data.hardSolved,
73+
});
74+
} else {
75+
done = true;
76+
break;
77+
}
78+
}
79+
}
80+
81+
history.sort((a, b) => new Date(a.date) - new Date(b.date));
82+
83+
return {
84+
username,
85+
history,
86+
};
87+
}
88+
89+
module.exports = fetchStudentHistory;

server.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
const express = require("express");
22
const cors = require("cors");
33
const path = require("path");
4-
const fs = require("fs");
4+
55
const app = express();
66
const PORT = process.env.PORT || 3000;
77

8+
const fetchStudentHistory = require("./scripts/fetch-student-info");
9+
810
app.use(cors());
911
app.use(express.static(path.join(__dirname, "frontend")));
1012

13+
/* ---------------- HOME ROUTES ---------------- */
1114
app.get("/", (req, res) => {
1215
res.sendFile(path.join(__dirname, "frontend", "index.html"));
1316
});
@@ -28,6 +31,32 @@ app.get("/uptime", (req, res) => {
2831
res.json({ status: "Website is running ✅" });
2932
});
3033

34+
const studentCache = new Map();
35+
36+
app.get("/api/student/:username", async (req, res) => {
37+
const username = req.params.username;
38+
39+
if (studentCache.has(username)) {
40+
const cached = studentCache.get(username);
41+
if (Date.now() - cached.timestamp < 5 * 60 * 1000) {
42+
return res.json(cached.data);
43+
}
44+
}
45+
46+
try {
47+
const data = await fetchStudentHistory(username);
48+
49+
studentCache.set(username, { timestamp: Date.now(), data });
50+
51+
res.json(data);
52+
} catch (err) {
53+
res.status(500).json({
54+
error: "Failed to fetch student details",
55+
details: err.message,
56+
});
57+
}
58+
});
59+
3160
app.use((req, res) => {
3261
res.status(404).send("Page not found");
3362
});

0 commit comments

Comments
 (0)