Skip to content

Commit

Permalink
Add contributors query
Browse files Browse the repository at this point in the history
Use contributioncollection type in gql to fetch data in weekly
intervals for a given user.

Tabulate the various contribution types by week, separate table for each
user

Add new options for team and user

Add option for output table format, default to fancy_grid
  • Loading branch information
mshriver committed Jan 5, 2022
1 parent a823756 commit ffb623e
Show file tree
Hide file tree
Showing 6 changed files with 391 additions and 38 deletions.
104 changes: 97 additions & 7 deletions scripts/gh_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
from pathlib import Path

import click
from tabulate import multiline_formats
from tabulate import tabulate

from config import METRICS_OUTPUT
from config import settings
from utils import file_io
from utils import metrics_calculators
from utils.GQL_Queries.github_wrappers import OrgWrapper


# keys that will be read from settings files (dynaconf parsing) for command input defaults
Expand All @@ -22,6 +24,7 @@ def report():


# reused options for multiple metrics functions
# TODO read defaults from settings
output_prefix_option = click.option(
"--output-file-prefix",
default=settings.get(SETTINGS_OUTPUT_PREFIX, "metrics-report"),
Expand All @@ -39,11 +42,35 @@ def report():
multiple=True,
help="The repository name, like robottelo. ",
)
team_name_option = click.option(
"--team",
default=[],
multiple=True,
help="The github team name slug (URL field form, like quality-engineers)",
)
user_name_option = click.option(
"--user",
default=[],
multiple=True,
help="The github login name (URL field form, like mshriver)",
)
pr_count_option = click.option(
"--pr-count",
default=50,
help="Number of PRs to include in metrics counts, will start from most recently created",
)
num_weeks_option = click.option(
"--num-weeks",
default=4,
type=click.IntRange(1, 52),
help="Number of weeks of metrics history to collect",
)
table_format_option = click.option(
"--table-format",
default="fancy_grid",
type=click.Choice(multiline_formats),
help="The tabulate output format, https://github.com/astanin/python-tabulate#multiline-cells",
)


@report.command(
Expand All @@ -54,7 +81,8 @@ def report():
@repo_name_option
@output_prefix_option
@pr_count_option
def repo_pr_metrics(org, repo, output_file_prefix, pr_count):
@table_format_option
def repo_pr_metrics(org, repo, output_file_prefix, pr_count, table_format):
for repo_name in repo:
click.echo(f"Collecting metrics for {org}/{repo_name} ...")

Expand All @@ -67,15 +95,17 @@ def repo_pr_metrics(org, repo, output_file_prefix, pr_count):
click.echo(header)
click.echo("-" * len(header))
click.echo(
tabulate(pr_metrics, headers="keys", tablefmt="github", floatfmt=".1f")
tabulate(pr_metrics, headers="keys", tablefmt=table_format, floatfmt=".1f")
)

header = f"Review Metric Statistics for [{repo_name}]"
click.echo(f"\n{'-' * len(header)}")
click.echo(header)
click.echo("-" * len(header))
click.echo(
tabulate(stat_metrics, headers="keys", tablefmt="github", floatfmt=".1f")
tabulate(
stat_metrics, headers="keys", tablefmt=table_format, floatfmt=".1f"
)
)

pr_metrics_filename = METRICS_OUTPUT.joinpath(
Expand Down Expand Up @@ -105,12 +135,15 @@ def repo_pr_metrics(org, repo, output_file_prefix, pr_count):
)


@report.command("reviewer-report")
@report.command(
"reviewer-report", help="Gather metrics on reviewer actions within a GH repo"
)
@org_name_option
@repo_name_option
@output_prefix_option
@pr_count_option
def reviewer_actions(org, repo, output_file_prefix, pr_count):
@table_format_option
def reviewer_actions(org, repo, output_file_prefix, pr_count, table_format):
""" Generate metrics for tier reviewer groups, and general contributors
Will collect tier reviewer teams from the github org
Expand All @@ -127,13 +160,13 @@ def reviewer_actions(org, repo, output_file_prefix, pr_count):
click.echo(f"\n{'-' * len(header)}")
click.echo(header)
click.echo("-" * len(header))
click.echo(tabulate(t1_metrics, headers="keys", tablefmt="github"))
click.echo(tabulate(t1_metrics, headers="keys", tablefmt=table_format))

header = f"Tier2 Reviewer actions by week for [{repo_name}]"
click.echo(f"\n{'-' * len(header)}")
click.echo(header)
click.echo("-" * len(header))
click.echo(tabulate(t2_metrics, headers="keys", tablefmt="github"))
click.echo(tabulate(t2_metrics, headers="keys", tablefmt=table_format))

tier1_metrics_filename = METRICS_OUTPUT.joinpath(
f"{Path(output_file_prefix).stem}-"
Expand All @@ -160,3 +193,60 @@ def reviewer_actions(org, repo, output_file_prefix, pr_count):
tier2_metrics_filename,
tabulate(t2_metrics, headers="keys", tablefmt="html"),
)


@report.command("contributor-report")
@org_name_option
@output_prefix_option
@team_name_option
@num_weeks_option
@table_format_option
@user_name_option
def contributor_actions(org, output_file_prefix, team, num_weeks, table_format, user):
"""Collect count metrics of various contribution types"""

orgwrap = OrgWrapper(name=org)

collected_users = []

if not (user or team):
click.echo("ERROR: Need to specify either a team and/or user")

collaborators = list(user) # might be empty, we're gonna add users from the team(s)

for team_name in team:
# Assert the team exists and list its members
team_members = orgwrap.team_members(team=team_name)
click.echo(f"Team members for {org}/{team_name}:\n" + "\n".join(team_members))
collaborators.extend(team_members)

# maybe drop this into an OrgWrapper function
# replacing the function label here in the loop
for user in collaborators:
if user not in collected_users:
click.echo(f"Retrieving metrics for user: {user}")
collected_users.append(user)
else:
click.echo(f"Skipping user (member of multiple teams): {user}")
# collect metrics for the given user if not already covered by another team
contributor_counts = metrics_calculators.contributor_actions(
user=user, num_weeks=num_weeks
)

header = f"Contributions by week for [{user}]"
click.echo(f"\n{'-' * len(header)}")
click.echo(header)
click.echo("-" * len(header))
click.echo(tabulate(contributor_counts, tablefmt=table_format, headers="keys"))

user_metrics_filename = METRICS_OUTPUT.joinpath(
f"{Path(output_file_prefix).stem}-"
f"{user}-"
"contributor-"
f"{datetime.now().isoformat(timespec='minutes')}.html"
)
click.echo(f"\nWriting contributor metrics as HTML to {user_metrics_filename}")
file_io.write_to_output(
user_metrics_filename,
tabulate(contributor_counts, headers="keys", tablefmt="html"),
)
9 changes: 6 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@ author-email = '[email protected]'
url='https://gitlab.cee.redhat.com/mshriver/qe-pr-metrics'

[options]
python_requires = >=3.8
packages = find:
setup_requires = setuptools_scm>=3.0.0
install_requires =
attrs
attrdict
python-box
cached-property
click
dynaconf>=3.0
gql
dynaconf
gql>=3.0.0rc0
logzero
PyGithub
python-dateutil
requests
requests-toolbelt # gql 3.0 http transport
tabulate

[options.extras_require]
Expand Down
114 changes: 114 additions & 0 deletions utils/GQL_Queries/contributors_query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Importable string for GQL query

org_team_members_query = """query orgTeamMembers($organization: String!, $team: String!) {
organization(login:$organization) {
team(slug:$team) {
name
members{
nodes {
login
name
}
}
}
}
}
""" # noqa: E501

contributions_counts_by_user_query = """query getContributions($user: String! $from_date: DateTime!, $to_date: DateTime!) {
user(login:$user) {
contributionsCollection (from:$from_date, to: $to_date) {
pullRequestContributionsByRepository {
repository {name}
contributions { totalCount }
}
pullRequestReviewContributionsByRepository {
repository {name}
contributions { totalCount }
}
issueContributionsByRepository {
repository {name}
contributions {totalCount}
}
commitContributionsByRepository {
repository {name}
contributions {totalCount}
}
}
}
}
""" # noqa: E501

contributions_by_org_members_query = """query getContributions ($organization: String!, $team: String!, $from_date: DateTime!, $to_date: DateTime!) {
organization(login:$organization) {
team(slug:$team) {
name
members {
nodes {
login
contributionsCollection (from: $from_date, to: $to_date) {
pullRequestContributionsByRepository (maxRepositories:10) {
repository {name}
contributions (last:10){
nodes {
pullRequest {
number
changedFiles
deletions
additions
}
occurredAt
}
}
}
pullRequestReviewContributionsByRepository (maxRepositories: 10) {
repository {name}
contributions (last:50) {
nodes {
occurredAt
pullRequest { number }
}
}
}
}
}
}
}
}
}
""" # noqa: E501

contributions_counts_by_org_members_query = """
query contributions_counts_by_org_members_query ($organization: String!, $team: String!, $from_date: DateTime!, $to_date: DateTime!) {
organization(login: $organization) {
team(slug: $team) {
name
members {
nodes {
login
contributionsCollection (from: $from_date, to: $to_date) {
pullRequestContributionsByRepository {
repository {name}
contributions { totalCount }
}
pullRequestReviewContributionsByRepository {
repository {name}
contributions { totalCount }
}
issueContributionsByRepository {
repository {name}
contributions {totalCount}
}
commitContributionsByRepository {
repository {name}
contributions {totalCount}
}
}
}
}
}
}
}
""" # noqa: E501
Loading

0 comments on commit ffb623e

Please sign in to comment.