├── .gitignore
├── README.md
├── git-gui.py
├── gitfiles.py
├── install-pyqt.sh
└── requirements.py
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/linux,emacs,sublimetext
3 |
4 | ### Linux ###
5 | *~
6 |
7 | # temporary files which can be created if a process still has a handle open of a deleted file
8 | .fuse_hidden*
9 |
10 | # KDE directory preferences
11 | .directory
12 |
13 | # Linux trash folder which might appear on any partition or disk
14 | .Trash-*
15 |
16 |
17 | ### Emacs ###
18 | # -*- mode: gitignore; -*-
19 | *~
20 | \#*\#
21 | /.emacs.desktop
22 | /.emacs.desktop.lock
23 | *.elc
24 | auto-save-list
25 | tramp
26 | .\#*
27 |
28 | # Org-mode
29 | .org-id-locations
30 | *_archive
31 |
32 | # flymake-mode
33 | *_flymake.*
34 |
35 | # eshell files
36 | /eshell/history
37 | /eshell/lastdir
38 |
39 | # elpa packages
40 | /elpa/
41 |
42 | # reftex files
43 | *.rel
44 |
45 | # AUCTeX auto folder
46 | /auto/
47 |
48 | # cask packages
49 | .cask/
50 | dist/
51 |
52 | # Flycheck
53 | flycheck_*.el
54 |
55 | # server auth directory
56 | /server/
57 |
58 | # projectiles files
59 | .projectile
60 |
61 | ### SublimeText ###
62 | # cache files for sublime text
63 | *.tmlanguage.cache
64 | *.tmPreferences.cache
65 | *.stTheme.cache
66 |
67 | # workspace files are user-specific
68 | *.sublime-workspace
69 |
70 | # project files should be checked into the repository, unless a significant
71 | # proportion of contributors will probably not be using SublimeText
72 | # *.sublime-project
73 |
74 | # sftp configuration file
75 | sftp-config.json
76 |
77 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # git-repo
2 | A python script that tells about github user or any particular repository!
3 | ## Getting Started
4 | These instructions will get you a copy of the project up and running on your local machine.
5 | First of all, download the zip file of the repository or clone it locally.
6 | ### Prerequisities
7 | To install all the prerequisities run
8 | ```
9 | pip install -r requirements.py
10 | ```
11 | To install PyQt package
12 | ```
13 | $ chmod +x install-pyqt.sh
14 | $ ./install-pyqt.sh
15 | ```
16 | in the project folder.
17 | ## How to use the script
18 | To execute the script just run
19 | ```
20 | python git-gui.py
21 | ```
22 | for the GUI version of the script or
23 | ```
24 | python gitfiles.py
25 | ```
26 | for the console version.
27 | ### Main functions :
28 | 1. A menu driven app that asks about you want user information or a repository information
29 | 2. If user is selected, then asks for username and provides user information.
30 | 3. If Repository is selected, then shows the repository related information.
31 |
32 | ### Screenshots
33 | Console version Screenshots
34 |
35 |
36 |
37 | # Please contribute
38 | Contribute to make a complete GIT Management System. Send pull requests.
39 | ## Things to do:
40 | 1. Add more fields in repository section.
41 | 2. Add more fields in user section.
42 | 3. Have a look to the Issue section for more.
43 |
--------------------------------------------------------------------------------
/git-gui.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | import sys
4 | from bs4 import BeautifulSoup
5 | import requests
6 | from PyQt4.QtCore import *
7 | from PyQt4.QtGui import *
8 |
9 | repo_name = None
10 | user_name = None
11 |
12 |
13 | class Main(QMainWindow):
14 | def __init__(self, parent=None):
15 | QMainWindow.__init__(self, parent)
16 | self.initUI()
17 |
18 | def initUI(self):
19 | self.win = QWidget()
20 |
21 | self.repo_view = False
22 |
23 | self.l1 = QLabel("Github Username")
24 |
25 | self.nm = QLineEdit(self)
26 | self.nm.setMinimumWidth(200)
27 | self.nm.editingFinished.connect(self.enterPress)
28 |
29 | self.l2 = QLabel("Repository")
30 |
31 | self.add1 = QLineEdit(self)
32 | self.add1.setMinimumWidth(200)
33 |
34 | self.fbox = QFormLayout()
35 | self.fbox.addRow(self.l1, self.nm)
36 |
37 | self.vbox = QVBoxLayout()
38 |
39 | self.vbox.addWidget(self.add1)
40 | self.fbox.addRow(self.l2, self.vbox)
41 |
42 | self.submitButton = QPushButton("Submit")
43 | self.cancelButton = QPushButton("Cancel")
44 | self.fbox.addRow(self.submitButton, self.cancelButton)
45 | self.submitButton.clicked.connect(self.submit)
46 |
47 | self.win.setLayout(self.fbox)
48 | self.setWindowTitle("Github GUI")
49 | self.setCentralWidget(self.win)
50 | self.resize(self.win.minimumSize())
51 | self.show()
52 |
53 | def enterPress(self):
54 | self.add1.setFocus()
55 |
56 | def submit(self):
57 | global repo_name
58 | global user_name
59 | repo_name = str(self.add1.text()).strip()
60 | user_name = str(self.nm.text()).strip()
61 | if(len(repo_name) == 0 or len(user_name) == 0):
62 | msg = QMessageBox()
63 | msg.setIcon(QMessageBox.Critical)
64 | msg.setText("User or repo name can't be empty")
65 | msg.setWindowTitle("Error")
66 | msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
67 | msg.exec_()
68 | return
69 | self.switch_view()
70 |
71 | def switch_view(self):
72 | if(self.repo_view is False):
73 | self.repo_view = True
74 | global user_name
75 | global repo_name
76 | user_page = "http://github.com/" + user_name
77 | repo_page = user_page + "/" + repo_name
78 | res = requests.get(user_page)
79 | if(res.status_code == 404):
80 | msg = QMessageBox()
81 | msg.setIcon(QMessageBox.Critical)
82 | msg.setText("User doesn't exists.")
83 | msg.setWindowTitle("Error")
84 | msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
85 | msg.exec_()
86 | self.repo_view = False
87 | return
88 | else:
89 | soup = BeautifulSoup(res.text, "html.parser")
90 | followers = soup.find('a', {'href': '/' + user_name + '?tab=followers'}).find('span').text.strip()
91 | following = soup.find('a', {'href': '/' + user_name + '?tab=following'}).find('span').text.strip()
92 | res = requests.get(repo_page)
93 | if(res.status_code == 404):
94 | msg = QMessageBox()
95 | msg.setIcon(QMessageBox.Critical)
96 | msg.setText("Repository doesn't exists.")
97 | msg.setWindowTitle("Error")
98 | msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
99 | msg.exec_()
100 | self.repo_view = False
101 | return
102 |
103 | soup = BeautifulSoup(res.text, "html.parser")
104 | try:
105 | short_desc = soup.find("span", {"itemprop": "about"}).text
106 | except:
107 | short_desc = "No description available"
108 |
109 | issue_tag = soup.find('a', {"data-hotkey": "g i"})
110 | issues = issue_tag.find('span', {'class': 'counter'}).text
111 |
112 | pr_tag = soup.find('a', {"data-hotkey": "g p"})
113 | prs = pr_tag.find('span', {'class': 'counter'}).text
114 |
115 | main_tag = soup.find('li', {'class': 'commits'})
116 |
117 | commits = main_tag.find('span').text.strip()
118 | branches = main_tag.find_next("li").find('span').text.strip()
119 | releases = main_tag.find_next("li").find_next("li").find('span').text.strip()
120 | contributors = main_tag.find_next("li").find_next("li").find_next("li").find('span').text.strip()
121 |
122 | overview_files = soup.findAll('tr', {'class': 'js-navigation-item'})
123 | files = list()
124 |
125 | for file in overview_files:
126 | files.append([file.find('a').text.strip(),
127 | file.find('td', {'class': 'message'}).find('a').attrs["title"],
128 | file.find('td', {'class': 'age'}).text.strip()
129 | ])
130 |
131 | try:
132 | watchers = soup.find('a', {'href': '/' + user_name + '/' + repo_name + '/watchers'}).text.strip()
133 | except:
134 | watchers = "0"
135 | try:
136 | stars = soup.find('a', {'href': '/' + user_name + '/' + repo_name + '/stargazers'}).text.strip()
137 | except:
138 | stars = "0"
139 | try:
140 | forks = soup.find('a', {'href': '/' + user_name + '/' + repo_name + '/network'}).text.strip()
141 | except:
142 | forks = "0"
143 |
144 | self.font = QFont("Times", 16, QFont.Bold)
145 |
146 | self.user = QLabel(user_name)
147 | self.user.setFont(self.font)
148 | self.repo_name = QLabel(repo_name)
149 | self.repo_name.setFont(self.font)
150 | self.title = QLabel(short_desc)
151 | self.title.setFont(self.font)
152 |
153 | self.issue = QLabel("Issues | " + issues)
154 | self.pr = QLabel("Pull Requests | " + prs)
155 |
156 | self.follower = QLabel("Followers | " + followers)
157 | self.followee = QLabel("Following | " + following)
158 |
159 | self.watcher = QLabel("Watchers | " + watchers)
160 | self.star = QLabel("Stars | " + stars)
161 | self.fork = QLabel("Forks | " + forks)
162 |
163 | self.commit = QLabel(commits + " commits")
164 | self.branch = QLabel(branches + " branches")
165 | self.release = QLabel(releases + " releases")
166 | self.contributor = QLabel(contributors + " contributors")
167 |
168 | self.file_overview = QLabel("Files Overview")
169 |
170 | self.main_grid = QGridLayout()
171 | self.main_grid.setSpacing(0)
172 |
173 | self.area = QScrollArea(self)
174 | self.area.setWidgetResizable(True)
175 |
176 | self.main_grid.addWidget(self.user, 0, 0)
177 | self.main_grid.addWidget(self.follower, 0, 2)
178 | self.main_grid.addWidget(self.followee, 0, 3)
179 | self.main_grid.addWidget(self.repo_name, 1, 0)
180 | self.main_grid.addWidget(self.watcher, 2, 1)
181 | self.main_grid.addWidget(self.star, 2, 2)
182 | self.main_grid.addWidget(self.fork, 2, 3)
183 | self.main_grid.addWidget(self.title, 3, 0)
184 |
185 | self.main_grid.addWidget(self.issue, 4, 0)
186 | self.main_grid.addWidget(self.pr, 4, 2)
187 |
188 | for i in range(len(files)):
189 | self.main_grid.addWidget(QLabel(files[i][0]), i + 6, 0)
190 | self.main_grid.addWidget(QLabel(files[i][1]), i + 6, 1)
191 | self.main_grid.addWidget(QLabel(files[i][2]), i + 6, 3)
192 |
193 | self.central_widget = QWidget()
194 | self.central_widget.setLayout(self.main_grid)
195 | self.area.setWidget(self.central_widget)
196 |
197 | self.vbar = self.area.verticalScrollBar()
198 | self.vbar.setValue(self.vbar.maximum())
199 |
200 | self.setCentralWidget(self.area)
201 |
202 | self.resize(self.central_widget.width() + 20, self.central_widget.height() + 20)
203 | self.show()
204 |
205 | else:
206 | self.repo_view = False
207 | self.initUI()
208 |
209 | def keyPressEvent(self, event):
210 | key = event.key()
211 | if(key == Qt.Key_Backspace):
212 | self.switch_view()
213 |
214 |
215 | if __name__ == '__main__':
216 | app = QApplication(sys.argv)
217 | window = Main()
218 | sys.exit(app.exec_())
219 |
--------------------------------------------------------------------------------
/gitfiles.py:
--------------------------------------------------------------------------------
1 | """
2 | Program to find information about given Github username or
3 | repository from the terminal.
4 | """
5 | import urllib2
6 | from bs4 import BeautifulSoup
7 | import requests
8 | import re
9 |
10 | def infoAboutRepo():
11 | """
12 | Given username and repository, Returns information about the repository.
13 | """
14 | user = raw_input('Enter the user name ')
15 | url = 'https://github.com/'+user
16 | # Check If username is invalid
17 | try:
18 | soup = BeautifulSoup(urllib2.urlopen(url).read(), "html.parser")
19 | except Exception:
20 | print 'User "%s" does not exist! Please try again.' %(user)
21 | exit()
22 |
23 | popularRepo = soup.find_all('span' , {'class': 'repo js-repo'})
24 | print "These are the some popular repo of user",user
25 | for repo in popularRepo:
26 | print repo.string
27 |
28 | repo = raw_input('Enter the repository name : ')
29 | url = "https://github.com/"+user+'/'+repo
30 | try:
31 | urllib2.urlopen(url)
32 | except urllib2.HTTPError, e:
33 | print 'Sorry, there is no such repository named "%s" for user "%s"'%(repo, user)
34 | exit()
35 |
36 |
37 | def pulse(url):
38 | """
39 | """
40 | url += '/pulse/monthly'
41 | page = urllib2.urlopen(url)
42 | soup = BeautifulSoup(page.read(), "html.parser")
43 | div_all = soup.findAll('div',{'class':'section diffstat-summary'})
44 | if not div_all:
45 | print 'No Recent activities in the repository.'
46 | return
47 | print '\nThe whole information about the repository is as follows :\n'
48 | for each_div in div_all:
49 | print ' '.join(each_div.get_text().split())
50 |
51 | def readme(url):
52 | """
53 | """
54 | url+= '/blob/master/README.md'
55 | # Check if ReadMe exists.
56 | try:
57 | soup = BeautifulSoup(urllib2.urlopen(url).read(), "html.parser")
58 | paragraphs = soup.find('article', {"class" : "markdown-body entry-content"}).get_text()
59 | except Exception:
60 | print 'ReadMe file for the repository doesn\'t exist'
61 | return
62 |
63 | print '\nREADME\n'
64 | print paragraphs
65 |
66 |
67 | def watching(url):
68 | """
69 | """
70 | # TODO: watching not working as of now. Only giving 0 as Watcher...
71 | soup = BeautifulSoup(urllib2.urlopen(url).read(), "html.parser")
72 | watch = soup.find('a' , {"class" : "social-count js-social-count"}).text
73 | print 'Watchers: %s' %(watch.split()[0])
74 |
75 |
76 | def statistics(url):
77 | """
78 | """
79 | soup = BeautifulSoup(urllib2.urlopen(url).read(), "html.parser")
80 | ultags_all= soup.find_all('ul', {'class' : 'numbers-summary'})
81 | if not ultags_all:
82 | print 'No activities in the repository.'
83 | return
84 | print "\nUsers Activities in Repo"
85 | for ultag in ultags_all :
86 | for litag in ultag.find_all('li'):
87 | if ' '.join(litag.text.split()) != "Fetching contributors":
88 | print ' '.join(litag.text.split())
89 |
90 | statistics(url)
91 | pulse(url)
92 | readme(url)
93 | watching(url)
94 | #more features to be added...
95 |
96 |
97 | def infoAboutUser():
98 | """
99 | Given username, Returns information about user if exists.
100 | """
101 | user = raw_input('Enter the user name of the person you want to see : ')
102 | url = 'https://github.com/'+user
103 | # Check If username is invalid
104 | try:
105 | soup = BeautifulSoup(urllib2.urlopen(url).read(), "html.parser")
106 | except Exception:
107 | print 'User "%s" does not exist! Please try again.' %(user)
108 | exit()
109 |
110 |
111 | def profileInfo(soup):
112 | """
113 | Returns the Profile specific information for the User.
114 | """
115 | # TODO: remove unwanted code
116 |
117 | #Give users full name
118 | fullName = soup.find('span', attrs = {'class': "vcard-fullname"}).text
119 | print "Full name: ",fullName
120 |
121 | #Give users username
122 | userName = soup.find('span', attrs = {'class': "vcard-username"}).text
123 | print "username: ",userName
124 |
125 | #Give users home town
126 | try:
127 | homeTown = soup.find('li',{'aria-label':"Home location"}).text
128 | print "Home Town: ",homeTown
129 | except:
130 | print "User does not add his/her hometown on github!"
131 | #Give user Email-Id
132 | try:
133 | email_id = soup.find('li',{'aria-label' : "Email"}).text
134 | print "email-id: ",email_id
135 | except:
136 | print "User does not add his/her email-id on github!"
137 |
138 | #Give Joining date
139 | join = soup.find('li',{'aria-label':"Member since" }).text
140 | print "Joining date of github: ",join[10:]
141 |
142 | #Give users organisation
143 | try:
144 | organization = soup.find('li',{'aria-label' : "Organization"}).text
145 | print "Organization: ",organization
146 | except:
147 | print "User does not add his/her working Organization on github!"
148 |
149 | #Give users Blog or Website
150 | try:
151 | website = soup.find('li',{'aria-label' : "Blog or website"}).text
152 | print "Personal website: ",website
153 | except:
154 | print "User does not add his/her personal website on github!"
155 |
156 | # Get followers
157 | for followersCount in soup.findAll('span', attrs = {'class': "counter"}):
158 | parent = followersCount.parent
159 | if parent.name == 'a' and 'followers' in parent['href']:
160 | count = followersCount.text.replace(" ",'').replace("\n", "")
161 | #count = int(re.search(r'\d+', str(followersCount.text)).group())
162 | print "Followers: ",count
163 | count = int(re.search(r'\d+', str(followersCount.text)).group())
164 | if (count > 0):
165 | follow(url,'followers')
166 |
167 | for followingCount in soup.findAll('span', attrs = {'class': "counter"}):
168 | parent = followingCount.parent
169 | if parent.name == 'a' and 'following' in parent['href']:
170 | count = followersCount.text.replace(" ", '').replace("\n", "")
171 | print "Following: ", count
172 | count = int(re.search(r'\d+', str(followingCount.text)).group())
173 | if (count > 0):
174 | follow(url,'following')
175 |
176 | #Give user bio
177 | userdesc(soup)
178 |
179 | def follow(url,str):
180 | url_new = url + '?tab=' + str
181 |
182 | try:
183 | soup = BeautifulSoup(urllib2.urlopen(url_new).read(), 'html.parser')
184 | except Exception:
185 | print 'Connection Error!'
186 | exit()
187 |
188 | user_names = soup.find_all('span', {'class': 'f4 link-gray-dark'})
189 |
190 | for uname in user_names:
191 | ustring = '-> ' + uname.string
192 | try:
193 | print ustring
194 | except:
195 | continue
196 | print ''
197 |
198 |
199 |
200 | def contributions(soup):
201 | """
202 | Returns the contributions done by user in given Period.
203 | """
204 | # TODO: Generates error. Needs modification
205 | print "\nContributions of User\n"
206 |
207 | totalContributions = soup.find('div' , {'class' : 'js-contribution-graph'}).find('h2',{'class' : 'f4 text-normal mb-3'}).text
208 | print "Total contributions last year",totalContributions.split()[0]
209 |
210 |
211 | Streaks = soup.find('svg' , {'class' : 'js-calendar-graph-svg'}).find_all('rect')
212 | longestStreak = 0
213 | streakList = []
214 | for streak in Streaks:
215 | streakList.append(int(streak['data-count']))
216 | longestStreak = max(int(streak['data-count']),longestStreak)
217 | print "Longest Streak: ",longestStreak
218 |
219 | print "Total contributions last weeks: ",sum(streakList[-7:])
220 |
221 |
222 | def popularRepos(soup):
223 | """
224 | Returns Public repositories of the user.
225 | """
226 | popularRepo = soup.find_all('span' , {'class': 'repo js-repo'})
227 |
228 | if not popularRepo:
229 | print 'No public repositories for the given user.'
230 | return
231 | desc= soup.findAll('p',{'class':'pinned-repo-desc text-gray text-small d-block mt-2 mb-3'})
232 | stars= soup.findAll('p',{'class':'mb-0 f6 text-gray'})
233 | length= len(popularRepo)
234 | countPopularRepo =1
235 | for i in range(0,length):
236 | print str(countPopularRepo) + ': '
237 | countPopularRepo = countPopularRepo+1
238 | print 'Name :' + popularRepo[i].text
239 | print 'Description :' + desc[i].text
240 | descriptions = list(stars[i].stripped_strings)
241 | description = "\nLanguage : ".join(descriptions) if descriptions else ""
242 | print "Stars :" +description
243 |
244 |
245 | def userdesc(soup):
246 | try:
247 | desc= soup.find('div',{'class':'user-profile-bio'}).text
248 | print "User's Bio:",desc
249 | except Exception:
250 | print "User's Bio: This User doesn\'t have a bio"
251 |
252 | print "\nUsers Info\n"
253 | profileInfo(soup)
254 | # followers(url)
255 | contributions(soup)
256 | print "\nUsers Popular Repositories\n"
257 | popularRepos(soup)
258 |
259 | if __name__ == "__main__":
260 | print "Welcome to the Python Interface of GitHub!"
261 | print '''Please enter your choice :\n
262 | 1. Get information about user
263 | 2. Get information about a particular repository\n'''
264 |
265 | #Small changes here for ask user for his choice
266 | while True:
267 | choice = raw_input('Enter your choice here: ')
268 | if choice == '1':
269 | infoAboutUser()
270 | break
271 | elif choice == '2':
272 | infoAboutRepo()
273 | break
274 | else:
275 | print "Sorry, It is not a valid choice.Please select from 1 and 2!\n\n"
276 |
--------------------------------------------------------------------------------
/install-pyqt.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #This script is for installing PyQt package
3 | sudo apt-get install python-qt4 -y
4 |
--------------------------------------------------------------------------------
/requirements.py:
--------------------------------------------------------------------------------
1 | bs4
2 | requests
3 | BeautifulSoup
4 |
--------------------------------------------------------------------------------