blob: 4506dbbbc678c40f56b334234e46439203dbdfb6 [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This script downloads information about changes landed in the Fuchsia tree
# and computes the dates at which those changes receive Code, API, and
# Testability reviews. It produces four fields into a CSV file:
#
# * Change-ID
# * Time at which the change first received Code-Review+2 and API-Review+1.
# If a change never received API-Review+1, the time the change first received
# Code-Review+2 is used. This is an approximation of when the change is first
# eligible to receive a Testability Review.
# * Time at which the change first received a Testability-Review vote, either
# +1 or -1.
# * Email of the first reviewer to set Testability-Review
#
# It caches information from Gerrit into //local/change_id_cache/.
import argparse
import datetime
import json
import os
import sys
import urllib
FUCHSIA_DIR = os.path.abspath(os.path.join(__file__, os.pardir, os.pardir))
def parse_date_from_gerrit(date_str):
# Gerrit returns timestamps with 9 digits of precision, but the %f
# formatter only expects 6 digits of precision, so truncate to parse.
return datetime.datetime.strptime(date_str[:-3], "%Y-%m-%d %H:%M:%S.%f")
def cache_dir():
dirname = os.path.join(FUCHSIA_DIR, "local", "change_id_cache")
if not os.path.exists(dirname):
os.makedirs(dirname)
return dirname
def cache_filename(c):
return os.path.join(cache_dir(), c)
def cache_change_data(c):
filename = cache_filename(c)
if os.path.exists(filename):
return
print("Data for %s not found in cache, fetching from gerrit" % c)
url = "https://fuchsia-review.googlesource.com/changes/%s/detail" % c
r = urllib.request.urlopen(url)
if r.status != 200:
print("Gerrit returned error for %s: %d" % (c, r.status_code))
return
response_json = r.read().decode("utf-8")[5:] # skip preamble
data = json.loads(response_json)
with open(cache_filename(c), "w") as f:
json.dump(data, f, indent=2)
def read_change_data(c):
with open(cache_filename(c)) as f:
return json.load(f)
def process_change(c):
cache_change_data(c)
data = read_change_data(c)
testability_date = None
code_review_date = None
api_review_date = None
for message in data["messages"]:
m = message["message"]
date = parse_date_from_gerrit(message["date"])
if testability_date is None:
if "Testability-Review+1" in m or "Testability-Review-1" in m:
testability_reviewer = message["real_author"]["email"]
testability_date = parse_date_from_gerrit(message["date"])
if "Code-Review+2" in message["message"]:
if code_review_date is None:
code_review_date = date
if "API-Review+1" in message["message"]:
if api_review_date is None:
api_review_date = date
if testability_date is None or code_review_date is None:
return
last_approval_date = code_review_date
if api_review_date is not None:
if api_review_date > last_approval_date:
last_approval_date = api_review_date
return (c, testability_date, last_approval_date, testability_reviewer)
header = "ChangeID, First Testability-Review Date, First {Code + API}-Review date, Testability Reviewer"
def main():
parser = argparse.ArgumentParser(
description="Compute Testability-Review stats for set of changes."
)
parser.add_argument(
"--output", "-o", default="output.txt", help="Name of output file"
)
parser.add_argument(
"changes",
default="last_week_change_ids.txt",
help="File containing Change-Id values to analyze",
)
args = parser.parse_args()
with open(args.changes) as f:
changes = f.readlines()
with open(args.output, "w") as out:
out.write(header + "\n")
for c in changes:
c = c.strip()
try:
out.write("%s, %s, %s, %s\n" % process_change(c))
except KeyboardInterrupt:
raise
except:
sys.stderr.write("Could not process %s, skipping\n" % c)
if __name__ == "__main__":
sys.exit(main())