├── .gitignore ├── LICENSE.md ├── README.md ├── requirements.txt └── src ├── core └── utils.py ├── htmlviewers ├── linux.py └── win.py └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .vscode 3 | received_events/ 4 | *.html 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 QuantumByteStudios 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHubUserDataExtractor 2 | 3 | **GitHubUserDataExtractor** is a cross-platform Python tool designed to extract and display public GitHub user data both in the terminal and through a visual HTML dashboard. It provides a streamlined way to fetch a user’s profile, recent activity, and contribution statistics using GitHub’s REST API and external visualization services. 4 | 5 | ## Features 6 | 7 | - Retrieves a GitHub user's public profile and recent activity 8 | - Displays formatted JSON data in the terminal with color-coded output 9 | - Generates a dynamic HTML dashboard styled with Bootstrap 10 | - Embeds contribution graphs, language usage charts, and GitHub stats 11 | - Native HTML viewer support for Linux (PyWebView) and Windows (PyQt5) 12 | - Clean file structure with modular code organization 13 | 14 | ## Installation 15 | 16 | Clone the repository and install dependencies: 17 | 18 | ```bash 19 | git clone https://github.com/QuantumByteStudios/GitHubUserDataExtractor.git 20 | cd GitHubUserDataExtractor 21 | pip install -r requirements.txt 22 | ``` 23 | 24 | Ensure you are using Python 3.7 or higher. 25 | 26 | ## Usage 27 | 28 | Run the application using: 29 | 30 | ```bash 31 | python src/main.py 32 | ``` 33 | 34 | You will be prompted to enter a GitHub username. The tool will: 35 | 36 | 1. Fetch the user's public GitHub profile and display it in the terminal. 37 | 2. Retrieve recent events like stars, forks, and releases. 38 | 3. Generate an HTML report and open it in a native desktop viewer. 39 | 4. Clean up temporary files after closing the viewer. 40 | 41 | Runtime Screenshot 42 | ![0](https://github.com/user-attachments/assets/83208bff-8061-4e5a-b03e-ea7192265cba) 43 | ![1](https://github.com/user-attachments/assets/67d03132-982a-48d9-8dbc-9a365462f3d4) 44 | 45 | 46 | ## Project Structure 47 | 48 | ``` 49 | GitHubUserDataExtractor/ 50 | │ 51 | ├── .temp/ # Temporary files (auto-cleaned) 52 | ├── .vscode/ # VS Code workspace settings 53 | ├── src/ # Source code directory 54 | | ├── main.py # Main.py 55 | │ ├── core/ # API and utility logic 56 | │ │ └── utils.py # Helper functions for API and data processing 57 | │ ├── htmlviewers/ # Platform-specific GUI viewers 58 | │ │ ├── linux.py # HTML viewer for Linux (PyWebView) 59 | │ │ └── windows.py # HTML viewer for Windows (PyQt5) 60 | │ 61 | ├── .gitignore # Git ignore rules 62 | ├── LICENSE.md # License information 63 | ├── README.md # Project documentation 64 | ├── requirements.txt # Python dependencies 65 | ``` 66 | 67 | ## Output 68 | 69 | The tool creates a temporary HTML report (`index.html`) inside the `.temp/` directory. This report includes: 70 | 71 | - Recent GitHub events (filtered by event types) 72 | - Contribution graphs and language usage charts 73 | - Visual badges and statistics via third-party services 74 | 75 | This file is opened in a native GUI window and then automatically cleared on exit. 76 | 77 | ## Use Cases 78 | 79 | - Developers reviewing their own GitHub contributions and stats 80 | - Technical recruiters assessing candidate GitHub activity 81 | - Educators demonstrating API integration and data visualization 82 | - Hackathon teams building offline GitHub user snapshots 83 | 84 | ## Technologies 85 | 86 | - Python 3 87 | - GitHub REST API 88 | - Requests (HTTP client) 89 | - Colorama (terminal color formatting) 90 | - PyQt5 (Windows HTML viewer) 91 | - PyWebView (Linux HTML viewer) 92 | - Bootstrap 5 (UI styling) 93 | - External visualization APIs: 94 | - GitHub Readme Stats 95 | - GitHub Streak Stats 96 | - GitHub Profile Summary Cards 97 | 98 | ## API Limitations 99 | 100 | This tool uses unauthenticated GitHub API requests and is therefore subject to rate limiting: 101 | 102 | - 60 requests per hour per IP address 103 | 104 | To increase limits, you can optionally extend the tool to use a personal access token. 105 | 106 | ## License 107 | 108 | This project is licensed under the MIT License. See `LICENSE.md` for details. 109 | 110 | ## Author 111 | 112 | Developed by QuantumByteStudios. Contributions and suggestions are welcome. 113 | For inquiries, email us at [contact@quantumbytestudios.in](mailto:contact@quantumbytestudios.in). 114 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | colorama==0.4.6 2 | PyQt5==5.15.11 3 | PyQt5_sip==12.15.0 4 | Requests==2.32.3 5 | webview==0.1.5 6 | -------------------------------------------------------------------------------- /src/core/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import requests 4 | from colorama import Fore, Style 5 | from requests.exceptions import HTTPError 6 | 7 | 8 | class colors: 9 | HEADER = Fore.MAGENTA 10 | BLUE = Fore.BLUE 11 | CYAN = Fore.CYAN 12 | GREEN = Fore.GREEN 13 | WARNING = Fore.YELLOW 14 | FAIL = Fore.RED 15 | RED = Fore.RED 16 | ENDC = Style.RESET_ALL 17 | BOLD = Style.BRIGHT 18 | 19 | 20 | def clear(): 21 | os.system("cls" if platform.system() == "Windows" else "clear") 22 | 23 | 24 | def fetch_and_print_data(username): 25 | print(f"Fetching data for user: {colors.FAIL}{username}{colors.ENDC}") 26 | url = f"https://api.github.com/users/{username}" 27 | try: 28 | response = requests.get(url, timeout=10) 29 | response.raise_for_status() 30 | data = response.json() 31 | print(f"\n{colors.WARNING}User Data:{colors.ENDC}") 32 | for key, value in data.items(): 33 | key = key.replace("_", " ").capitalize() 34 | print(f"{colors.CYAN}{key}:{colors.ENDC} {value}") 35 | 36 | except HTTPError as http_err: 37 | print(f"{colors.FAIL}HTTP error occurred: {http_err}{colors.ENDC}") 38 | except Exception as err: 39 | print(f"{colors.FAIL}Unexpected error: {err}{colors.ENDC}") 40 | 41 | 42 | def show_events_and_graphs(urls): 43 | print( 44 | f"\nGraphs available in Received Events [{colors.GREEN}✓{colors.ENDC}]" 45 | ) 46 | 47 | 48 | def generate_html_event_row(avatar, login, event_type, repo_name, repo_url, badge_class, action_text): 49 | return f""" 50 |
51 | Avatar of {login} 52 |
53 | {login} 54 |

{action_text}

55 | {repo_name} 56 |
57 |
""" 58 | 59 | 60 | def create_and_display_html_user_events(username, urls): 61 | events_url = f"https://api.github.com/users/{username}/received_events" 62 | html_path = ".temp/index.html" 63 | 64 | print(f"Generating HTML report... [{colors.GREEN}✓{colors.ENDC}]\n") 65 | 66 | url = f"https://api.github.com/users/{username}" 67 | try: 68 | response = requests.get(url, timeout=10) 69 | response.raise_for_status() 70 | user_data = response.json() 71 | 72 | login = user_data.get("login", "") 73 | name = user_data.get("name", "") 74 | location = user_data.get("location", "") 75 | html_url = user_data.get("html_url", "") 76 | avatar_url = user_data.get("avatar_url", "") 77 | bio = user_data.get("bio", "") 78 | followers = user_data.get("followers", 0) 79 | following = user_data.get("following", 0) 80 | 81 | except HTTPError as http_err: 82 | print(f"{colors.FAIL}HTTP error occurred: {http_err}{colors.ENDC}") 83 | return 84 | except Exception as err: 85 | print(f"{colors.FAIL}Unexpected error: {err}{colors.ENDC}") 86 | return 87 | 88 | os.makedirs(os.path.dirname(html_path), exist_ok=True) 89 | if os.path.exists(html_path): 90 | os.remove(html_path) 91 | 92 | try: 93 | response = requests.get(events_url, timeout=10) 94 | response.raise_for_status() 95 | events = response.json() 96 | 97 | with open(html_path, "w", encoding="utf_8") as f: 98 | f.write(f""" 99 | 100 | 101 | 102 | 103 | GitHubUserDataExtractor - Dashboard 104 | 105 | 106 | 107 | 190 | 191 | 192 |
193 |

GitHub Activity Dashboard

194 |

Visual summary of recent contributions

195 |
196 |
197 |
198 |
199 |

User Profile

200 |
201 |
202 | Avatar of {login} 203 |
204 |
205 |

{name}

206 |

207 | Username: {login}
208 | About: {bio}
209 | Followers: {followers}, Following: {following}
210 | Location: {location} 211 |

212 | View Profile 213 |
214 |
215 |
216 |
217 |

Received Events

""") 218 | 219 | for event in events: 220 | event_type = event.get("type") 221 | actor = event.get("actor", {}) 222 | payload = event.get("payload", {}) 223 | repo = event.get("repo", {}) 224 | login = actor.get("login", "") 225 | avatar = actor.get("avatar_url", "") 226 | repo_name = repo.get("name", "") 227 | repo_url = f"https://github.com/{repo_name}" 228 | 229 | badge_class = "" 230 | action_text = "" 231 | 232 | if event_type == "ForkEvent": 233 | badge_class = "text-danger" 234 | action_text = "Forked a repository" 235 | repo_url = payload.get("forkee", {}).get("html_url", "#") 236 | 237 | elif event_type == "WatchEvent": 238 | badge_class = "text-warning" 239 | action_text = "Watch/Starred a repository" 240 | 241 | elif event_type == "CreateEvent": 242 | badge_class = "text-success" 243 | action_text = "Created a repository" 244 | 245 | elif event_type == "PublicEvent": 246 | badge_class = "text-primary" 247 | action_text = "Published a repository" 248 | 249 | elif event_type == "ReleaseEvent": 250 | badge_class = "text-primary" 251 | action_text = "Released a repository" 252 | repo_url = payload.get("release", {}).get("html_url", "#") 253 | 254 | if action_text: # Only write if action_text is set 255 | f.write(generate_html_event_row( 256 | avatar, login, event_type, repo_name, 257 | repo_url, badge_class, action_text)) 258 | 259 | f.write(f"""
260 |
261 |

Contribution Insights

262 |
263 |
264 | Top Languages 265 | GitHub Stats 266 | Streak Stats 267 |
268 |
269 |
270 |
271 | 272 | 273 | """) 274 | except HTTPError as http_err: 275 | print(f"{colors.FAIL}HTTP error occurred: {http_err}{colors.ENDC}") 276 | except Exception as err: 277 | print(f"{colors.FAIL}Unexpected error: {err}{colors.ENDC}") 278 | -------------------------------------------------------------------------------- /src/htmlviewers/linux.py: -------------------------------------------------------------------------------- 1 | import webview # type: ignore 2 | import os 3 | import sys 4 | 5 | 6 | def showHTMLLinux(): 7 | # Functions & Global Variables 8 | app_name = "GitHubUserDataExtractor - HTML Viewer" 9 | html_file = os.path.abspath(os.path.join( 10 | "Data", "ReceivedEvents", "index.html")) 11 | 12 | # Check if the file exists 13 | if not os.path.exists(html_file): 14 | print(f"Error: The file '{html_file}' does not exist.") 15 | sys.exit(1) # Gracefully exit the program if the file is missing 16 | 17 | # Normalize path for webview 18 | file_url = f"file://{html_file}" 19 | 20 | # Open HTML File in a new pywebview window 21 | webview.create_window(app_name, file_url) 22 | webview.start() 23 | -------------------------------------------------------------------------------- /src/htmlviewers/win.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | from PyQt5.QtCore import QUrl 4 | from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QMessageBox, QDesktopWidget 5 | from PyQt5.QtWebEngineWidgets import QWebEngineView 6 | 7 | 8 | class HTMLViewer(QMainWindow): 9 | def __init__(self, html_path): 10 | super().__init__() 11 | self.setWindowTitle("GitHubUserDataExtractor - HTML Viewer") 12 | 13 | self.html_path = html_path 14 | 15 | if not os.path.exists(self.html_path): 16 | QMessageBox.critical(self, "File Not Found", 17 | f"The file '{self.html_path}' does not exist.") 18 | sys.exit(1) # Exit the application if file is missing 19 | 20 | # Set up the web view to display the HTML file 21 | self.webview = QWebEngineView() 22 | self.webview.setUrl(QUrl.fromLocalFile(self.html_path)) 23 | 24 | # Layout to hold the web view 25 | layout = QVBoxLayout() 26 | layout.addWidget(self.webview) 27 | layout.setContentsMargins(0, 0, 0, 0) 28 | layout.setSpacing(0) 29 | layout.setStretch(0, 1) 30 | 31 | # Main widget setup 32 | central_widget = QWidget() 33 | central_widget.setLayout(layout) 34 | self.setCentralWidget(central_widget) 35 | 36 | # Adjust window properties 37 | self.resize(1000, 800) # Make window resizable 38 | self.center_on_screen() 39 | 40 | def center_on_screen(self): 41 | """Centers the window on the screen.""" 42 | frame_geometry = self.frameGeometry() 43 | center_point = QDesktopWidget().availableGeometry().center() 44 | frame_geometry.moveCenter(center_point) 45 | self.move(frame_geometry.topLeft()) 46 | 47 | 48 | def showHTMLWindow(): 49 | """Launches the PyQt5 application to display the HTML content.""" 50 | app = QApplication(sys.argv) 51 | 52 | # Absolute path to the HTML file 53 | html_file_path = os.path.abspath(".temp/index.html") 54 | 55 | # Create and display the browser window 56 | browser = HTMLViewer(html_file_path) 57 | browser.show() 58 | 59 | # Execute the application and ensure proper exit 60 | sys.exit(app.exec_()) 61 | 62 | # Delete the HTML file after closing the viewer 63 | try: 64 | os.remove(html_file_path) 65 | except FileNotFoundError: 66 | print(f"Warning: HTML file '{html_file_path}' not found to delete.") 67 | except Exception as e: 68 | print(f"Error deleting HTML file: {e}") 69 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import core.utils as session 4 | from core.utils import colors 5 | 6 | if platform.system() == "Windows": 7 | import htmlviewers.win as windows 8 | else: 9 | import htmlviewers.linux as linux 10 | 11 | 12 | def display_banner(): 13 | os.system("cls" if platform.system() == "Windows" else "clear") 14 | banner = r''' 15 | ____ _ _ _ _ _ _ _ ____ _ 16 | / ___(_) |_| | | |_ _| |__ | | | |___ ___ _ __ | _ \ __ _| |_ __ _ 17 | | | _| | __| |_| | | | | '_ \ | | | / __|/ _ \ '__| | | | |/ _` | __/ _` | 18 | | |_| | | |_| _ | |_| | |_) | | |_| \__ \ __/ | | |_| | (_| | || (_| | 19 | \____|_|\__|_| |_|\__,_|_.__/ \___/|___/\___|_| |____/ \__,_|\__\__,_| 20 | ''' 21 | print(f"{colors.GREEN}{banner}{colors.ENDC}") 22 | print(f"Developer: {colors.CYAN}QuantumByteStudios{colors.ENDC}") 23 | print( 24 | f"Website : {colors.CYAN}https://quantumbytestudios.in{colors.ENDC}\n") 25 | 26 | 27 | def get_username(): 28 | username = input( 29 | f"Enter a GitHub Username: {colors.WARNING}").strip().lower() 30 | print(colors.ENDC, end="") 31 | if not username: 32 | print(f"{colors.RED}Username cannot be empty!{colors.ENDC}") 33 | exit(1) 34 | if username == "exit": 35 | print(colors.RED + 'Bye.' + colors.ENDC) 36 | exit(0) 37 | return username 38 | 39 | 40 | def get_stat_urls(username): 41 | return { 42 | "mostUsedLanguages": f"https://github-readme-stats.vercel.app/api/top-langs?username={username}&langs_count=8", 43 | "githubStats": f"https://github-readme-stats.vercel.app/api?username={username}&show_icons=true&locale=en", 44 | "streakContributionsLS": f"https://streak-stats.demolab.com/?user={username}", 45 | "contributorGraphOne": f"https://github-readme-activity-graph.vercel.app/graph?username={username}&bg_color=000000&color=ffffff&line=ffffff&point=ffffff&area=true&hide_border=true", 46 | "contributorGraphTwo": f"https://github-profile-summary-cards.vercel.app/api/cards/profile-details?username={username}&theme=dark" 47 | } 48 | 49 | 50 | def open_html_viewer(): 51 | html_file = os.path.join(".temp", "index.html") 52 | if platform.system() == "Linux": 53 | try: 54 | linux.showHTMLLinux() 55 | except ImportError: 56 | print( 57 | f"{colors.RED}Error: HTMLViewer_Linux module not found.{colors.ENDC}") 58 | elif platform.system() == "Windows": 59 | try: 60 | windows.showHTMLWindow() 61 | except ImportError: 62 | print( 63 | f"{colors.RED}Error: HTMLViewer_Windows module not found.{colors.ENDC}") 64 | else: 65 | print(f"{colors.RED}Error: Unsupported platform.{colors.ENDC}") 66 | 67 | input("Press any key to exit...") 68 | try: 69 | with open(html_file, "w", encoding="utf_8") as f: 70 | f.write("") # Clear the file content without deleting it 71 | except FileNotFoundError: 72 | print(f"{colors.WARNING}Warning: HTML file not found to clear.{colors.ENDC}") 73 | 74 | 75 | def main(): 76 | display_banner() 77 | username = get_username() 78 | urls = get_stat_urls(username) 79 | 80 | session.fetch_and_print_data(username) 81 | session.show_events_and_graphs(urls) 82 | session.create_and_display_html_user_events(username, urls) 83 | open_html_viewer() 84 | 85 | print("Closing...") 86 | 87 | 88 | if __name__ == "__main__": 89 | main() 90 | --------------------------------------------------------------------------------