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