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 ;
0 commit comments