| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990 | import argparsefrom datetime import datetime, timedeltaimport osimport reimport sysfrom matplotlib import pyplotimport matplotlib.dates as mdatesimport numpyimport pandasimport yamldef parse_args():    parser = argparse.ArgumentParser()    parser.add_argument("input", help="Path to the input YAML file. \"-\" means stdin.")    parser.add_argument("-o", "--output", help="Output directory. If empty, display the plots.")    parser.add_argument("-f", "--format", choices=("png", "svg"), default="png",                        help="Output format")    parser.add_argument("--tick-days", type=int, default=7, help="Ticks interval in days.")    args = parser.parse_args()    return argsdef parse_input(file):    yaml.reader.Reader.NON_PRINTABLE = re.compile(r"(?!x)x")    try:        loader = yaml.CLoader    except AttributeError:        print("Warning: failed to import yaml.CLoader, falling back to slow yaml.Loader")        loader = yaml.Loader    try:        if file != "-":            with open(file) as fin:                return yaml.load(fin, Loader=loader)        else:            return yaml.load(sys.stdin, Loader=loader)    except (UnicodeEncodeError, yaml.reader.ReaderError) as e:        print("\nInvalid unicode in the input: %s\nPlease filter it through "              "fix_yaml_unicode.py" % e)        sys.exit(1)def plot_churn(name, data, url, beginTime, endTime, output, fmt, tick_interval):    days, adds, dels = data["days"], data["additions"], data["removals"]    dates = [beginTime + timedelta(days=d) for d in days]    df = pandas.DataFrame(data=list(zip(adds, dels)),                          index=dates,                          columns=("additions", "removals"))    df["removals"] = -df["removals"]    df = df.reindex(pandas.date_range(beginTime, endTime, freq="D"))    effective = df["additions"] + df["removals"]    effective = effective.cumsum()    effective.fillna(method="ffill", inplace=True)    scale = numpy.maximum(df.max(), -df.min()).max()    effective = effective / effective.max() * scale    pyplot.figure(figsize=(16, 9))    for spine in pyplot.gca().spines.values():        spine.set_visible(False)    pyplot.gca().xaxis.set_major_locator(mdates.DayLocator(interval=tick_interval))    pyplot.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d"))    pyplot.tick_params(top="off", bottom="off", left="off", right="off",                       labelleft="off", labelbottom="on")    pyplot.bar(df.index, df["additions"], label="additions")    pyplot.bar(df.index, df["removals"], label="removals")    pyplot.plot(df.index, effective, "black", label="effective")    pyplot.xticks(rotation="vertical")    pyplot.legend(loc=2, fontsize=18)    pyplot.title("%s churn plot, %s" % (name, url), fontsize=24)    if not output:        pyplot.show()    else:        os.makedirs(output, exist_ok=True)        pyplot.savefig(os.path.join(output, name.replace("/", "_") + "." + fmt),                       bbox_inches="tight", transparent=True)def main():    args = parse_args()    data = parse_input(args.input)    beginTime, endTime = (datetime.fromtimestamp(data["hercules"][t])                          for t in ("begin_unix_time", "end_unix_time"))    for key, val in data["ChurnAnalysis"].items():        plot_churn(key, val, data["hercules"]["repository"], beginTime, endTime,                   args.output, args.format, args.tick_days)if __name__ == "__main__":    sys.exit(main())
 |