diff --git a/estimage/visualize/completion.py b/estimage/visualize/completion.py new file mode 100644 index 0000000..de48e03 --- /dev/null +++ b/estimage/visualize/completion.py @@ -0,0 +1,64 @@ +import typing +import datetime + +import numpy as np + +from . import utils +from .. import utilities + + +class MPLCompletionPlot: + width = 2 + + def __init__(self, todo_estimation, perweek_velocity_mean_std): + dom, pdf = todo_estimation.divide_by_gauss_pdf(200, * perweek_velocity_mean_std) + self.distribution = utilities.get_random_variable(dom, pdf) + DAYS_IN_WEEK = 7 + lower_bound = self.distribution.ppf(0.02) + upper_bound = self.distribution.ppf(0.98) + self.dom = np.arange( + int(np.floor(lower_bound * DAYS_IN_WEEK)), + int(np.ceil(upper_bound * DAYS_IN_WEEK)) + 1) + self.probs = self.distribution.cdf(self.dom / DAYS_IN_WEEK) + self.probs -= self.probs[0] + self.probs /= self.probs[-1] + self.probs *= 100 + + def _calculate_plan_wrt_day(self, ax, start): + ax.plot(self.dom, self.probs, color="green", + linewidth=self.width, label="prob of completion") + utils.x_axis_weeks_and_months(ax, start, start + utils.ONE_DAY * self.dom[-1]) + + def get_figure(self): + plt = utils.get_standard_pyplot() + + fig, ax = plt.subplots() + ax.grid(True) + + self._calculate_plan_wrt_day(ax, datetime.datetime.today()) + ax.legend(loc="upper left") + + ax.set_ylabel("percents") + + return fig + + def get_small_figure(self): + plt = utils.get_standard_pyplot() + + fig, ax = plt.subplots() + + self._prepare_plots() + self._plot_prepared_arrays(ax) + self._show_plan(ax) + self._show_today(ax) + + ax.set_axis_off() + fig.subplots_adjust(0, 0, 1, 1) + + return fig + + def plot_stuff(self): + plt = utils.get_standard_pyplot() + self.get_figure() + + plt.show() diff --git a/estimage/visualize/utils.py b/estimage/visualize/utils.py index 849bbb4..363f191 100644 --- a/estimage/visualize/utils.py +++ b/estimage/visualize/utils.py @@ -29,10 +29,10 @@ def x_axis_weeks_and_months(ax, start, end): ax.set_xlabel("time / weeks") -def set_week_ticks_to_mondays(ticks, start, end): - week_index = 0 +def set_week_ticks_to_mondays(ticks, start, end, week_index_start=0): + week_index = week_index_start if start.weekday != 0: - week_index = 1 + week_index = week_index_start + 1 for day in range((end - start).days + 1): if (start + day * ONE_DAY).weekday() == 0: ticks[day] = str(week_index) diff --git a/estimage/webapp/vis/routes.py b/estimage/webapp/vis/routes.py index f4c76b3..61df5c8 100644 --- a/estimage/webapp/vis/routes.py +++ b/estimage/webapp/vis/routes.py @@ -4,13 +4,14 @@ import flask import flask_login +import numpy as np from . import bp from .. import web_utils from ... import utilities from ... import simpledata as webdata from ... import history -from ...visualize import utils, velocity, burndown, pert +from ...visualize import utils, velocity, burndown, pert, completion import matplotlib @@ -52,6 +53,38 @@ def get_pert_in_figure(estimation, task_name): return fig +@bp.route('/completion.svg') +@flask_login.login_required +def visualize_completion(): + user = flask_login.current_user + user_id = user.get_id() + all_events = webdata.EventManager() + all_events.load() + + all_targets_by_id, model = web_utils.get_all_tasks_by_id_and_user_model("retro", user_id) + all_targets = list(all_targets_by_id.values()) + targets_tree_without_duplicates = utilities.reduce_subsets_from_sets([t for t in all_targets if t.tier == 0]) + model = web_utils.get_user_model(user_id, targets_tree_without_duplicates) + todo = model.remaining_point_estimate + + start, end = flask.current_app.config["RETROSPECTIVE_PERIOD"] + aggregation = history.Aggregation.from_targets(targets_tree_without_duplicates, start, end) + aggregation.process_event_manager(all_events) + + velocity_array = aggregation.get_velocity_array() + velocity_array = velocity_array[velocity_array > 0] * 7 + velocity_mean = velocity_array.mean() + velocity_array = np.convolve(velocity_array, np.ones(7) / 7, "same") + velocity_stdev = velocity_array.var()**0.5 + + matplotlib.use("svg") + print(f"{velocity_mean=}, {velocity_stdev=}") + print(f"{velocity_array=}") + fig = completion.MPLCompletionPlot(todo, (velocity_mean, velocity_stdev)).get_figure() + fig.set_size_inches(* NORMAL_FIGURE_SIZE) + return send_figure_as_svg(fig, "completion.svg") + + @bp.route('/-velocity.svg') @flask_login.login_required def visualize_velocity(epic_name):