burndown.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import contextlib
  2. import io
  3. import json
  4. import sys
  5. import tqdm
  6. from labours.burndown import load_burndown
  7. from labours.plotting import apply_plot_style, deploy_plot, get_plot_path, import_pyplot
  8. from labours.utils import default_json, parse_date
  9. def plot_burndown(args, target, name, matrix, date_range_sampling, labels, granularity,
  10. sampling, resample):
  11. if args.output and args.output.endswith(".json"):
  12. data = locals().copy()
  13. del data["args"]
  14. data["type"] = "burndown"
  15. if args.mode == "project" and target == "project":
  16. output = args.output
  17. else:
  18. if target == "project":
  19. name = "project"
  20. output = get_plot_path(args.output, name)
  21. with open(output, "w") as fout:
  22. json.dump(data, fout, sort_keys=True, default=default_json)
  23. return
  24. matplotlib, pyplot = import_pyplot(args.backend, args.style)
  25. pyplot.stackplot(date_range_sampling, matrix, labels=labels)
  26. if args.relative:
  27. for i in range(matrix.shape[1]):
  28. matrix[:, i] /= matrix[:, i].sum()
  29. pyplot.ylim(0, 1)
  30. legend_loc = 3
  31. else:
  32. legend_loc = 2
  33. legend = pyplot.legend(loc=legend_loc, fontsize=args.font_size)
  34. pyplot.ylabel("Lines of code")
  35. pyplot.xlabel("Time")
  36. apply_plot_style(pyplot.gcf(), pyplot.gca(), legend, args.background,
  37. args.font_size, args.size)
  38. pyplot.xlim(parse_date(args.start_date, date_range_sampling[0]),
  39. parse_date(args.end_date, date_range_sampling[-1]))
  40. locator = pyplot.gca().xaxis.get_major_locator()
  41. # set the optimal xticks locator
  42. if "M" not in resample:
  43. pyplot.gca().xaxis.set_major_locator(matplotlib.dates.YearLocator())
  44. locs = pyplot.gca().get_xticks().tolist()
  45. if len(locs) >= 16:
  46. pyplot.gca().xaxis.set_major_locator(matplotlib.dates.YearLocator())
  47. locs = pyplot.gca().get_xticks().tolist()
  48. if len(locs) >= 16:
  49. pyplot.gca().xaxis.set_major_locator(locator)
  50. if locs[0] < pyplot.xlim()[0]:
  51. del locs[0]
  52. endindex = -1
  53. if len(locs) >= 2 and pyplot.xlim()[1] - locs[-1] > (locs[-1] - locs[-2]) / 2:
  54. locs.append(pyplot.xlim()[1])
  55. endindex = len(locs) - 1
  56. startindex = -1
  57. if len(locs) >= 2 and locs[0] - pyplot.xlim()[0] > (locs[1] - locs[0]) / 2:
  58. locs.append(pyplot.xlim()[0])
  59. startindex = len(locs) - 1
  60. pyplot.gca().set_xticks(locs)
  61. # hacking time!
  62. labels = pyplot.gca().get_xticklabels()
  63. if startindex >= 0:
  64. labels[startindex].set_text(date_range_sampling[0].date())
  65. labels[startindex].set_text = lambda _: None
  66. labels[startindex].set_rotation(30)
  67. labels[startindex].set_ha("right")
  68. if endindex >= 0:
  69. labels[endindex].set_text(date_range_sampling[-1].date())
  70. labels[endindex].set_text = lambda _: None
  71. labels[endindex].set_rotation(30)
  72. labels[endindex].set_ha("right")
  73. title = "%s %d x %d (granularity %d, sampling %d)" % \
  74. ((name,) + matrix.shape + (granularity, sampling))
  75. output = args.output
  76. if output:
  77. if args.mode == "project" and target == "project":
  78. output = args.output
  79. else:
  80. if target == "project":
  81. name = "project"
  82. output = get_plot_path(args.output, name)
  83. deploy_plot(title, output, args.background)
  84. def plot_many_burndown(args, target, header, parts):
  85. if not args.output:
  86. print("Warning: output not set, showing %d plots." % len(parts))
  87. stdout = io.StringIO()
  88. for name, matrix in tqdm.tqdm(parts):
  89. with contextlib.redirect_stdout(stdout):
  90. plot_burndown(args, target, *load_burndown(header, name, matrix, args.resample))
  91. sys.stdout.write(stdout.getvalue())