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