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