Skip to content

Commit

Permalink
Brought the demo to a functioning state
Browse files Browse the repository at this point in the history
  • Loading branch information
matejak committed Jun 16, 2023
1 parent 38dd9d8 commit 931b3d7
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 34 deletions.
67 changes: 58 additions & 9 deletions estimage/plugins/demo/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import re
import datetime
import dataclasses
import collections
import dateutil.relativedelta
import json

from ... import simpledata, data, persistence
from .. import jira
import flask

from ... import simpledata, data


def load_data():
Expand All @@ -28,17 +25,67 @@ def save_data(what):
class NotToday:
@property
def DDAY_LABEL(self):
return "Week from now"
data = load_data()
day_index = data.get("day_index", 0)
return f"Day {day_index + 1}"

def get_date_of_dday(self):
data = load_data()
day_index = data.get("day_index", 0)
return datetime.datetime.today() + datetime.timedelta(days=day_index)
start = flask.current_app.config["RETROSPECTIVE_PERIOD"][0]
return start + datetime.timedelta(days=day_index)


def start(targets, loader):
start = flask.current_app.config["RETROSPECTIVE_PERIOD"][0]
date = start - datetime.timedelta(days=20)
mgr = simpledata.EventManager()
for t in targets:
evt = data.Event(t.name, "state", date)
evt.value_before = data.State.unknown
evt.value_after = data.State.todo
mgr.add_event(evt)

t.state = data.State.todo
t.save_metadata(loader)
mgr.save()


def begin_target(target, loader, day_index):
start = flask.current_app.config["RETROSPECTIVE_PERIOD"][0]
date = start + datetime.timedelta(days=day_index)
mgr = simpledata.EventManager()
mgr.load()
if len(mgr.get_chronological_task_events_by_type(target.name)["state"]) < 2:
evt = data.Event(target.name, "state", date)
evt.value_before = data.State.todo
evt.value_after = data.State.in_progress
mgr.add_event(evt)
mgr.save()

target.state = data.State.in_progress
target.save_metadata(loader)


def conclude_target(target, loader, day_index):
start = flask.current_app.config["RETROSPECTIVE_PERIOD"][0]
date = start + datetime.timedelta(days=day_index)
mgr = simpledata.EventManager()
mgr.load()
evt = data.Event(target.name, "state", date)
evt.value_before = data.State.in_progress
evt.value_after = data.State.done
mgr.add_event(evt)
mgr.save()

target.state = data.State.done
target.save_metadata(loader)


EXPORTS = dict(
MPLPointPlot="NotToday",
MPLVelocityPlot="NotToday",
MPLCompletionPlot="NotToday",
)


Expand All @@ -51,4 +98,6 @@ def get_date_of_dday(self):


def get_not_finished_targets(targets):
return [t for t in targets if t.state in (data.State.todo, data.State.in_progress)]
ret = [t for t in targets if t.state in (data.State.todo, data.State.in_progress)]
ret = [t for t in ret if not t.dependents]
return ret
92 changes: 92 additions & 0 deletions estimage/plugins/demo/projective.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
[DEMO-1]
title = Upstream Release
description = Upstream project is released
point_cost = 0.0
depnames = DEMO-1.1,DEMO-1.2,DEMO-1.3
assignee =
collaborators =
state = 3
priority = 0.0
tier = 0
tags =
loading_plugin = demo
work_end = 2023-06-10T00:00:00

[DEMO-1.1]
title = Fix Blockers
description = Fix all the blocker bugs
point_cost = 0.0
depnames =
assignee =
collaborators =
state = 3
priority = 0.0
tier = 0
tags =
loading_plugin = demo

[DEMO-1.2]
title = Release Upstream
description = Perform the upstream release procedure
point_cost = 0.0
depnames =
assignee =
collaborators =
state = 3
priority = 0.0
tier = 0
tags =
loading_plugin = demo

[DEMO-1.3]
title = Blog
description = Write an upstream blog post
point_cost = 0.0
depnames =
assignee =
collaborators =
state = 3
priority = 0.0
tier = 0
tags =
loading_plugin = demo

[DEMO-2]
title = Downstream Release
description = Project is packaged downstream
point_cost = 0.0
depnames = DEMO-2.1,DEMO-2.2
assignee =
collaborators =
state = 3
priority = 0.0
tier = 0
tags =
loading_plugin = demo
work_start = 2023-06-08T00:00:00

[DEMO-2.1]
title = Test + Fix
description = Run the downstream test suite against the upstream version, fix issues
point_cost = 0.0
depnames =
assignee =
collaborators =
state = 3
priority = 0.0
tier = 0
tags =
loading_plugin = demo

[DEMO-2.2]
title = Release Downstream
description = Perform the downstream release steps
point_cost = 0.0
depnames =
assignee =
collaborators =
state = 3
priority = 0.0
tier = 0
tags =
loading_plugin = demo
41 changes: 35 additions & 6 deletions estimage/plugins/demo/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,54 @@

from ...webapp import web_utils
from . import forms
from . import get_not_finished_targets, load_data, save_data
from . import get_not_finished_targets, load_data, save_data, start, conclude_target, begin_target

bp = flask.Blueprint("demo", __name__, template_folder="templates")


@bp.route('/demo', methods=("GET", "POST"))
@flask_login.login_required
def next_day():
user = flask_login.current_user
user_id = user.get_id()

cls, loader = web_utils.get_retro_loader()
targets_by_id = loader.get_loaded_targets_by_id()
targets_by_id, model = web_utils.get_all_tasks_by_id_and_user_model("retro", user_id)
targets = get_not_finished_targets(targets_by_id.values())
targets = [targets_by_id[n] for n in sorted([t.name for t in targets])]

plugin_data = load_data()
if (day_index := plugin_data.get("day_index", 0)) == 0:
start(targets_by_id.values(), loader)

form = forms.DemoForm()
choices = [(t.name, t.title) for t in targets]
if not choices:
choices = [("noop", "Do Nothing")]
form.issues.choices = choices
if form.validate_on_submit():
old_plugin_data = load_data()
old_plugin_data["day_index"] = old_plugin_data.get("day_index", 0) + 1
save_data(old_plugin_data)
name = form.issues.data
plugin_data = load_data()
plugin_data["day_index"] = day_index + 1
velocity_in_stash = plugin_data.get("velocity_in_stash", dict())
velocity_in_stash[name] = velocity_in_stash.get(name, 0) + float(form.progress.data)
if form.issues.data != "noop":
target = targets_by_id[name]

if velocity_in_stash[name] > model.remaining_point_estimate_of(target.name).expected:
flask.flash(f"Finished {target.name}")
conclude_target(target, loader, plugin_data["day_index"])
else:
begin_target(target, loader, plugin_data["day_index"])

plugin_data["velocity_in_stash"] = velocity_in_stash
save_data(plugin_data)

targets = get_not_finished_targets(targets)
choices = [(t.name, t.title) for t in targets]
if not choices:
choices = [("noop", "Do Nothing")]
form.issues.choices = choices

return web_utils.render_template(
'demo.html', title='Demo Plugin', plugin_form=form, day_index=0)
'demo.html', title='Demo Plugin', plugin_form=form, day_index=day_index)
47 changes: 33 additions & 14 deletions estimage/visualize/completion.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,60 @@
import typing
import datetime

import numpy as np

from . import utils
from .. import utilities
from .. import utilities, PluginResolver


DAYS_IN_WEEK = 7


@PluginResolver.class_is_extendable("MPLCompletionPlot")
class MPLCompletionPlot:
DDAY_LABEL = "today"
width = 2

def __init__(self, todo_estimation, perweek_velocity_mean_std):
dom, pdf = todo_estimation.divide_by_gauss_pdf(200, * perweek_velocity_mean_std)
def __init__(self, period_start, todo_estimation, perweek_velocity_mean_std):
self.period_start = period_start
dom, pdf = todo_estimation.divide_by_gauss_pdf(200, perweek_velocity_mean_std[0], perweek_velocity_mean_std[1])
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)
upper_bound = self.distribution.ppf(1)
self.dom = np.arange(
int(np.floor(lower_bound * DAYS_IN_WEEK)),
- (self.get_date_of_dday() - period_start).days,
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",
def _dom_to_days(self, dom_numbers):
return dom_numbers - self.dom[0]

def get_date_of_dday(self):
return datetime.datetime.today()

def _plot_plan_wrt_day(self, ax):
start = self.get_date_of_dday()
ax.plot(self._dom_to_days(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])
utils.x_axis_weeks_and_months(ax, start + utils.ONE_DAY * self.dom[0], start + utils.ONE_DAY * self.dom[-1])

def _plot_percentile(self, ax, value_in_percents):
where = self.distribution.ppf(value_in_percents / 100.0) * DAYS_IN_WEEK
ax.axvline(self._dom_to_days(where), color="orange",
linewidth=self.width, label=f"confidence {round(value_in_percents)} %")

def _plot_dday(self, ax):
ax.axvline(self._dom_to_days(0), label=self.DDAY_LABEL, color="grey", linewidth=2)

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())
self._plot_plan_wrt_day(ax)
self._plot_percentile(ax, 95.0)
self._plot_dday(ax)

ax.legend(loc="upper left")

ax.set_ylabel("percents")
Expand Down
4 changes: 2 additions & 2 deletions estimage/visualize/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ def get_standard_pyplot():
return plt


def x_axis_weeks_and_months(ax, start, end):
def x_axis_weeks_and_months(ax, start, end, week_index_start=0):
ticks = dict()
set_week_ticks_to_mondays(ticks, start, end)
set_week_ticks_to_mondays(ticks, start, end, week_index_start)
set_ticks_to_months(ticks, start, end)

ax.set_xticks(list(ticks.keys()))
Expand Down
11 changes: 11 additions & 0 deletions estimage/webapp/templates/retrospective_overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ <h2>Velocity</h2>
{% endblock %}
</div>
</div>
<div class="row">
{% block completion %}
<h2>Completion</h2>
<p>
Projection of completion
</p>
<p>
<img src="{{ url_for('vis.visualize_completion') }}" alt="Completion projection"/>
</p>
{% endblock %}
</div>
{% endblock retrospective_content %}
</div>
{% endblock content %}
7 changes: 4 additions & 3 deletions estimage/webapp/vis/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ def visualize_completion():
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()

completion_class = flask.current_app.config["classes"]["MPLCompletionPlot"]

fig = completion_class(start, todo, (velocity_mean, velocity_stdev)).get_figure()
fig.set_size_inches(* NORMAL_FIGURE_SIZE)
return send_figure_as_svg(fig, "completion.svg")

Expand Down

0 comments on commit 931b3d7

Please sign in to comment.