import github3 import datetime import os import getpass import requests class GitHub_Stargazers: def __init__(self): self.repos = {} self.stargazers = {} self.total_count = 0 def get_stats(self, username="", password="", organization="llnl", force=True): """ Retrieves the traffic for the users of the given organization. Requires organization admin credentials token to access the data. """ stargazers_file_path = "../github_stats_output/stargazers.csv" if force or not os.path.isfile(file_path): my_github.login(username, password) calls_beginning = self.logged_in_gh.ratelimit_remaining + 1 print("Rate Limit: " + str(calls_beginning)) my_github.get_org(organization) my_github.get_repos() my_github.write_to_file(file_path=stargazers_file_path) # my_github.write_to_file(file_path=stargazers_file_path) calls_remaining = self.logged_in_gh.ratelimit_remaining calls_used = calls_beginning - calls_remaining print( "Rate Limit Remaining: " + str(calls_remaining) + "\nUsed " + str(calls_used) + " API calls." ) def login(self, username="", password=""): """ Performs a login and sets the Github object via given credentials. If credentials are empty or incorrect then prompts user for credentials. Stores the authentication token in a CREDENTIALS_FILE used for future logins. Handles Two Factor Authentication. """ try: self.token = "" id = "" if not os.path.isfile("CREDENTIALS_FILE"): if username == "" or password == "": username = raw_input("Username: ") password = getpass.getpass("Password: ") note = "GitHub Organization Stats App" note_url = "http://software.llnl.gov/" scopes = ["user", "repo"] auth = github3.authorize( username, password, scopes, note, note_url, two_factor_callback=self.prompt_2fa, ) self.token = auth.token id = auth.id with open("CREDENTIALS_FILE", "w+") as fd: fd.write(self.token + "\n") fd.write(str(id)) fd.close() else: with open("CREDENTIALS_FILE", "r") as fd: self.token = fd.readline().strip() id = fd.readline().strip() fd.close() print("Logging in.") self.logged_in_gh = github3.login( token=self.token, two_factor_callback=self.prompt_2fa ) self.logged_in_gh.user().to_json() except (ValueError, AttributeError, github3.models.GitHubError): print("Bad credentials. Try again.") self.login() def prompt_2fa(self): """ Taken from http://github3py.readthedocs.io/en/master/examples/two_factor_auth.html Prompts a user for their 2FA code and returns it. """ code = "" while not code: code = raw_input("Enter 2FA code: ") return code def get_org(self, organization_name=""): """ Retrieves an organization via given org name. If given empty string, prompts user for an org name. """ self.organization_name = organization_name if organization_name == "": self.organization_name = raw_input("Organization: ") print("Getting organization.") self.org_retrieved = self.logged_in_gh.organization(organization_name) def get_repos(self): """ Gets the repos for the organization and builds the URL/headers for getting timestamps of stargazers. """ print("Getting repos.") # Uses the developer API. Note this could change. headers = { "Accept": "application/vnd.github.v3.star+json", "Authorization": "token " + self.token, } temp_count = 0 for repo in self.org_retrieved.iter_repos(): temp_count += 1 url = ( "https://api.github.com/repos/" + self.organization_name + "/" + repo.name ) self.repos[repo.name] = self.get_stargazers(url=url, headers=headers) self.calc_stargazers(start_count=650) print("total count: \t" + str(self.total_count)) print(str(temp_count) + " repos") def get_stargazers(self, url, headers={}): """ Return a list of the stargazers of a GitHub repo Includes both the 'starred_at' and 'user' data. param: url url is the 'stargazers_url' of the form: https://api.github.com/repos/LLNL/spack/stargazers """ url = url + "/stargazers?per_page=100&page=%s" page = 1 gazers = [] json_data = requests.get(url % page, headers=headers).json() while json_data: gazers.extend(json_data) page += 1 json_data = requests.get(url % page, headers=headers).json() return gazers def calc_stargazers(self, date=(datetime.date.today()), start_count=0): for repo_json in self.repos: for stargazer in self.repos[repo_json]: print(stargazer) date = stargazer["starred_at"][:10] try: self.stargazers[date] += 1 except KeyError: count = self.stargazers[date] = 1 sorted_stargazers = sorted(self.stargazers) for stargazer in reversed(sorted_stargazers): number_starred = self.stargazers[stargazer] self.stargazers[stargazer] = start_count - number_starred start_count = start_count - number_starred def write_to_file( self, file_path="", date=(datetime.date.today()), organization="llnl" ): """ Writes stargazers data to file. """ with open(file_path, "w+") as out: out.write("date,organization,stargazers\n") sorted_stargazers = sorted(self.stargazers) # sort based on lowercase for star in sorted_stargazers: out.write(star + "," + str(self.stargazers[star]) + "\n") out.close() if __name__ == "__main__": my_github = GitHub_Stargazers() my_github.get_stats()