diff --git a/README.md b/README.md index de8d4e9..5eb88b7 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,165 @@ -# GitHub Repository Insights +# GitHub Repository Insights ✨ -GitHub Repository Insights is a Ruby-based CLI tool for tracking and analyzing repositories of any GitHub user. The application fetches repository data using the GitHub API, displays it in a readable table, and provides options for filtering, sorting, and exporting the data. +**GitHub Repository Insights** is your go-to CLI tool for exploring and analyzing GitHub repos like a pro – no browser needed! πŸ’€ It’s sleek, it’s fast, and it’s built with Ruby. Whether you're stalking your favorite dev’s projects or managing your own, this app has got you covered. --- -## **Features** -- Fetches repositories using the GitHub API. -- Displays repository details such as: - - Name, stars, forks, open issues, language, size, and yearly commit count. -- Filter repositories by criteria (stars, forks, issues, archived status). -- Sort repositories by stars, forks, or issues. -- Export repository data to a CSV file. -- Handles paginated API responses. -- Uses advanced commit activity insights. +## **Why You'll Love It 🌟** +- **Fetch Repos Like a Boss**: Get a complete list of public repos from any GitHub user. +- **Deets Galore**: Check out stats like stars, forks, issues, language, size, and commit counts. +- **Filter & Sort FTW**: Flex those sorting skillsβ€”stars, forks, issues, you name it. +- **Export Data**: Save repo info to CSV or JSON (because spreadsheets rule). +- **Zero Clutter**: Simple, intuitive CLI vibes. +- **Error-Handled Like a Pro**: No crashing, no burning. πŸ›Έ --- -## **Prerequisites** -- Ruby (version >= 2.7 recommended) -- GitHub Personal Access Token (PAT) with `public_repo` access. +## **Before You Start ⚑** + +### Prerequisites +1. **Ruby**: Make sure you have Ruby installed (v2.7 or later is 🌈). + - To install Ruby: [Check this out](https://www.ruby-lang.org/en/documentation/installation/) +2. **GitHub PAT (Personal Access Token)**: Create one with `public_repo` access. [Here’s how](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token). +3. **Required Gems**: Install dependencies by running: + ```bash + gem install terminal-table dotenv + ``` --- -## **Setup Instructions** -1. Clone the repository: +## **Setup πŸ‘Œ** + +1. Clone this bad boy: ```bash git clone https://github.com/NML-LETRAS/github_repository_insights.git cd github_repository_insights ``` -2. Install dependencies: - ```bash - bundle install - ``` -3. Create a `.env` file in the root directory and add your GitHub API key: +2. Create a `.env` file and paste your GitHub PAT: ```env GITHUB_API_KEY=your_personal_access_token_here ``` -4. Run the application: +3. Run the app: ```bash ruby main.rb ``` --- -## **Usage** -1. Enter the GitHub username when prompted. -2. View the fetched repository data in a formatted table. -3. Apply filters and sorting to refine the displayed data. -4. Export the repository data to a CSV file if needed. +## **How It Works πŸš€** ---- - -## **Example** -```bash -Enter GitHub username: -your-user-name +### Main Menu πŸ” +When you fire up the app, you’ll see: +```plaintext +--- GitHub Repository Insights --- +1. View Repositories +2. Search Repository by Name +3. View Detailed Repository Information +4. Export Repository Data +5. Help +6. Exit ``` -Output: + +### Features Explained 🎯 + +#### **1. View Repositories** +Get all public repos for a GitHub user. See them laid out in a sick table: +```plaintext ++-------------------------+-------+-------+-------------+----------+-----------+---------+ +| Name | Stars | Forks | Open Issues | Language | Size (MB) | Commits | ++-------------------------+-------+-------+-------------+----------+-----------+---------+ +| repo-1 | 150 | 25 | 3 | Ruby | 0.20 | 500 | ++-------------------------+-------+-------+-------------+----------+-----------+---------+ ``` -+----------------+-------+-------+-------------+----------+---------+---------+ -| Name | Stars | Forks | Open Issues | Language | Size KB | Commits | -+----------------+-------+-------+-------------+----------+---------+---------+ -| my-repo | 150 | 25 | 3 | Ruby | 200 | 500 | -| another-repo | 75 | 10 | 1 | Python | 300 | 200 | -+----------------+-------+-------+-------------+----------+---------+---------+ + +#### **2. Search Repository by Name** +Looking for a specific repo? No problem. Search by keywords and get instant results. + +#### **3. View Detailed Repository Info** +Want the nitty-gritty on a repo? Dive deep: +```plaintext +--- Repository Details --- +Name: repo-1 +Description: CLI tool. +Stars: 150 +Forks: 25 +Open Issues: 3 +Language: Ruby +Size: 0.20 MB +Default Branch: main +Created At: 2025-01-01 +Last Updated: 2025-01-03 ``` +#### **4. Export Repository Data** +Save your results for later: +- CSV: Perfect for Excel nerds. +- JSON: Because who doesn’t love APIs? + +#### **5. Help** +Get a quick rundown of all features. 🌐 + +#### **6. Exit** +Peace out! πŸ™‹ + --- -## **Advanced Features** -- Filter repositories by minimum stars, forks, or open issues. -- Sort repositories based on metrics like stars or forks. -- Export the displayed data to `repositories.csv`. +## **Example Usage 🀩** + +1. Enter the GitHub username: + ```plaintext + Enter GitHub username: octocat + ``` +2. View repo data in style: + ```plaintext + +-----------+-------+-------+-------------+----------+-----------+---------+ + | Name | Stars | Forks | Open Issues | Language | Size (MB) | Commits | + +-----------+-------+-------+-------------+----------+-----------+---------+ + | repo-1 | 150 | 25 | 3 | Ruby | 0.20 | 500 | + +-----------+-------+-------+-------------+----------+-----------+---------+ + ``` +3. Export the data: + ```plaintext + Export data as: 1) CSV 2) JSON: 1 + Data exported to repositories.csv + ``` --- -## **Development** -### **Project Structure** +## **Project Structure πŸ”’** ```plaintext github_repository_insights/ β”œβ”€β”€ lib/ -β”‚ β”œβ”€β”€ api_client.rb # Handles API requests and authentication. -β”‚ β”œβ”€β”€ repository.rb # Defines the Repository model. -β”‚ β”œβ”€β”€ display.rb # Handles displaying repository data. -β”‚ └── utils.rb # Utility methods for sorting, filtering, and exporting. -β”œβ”€β”€ spec/ # Contains RSpec tests. -β”œβ”€β”€ .env # Stores the GitHub API key. -β”œβ”€β”€ .gitignore # Ignores sensitive files like .env and logs. -β”œβ”€β”€ Gemfile # Lists project dependencies. -β”œβ”€β”€ main.rb # Entry point for the application. +β”‚ β”œβ”€β”€ api_client.rb # GitHub API requests. +β”‚ β”œβ”€β”€ repository.rb # Repo model. +β”‚ β”œβ”€β”€ display.rb # CLI output. +β”‚ └── utils.rb # Filtering, sorting, exporting. +β”œβ”€β”€ main.rb # App entry point. +β”œβ”€β”€ .env # Your API key lives here. +β”œβ”€β”€ Gemfile # Dependencies. +└── README.md # This file! πŸ˜‰ ``` --- -## **Testing** -Run the tests using RSpec: +## **Testing 🐝** +To make sure it’s all working, run: ```bash rspec ``` +This will check your code and ensure no bugs are lurking. πŸ’‘ + +--- + +## **Future Plans 🌍** +- Add analytics for contributors and pull requests. +- Cache API responses for faster loading. +- Build a web app version (because CLI isn’t for everyone πŸ˜‚). --- -## **Future Enhancements** -- Add support for analyzing contributors and pull requests. -- Implement caching to minimize API calls. -- Add a web interface for better user interaction. +## **License πŸ”’** +MIT Licenseβ€”because sharing is caring. 😎 See the `LICENSE` file for details. --- -## **License** -This project is licensed under the MIT License. See the `LICENSE` file for details. +### **Made with ❀ by nmlletras** diff --git a/lib/api_client.rb b/lib/api_client.rb index 16a66e1..f5fb642 100644 --- a/lib/api_client.rb +++ b/lib/api_client.rb @@ -1,49 +1,43 @@ -# lib/api_client.rb -require 'httparty' -require 'dotenv/load' +require 'net/http' require 'json' class ApiClient BASE_URL = "https://api.github.com" - def initialize - @headers = { - "Authorization" => "token #{ENV['GITHUB_API_KEY']}", - "User-Agent" => "GitHub Repository Insights" - } - end - def fetch_repositories(username) - all_repos = [] - page = 1 - - loop do - response = HTTParty.get("#{BASE_URL}/users/#{username}/repos?per_page=100&page=#{page}", headers: @headers) - repos = handle_response(response) - break if repos.empty? - - all_repos.concat(repos) - page += 1 - end - - all_repos + url = URI("#{BASE_URL}/users/#{username}/repos") + response = make_request(url) + JSON.parse(response, symbolize_names: true) end def fetch_commit_activity(username, repo_name) - response = HTTParty.get("#{BASE_URL}/repos/#{username}/#{repo_name}/stats/commit_activity", headers: @headers) - handle_response(response).sum { |week| week['total'] } + url = URI("#{BASE_URL}/repos/#{username}/#{repo_name}/commits") + response = make_request(url) + commits = JSON.parse(response, symbolize_names: true) + commits.size rescue - nil + "N/A" + end + + def fetch_repository_details(username, repo_name) + url = URI("#{BASE_URL}/repos/#{username}/#{repo_name}") + response = make_request(url) + JSON.parse(response, symbolize_names: true) end private - def handle_response(response) - if response.code == 200 - JSON.parse(response.body) - else - raise "Error: #{response.message} (#{response.code})" + def make_request(url) + http = Net::HTTP.new(url.host, url.port) + http.use_ssl = true + request = Net::HTTP::Get.new(url) + request['User-Agent'] = 'Ruby' + response = http.request(request) + + unless response.is_a?(Net::HTTPSuccess) + raise "Error: #{response.message}" end + + response.body end end - diff --git a/lib/display.rb b/lib/display.rb index 5b51e46..c089076 100644 --- a/lib/display.rb +++ b/lib/display.rb @@ -1,16 +1,29 @@ -# lib/display.rb -require 'tty-table' +require 'terminal-table' -class Display +module Display def self.show_repositories(repositories) rows = repositories.map do |repo| - [repo.name, repo.stars, repo.forks, repo.open_issues, repo.language, repo.size, repo.commits || 'N/A'] + [repo.name, repo.stars, repo.forks, repo.open_issues, repo.language, repo.size.round(2), repo.commits] end - table = TTY::Table.new( - ['Name', 'Stars', 'Forks', 'Open Issues', 'Language', 'Size (KB)', 'Commits'], - rows + table = Terminal::Table.new( + headings: ['Name', 'Stars', 'Forks', 'Open Issues', 'Language', 'Size (MB)', 'Commits'], + rows: rows ) - puts table.render(:ascii) + puts table + end + + def self.show_repository_details(data) + puts "\n--- Repository Details ---" + puts "Name: #{data[:name]}" + puts "Description: #{data[:description] || 'N/A'}" + puts "Stars: #{data[:stargazers_count]}" + puts "Forks: #{data[:forks_count]}" + puts "Open Issues: #{data[:open_issues_count]}" + puts "Language: #{data[:language] || 'N/A'}" + puts "Size: #{data[:size] / 1024.0} MB" + puts "Default Branch: #{data[:default_branch]}" + puts "Created At: #{data[:created_at]}" + puts "Last Updated: #{data[:updated_at]}" end end diff --git a/lib/menu.rb b/lib/menu.rb new file mode 100644 index 0000000..60170d4 --- /dev/null +++ b/lib/menu.rb @@ -0,0 +1,114 @@ +require_relative 'api_client' +require_relative 'repository' +require_relative 'display' +require_relative 'utils' + +module Menu + def self.start + loop do + puts "\n--- GitHub Repository Insights ---" + puts "1. View Repositories" + puts "2. Search Repository by Name" + puts "3. View Detailed Repository Information" + puts "4. Export Repository Data" + puts "5. Help" + puts "6. Exit" + print "Choose an option: " + option = gets.chomp.to_i + + case option + when 1 then view_repositories + when 2 then search_repository + when 3 then view_repository_details + when 4 then export_data + when 5 then display_help + when 6 + puts "Goodbye!" + break + else + puts "Invalid option. Please try again." + end + end + end + + def self.view_repositories + print "Enter GitHub username: " + username = gets.chomp + client = ApiClient.new + + begin + repos_data = client.fetch_repositories(username) + repositories = repos_data.map { |data| Repository.new(data) } + repositories.each { |repo| repo.commits = client.fetch_commit_activity(username, repo.name) } + Display.show_repositories(repositories) + rescue StandardError => e + puts "Error: #{e.message}" + end + end + + def self.search_repository + print "Enter GitHub username: " + username = gets.chomp + print "Enter repository name or keyword: " + keyword = gets.chomp + client = ApiClient.new + + begin + repos_data = client.fetch_repositories(username) + filtered_repos = repos_data.select { |data| data[:name].downcase.include?(keyword.downcase) } + repositories = filtered_repos.map { |data| Repository.new(data) } + Display.show_repositories(repositories) + rescue StandardError => e + puts "Error: #{e.message}" + end + end + + def self.view_repository_details + print "Enter GitHub username: " + username = gets.chomp + print "Enter repository name: " + repo_name = gets.chomp + client = ApiClient.new + + begin + repo_details = client.fetch_repository_details(username, repo_name) + Display.show_repository_details(repo_details) + rescue StandardError => e + puts "Error: #{e.message}" + end + end + + def self.export_data + print "Enter GitHub username: " + username = gets.chomp + client = ApiClient.new + + begin + repos_data = client.fetch_repositories(username) + repositories = repos_data.map { |data| Repository.new(data) } + repositories.each { |repo| repo.commits = client.fetch_commit_activity(username, repo.name) } + + print "Export data as: 1) CSV 2) JSON: " + format = gets.chomp.to_i + + case format + when 1 then Utils.export_to_csv(repositories) + when 2 then Utils.export_to_json(repositories) + else + puts "Invalid format. Export canceled." + end + rescue StandardError => e + puts "Error: #{e.message}" + end + end + + def self.display_help + puts "\n--- Help ---" + puts "1. View Repositories: Fetch and display a list of repositories for a user." + puts "2. Search Repository: Search for repositories by name or keyword." + puts "3. View Details: View detailed information about a specific repository." + puts "4. Export Data: Export repository data to a CSV or JSON file." + puts "5. Help: Display this help menu." + puts "6. Exit: Exit the application." + end +end diff --git a/lib/repository.rb b/lib/repository.rb index f43da3a..d509854 100644 --- a/lib/repository.rb +++ b/lib/repository.rb @@ -1,20 +1,13 @@ -# lib/repository.rb class Repository - attr_reader :name, :stars, :forks, :open_issues, :description, :language, :size, :archived, :commits + attr_accessor :name, :stars, :forks, :open_issues, :language, :size, :commits def initialize(data) - @name = data['name'] - @stars = data['stargazers_count'] - @forks = data['forks_count'] - @open_issues = data['open_issues_count'] - @description = data['description'] || 'No description' - @language = data['language'] || 'N/A' - @size = data['size'] - @archived = data['archived'] - @commits = nil - end - - def commits=(value) - @commits = value + @name = data[:name] + @stars = data[:stargazers_count] + @forks = data[:forks_count] + @open_issues = data[:open_issues_count] + @language = data[:language] || "N/A" + @size = data[:size] / 1024.0 # Convert KB to MB + @commits = "N/A" end end diff --git a/lib/utils.rb b/lib/utils.rb index 0a77b2e..01e1078 100644 --- a/lib/utils.rb +++ b/lib/utils.rb @@ -1,56 +1,31 @@ -# lib/utils.rb -module Utils - def self.filter_repositories(repositories) - puts "Filter by (1) Stars, (2) Forks, (3) Open Issues, (4) Archived Status, or press Enter to skip:" - filter_option = gets.chomp.to_i +require 'csv' +require 'json' - case filter_option - when 1 - puts "Minimum stars:" - min_stars = gets.chomp.to_i - repositories.select! { |repo| repo.stars >= min_stars } - when 2 - puts "Minimum forks:" - min_forks = gets.chomp.to_i - repositories.select! { |repo| repo.forks >= min_forks } - when 3 - puts "Minimum open issues:" - min_issues = gets.chomp.to_i - repositories.select! { |repo| repo.open_issues >= min_issues } - when 4 - puts "Show only (1) Archived or (2) Non-Archived repositories:" - archived = gets.chomp.to_i == 1 - repositories.select! { |repo| repo.archived == archived } +module Utils + def self.export_to_csv(repositories) + CSV.open('repositories.csv', 'w') do |csv| + csv << ['Name', 'Stars', 'Forks', 'Open Issues', 'Language', 'Size (MB)', 'Commits'] + repositories.each do |repo| + csv << [repo.name, repo.stars, repo.forks, repo.open_issues, repo.language, repo.size.round(2), repo.commits] + end end - - repositories + puts "Data exported to repositories.csv" end - def self.sort_repositories(repositories) - puts "Sort by (1) Stars, (2) Forks, (3) Open Issues, or press Enter to skip:" - sort_option = gets.chomp.to_i - - case sort_option - when 1 - repositories.sort_by! { |repo| -repo.stars } - when 2 - repositories.sort_by! { |repo| -repo.forks } - when 3 - repositories.sort_by! { |repo| -repo.open_issues } + def self.export_to_json(repositories) + data = repositories.map do |repo| + { + name: repo.name, + stars: repo.stars, + forks: repo.forks, + open_issues: repo.open_issues, + language: repo.language, + size: repo.size.round(2), + commits: repo.commits + } end - repositories - end - - def self.export_to_csv(repositories, filename = "repositories.csv") - require 'csv' - - CSV.open(filename, 'w') do |csv| - csv << ['Name', 'Stars', 'Forks', 'Open Issues', 'Language', 'Size (KB)', 'Commits'] - repositories.each do |repo| - csv << [repo.name, repo.stars, repo.forks, repo.open_issues, repo.language, repo.size, repo.commits || 'N/A'] - end - end - puts "Data exported to #{filename}" + File.write('repositories.json', JSON.pretty_generate(data)) + puts "Data exported to repositories.json" end -end \ No newline at end of file +end diff --git a/main.rb b/main.rb index afe5b4e..84b34da 100644 --- a/main.rb +++ b/main.rb @@ -1,32 +1,3 @@ -# Load gems and libraries from bundled Ruby -$LOAD_PATH << File.expand_path('./ruby_runtime/lib/ruby/gems') -require 'bundler/setup' +require_relative 'lib/menu' -require_relative './lib/api_client' -require_relative './lib/repository' -require_relative './lib/display' -require_relative './lib/utils' - -client = ApiClient.new - -puts 'Enter GitHub username:' -username = gets.chomp - -begin - repos_data = client.fetch_repositories(username) - repositories = repos_data.map { |data| Repository.new(data) } - - repositories.each do |repo| - repo.commits = client.fetch_commit_activity(username, repo.name) - end - - repositories = Utils.filter_repositories(repositories) - repositories = Utils.sort_repositories(repositories) - - Display.show_repositories(repositories) - - puts "Would you like to export the data to a CSV file? (yes/no)" - Utils.export_to_csv(repositories) if gets.chomp.downcase == 'yes' -rescue => e - puts e.message -end +Menu.start