Skip to content

Commit 332d4e8

Browse files
Moving data fetching to backend
1 parent 61dc8a2 commit 332d4e8

8 files changed

Lines changed: 493 additions & 89 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const {Octokit} = require("@octokit/rest");
2+
//ashish-choudhari-git Code
3+
const authenticateGitHub = (req,res,next)=>{
4+
const {username,token} = req.body;
5+
6+
if(!username || !token) {
7+
return res.status(400).json({ message : 'Username and token are required'});
8+
}
9+
10+
req.octokit = new Octokit({auth:token});
11+
req.githubUsername = username;
12+
next();
13+
}
14+
15+
module.exports = authenticateGitHub;

backend/routes/details.js

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
//Aashish Choudhari's Code | GSSoC Contributor
2+
const express = require('express');
3+
const router = express.Router();
4+
const authenticateGitHub = require('../middlewares/authenticateGitHub');
5+
6+
router.post('/get-data', authenticateGitHub, async (req,res)=>{
7+
8+
try{
9+
const { octokit, githubUsername } = req;
10+
11+
// Fetch user's issues and PRs specifically for Home page
12+
const [issuesResponse, prsResponse] = await Promise.all([
13+
// Get issues created by user
14+
octokit.rest.search.issuesAndPullRequests({
15+
q: `author:${githubUsername} type:issue`,
16+
sort: 'created',
17+
order: 'desc',
18+
per_page: 100
19+
}),
20+
// Get pull requests created by user
21+
octokit.rest.search.issuesAndPullRequests({
22+
q: `author:${githubUsername} type:pr`,
23+
sort: 'created',
24+
order: 'desc',
25+
per_page: 100
26+
})
27+
]);
28+
29+
// Process issues data
30+
const issues = issuesResponse.data.items.map(issue => ({
31+
id: issue.id,
32+
title: issue.title,
33+
state: issue.state,
34+
created_at: issue.created_at,
35+
repository_url: issue.repository_url,
36+
html_url: issue.html_url,
37+
number: issue.number,
38+
labels: issue.labels,
39+
assignees: issue.assignees,
40+
user: issue.user
41+
}));
42+
// Process pull requests data
43+
const prs = prsResponse.data.items.map(pr => ({
44+
id: pr.id,
45+
title: pr.title,
46+
state: pr.state,
47+
created_at: pr.created_at,
48+
repository_url: pr.repository_url,
49+
html_url: pr.html_url,
50+
number: pr.number,
51+
pull_request: {
52+
merged_at: pr.pull_request?.merged_at || null
53+
},
54+
labels: pr.labels,
55+
assignees: pr.assignees,
56+
user: pr.user
57+
}));
58+
59+
const responseData = {
60+
issues,
61+
prs,
62+
totalIssues: issuesResponse.data.total_count,
63+
totalPrs: prsResponse.data.total_count
64+
};
65+
66+
res.json(responseData);
67+
} catch (error) {
68+
console.error('Error fetching user data:', error);
69+
res.status(500).json({ message: 'Error fetching user data', error: error.message });
70+
}
71+
});
72+
73+
// New route for comprehensive user analytics
74+
router.post('/user-profile', authenticateGitHub, async (req, res) => {
75+
try {
76+
const { octokit, githubUsername } = req;
77+
78+
// Fetch user profile
79+
const userResponse = await octokit.rest.users.getByUsername({
80+
username: githubUsername
81+
});
82+
83+
// Fetch user repositories
84+
const reposResponse = await octokit.rest.repos.listForUser({
85+
username: githubUsername,
86+
type: 'all',
87+
sort: 'updated',
88+
per_page: 100
89+
});
90+
91+
// Calculate language statistics
92+
const languageStats = {};
93+
const repositories = [];
94+
95+
for (const repo of reposResponse.data) {
96+
try {
97+
const languagesResponse = await octokit.rest.repos.listLanguages({
98+
owner: githubUsername,
99+
repo: repo.name
100+
});
101+
102+
Object.entries(languagesResponse.data).forEach(([lang, bytes]) => {
103+
languageStats[lang] = (languageStats[lang] || 0) + bytes;
104+
});
105+
106+
repositories.push({
107+
name: repo.name,
108+
description: repo.description,
109+
stars: repo.stargazers_count,
110+
forks: repo.forks_count,
111+
watchers: repo.watchers_count,
112+
language: repo.language,
113+
html_url: repo.html_url,
114+
updated_at: repo.updated_at
115+
});
116+
} catch (err) {
117+
console.log(`Error fetching languages for ${repo.name}:`, err.message);
118+
}
119+
}
120+
121+
// Sort repositories by stars
122+
const repositoryRanking = repositories.sort((a, b) => b.stars - a.stars);
123+
124+
// Calculate social stats
125+
const socialStats = {
126+
totalStars: repositories.reduce((sum, repo) => sum + repo.stars, 0),
127+
totalForks: repositories.reduce((sum, repo) => sum + repo.forks, 0),
128+
totalWatchers: repositories.reduce((sum, repo) => sum + repo.watchers, 0)
129+
};
130+
131+
// Calculate real contribution stats
132+
const currentDate = new Date();
133+
const oneYearAgo = new Date(currentDate.getFullYear() - 1, currentDate.getMonth(), currentDate.getDate());
134+
135+
// Get user events for contribution analysis
136+
let userEvents = [];
137+
try {
138+
const eventsResponse = await octokit.rest.activity.listPublicEventsForUser({
139+
username: githubUsername,
140+
per_page: 100
141+
});
142+
userEvents = eventsResponse.data;
143+
} catch (err) {
144+
console.log('Could not fetch user events:', err.message);
145+
}
146+
147+
// Calculate contributions from repositories and events
148+
const totalContributions = userResponse.data.public_repos +
149+
userResponse.data.public_gists +
150+
(userEvents.length || 0);
151+
152+
// Calculate streaks and activity patterns
153+
const contributionsByDate = {};
154+
userEvents.forEach(event => {
155+
const date = event.created_at.split('T')[0];
156+
contributionsByDate[date] = (contributionsByDate[date] || 0) + 1;
157+
});
158+
159+
// Calculate longest and current streak
160+
const dates = Object.keys(contributionsByDate).sort();
161+
let longestStreak = 0;
162+
let currentStreak = 0;
163+
let tempStreak = 0;
164+
165+
for (let i = 0; i < dates.length; i++) {
166+
if (i === 0 || isConsecutiveDay(dates[i-1], dates[i])) {
167+
tempStreak++;
168+
} else {
169+
longestStreak = Math.max(longestStreak, tempStreak);
170+
tempStreak = 1;
171+
}
172+
}
173+
longestStreak = Math.max(longestStreak, tempStreak);
174+
175+
// Current streak calculation
176+
const today = new Date().toISOString().split('T')[0];
177+
if (dates.length > 0) {
178+
const lastDate = dates[dates.length - 1];
179+
if (isRecentDate(lastDate, today)) {
180+
currentStreak = calculateCurrentStreak(dates);
181+
}
182+
}
183+
184+
// Most active day calculation
185+
const dayActivityCount = { Sun: 0, Mon: 0, Tue: 0, Wed: 0, Thu: 0, Fri: 0, Sat: 0 };
186+
userEvents.forEach(event => {
187+
const dayOfWeek = new Date(event.created_at).getDay();
188+
const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
189+
dayActivityCount[dayNames[dayOfWeek]]++;
190+
});
191+
192+
const mostActiveDay = Object.keys(dayActivityCount).reduce((a, b) =>
193+
dayActivityCount[a] > dayActivityCount[b] ? a : b
194+
);
195+
196+
// Average contributions per day (last 365 days)
197+
const averagePerDay = userEvents.length > 0 ? (userEvents.length / 365).toFixed(1) : 0;
198+
199+
const contributionStats = {
200+
totalContributions,
201+
longestStreak,
202+
currentStreak,
203+
mostActiveDay: getDayFullName(mostActiveDay),
204+
averagePerDay: parseFloat(averagePerDay)
205+
};
206+
207+
// Helper functions
208+
function isConsecutiveDay(date1, date2) {
209+
const d1 = new Date(date1);
210+
const d2 = new Date(date2);
211+
const diffTime = Math.abs(d2 - d1);
212+
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
213+
return diffDays === 1;
214+
}
215+
216+
function isRecentDate(date, today) {
217+
const diffTime = Math.abs(new Date(today) - new Date(date));
218+
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
219+
return diffDays <= 1;
220+
}
221+
222+
function calculateCurrentStreak(dates) {
223+
let streak = 0;
224+
for (let i = dates.length - 1; i > 0; i--) {
225+
if (isConsecutiveDay(dates[i-1], dates[i])) {
226+
streak++;
227+
} else {
228+
break;
229+
}
230+
}
231+
return streak + 1;
232+
}
233+
234+
function getDayFullName(shortDay) {
235+
const dayMap = {
236+
'Sun': 'Sunday',
237+
'Mon': 'Monday',
238+
'Tue': 'Tuesday',
239+
'Wed': 'Wednesday',
240+
'Thu': 'Thursday',
241+
'Fri': 'Friday',
242+
'Sat': 'Saturday'
243+
};
244+
return dayMap[shortDay] || 'Monday';
245+
}
246+
247+
const responseData = {
248+
profile: userResponse.data,
249+
repositories,
250+
languageStats,
251+
contributionStats,
252+
rankings: {
253+
repositoryRanking
254+
},
255+
highlights: {
256+
topRepo: repositoryRanking[0] || null,
257+
totalStars: socialStats.totalStars
258+
},
259+
stars: repositories.filter(repo => repo.stars > 0),
260+
commitHistory: [], // Would need more complex API calls
261+
socialStats
262+
};
263+
264+
res.json(responseData);
265+
} catch (error) {
266+
console.error('Error fetching user profile:', error);
267+
res.status(500).json({ message: 'Error fetching user profile', error: error.message });
268+
}
269+
});
270+
271+
module.exports = router;

backend/server.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ app.use(session({
2323
}));
2424
app.use(passport.initialize());
2525
app.use(passport.session());
26+
const githubRoutes = require('./routes/details');
27+
app.use('/api/github', githubRoutes);
2628

2729
// Routes
2830
const authRoutes = require('./routes/auth');

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
"@mui/icons-material": "^5.15.6",
1616
"@mui/material": "^5.15.6",
1717
"@vitejs/plugin-react": "^4.3.3",
18-
"axios": "^1.7.7",
19-
"lucide-react": "^0.525.0",
18+
"axios": "^1.11.0",
19+
"lucide-react": "^0.532.0",
2020
"octokit": "^4.0.2",
2121
"react": "^18.3.1",
2222
"react-dom": "^18.3.1",

src/Routes/Router.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Contributors from "../pages/Contributors/Contributors"
66
import Signup from "../pages/Signup/Signup.tsx"
77
import Login from "../pages/Login/Login.tsx"
88
import UserProfile from "../pages/UserProfile/UserProfile.tsx"
9+
import UserAnalytics from "../pages/UserAnalytics/UserAnalytics.tsx";
910

1011

1112

@@ -16,6 +17,7 @@ const Router = () => {
1617
<Route path="/signup" element={<Signup />} />
1718
<Route path="/login" element={<Login />} />
1819
<Route path="/" element={<Home />} />
20+
<Route path="/analytics" element={<UserAnalytics />} />
1921
<Route path="/about" element={<About />} />
2022
<Route path="/contact" element={<Contact />} />
2123
<Route path="/contributors" element={<Contributors />} />

0 commit comments

Comments
 (0)