xref: /openbmc/openbmc-tools/tof-voters/libvoters/subcmd/analyze-commits.py (revision ed5643f621d2fe4dc243bcff0005e97f9656b045)
1215c1c3bSPatrick Williams#!/usr/bin/python3
2215c1c3bSPatrick Williams
3215c1c3bSPatrick Williamsimport argparse
4215c1c3bSPatrick Williamsimport json
5215c1c3bSPatrick Williamsimport os
6215c1c3bSPatrick Williamsimport re
7215c1c3bSPatrick Williamsfrom collections import defaultdict
856acf685Snitroglycerinefrom typing import Dict
9215c1c3bSPatrick Williams
10a3db66b3SPatrick Williamsimport libvoters.acceptable as acceptable
11*ed5643f6SAndrew Jefferyfrom libvoters import UserChanges, changes_factory
12a3db66b3SPatrick Williamsfrom libvoters.time import TimeOfDay, timestamp
13a3db66b3SPatrick Williams
14215c1c3bSPatrick Williams
15215c1c3bSPatrick Williamsclass subcmd:
16215c1c3bSPatrick Williams    def __init__(self, parser: argparse._SubParsersAction) -> None:
17215c1c3bSPatrick Williams        p = parser.add_parser(
18215c1c3bSPatrick Williams            "analyze-commits", help="Determine points for commits"
19215c1c3bSPatrick Williams        )
20215c1c3bSPatrick Williams
21215c1c3bSPatrick Williams        p.add_argument(
22215c1c3bSPatrick Williams            "--before",
23215c1c3bSPatrick Williams            "-b",
24215c1c3bSPatrick Williams            help="Before timestamp (YYYY-MM-DD)",
25215c1c3bSPatrick Williams            required=True,
26215c1c3bSPatrick Williams        )
27215c1c3bSPatrick Williams        p.add_argument(
28215c1c3bSPatrick Williams            "--after",
29215c1c3bSPatrick Williams            "-a",
30215c1c3bSPatrick Williams            help="After timestamp (YYYY-MM-DD)",
31215c1c3bSPatrick Williams            required=True,
32215c1c3bSPatrick Williams        )
33215c1c3bSPatrick Williams
34215c1c3bSPatrick Williams        p.set_defaults(cmd=self)
35215c1c3bSPatrick Williams
36215c1c3bSPatrick Williams    def run(self, args: argparse.Namespace) -> int:
37215c1c3bSPatrick Williams        before = timestamp(args.before, TimeOfDay.AM)
38215c1c3bSPatrick Williams        after = timestamp(args.after, TimeOfDay.PM)
39215c1c3bSPatrick Williams
40*ed5643f6SAndrew Jeffery        changes_per_user: Dict[str, UserChanges] = defaultdict(changes_factory)
41215c1c3bSPatrick Williams
42215c1c3bSPatrick Williams        for f in sorted(os.listdir(args.dir)):
43215c1c3bSPatrick Williams            path = os.path.join(args.dir, f)
44215c1c3bSPatrick Williams            if not os.path.isfile(path):
45215c1c3bSPatrick Williams                continue
46215c1c3bSPatrick Williams
47a3db66b3SPatrick Williams            if not re.match(r"[0-9]*\.json", f):
48215c1c3bSPatrick Williams                continue
49215c1c3bSPatrick Williams
50215c1c3bSPatrick Williams            with open(path, "r") as file:
51215c1c3bSPatrick Williams                data = json.load(file)
52215c1c3bSPatrick Williams
53215c1c3bSPatrick Williams            if data["status"] != "MERGED":
54215c1c3bSPatrick Williams                continue
55215c1c3bSPatrick Williams
56215c1c3bSPatrick Williams            merged_at = 0
57215c1c3bSPatrick Williams            for c in data["comments"]:
58215c1c3bSPatrick Williams                if "timestamp" not in c:
59215c1c3bSPatrick Williams                    continue
60215c1c3bSPatrick Williams                if "message" in c and re.match(
61215c1c3bSPatrick Williams                    "Change has been successfully .*", c["message"]
62215c1c3bSPatrick Williams                ):
63215c1c3bSPatrick Williams                    merged_at = c["timestamp"]
64215c1c3bSPatrick Williams
65215c1c3bSPatrick Williams            if merged_at == 0:
66215c1c3bSPatrick Williams                raise RuntimeError(f"Missing merge timestamp on {f}")
67215c1c3bSPatrick Williams
68215c1c3bSPatrick Williams            if merged_at > before or merged_at < after:
69215c1c3bSPatrick Williams                continue
70215c1c3bSPatrick Williams
71215c1c3bSPatrick Williams            project = data["project"]
72215c1c3bSPatrick Williams            id_number = data["number"]
73*ed5643f6SAndrew Jeffery            username = data["owner"]["username"]
74215c1c3bSPatrick Williams
75215c1c3bSPatrick Williams            if not acceptable.project(project):
76215c1c3bSPatrick Williams                print("Rejected project:", project, id_number)
77215c1c3bSPatrick Williams                continue
78215c1c3bSPatrick Williams
79215c1c3bSPatrick Williams            changes = 0
80215c1c3bSPatrick Williams            touched_files = []
81215c1c3bSPatrick Williams            for file_data in sorted(
82215c1c3bSPatrick Williams                data["patchSets"], key=lambda x: x["number"]
83215c1c3bSPatrick Williams            )[-1][
84215c1c3bSPatrick Williams                "files"
85215c1c3bSPatrick Williams            ]:  # type: Dict[str, Any]
86215c1c3bSPatrick Williams                if not acceptable.file(project, file_data["file"]):
87215c1c3bSPatrick Williams                    continue
88215c1c3bSPatrick Williams                changes += int(file_data["insertions"]) + abs(
89215c1c3bSPatrick Williams                    int(file_data["deletions"])
90215c1c3bSPatrick Williams                )
91215c1c3bSPatrick Williams                touched_files.append(file_data["file"])
92215c1c3bSPatrick Williams
93215c1c3bSPatrick Williams            if changes < 10:
94215c1c3bSPatrick Williams                print("Rejected for limited changes:", project, id_number)
95215c1c3bSPatrick Williams                continue
96215c1c3bSPatrick Williams
97*ed5643f6SAndrew Jeffery            print(project, id_number, username)
98215c1c3bSPatrick Williams            for f in touched_files:
99215c1c3bSPatrick Williams                print(f"    {f}")
100215c1c3bSPatrick Williams
101*ed5643f6SAndrew Jeffery            user = changes_per_user[username]
102*ed5643f6SAndrew Jeffery            user["name"] = data["owner"]["name"]
103*ed5643f6SAndrew Jeffery            user["email"] = data["owner"]["email"]
104*ed5643f6SAndrew Jeffery            user["changes"].append(id_number)
105215c1c3bSPatrick Williams
106215c1c3bSPatrick Williams        with open(os.path.join(args.dir, "commits.json"), "w") as outfile:
107215c1c3bSPatrick Williams            outfile.write(json.dumps(changes_per_user, indent=4))
108215c1c3bSPatrick Williams
109215c1c3bSPatrick Williams        return 0
110