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