yasserrmd commited on
Commit
67e86f9
Β·
verified Β·
1 Parent(s): d6ddd9e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +245 -0
app.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import pandas as pd
4
+ from datetime import datetime, timedelta
5
+ import time
6
+ import os
7
+
8
+ def fetch_github_users(github_token=None, max_users=256, min_followers=34):
9
+ """
10
+ Fetch GitHub users from UAE directly using GitHub API
11
+ """
12
+ # Read token from environment variable if not provided
13
+ if not github_token:
14
+ github_token = os.getenv('GITHUB_TOKEN')
15
+
16
+ headers = {
17
+ 'Accept': 'application/vnd.github.v3+json',
18
+ }
19
+
20
+ if github_token:
21
+ headers['Authorization'] = f'token {github_token}'
22
+
23
+ all_users = []
24
+
25
+ # Search locations in UAE
26
+ locations = [
27
+ 'Dubai', 'Abu Dhabi', 'Sharjah', 'Ajman',
28
+ 'United Arab Emirates', 'UAE',
29
+ 'Ras Al Khaimah', 'Fujairah', 'Umm Al Quwain'
30
+ ]
31
+
32
+ status_updates = []
33
+
34
+ try:
35
+ for location in locations:
36
+ status_updates.append(f"πŸ” Searching users in {location}...")
37
+
38
+ # Search users by location and followers
39
+ search_url = f'https://api.github.com/search/users?q=location:{location}+followers:>={min_followers}&sort=followers&order=desc&per_page=100'
40
+
41
+ response = requests.get(search_url, headers=headers)
42
+
43
+ if response.status_code == 200:
44
+ data = response.json()
45
+ users = data.get('items', [])
46
+
47
+ for user in users:
48
+ if not any(u['login'] == user['login'] for u in all_users):
49
+ # Fetch detailed user info
50
+ user_url = user['url']
51
+ user_response = requests.get(user_url, headers=headers)
52
+
53
+ if user_response.status_code == 200:
54
+ user_data = user_response.json()
55
+
56
+ # Fetch user's contribution data (public repos and events)
57
+ events_url = f"https://api.github.com/users/{user['login']}/events/public"
58
+ events_response = requests.get(events_url, headers=headers)
59
+
60
+ contributions = 0
61
+ if events_response.status_code == 200:
62
+ events = events_response.json()
63
+ # Count push events as contributions
64
+ contributions = len([e for e in events if e.get('type') == 'PushEvent'])
65
+
66
+ all_users.append({
67
+ 'login': user_data.get('login', ''),
68
+ 'name': user_data.get('name', user_data.get('login', '')),
69
+ 'avatar': user_data.get('avatar_url', ''),
70
+ 'followers': user_data.get('followers', 0),
71
+ 'public_repos': user_data.get('public_repos', 0),
72
+ 'contributions': contributions + user_data.get('public_repos', 0),
73
+ 'location': user_data.get('location', ''),
74
+ 'bio': user_data.get('bio', ''),
75
+ 'company': user_data.get('company', ''),
76
+ })
77
+
78
+ time.sleep(0.5) # Rate limiting
79
+
80
+ if len(all_users) >= max_users:
81
+ break
82
+
83
+ status_updates.append(f"βœ… Found {len(users)} users in {location}")
84
+ time.sleep(1) # Rate limiting between searches
85
+
86
+ elif response.status_code == 403:
87
+ status_updates.append(f"⚠️ Rate limit reached. Please add a GitHub token.")
88
+ break
89
+ else:
90
+ status_updates.append(f"❌ Error searching {location}: {response.status_code}")
91
+
92
+ if len(all_users) >= max_users:
93
+ break
94
+
95
+ # Sort by followers and contributions
96
+ all_users.sort(key=lambda x: (x['followers'], x['contributions']), reverse=True)
97
+
98
+ # Add rank
99
+ for i, user in enumerate(all_users[:max_users], 1):
100
+ user['rank'] = i
101
+
102
+ # Convert to DataFrame
103
+ df = pd.DataFrame(all_users[:max_users])
104
+
105
+ if not df.empty:
106
+ display_df = df[['rank', 'name', 'login', 'followers', 'public_repos', 'contributions', 'location']].copy()
107
+ display_df.columns = ['Rank', 'Name', 'Username', 'Followers', 'Public Repos', 'Contributions', 'Location']
108
+ display_df['GitHub Profile'] = df['login'].apply(lambda x: f"https://github.com/{x}")
109
+
110
+ status_message = f"βœ… Successfully fetched {len(df)} users\n" + "\n".join(status_updates[-5:])
111
+ return display_df, status_message
112
+ else:
113
+ return pd.DataFrame(), "⚠️ No users found\n" + "\n".join(status_updates)
114
+
115
+ except Exception as e:
116
+ return pd.DataFrame(), f"❌ Error: {str(e)}\n" + "\n".join(status_updates)
117
+
118
+ def search_users(df, search_term):
119
+ """Filter users based on search term"""
120
+ if df is None or df.empty:
121
+ return df
122
+
123
+ if not search_term:
124
+ return df
125
+
126
+ search_term = search_term.lower()
127
+ mask = (
128
+ df['Name'].str.lower().str.contains(search_term, na=False) |
129
+ df['Username'].str.lower().str.contains(search_term, na=False)
130
+ )
131
+ return df[mask]
132
+
133
+ # Create Gradio interface
134
+ with gr.Blocks(theme=gr.themes.Soft(), title="GitHub UAE Users Fetcher") as app:
135
+
136
+ gr.Markdown("""
137
+ # πŸ† GitHub Users in UAE - Direct Fetcher
138
+ ### Fetch active GitHub users directly from GitHub API
139
+
140
+ **Note:** The app will automatically use `GITHUB_TOKEN` environment variable if set.
141
+ Without a token, you're limited to 60 requests/hour. With a token: 5000 requests/hour.
142
+ """)
143
+
144
+ with gr.Row():
145
+ token_input = gr.Textbox(
146
+ label="GitHub Personal Access Token (Optional - overrides env variable)",
147
+ placeholder="ghp_xxxxxxxxxxxx or leave empty to use GITHUB_TOKEN env var",
148
+ type="password",
149
+ scale=3
150
+ )
151
+ max_users_input = gr.Slider(
152
+ label="Max Users",
153
+ minimum=10,
154
+ maximum=500,
155
+ value=256,
156
+ step=1,
157
+ scale=1
158
+ )
159
+
160
+ with gr.Row():
161
+ min_followers_input = gr.Slider(
162
+ label="Minimum Followers",
163
+ minimum=1,
164
+ maximum=100,
165
+ value=34,
166
+ step=1
167
+ )
168
+
169
+ with gr.Row():
170
+ fetch_btn = gr.Button("πŸš€ Fetch Users from GitHub", variant="primary", size="lg")
171
+
172
+ status_msg = gr.Textbox(label="Status", interactive=False, lines=3)
173
+
174
+ with gr.Row():
175
+ search_box = gr.Textbox(
176
+ label="πŸ” Search by Name or Username",
177
+ placeholder="Type to search...",
178
+ scale=4
179
+ )
180
+ clear_btn = gr.Button("Clear", scale=1)
181
+
182
+ # Store the full dataframe
183
+ full_data = gr.State(value=pd.DataFrame())
184
+
185
+ # Display dataframe
186
+ data_display = gr.Dataframe(
187
+ headers=["Rank", "Name", "Username", "Followers", "Public Repos", "Contributions", "Location", "GitHub Profile"],
188
+ datatype=["number", "str", "str", "number", "number", "number", "str", "str"],
189
+ wrap=True,
190
+ interactive=False
191
+ )
192
+
193
+ gr.Markdown("""
194
+ ---
195
+ **How to set up GitHub Token:**
196
+
197
+ **Option 1: Environment Variable (Recommended)**
198
+ ```bash
199
+ export GITHUB_TOKEN="ghp_your_token_here"
200
+ python app.py
201
+ ```
202
+
203
+ **Option 2: Manual Input**
204
+ 1. Go to [GitHub Settings > Developer settings > Personal access tokens](https://github.com/settings/tokens)
205
+ 2. Generate new token (classic)
206
+ 3. No special scopes needed for public data
207
+ 4. Copy and paste the token in the field above
208
+
209
+ **Data fetched directly from:** GitHub API
210
+ """)
211
+
212
+ # Event handlers
213
+ def fetch_and_display(token, max_users, min_followers):
214
+ df, msg = fetch_github_users(token if token else None, int(max_users), int(min_followers))
215
+ return df, df, msg
216
+
217
+ def filter_data(df, search):
218
+ if df is None or df.empty:
219
+ return df
220
+ return search_users(df, search)
221
+
222
+ def clear_search(df):
223
+ return "", df
224
+
225
+ fetch_btn.click(
226
+ fn=fetch_and_display,
227
+ inputs=[token_input, max_users_input, min_followers_input],
228
+ outputs=[full_data, data_display, status_msg]
229
+ )
230
+
231
+ search_box.change(
232
+ fn=filter_data,
233
+ inputs=[full_data, search_box],
234
+ outputs=data_display
235
+ )
236
+
237
+ clear_btn.click(
238
+ fn=clear_search,
239
+ inputs=[full_data],
240
+ outputs=[search_box, data_display]
241
+ )
242
+
243
+ # Launch the app
244
+ if __name__ == "__main__":
245
+ app.launch()