Files
test/tools/triage/weekly_report.py
Julian Eisel 13e93ac7f1 Tools: Don't include commits authored by others in weekly report script
So far this would include commits committed by the given user, but
authored by someone else. Unfotunately we can't use email addresses to
filter these out, since we can't get the email addresses associated with
an account from gitea, or do a user lookup by email. In my testing the
commit author email and the publicly visible account email would
mismatch in most cases.
2024-02-26 14:29:32 +01:00

297 lines
11 KiB
Python

#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
"""
# Generates the weekly report containing information on:
# - Pull Requests created,
# - Pull Requests revised,
# - Issues closed,
# - Issues confirmed,
# - Commits,
Example usage:
python ./weekly_report.py --username mano-wii
"""
import argparse
import datetime
import json
import re
from gitea_utils import gitea_json_activities_get, gitea_json_issue_get, gitea_json_issue_events_filter, gitea_user_get, git_username_detect
def argparse_create():
parser = argparse.ArgumentParser(
description="Generate Weekly Report",
epilog="This script is typically used to help write weekly reports")
parser.add_argument(
"--username",
dest="username",
metavar='USERNAME',
type=str,
required=False,
help="")
parser.add_argument(
"--weeks-ago",
dest="weeks_ago",
type=int,
default=1,
help="Determine which week the report should be generated for. 0 means the current week. "
"The default is 1, to create a report for the previous week.")
parser.add_argument(
"-v",
"--verbose",
action="store_true",
help="increase output verbosity")
return parser
def report_personal_weekly_get(username, start, verbose=True):
data_cache = {}
def gitea_json_issue_get_cached(issue_fullname):
if issue_fullname not in data_cache:
data_cache[issue_fullname] = gitea_json_issue_get(issue_fullname)
return data_cache[issue_fullname]
pulls_closed = set()
pulls_commented = set()
pulls_created = set()
issues_closed = set()
issues_commented = set()
issues_created = set()
pulls_reviewed = []
issues_confirmed = []
issues_needing_user_info = []
issues_needing_developer_info = []
issues_fixed = []
issues_duplicated = []
issues_archived = []
commits_main = []
user_data = gitea_user_get(username)
for i in range(7):
date_curr = start + datetime.timedelta(days=i)
date_curr_str = date_curr.strftime("%Y-%m-%d")
print(f"Requesting activity of {date_curr_str}", end="\r", flush=True)
for activity in gitea_json_activities_get(username, date_curr_str):
op_type = activity["op_type"]
if op_type == "close_issue":
fullname = activity["repo"]["full_name"] + "/issues/" + activity["content"].split('|')[0]
issues_closed.add(fullname)
elif op_type == "comment_issue":
fullname = activity["repo"]["full_name"] + "/issues/" + activity["content"].split('|')[0]
issues_commented.add(fullname)
elif op_type == "create_issue":
fullname = activity["repo"]["full_name"] + "/issues/" + activity["content"].split('|')[0]
issues_created.add(fullname)
elif op_type == "merge_pull_request":
fullname = activity["repo"]["full_name"] + "/pulls/" + activity["content"].split('|')[0]
pulls_closed.add(fullname)
elif op_type == "comment_pull":
fullname = activity["repo"]["full_name"] + "/pulls/" + activity["content"].split('|')[0]
pulls_commented.add(fullname)
elif op_type == "create_pull_request":
fullname = activity["repo"]["full_name"] + "/pulls/" + activity["content"].split('|')[0]
pulls_created.add(fullname)
elif op_type in {"approve_pull_request", "reject_pull_request"}:
fullname = activity["repo"]["full_name"] + "/pulls/" + activity["content"].split('|')[0]
pulls_reviewed.append(fullname)
elif op_type == "commit_repo":
if activity["ref_name"] == "refs/heads/main" and activity["content"] and activity["repo"]["name"] != ".profile":
content_json = json.loads(activity["content"])
repo_fullname = activity["repo"]["full_name"]
for commits in content_json["Commits"]:
# Skip commits that were not made by this user. Using email doesn't seem to
# be possible unfortunately.
if commits["AuthorName"] != user_data["full_name"]:
continue
title = commits["Message"].split('\n', 1)[0]
if title.startswith("Merge branch "):
continue
# Substitute occurrences of "#\d+" with "repo#\d+"
title = re.sub(r"#(\d+)", rf"{repo_fullname}#\1", title)
hash_value = commits["Sha1"][:10]
commits_main.append(f"{title} ({repo_fullname}@{hash_value})")
date_end = date_curr
len_total = len(issues_closed) + len(issues_commented) + len(pulls_commented)
process = 0
for issue in issues_commented:
print(f"[{int(100 * (process / len_total))}%] Checking issue {issue} ", end="\r", flush=True)
process += 1
issue_events = gitea_json_issue_events_filter(issue,
date_start=start,
date_end=date_end,
username=username,
labels={
"Status/Confirmed",
"Status/Needs Information from User",
"Status/Needs Info from Developers"})
for event in issue_events:
label_name = event["label"]["name"]
if label_name == "Status/Confirmed":
issues_confirmed.append(issue)
elif label_name == "Status/Needs Information from User":
issues_needing_user_info.append(issue)
elif label_name == "Status/Needs Info from Developers":
issues_needing_developer_info.append(issue)
for issue in issues_closed:
print(f"[{int(100 * (process / len_total))}%] Checking issue {issue} ", end="\r", flush=True)
process += 1
issue_events = gitea_json_issue_events_filter(issue,
date_start=start,
date_end=date_end,
username=username,
event_type={"close", "commit_ref"},
labels={"Status/Duplicate"})
for event in issue_events:
event_type = event["type"]
if event_type == "commit_ref":
issues_fixed.append(issue)
elif event_type == "label":
issues_duplicated.append(issue)
else:
issues_archived.append(issue)
for pull in pulls_commented:
print(f"[{int(100 * (process / len_total))}%] Checking pull {pull} ", end="\r", flush=True)
process += 1
pull_events = gitea_json_issue_events_filter(pull.replace("pulls", "issues"),
date_start=start,
date_end=date_end,
username=username,
event_type={"comment"})
if pull_events:
pull_data = gitea_json_issue_get_cached(pull)
if pull_data["user"]["login"] != username:
pulls_reviewed.append(pull)
# Print triaging stats
issues_involved = issues_closed | issues_commented | issues_created
print("**Involved in %s reports:** " % len(issues_involved))
print("* Confirmed: %s" % len(issues_confirmed))
print("* Closed as Resolved: %s" % len(issues_fixed))
print("* Closed as Archived: %s" % len(issues_archived))
print("* Closed as Duplicate: %s" % len(issues_duplicated))
print("* Needs Info from User: %s" % len(issues_needing_user_info))
print("* Needs Info from Developers: %s" % len(issues_needing_developer_info))
print("* Actions total: %s" % (len(issues_closed) + len(issues_commented) + len(issues_created)))
print()
# Print review stats
def print_pulls(pulls):
for pull in pulls:
pull_data = gitea_json_issue_get_cached(pull)
title = pull_data["title"]
owner, repo, _, number = pull.split('/')
print(f"* {title} ({owner}/{repo}!{number})")
print("**Review: %s**" % len(pulls_reviewed))
print_pulls(pulls_reviewed)
print()
# Print created diffs
print("**Created Pull Requests: %s**" % len(pulls_created))
print_pulls(pulls_created)
print()
# Print commits
print("**Commits:**")
for commit in commits_main:
print("*", commit)
print()
if verbose:
# Debug
def print_links(issues):
for fullname in issues:
print(f"https://projects.blender.org/{fullname}")
print("Debug:")
print(f"Activities from {start.isoformat()} to {date_end.isoformat()}:")
print()
print("Pull Requests Created:")
print_links(pulls_created)
print("Pull Requests Reviewed:")
print_links(pulls_reviewed)
print("Issues Confirmed:")
print_links(issues_confirmed)
print("Issues Closed as Resolved:")
print_links(issues_fixed)
print("Issues Closed as Archived:")
print_links(issues_closed)
print("Issues Closed as Duplicate:")
print_links(issues_duplicated)
print("Issues Needing Info from User:")
print_links(issues_needing_user_info)
print("Issues Needing Info from Developers:")
print_links(issues_needing_developer_info)
def main() -> None:
# ----------
# Parse Args
args = argparse_create().parse_args()
username = args.username
if not username:
username = git_username_detect()
if not username:
return
# end_date = datetime.datetime(2020, 3, 14)
end_date = datetime.datetime.now() - datetime.timedelta(weeks=(args.weeks_ago - 1))
weekday = end_date.weekday()
# Assuming I am lazy and making this at last moment or even later in worst case
if weekday < 2:
time_delta = 7 + weekday
start_date = end_date - datetime.timedelta(days=time_delta, hours=end_date.hour)
end_date -= datetime.timedelta(days=weekday, hours=end_date.hour)
else:
time_delta = weekday
start_date = end_date - datetime.timedelta(days=time_delta, hours=end_date.hour)
sunday = start_date + datetime.timedelta(days=6)
# week = start_date.isocalendar()[1]
start_date_str = start_date.strftime('%B ') + str(start_date.day)
end_date_str = str(sunday.day) if start_date.month == sunday.month else sunday.strftime('%B ') + str(sunday.day)
print(f"## {start_date_str} - {end_date_str}\n")
report_personal_weekly_get(username, start_date, verbose=args.verbose)
if __name__ == "__main__":
main()
# wait for input to close window
input()