Skip to content

Commit

Permalink
Create html table of contributions
Browse files Browse the repository at this point in the history
  • Loading branch information
christophfroehlich committed Jan 1, 2024
1 parent 6f7f83f commit 1801464
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 28 deletions.
20 changes: 18 additions & 2 deletions doc/acknowledgements/acknowledgements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,20 @@ Maintainers
The following people were maintaining the ``ros2_control`` framework, showing their all-time review activity:

.. raw:: html
:file: maintainers_stats.html
:file: reviewers_maintainers_stats.html

and their all-time contributions:

.. raw:: html
:file: contributors_maintainers_stats.html

Activity during the past 12 months:

.. raw:: html
:file: maintainers_stats_recent.html
:file: reviewers_maintainers_stats_recent.html

.. raw:: html
:file: contributors_maintainers_stats_recent.html

Reviewers' Stats
----------------
Expand All @@ -22,11 +30,19 @@ The following people have contributed to the development of this project by givi
.. raw:: html
:file: reviewers_stats.html

and with their contributions:

.. raw:: html
:file: contributors_stats.html

Activity during the past 12 months:

.. raw:: html
:file: reviewers_stats_recent.html

.. raw:: html
:file: contributors_stats_recent.html

Contributors
----------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ DIR="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
. "$DIR/deploy_defines" || { echo "Could not source deploy_defines script. This is needed for correct execution. Exiting!"; exit; }

add_reviewer_stats_file
add_pr_stats_file
2 changes: 1 addition & 1 deletion make_help_scripts/add_sub_repos
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ add_sub_repositories () {
}

add_sub_repositories
add_reviewer_stats_file
add_pr_stats_file
2 changes: 1 addition & 1 deletion make_help_scripts/add_tmp_commits
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ add_sub_repositories_and_commit () {
rm -rf .git/
cd ../../
done
add_reviewer_stats_file
add_pr_stats_file
git add .
# we don't want to use-precommit to check if subrepos are correct
git commit -m "Add temporary changes for multi version" --no-verify 1> /dev/null
Expand Down
209 changes: 199 additions & 10 deletions make_help_scripts/create_pr_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,13 +266,13 @@ def get_pr_stats(owner, repos, branches, whitelist, earliest_date=""):
current_dict = contributors
if contributor_login in current_dict:
current_dict[contributor_login]["total_changes"] += total_changes
current_dict[contributor_login]["ct_pull"] += 1
current_dict[contributor_login]["ct_pr"] += 1
current_dict[contributor_login]["last_pr_date"] = pull["created_at"]
else:
current_dict[contributor_login] = {
"avatar_url": pull_data['user']["avatar_url"],
"total_changes": total_changes,
"ct_pull": 1,
"ct_pr": 1,
"last_pr_date": pull["created_at"]
}
# if filter is set, only count reviews after earliest_date
Expand All @@ -284,13 +284,13 @@ def get_pr_stats(owner, repos, branches, whitelist, earliest_date=""):

if contributor_login in current_dict:
current_dict[contributor_login]["total_changes"] += total_changes
current_dict[contributor_login]["ct_pull"] += 1
current_dict[contributor_login]["ct_pr"] += 1
current_dict[contributor_login]["last_pr_date"] = pull["created_at"]
else:
current_dict[contributor_login] = {
"avatar_url": pull_data['user']["avatar_url"],
"total_changes": total_changes,
"ct_pull": 1,
"ct_pr": 1,
"last_pr_date": pull["created_at"]
}

Expand Down Expand Up @@ -457,6 +457,163 @@ def create_reviewers_table_with_graph(reviewers_stats, user_names, table_name):
return html_content


def create_contributors_table_with_graph(contributors_stats, user_names, table_name):
"""
Creates an HTML table with contributors statistics and graphs.
Args:
contributors_stats (dict): A dictionary containing contributors statistics.
The keys are contributors names and the values are dictionaries
containing the following keys:
- 'avatar_url' (str): The URL of the contributor's avatar image.
- 'total_changes' (int): The number of line changes by the contributor.
- 'ct_pr' (int): The number of reviews finished by the contributor.
- 'last_pr_date' (str): The date of the last pr by the contributor.
user_names (dict): A dictionary mapping contributors names to their corresponding user names.
table_name (str): The ID of the HTML table.
Returns:
str: The HTML content of the table with contributors statistics and graphs.
style sheet for the table, copy into css file:
<style>
table {{
font-family: Arial, sans-serif;
border-collapse: collapse;
width: 100%;
}}
th, td {{
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}}
tr:nth-child(even) {{
background-color: #f2f2f2;
}}
.progress-bar {{
width: 100%;
height: 20px;
margin: 0;
background-color: #ddd;
border-radius: 5px;
overflow: hidden;
}}
.progress-value-reviews {{
display: block;
height: 100%;
width: 0;
background-color: #2980b9;
border-radius: 5px;
}}
.progress-value-ratio {{
display: block;
height: 100%;
width: 0;
background-color: rgba(47, 64, 95, 0.5); /* Adjusted to 50% transparent */
border-radius: 5px;
}}
</style>
"""

html_content = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Contributors' Stats</title>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.5/css/jquery.dataTables.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.js"></script>
</head>
<body>
<p>
<table id="{table_name}" class="display">
<thead>
<tr>
<th></th>
<th>Contributor</th>
<th>PR Count</th>
<th>Line Changes</th>
<!--<th>Last Review Date</th>-->
</tr>
</thead>
<tbody>
"""
if contributors_stats:
# Find the contributors with the highest number of line_changes
max_total_changes = max(stats['total_changes'] for stats in contributors_stats.values())
max_pr_count = max(stats['ct_pr'] for stats in contributors_stats.values())

# Sort contributors by total change
sorted_contributors = sorted(contributors_stats.items(), key=lambda x: x[1]['total_changes'], reverse=True)

for idx, (contributor, stats) in enumerate(sorted_contributors):
total_changes_bar_len = (stats['total_changes'] / max_total_changes) * 100
ct_pr_bar_len = (stats['ct_pr'] / max_pr_count) * 100

# Add emojis for the first three contributors
medal = ""
if idx == 0:
medal = "🥇"
elif idx == 1:
medal = "🥈"
elif idx == 2:
medal = "🥉"

html_content += f"""
<tr>
<td style=" text-align: center;">{medal}</td>
<td>
<div style="display: flex; align-items: center;">
<div style="width: 40px;">
<img src="{stats['avatar_url']}" width="36" height="36" alt="{contributor}" style="border-radius: 50%;">
</div>
<div>
<div>
<b>{user_names[contributor]}</b> <br>
<a href="https://github.com/{contributor}" target="_blank"><img src="https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png" width="16" height="16" alt="{contributor}">{contributor}</a>
</div>
</div>
</div>
</td>
<td>{stats['ct_pr']}
<div class="progress-bar">
<div class="progress-value-reviews" style="width: {ct_pr_bar_len}%;"></div>
</div>
<td>{stats['total_changes']}
<div class="progress-bar">
<div class="progress-value-reviews" style="width: {total_changes_bar_len}%;"></div>
</div>
</td>
<!--<td>{stats['last_pr_date']}</td>-->
</tr>
"""

html_content += f"""
</tbody>
</table>
Fetched on {current_date.strftime("%Y-%m-%d %H:%M:%S")} UTC
</p>
"""
html_content +=f"""
<script>
$('#{table_name}').DataTable({{
"order": [[3, "desc"]]
}});
</script>
</body>
</html>
"""

return html_content


def print_reviewers_stats(reviewers_stats):
"""
Prints the statistics of the reviewers.
Expand All @@ -481,7 +638,7 @@ def print_contributors_stats(contributors_stats):
None
"""
for contributor, stats in sorted(contributors_stats.items(), key=lambda x: x[1]['total_changes'], reverse=True)[:10]:
print(f"Contributor: {contributor}, Number of PRs: {stats['ct_pull']}, Total Line Change: {stats['total_changes']}, Last PR Date: {stats['last_pr_date']}")
print(f"Contributor: {contributor}, Number of PRs: {stats['ct_pr']}, Total Line Change: {stats['total_changes']}, Last PR Date: {stats['last_pr_date']}")


# Replace with your GitHub repository owner and name
Expand Down Expand Up @@ -529,7 +686,7 @@ def print_contributors_stats(contributors_stats):
print(f"API limit: {limit}, next reset: {datetime.fromtimestamp(reset)}")
print("----------------------------------")
print(f"Fetch pull requests, all-time and after {formatted_date}:")
reviewers_stats, maintainers_stats, reviewers_stats_recent, maintainers_stats_recent, contributors_stats, contributor_maintainers_stats, contributors_stats_recent, contributor_maintainers_stats_recent, ct_pulls = get_pr_stats(owner, repos, branches, maintainers, formatted_date)
reviewers_stats, maintainers_stats, reviewers_stats_recent, maintainers_stats_recent, contributors_stats, contributors_maintainers_stats, contributors_stats_recent, contributors_maintainers_stats_recent, ct_pulls = get_pr_stats(owner, repos, branches, maintainers, formatted_date)
print("----------------------------------")
print("------------ Get User ------------")
print("----------------------------------")
Expand All @@ -539,7 +696,7 @@ def print_contributors_stats(contributors_stats):
+ list(reviewers_stats.keys())
+ list(maintainers_stats.keys())
+ list(contributors_stats.keys())
+ list(contributor_maintainers_stats.keys())
+ list(contributors_maintainers_stats.keys())
)
user_names = {}
for reviewer_login in unique_reviewers:
Expand All @@ -566,9 +723,16 @@ def print_contributors_stats(contributors_stats):
print_reviewers_stats(reviewers_stats)
print("----------------------------------")

print(f"Contributors' Stats, after {formatted_date}:")
print("---------- maintainers -----------")
print_contributors_stats(contributors_maintainers_stats_recent)

print("-------- not maintainers ---------")
print_contributors_stats(contributors_stats_recent)

print(f"Contributors' Stats, all-time:")
print("---------- maintainers -----------")
print_contributors_stats(contributor_maintainers_stats)
print_contributors_stats(contributors_maintainers_stats)

print("-------- not maintainers ---------")
print_contributors_stats(contributors_stats)
Expand All @@ -586,12 +750,21 @@ def print_contributors_stats(contributors_stats):
html_maintainers_stats = create_reviewers_table_with_graph(maintainers_stats, user_names, "maintainers_stats")
html_reviewers_stats = create_reviewers_table_with_graph(reviewers_stats, user_names, "reviewers_stats")

html_contributors_maintainers_stats_recent = create_contributors_table_with_graph(contributors_maintainers_stats_recent, user_names, "contributors_maintainers_stats_recent")
html_contributors_stats_recent = create_contributors_table_with_graph(contributors_stats_recent, user_names, "contributors_stats_recent")
html_contributors_maintainers_stats = create_contributors_table_with_graph(contributors_maintainers_stats, user_names, "contributors_maintainers_stats")
html_contributors_stats = create_contributors_table_with_graph(contributors_stats, user_names, "contributors_stats")

# Save the HTML content to a file named "reviewers_stats_with_graph.html"
home_directory = os.path.expanduser( '~' )
filename_maintainers_stats_recent = os.path.join(home_directory, 'reviews', 'maintainers_stats_recent.html')
filename_maintainers_stats_recent = os.path.join(home_directory, 'reviews', 'reviewers_maintainers_stats_recent.html')
filename_reviewers_stats_recent = os.path.join(home_directory, 'reviews', 'reviewers_stats_recent.html')
filename_maintainers_stats = os.path.join(home_directory, 'reviews', 'maintainers_stats.html')
filename_maintainers_stats = os.path.join(home_directory, 'reviews', 'reviewers_maintainers_stats.html')
filename_reviewers_stats = os.path.join(home_directory, 'reviews', 'reviewers_stats.html')
filename_contributors_maintainers_stats_recent = os.path.join(home_directory, 'reviews', 'contributors_maintainers_stats_recent.html')
filename_contributors_stats_recent = os.path.join(home_directory, 'reviews', 'contributors_stats_recent.html')
filename_contributors_maintainers_stats = os.path.join(home_directory, 'reviews', 'contributors_maintainers_stats.html')
filename_contributors_stats = os.path.join(home_directory, 'reviews', 'contributors_stats.html')
os.makedirs(os.path.dirname(filename_maintainers_stats_recent), exist_ok=True)

with open(filename_maintainers_stats_recent, 'w') as file:
Expand All @@ -609,3 +782,19 @@ def print_contributors_stats(contributors_stats):
with open(filename_reviewers_stats, 'w') as file:
file.write(html_reviewers_stats)
print(f"HTML file {filename_reviewers_stats} has been created.")

with open(filename_contributors_maintainers_stats_recent, 'w') as file:
file.write(html_contributors_maintainers_stats_recent)
print(f"HTML file {filename_contributors_maintainers_stats_recent} has been created.")

with open(filename_contributors_stats_recent, 'w') as file:
file.write(html_contributors_stats_recent)
print(f"HTML file {filename_contributors_stats_recent} has been created.")

with open(filename_contributors_maintainers_stats, 'w') as file:
file.write(html_contributors_maintainers_stats)
print(f"HTML file {filename_contributors_maintainers_stats} has been created.")

with open(filename_contributors_stats, 'w') as file:
file.write(html_contributors_stats)
print(f"HTML file {filename_contributors_stats} has been created.")
30 changes: 17 additions & 13 deletions make_help_scripts/deploy_defines
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,28 @@ base_dir="$(dirname "$script_base_dir")"
base_branch="master"
build_dir="_build"

# reviewer stats
reviewer_stats_files=("maintainers_stats_recent.html"
# pr stats
pr_stats_files=("reviewers_maintainers_stats_recent.html"
"reviewers_stats_recent.html"
"maintainers_stats.html"
"reviewers_stats.html")
reviewer_stats_cache_folder="reviews"
reviewer_stats_target_folder="./doc/acknowledgements"
"reviewers_maintainers_stats.html"
"reviewers_stats.html",
"contributors_maintainers_stats_recent.html"
"contributors_stats_recent.html"
"contributors_maintainers_stats.html"
"contributors_stats.html")
pr_stats_cache_folder="reviews"
pr_stats_target_folder="./doc/acknowledgements"

add_reviewer_stats_file () {
for reviewer_stats_filename in "${reviewer_stats_files[@]}"; do
ORIGFILE="$HOME/$reviewer_stats_cache_folder/$reviewer_stats_filename"
add_pr_stats_file () {
for pr_stats_filename in "${pr_stats_files[@]}"; do
ORIGFILE="$HOME/$pr_stats_cache_folder/$pr_stats_filename"
# echo $ORIGFILE
if test -f "$ORIGFILE"; then
echo "Copy reviewer stats file '${reviewer_stats_filename}' to '${reviewer_stats_target_folder}'"
cp ${ORIGFILE} ${reviewer_stats_target_folder}
echo "Copy PR stats file '${pr_stats_filename}' to '${pr_stats_target_folder}'"
cp ${ORIGFILE} ${pr_stats_target_folder}
else
echo "Create empty reviewer stats file '${reviewer_stats_filename}' in '${reviewer_stats_target_folder}'"
echo "<i>No review statistics available yet.</i>" > ${reviewer_stats_target_folder}/${reviewer_stats_filename}
echo "Create empty PR stats file '${pr_stats_filename}' in '${pr_stats_target_folder}'"
echo "<i>No pr statistics available yet.</i>" > ${pr_stats_target_folder}/${pr_stats_filename}
fi
done
}
Expand Down

0 comments on commit 1801464

Please sign in to comment.