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 UserChanges, changes_factory 12from libvoters.time import TimeOfDay, timestamp 13 14 15class subcmd: 16 def __init__(self, parser: argparse._SubParsersAction) -> None: 17 p = parser.add_parser( 18 "analyze-commits", help="Determine points for commits" 19 ) 20 21 p.add_argument( 22 "--before", 23 "-b", 24 help="Before timestamp (YYYY-MM-DD)", 25 required=True, 26 ) 27 p.add_argument( 28 "--after", 29 "-a", 30 help="After timestamp (YYYY-MM-DD)", 31 required=True, 32 ) 33 34 p.set_defaults(cmd=self) 35 36 def run(self, args: argparse.Namespace) -> int: 37 before = timestamp(args.before, TimeOfDay.AM) 38 after = timestamp(args.after, TimeOfDay.PM) 39 40 changes_per_user: Dict[str, UserChanges] = defaultdict(changes_factory) 41 42 for f in sorted(os.listdir(args.dir)): 43 path = os.path.join(args.dir, f) 44 if not os.path.isfile(path): 45 continue 46 47 if not re.match(r"[0-9]*\.json", f): 48 continue 49 50 with open(path, "r") as file: 51 data = json.load(file) 52 53 if data["status"] != "MERGED": 54 continue 55 56 merged_at = 0 57 for c in data["comments"]: 58 if "timestamp" not in c: 59 continue 60 if "message" in c and re.match( 61 "Change has been successfully .*", c["message"] 62 ): 63 merged_at = c["timestamp"] 64 65 if merged_at == 0: 66 raise RuntimeError(f"Missing merge timestamp on {f}") 67 68 if merged_at > before or merged_at < after: 69 continue 70 71 project = data["project"] 72 id_number = data["number"] 73 username = data["owner"]["username"] 74 75 if not acceptable.project(project): 76 print("Rejected project:", project, id_number) 77 continue 78 79 changes = 0 80 touched_files = [] 81 for file_data in sorted( 82 data["patchSets"], key=lambda x: x["number"] 83 )[-1][ 84 "files" 85 ]: # type: Dict[str, Any] 86 if not acceptable.file(project, file_data["file"]): 87 continue 88 changes += int(file_data["insertions"]) + abs( 89 int(file_data["deletions"]) 90 ) 91 touched_files.append(file_data["file"]) 92 93 if changes < 10: 94 print("Rejected for limited changes:", project, id_number) 95 continue 96 97 print(project, id_number, username) 98 for f in touched_files: 99 print(f" {f}") 100 101 user = changes_per_user[username] 102 user["name"] = data["owner"]["name"] 103 user["email"] = data["owner"]["email"] 104 user["changes"].append(id_number) 105 106 with open(os.path.join(args.dir, "commits.json"), "w") as outfile: 107 outfile.write(json.dumps(changes_per_user, indent=4)) 108 109 return 0 110