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