xref: /openbmc/openbmc-tools/tof-voters/libvoters/subcmd/analyze-reviews.py (revision 9217099cadfc744ff393825564f46a0420d19f1e)
1#!/usr/bin/python3
2
3import argparse
4import json
5import os
6import re
7from collections import defaultdict
8from typing import Dict
9
10import libvoters.acceptable as acceptable
11from libvoters import (
12    UserChanges,
13    UserComments,
14    changes_factory,
15    comments_factory,
16)
17from libvoters.time import TimeOfDay, timestamp
18
19
20class subcmd:
21    def __init__(self, parser: argparse._SubParsersAction) -> None:
22        p = parser.add_parser(
23            "analyze-reviews", help="Determine points for reviews"
24        )
25
26        p.add_argument(
27            "--before",
28            "-b",
29            help="Before timestamp (YYYY-MM-DD)",
30            required=True,
31        )
32        p.add_argument(
33            "--after",
34            "-a",
35            help="After timestamp (YYYY-MM-DD)",
36            required=True,
37        )
38
39        p.set_defaults(cmd=self)
40
41    def run(self, args: argparse.Namespace) -> int:
42        before = timestamp(args.before, TimeOfDay.AM)
43        after = timestamp(args.after, TimeOfDay.PM)
44
45        changes_per_user: Dict[str, UserChanges] = defaultdict(changes_factory)
46
47        for f in sorted(os.listdir(args.dir)):
48            path = os.path.join(args.dir, f)
49            if not os.path.isfile(path):
50                continue
51
52            if not re.match(r"[0-9]*\.json", f):
53                continue
54
55            with open(path, "r") as file:
56                data = json.load(file)
57
58            project = data["project"]
59            id_number = data["number"]
60            author = data["owner"]["username"]
61
62            if not acceptable.project(project):
63                print("Rejected project:", project, id_number)
64
65            comments_per_user: Dict[str, UserComments] = defaultdict(
66                comments_factory
67            )
68
69            for patch_set in data["patchSets"]:
70                created_on = data["createdOn"]
71
72                if created_on > before or created_on < after:
73                    continue
74
75                if "comments" not in patch_set:
76                    continue
77
78                for comment in patch_set["comments"]:
79                    reviewer = comment["reviewer"]["username"]
80
81                    if reviewer == author:
82                        continue
83                    if not acceptable.file(project, comment["file"]):
84                        continue
85
86                    user = comments_per_user[reviewer]
87                    user["name"] = comment["reviewer"]["name"]
88                    # We actually have a case where a reviewer does not have an email recorded[1]:
89                    #
90                    # [1]: https://gerrit.openbmc.org/c/openbmc/phosphor-pid-control/+/60303/comment/ceff60b9_9d2debe0/
91                    #
92                    # {"file": "conf.hpp",
93                    #  "line": 39,
94                    #  "reviewer": {"name": "Akshat Jain", "username": "AkshatZen"},
95                    #  "message": "If we design SensorInput as base class and have derived ..."}
96                    # Traceback (most recent call last):
97                    #   File "/mnt/host/andrew/home/andrew/src/openbmc/openbmc-tools/tof-voters/./voters", line 7, in <module>
98                    #     sys.exit(main())
99                    #              ^^^^^^
100                    #   File "/mnt/host/andrew/home/andrew/src/openbmc/openbmc-tools/tof-voters/libvoters/entry_point.py", line 33, in main
101                    #     return int(args.cmd.run(args))
102                    #                ^^^^^^^^^^^^^^^^^^
103                    #   File "/mnt/host/andrew/home/andrew/src/openbmc/openbmc-tools/tof-voters/libvoters/subcmd/analyze-reviews.py", line 82, in run
104                    #     user["email"] = comment["reviewer"]["email"]
105                    #                     ~~~~~~~~~~~~~~~~~~~^^^^^^^^^
106                    # KeyError: 'email'
107                    if "email" in comment["reviewer"]:
108                        user["email"] = comment["reviewer"]["email"]
109                    user["comments"] += 1
110
111            print(project, id_number)
112            for username, review in comments_per_user.items():
113                if review["comments"] < 3:
114                    continue
115                print("    ", user, review["comments"])
116                user = changes_per_user[username]
117                user["name"] = review["name"]
118                user["email"] = review["email"]
119                user["changes"].append(id_number)
120
121        with open(os.path.join(args.dir, "reviews.json"), "w") as outfile:
122            outfile.write(json.dumps(changes_per_user, indent=4))
123
124        return 0
125