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