diff --git a/.gitignore b/.gitignore index 48c488d..471df9c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,8 @@ metrics-env/ metrics-env/** output/** update_gsheet.py -*.csv \ No newline at end of file +*.csv + +.env + +.DS_Store diff --git a/export_traffic.py b/export_traffic.py index 4342a59..168f025 100644 --- a/export_traffic.py +++ b/export_traffic.py @@ -32,7 +32,7 @@ def test_push_access(organization, authToken): if orgs.login == organization: print("Checking push access for", orgs.name, "repositories. \n") for repo in orgs.get_repos(): - if repo.fork == False and repo.private == False: + if repo.fork == False and repo.archived == False: currentrepo = repo.name r = s.get("https://api.github.com/repos/" + organization + "/" + currentrepo + "/traffic/views") r_views = json.loads(r.text) @@ -70,7 +70,7 @@ def export_traffic(directory, organization, repos_ok, authtoken): for repo in allrepos: repo_owner = str(repo.owner) repo_owner = (repo_owner.replace('NamedUser(login="', "")).replace('")', "") - if repo.fork == False and repo.private == False and repo_owner == organization and repo.name in repos_ok: + if repo.fork == False and repo.archived == False and repo_owner == organization and repo.name in repos_ok: reposlist.append(repo.name) count=0 diff --git a/github_metrics.py b/github_metrics.py index df7442b..9974e32 100644 --- a/github_metrics.py +++ b/github_metrics.py @@ -154,6 +154,129 @@ def export_community_engagement(directory, organization, authToken): [todaystr, organization, repo.name, repo.forks_count, repo.stargazers_count, countcommit, countcollab]) +def export_repo_metrics(directory,organization, authToken): + g = Github(authToken) + dt_today = datetime.datetime.now() + today = dt_today.date() + today = str(today).replace("-", "") + + totalrepos = 0 + allorgs = g.get_user().get_orgs() + + with open(directory + "/github_repo_metrics_" + organization + "_" + today+ ".csv", 'w', encoding='utf-8') as csvfile: + csvwriter = csv.writer(csvfile, delimiter=',') + csvwriter.writerow( + ["date", "org", "repo", "open prs", "pr avg age", "pr max age", "newest pr created", "closed prs", "avg time to close", + "pr \% in org", "pr \% non org", "pr \% bot", + "open issues", "issue avg age", "issue max age", "newest issue opened", "issue \% in org", "issue \% non org", "issue \% bot"]) + for orgs in allorgs: + if orgs.login == organization: + print("Gathering repository metrics for", orgs.login) + members = orgs.get_members() + for repo in orgs.get_repos(): + if not repo.archived: + pr_open_count = 0 + pr_max_open = 0 + pr_max_date = datetime.datetime.min + pr_open_total_days = 0 + pr_closed_count = 0 + pr_closed_total_days = 0 + pr_in_org = 0 + pr_non_org = 0 + pr_bot = 0 + pulls = repo.get_pulls(state='all', sort='created') + for pr in pulls: + owner = pr.user + if owner.login and "bot" in owner.login.lower(): + pr_bot += 1 + elif owner in members: + pr_in_org += 1 + else: + pr_non_org += 1 + + if pr.created_at > pr_max_date: + pr_max_date = pr.created_at + + if pr.state == 'open': + pr_open_count += 1 + + days_open = (dt_today - pr.created_at).days + pr_open_total_days += days_open + if days_open > pr_max_open: + pr_max_open = days_open + + else: + pr_closed_count += 1 + + closed = pr.closed_at if pr.closed_at else datetime.datetime.min + merged = pr.merged_at if pr.merged_at else datetime.datetime.min + end_date = max(closed, merged) + days_open = (end_date- pr.created_at).days + pr_closed_total_days += days_open + if days_open > pr_max_open: + pr_max_open = days_open + + print(f"{repo.name} - {pulls.totalCount} - {pr_open_count}") + + pr_avg_open = f"{pr_open_total_days/pr_open_count:.2f}" if pr_open_count > 0 else 0 + pr_avg_close_time = f"{pr_closed_total_days/pr_closed_count:.2f}" if pr_closed_count > 0 else 0 + pr_pct_org = f"{(pr_in_org/(pr_open_count + pr_closed_count)) * 100:.2f}" if pr_closed_count + pr_open_count > 0 else 0 + pr_pct_non_org = f"{(pr_non_org/(pr_open_count + pr_closed_count)) * 100:.2f}" if pr_closed_count + pr_open_count > 0 else 0 + pr_pct_bot = f"{(pr_bot/(pr_open_count + pr_closed_count)) * 100:.2f}" if pr_closed_count + pr_open_count > 0 else 0 + pr_display_date = pr_max_date.date() if pr_max_date != datetime.datetime.min else "N/A" + + i_open_count = 0 + i_max_open = 0 + i_max_date = datetime.datetime.min + i_open_total_days = 0 + i_closed_count = 0 + i_closed_total_days = 0 + i_in_org = 0 + i_non_org = 0 + i_bot = 0 + for iss in repo.get_issues(state='all', sort='created'): + owner = iss.user + if owner.login and "bot" in owner.login.lower(): + i_bot += 1 + elif owner in members: + i_in_org += 1 + else: + i_non_org += 1 + + if iss.created_at > i_max_date: + i_max_date = iss.created_at + + if iss.state == 'open': + i_open_count += 1 + + # print(f'{pr.created_at}') + days_open = (dt_today - iss.created_at).days + i_open_total_days += days_open + if days_open > i_max_open: + i_max_open = days_open + + else: + i_closed_count += 1 + + days_open = (iss.closed_at- iss.created_at).days + i_closed_total_days += days_open + # if days_open > pr_max_open: + # pr_max_open = days_open + # if pr.created_at > pr_max_date: + # pr_max_date = pr.created_at + + i_avg_open = f"{i_open_total_days/i_open_count:.2f}" if i_open_count > 0 else 0 + i_avg_close_time = f"{i_closed_total_days/i_closed_count:.2f}" if i_closed_count > 0 else 0 + i_pct_org = f"{(i_in_org/(i_open_count + i_closed_count)) * 100:.2f}" if i_closed_count + i_open_count > 0 else 0 + i_pct_non_org = f"{(i_non_org/(i_open_count + i_closed_count)) * 100:.2f}" if i_closed_count + i_open_count > 0 else 0 + i_pct_bot = f"{(i_bot/(i_open_count + i_closed_count)) * 100:.2f}" if i_closed_count + i_open_count > 0 else 0 + i_display_date = i_max_date.date() if i_max_date != datetime.datetime.min else "N/A" + + csvwriter.writerow( + [todaystr, organization, repo.name, pr_open_count, pr_avg_open, pr_max_open, pr_display_date, + pr_closed_count, pr_avg_close_time, pr_pct_org, pr_pct_non_org, pr_pct_bot, i_open_count, + i_avg_open, i_max_open, i_display_date, i_pct_org, i_pct_non_org, i_pct_bot]) + def list_unique_collaborators(directory, organization, authToken): g = Github(authToken) with open(directory + "/github_unique_collaborators_" + organization + ".csv", "w", encoding="utf-8") as csvfile: @@ -200,11 +323,14 @@ def main(): try: print("Valid token. Starting process. \n") + print("") list_org_members(organization, authToken) print("") export_code_frequency(directory, organization, authToken) print("") export_community_engagement(directory, organization, authToken) + print("") + export_repo_metrics(directory, organization, authToken) except Exception as e: print(e) diff --git a/requirements.txt b/requirements.txt index cfdc04d..9b77b00 100644 --- a/requirements.txt +++ b/requirements.txt @@ -51,6 +51,7 @@ oauthlib==3.1.0 opt-einsum==3.3.0 packaging==20.9 pandocfilters==1.4.3 +pandas==1.3.1 parso==0.8.2 pexpect==4.8.0 pickleshare==0.7.5 @@ -63,6 +64,7 @@ pyasn1==0.4.8 pyasn1-modules==0.2.8 pycparser==2.20 Pygments==2.8.1 +PyGithub==1.55 pyparsing==2.4.7 pyrsistent==0.17.3 python-dateutil==2.8.1