trollolo 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/CHANGELOG.md +11 -0
- data/lib/version.rb +1 -1
- data/scripts/burndowndata.py +69 -8
- data/scripts/create_burndown.py +16 -3
- data/scripts/graph.py +36 -14
- data/scripts/plot.py +22 -0
- data/spec/containers/matplotlib/Dockerfile +6 -0
- data/spec/containers/matplotlib/font/Humor-Sans-1.0.ttf +0 -0
- data/spec/containers/matplotlib/font/Humor-SansOFL-1.0.txt +94 -0
- data/spec/data/board-list.yaml +4 -0
- data/spec/data/burndown-data-10.yaml +9 -0
- data/spec/data/burndown-data-with-config.yaml +33 -0
- data/spec/data/burndown-data.yaml +35 -0
- data/spec/data/burndown_dir/burndown-data-01.yaml +9 -0
- data/spec/data/burndown_dir/burndown-data-02.yaml +21 -0
- data/spec/data/create_burndown_helper/burndown-data-08.yaml +111 -0
- data/spec/data/create_burndown_helper/burndown-data-23.yaml +85 -0
- data/spec/data/create_burndown_helper/burndown-data-31.yaml +109 -0
- data/spec/data/create_burndown_helper/burndown-data-35.yaml +24 -0
- data/spec/data/create_burndown_helper/burndown-data-42.yaml +65 -0
- data/spec/data/create_burndown_helper/burndown-data-56.yaml +140 -0
- data/spec/integration/create_burndown_spec.rb +40 -34
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e965a5dd1f21efe913566b48fdd34366c96a281d
|
4
|
+
data.tar.gz: 3ceddeef3b6156896c240274603422f456b4a774
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4a388af4ab07d713d63fcdc1b1ef70601de633e6755a462988893f9cd42cfbf4401fe02a24af1869ef1124f1e09291a711a0f9abb311e0c01e33933b9b07beb
|
7
|
+
data.tar.gz: dc7bd9ccb6dcca2f136163929e68b6842e5cfd1f88da59f5450547a8af4d5a238826b9a1050b880942956b3cc67da2b1789ad568340082b09c3be3932d56b2fd
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Trollolo Changelog
|
2
2
|
|
3
|
+
## Version 0.0.8
|
4
|
+
|
5
|
+
* Burndown chart reflects unplanned work
|
6
|
+
|
7
|
+
If unplanned work is added to the board during the sprint, separate graphs
|
8
|
+
for the corresponding story points and tasks are drawn. For easier
|
9
|
+
distinction, the additional graphs got different colors and are mentioned in
|
10
|
+
the also newly added legend of the plot.
|
11
|
+
|
12
|
+
* Run integration tests of the image generation in Docker
|
13
|
+
|
3
14
|
## Version 0.0.7
|
4
15
|
|
5
16
|
* Add set-description command
|
data/lib/version.rb
CHANGED
data/scripts/burndowndata.py
CHANGED
@@ -10,10 +10,13 @@ class BurndownData:
|
|
10
10
|
burndown = self.readYAML(self.args.sprint)
|
11
11
|
self.getSprintData(burndown)
|
12
12
|
self.calculateMaxStoryPoints()
|
13
|
+
self.calculateMaxTasks()
|
13
14
|
self.setBonusTasksDayOne(burndown)
|
15
|
+
self.setUnplannedTasksDayOne(burndown)
|
14
16
|
self.setExtraDays()
|
15
|
-
self.
|
16
|
-
self.
|
17
|
+
self.setUnplannedDays()
|
18
|
+
self.calculateYRange(self.max_story_points, self.bonus_tasks_done, self.bonus_story_points_done, self.unplanned_tasks_done, self.unplanned_story_points_done)
|
19
|
+
self.setScaleFactor(self.max_tasks, self.max_story_points)
|
17
20
|
|
18
21
|
def readYAML(self, sprint_number):
|
19
22
|
with open('burndown-data-' + sprint_number + '.yaml', 'r') as f:
|
@@ -25,20 +28,29 @@ class BurndownData:
|
|
25
28
|
self.weekend_lines = burndown["meta"]["weekend_lines"]
|
26
29
|
self.total_days = burndown["meta"]["total_days"]
|
27
30
|
self.extra_day = 0
|
31
|
+
self.unplanned_day = 0
|
28
32
|
self.current_day = 1
|
29
33
|
self.days = []
|
30
34
|
self.tasks_extra_days = []
|
35
|
+
self.unplanned_tasks_days = []
|
31
36
|
self.story_points_extra_days = []
|
37
|
+
self.unplanned_story_points_days = []
|
32
38
|
self.open_story_points = []
|
33
39
|
self.total_story_points = []
|
34
40
|
self.bonus_story_points_done = []
|
41
|
+
self.total_unplanned_story_points = []
|
42
|
+
self.unplanned_story_points_done = []
|
35
43
|
self.open_tasks = []
|
36
44
|
self.total_tasks = []
|
37
45
|
self.bonus_tasks_done = []
|
46
|
+
self.total_unplanned_tasks = []
|
47
|
+
self.unplanned_tasks_done = []
|
38
48
|
self.x_fast_lane = []
|
39
49
|
self.y_fast_lane = []
|
40
50
|
self.total_fast_lane = []
|
51
|
+
self.total_unplanned_fast_lane = []
|
41
52
|
self.max_story_points = 0
|
53
|
+
self.max_tasks = 0
|
42
54
|
|
43
55
|
for day in burndown["days"]:
|
44
56
|
self.days.append(self.current_day)
|
@@ -57,10 +69,27 @@ class BurndownData:
|
|
57
69
|
points = -day["story_points_extra"]["done"]
|
58
70
|
self.bonus_story_points_done.append(points)
|
59
71
|
|
72
|
+
if "unplanned_tasks" in day:
|
73
|
+
self.unplanned_tasks_days.append(self.current_day)
|
74
|
+
tasks = day["unplanned_tasks"]["open"] - day["unplanned_tasks"]["total"]
|
75
|
+
self.unplanned_tasks_done.append(tasks)
|
76
|
+
self.total_unplanned_tasks.append(day["unplanned_tasks"]["total"])
|
77
|
+
else:
|
78
|
+
self.total_unplanned_tasks.append(0)
|
79
|
+
|
80
|
+
if "unplanned_story_points" in day:
|
81
|
+
self.unplanned_story_points_days.append(self.current_day)
|
82
|
+
points = day["unplanned_story_points"]["open"] - day["unplanned_story_points"]["total"]
|
83
|
+
self.unplanned_story_points_done.append(points)
|
84
|
+
self.total_unplanned_story_points.append(day["unplanned_story_points"]["total"])
|
85
|
+
else:
|
86
|
+
self.total_unplanned_story_points.append(0)
|
87
|
+
|
60
88
|
if day.has_key("fast_lane"):
|
61
89
|
self.x_fast_lane.append(self.current_day)
|
62
90
|
self.y_fast_lane.append(day["fast_lane"]["open"])
|
63
91
|
self.total_fast_lane.append(day["fast_lane"]["total"])
|
92
|
+
self.total_unplanned_fast_lane.append(0)
|
64
93
|
|
65
94
|
self.current_day += 1
|
66
95
|
return
|
@@ -70,6 +99,11 @@ class BurndownData:
|
|
70
99
|
self.max_story_points = max(self.max_story_points, sp)
|
71
100
|
return
|
72
101
|
|
102
|
+
def calculateMaxTasks(self):
|
103
|
+
for t in self.total_tasks:
|
104
|
+
self.max_tasks = max(self.max_tasks, t)
|
105
|
+
return
|
106
|
+
|
73
107
|
def setBonusTasksDayOne(self, burndown):
|
74
108
|
if burndown["days"][0].has_key("tasks_extra"):
|
75
109
|
self.bonus_tasks_day_one = burndown["days"][0]["tasks_extra"]["done"]
|
@@ -77,6 +111,13 @@ class BurndownData:
|
|
77
111
|
self.bonus_tasks_day_one = 0
|
78
112
|
return
|
79
113
|
|
114
|
+
def setUnplannedTasksDayOne(self, burndown):
|
115
|
+
if burndown["days"][0].has_key("unplanned_tasks"):
|
116
|
+
self.unplanned_tasks_day_one = burndown["days"][0]["unplanned_tasks"]["done"]
|
117
|
+
else:
|
118
|
+
self.unplanned_tasks_day_one = 0
|
119
|
+
return
|
120
|
+
|
80
121
|
def setExtraDays(self):
|
81
122
|
if len(self.story_points_extra_days) > 0:
|
82
123
|
self.story_points_extra_days = [self.story_points_extra_days[0] - 1] + self.story_points_extra_days
|
@@ -88,7 +129,18 @@ class BurndownData:
|
|
88
129
|
self.extra_day = 1
|
89
130
|
return
|
90
131
|
|
91
|
-
def
|
132
|
+
def setUnplannedDays(self):
|
133
|
+
if len(self.unplanned_story_points_days) > 0:
|
134
|
+
self.unplanned_story_points_days = [self.unplanned_story_points_days[0] - 1] + self.unplanned_story_points_days
|
135
|
+
self.unplanned_story_points_done = [0] + self.unplanned_story_points_done
|
136
|
+
if len(self.unplanned_tasks_days) > 0:
|
137
|
+
if not self.args.no_tasks and not self.unplanned_tasks_day_one:
|
138
|
+
self.unplanned_tasks_days = [self.unplanned_tasks_days[0] - 1] + self.unplanned_tasks_days
|
139
|
+
self.unplanned_tasks_done = [0] + self.unplanned_tasks_done
|
140
|
+
self.unplanned_day = 1
|
141
|
+
return
|
142
|
+
|
143
|
+
def calculateYRange(self, max_story_points, bonus_tasks_done, bonus_story_points_done, unplanned_tasks_done, unplanned_story_points_done):
|
92
144
|
self.ymax = max_story_points + 3
|
93
145
|
|
94
146
|
if len(bonus_tasks_done) > 0:
|
@@ -96,18 +148,27 @@ class BurndownData:
|
|
96
148
|
else:
|
97
149
|
ymin_bonus_tasks = 0
|
98
150
|
|
151
|
+
if len(unplanned_tasks_done) > 0:
|
152
|
+
ymin_unplanned_tasks = min(unplanned_tasks_done) -3
|
153
|
+
else:
|
154
|
+
ymin_unplanned_tasks = 0
|
155
|
+
|
99
156
|
ymin_bonus_story_points = 0
|
100
157
|
|
101
158
|
if len(bonus_story_points_done) > 0:
|
102
159
|
ymin_bonus_story_points = min(bonus_story_points_done) -3
|
103
160
|
|
104
|
-
|
161
|
+
ymin_unplanned_story_points = 0
|
162
|
+
|
163
|
+
if len(unplanned_story_points_done) > 0:
|
164
|
+
ymin_unplanned_story_points = min(unplanned_story_points_done) -3
|
165
|
+
|
166
|
+
self.ymin = min(ymin_bonus_tasks, ymin_bonus_story_points, ymin_unplanned_tasks, ymin_unplanned_story_points)
|
167
|
+
if self.ymin > -3:
|
105
168
|
self.ymin = -3
|
106
|
-
else:
|
107
|
-
self.ymin = min(ymin_bonus_tasks, ymin_bonus_story_points)
|
108
169
|
return
|
109
170
|
|
110
|
-
def setScaleFactor(self,
|
111
|
-
self.scalefactor = float(
|
171
|
+
def setScaleFactor(self, max_tasks, max_story_points):
|
172
|
+
self.scalefactor = float(max_tasks) / float(max_story_points)
|
112
173
|
return
|
113
174
|
|
data/scripts/create_burndown.py
CHANGED
@@ -87,27 +87,40 @@ graph_story_points = graph.Graph(plot.storyPoints())
|
|
87
87
|
|
88
88
|
y_label = "Story Points"
|
89
89
|
color = "black"
|
90
|
+
color_unplanned = "magenta"
|
90
91
|
marker = "o"
|
91
92
|
linestyle = "solid"
|
92
93
|
linewidth = 2
|
94
|
+
label_unplanned = "Unplanned Story Points"
|
95
|
+
legend_list = []
|
93
96
|
|
94
|
-
graph_story_points.draw(y_label, color, marker, linestyle, linewidth, plot)
|
97
|
+
graph_story_points.draw(y_label, color, color_unplanned, marker, linestyle, linewidth, label_unplanned, plot)
|
98
|
+
|
99
|
+
legend_list.append(graph_story_points.subplot)
|
95
100
|
|
96
101
|
if not args.no_tasks:
|
97
102
|
graph_tasks = graph.Graph(plot.tasks())
|
98
103
|
|
99
104
|
y_label = "Tasks"
|
100
105
|
color = "green"
|
106
|
+
color_unplanned = "chartreuse"
|
107
|
+
label_unplanned = "Unplanned Tasks"
|
101
108
|
|
102
|
-
graph_tasks.draw(y_label, color, marker, linestyle, linewidth, plot)
|
109
|
+
graph_tasks.draw(y_label, color, color_unplanned, marker, linestyle, linewidth, label_unplanned, plot)
|
110
|
+
legend_list.append(graph_tasks.subplot)
|
103
111
|
|
104
112
|
if args.with_fast_lane:
|
105
113
|
graph_fast_lane = graph.Graph(plot.fastLane())
|
106
114
|
|
107
115
|
y_label = "Fast Lane"
|
108
116
|
color = "red"
|
117
|
+
label = "Fast Lane"
|
109
118
|
|
110
|
-
graph_fast_lane.draw(y_label, color, marker, linestyle, linewidth, plot)
|
119
|
+
graph_fast_lane.draw(y_label, color, color_unplanned, marker, linestyle, linewidth, label, plot)
|
120
|
+
legend_list.append(graph_fast_lane.subplot)
|
121
|
+
|
122
|
+
# Draw legend
|
123
|
+
plot.drawLegend(legend_list)
|
111
124
|
|
112
125
|
# Save the burndown chart
|
113
126
|
plot.saveImage(args)
|
data/scripts/graph.py
CHANGED
@@ -10,9 +10,11 @@ class Graph:
|
|
10
10
|
self.x = graph_data['x']
|
11
11
|
self.y = graph_data['y']
|
12
12
|
self.xy_extra = 0
|
13
|
+
self.xy_unplanned = 0
|
13
14
|
self.ymin = graph_data['ymin']
|
14
15
|
self.ymax = graph_data['ymax']
|
15
16
|
self.total = graph_data['total']
|
17
|
+
self.total_unplanned = graph_data['total_unplanned']
|
16
18
|
self.plot_count = graph_data['plot_count']
|
17
19
|
self.draw_tasks_diff = graph_data['draw_tasks_diff']
|
18
20
|
self.draw_bonus_tasks_diff = graph_data['draw_bonus_tasks_diff']
|
@@ -22,6 +24,11 @@ class Graph:
|
|
22
24
|
self.y_extra = graph_data['y_extra']
|
23
25
|
self.xy_extra = 1
|
24
26
|
|
27
|
+
if 'x_unplanned' in graph_data:
|
28
|
+
self.x_unplanned = graph_data['x_unplanned']
|
29
|
+
self.y_unplanned = graph_data['y_unplanned']
|
30
|
+
self.xy_unplanned = 1
|
31
|
+
|
25
32
|
if self.draw_tasks_diff:
|
26
33
|
self.x_arrow_start_end = graph_data['x_arrow_start_end']
|
27
34
|
self.y_arrow_start = graph_data['y_arrow_start']
|
@@ -37,7 +44,7 @@ class Graph:
|
|
37
44
|
self.subplot = graph_data['subplot']
|
38
45
|
return
|
39
46
|
|
40
|
-
def draw(self, y_label, color, marker, linestyle, linewidth, plot):
|
47
|
+
def draw(self, y_label, color, color_unplanned, marker, linestyle, linewidth, label, plot):
|
41
48
|
self.plot = plot
|
42
49
|
self.subplot.set_ylabel(y_label, color=color)
|
43
50
|
self.subplot.set_ylim([self.ymin, self.ymax])
|
@@ -52,7 +59,8 @@ class Graph:
|
|
52
59
|
|
53
60
|
self.subplot.plot(self.x, self.y, color=color, marker=marker, linestyle=linestyle, linewidth=linewidth)
|
54
61
|
self.drawBonus(color, marker, linestyle, linewidth)
|
55
|
-
self.
|
62
|
+
self.drawUnplanned(color_unplanned, marker, linestyle, linewidth, label)
|
63
|
+
self.drawBars(color, color_unplanned)
|
56
64
|
if self.draw_tasks_diff:
|
57
65
|
self.drawTasksDiff(color)
|
58
66
|
if self.draw_bonus_tasks_diff:
|
@@ -64,23 +72,37 @@ class Graph:
|
|
64
72
|
self.subplot.plot(self.x_extra, self.y_extra, color=color, marker=marker, linestyle=linestyle, linewidth=linewidth)
|
65
73
|
return
|
66
74
|
|
67
|
-
def
|
75
|
+
def drawUnplanned(self, color_unplanned, marker, linestyle, linewidth, label):
|
76
|
+
if self.xy_unplanned and len(self.x_unplanned) > 0:
|
77
|
+
self.subplot.plot(self.x_unplanned, self.y_unplanned, color=color_unplanned, marker=marker, linestyle=linestyle, linewidth=linewidth, label=label)
|
78
|
+
return
|
79
|
+
|
80
|
+
def drawBars(self, color, color_unplanned):
|
68
81
|
if len(self.total) > 1:
|
69
82
|
width = 0.2
|
70
83
|
offset = 0
|
84
|
+
days = self.x
|
85
|
+
days = [i+1 for i in days]
|
86
|
+
|
71
87
|
if self.plot_count == 1:
|
72
|
-
offset
|
73
|
-
|
74
|
-
for i in range(
|
75
|
-
|
76
|
-
|
88
|
+
offset += width
|
89
|
+
|
90
|
+
for i in range(len(days)):
|
91
|
+
days[i] = days[i] - offset
|
92
|
+
|
77
93
|
additional = []
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
94
|
+
additional_unplanned = []
|
95
|
+
|
96
|
+
for i in range(1, len(self.total)):
|
97
|
+
additional.append(self.total[i] - self.total[i - 1])
|
98
|
+
additional_unplanned.append(self.total_unplanned[i] - self.total_unplanned[i - 1])
|
99
|
+
|
100
|
+
for i in range(len(additional)):
|
101
|
+
if additional[i] != 0:
|
102
|
+
self.subplot.bar(days[i], additional[i], width, color=color)
|
103
|
+
if additional_unplanned[i] != 0:
|
104
|
+
bottom = (0 if additional[i] < 0 else additional[i])
|
105
|
+
self.subplot.bar(days[i], additional_unplanned[i], width, color=color_unplanned, bottom=bottom)
|
84
106
|
return
|
85
107
|
|
86
108
|
def drawTasksDiff(self, color):
|
data/scripts/plot.py
CHANGED
@@ -56,6 +56,17 @@ class Plot:
|
|
56
56
|
plt.xticks(x_range, x_labels)
|
57
57
|
return
|
58
58
|
|
59
|
+
def drawLegend(self, legend_list):
|
60
|
+
handles = []
|
61
|
+
labels = []
|
62
|
+
for leg in legend_list:
|
63
|
+
h, l = leg.get_legend_handles_labels()
|
64
|
+
handles.extend(h)
|
65
|
+
labels.extend(l)
|
66
|
+
if handles:
|
67
|
+
plt.legend(handles, labels, loc="upper right", fontsize="small")
|
68
|
+
return
|
69
|
+
|
59
70
|
def saveImage(self, args):
|
60
71
|
plt.savefig('burndown-' + args.sprint + '.png',bbox_inches='tight')
|
61
72
|
if not args.no_head:
|
@@ -79,11 +90,16 @@ class Plot:
|
|
79
90
|
if self.data.extra_day:
|
80
91
|
story_points['x_extra'] = self.data.story_points_extra_days
|
81
92
|
story_points['y_extra'] = self.data.bonus_story_points_done
|
93
|
+
if self.data.unplanned_day:
|
94
|
+
story_points['x_unplanned'] = self.data.unplanned_story_points_days
|
95
|
+
story_points['y_unplanned'] = self.data.unplanned_story_points_done
|
82
96
|
story_points['total'] = self.data.total_story_points
|
97
|
+
story_points['total_unplanned'] = self.data.total_unplanned_story_points
|
83
98
|
story_points['ymin'] = self.data.ymin
|
84
99
|
story_points['ymax'] = self.data.ymax
|
85
100
|
story_points['subplot'] = self.createSubplot()
|
86
101
|
story_points['plot_count'] = self.plot_count
|
102
|
+
story_points['label_unplanned'] = "Unplanned Story Points"
|
87
103
|
self.plot_count += 1
|
88
104
|
return story_points
|
89
105
|
|
@@ -100,6 +116,9 @@ class Plot:
|
|
100
116
|
if self.data.extra_day:
|
101
117
|
tasks['x_extra'] = self.data.tasks_extra_days
|
102
118
|
tasks['y_extra'] = self.data.bonus_tasks_done
|
119
|
+
if self.data.unplanned_day:
|
120
|
+
tasks['x_unplanned'] = self.data.unplanned_tasks_days
|
121
|
+
tasks['y_unplanned'] = self.data.unplanned_tasks_done
|
103
122
|
if self.data.bonus_tasks_day_one:
|
104
123
|
tasks['draw_bonus_tasks_diff'] = 1
|
105
124
|
tasks['y_arrow_start_bonus'] = 0
|
@@ -107,10 +126,12 @@ class Plot:
|
|
107
126
|
tasks['y_text_bonus'] = self.data.bonus_tasks_done[0] / 2
|
108
127
|
tasks['bonus_tasks_day_one'] = self.data.bonus_tasks_day_one
|
109
128
|
tasks['total'] = self.data.total_tasks
|
129
|
+
tasks['total_unplanned'] = self.data.total_unplanned_tasks
|
110
130
|
tasks['ymin'] = self.data.ymin * self.data.scalefactor
|
111
131
|
tasks['ymax'] = self.data.ymax * self.data.scalefactor
|
112
132
|
tasks['subplot'] = self.createSubplot()
|
113
133
|
tasks['plot_count'] = self.plot_count
|
134
|
+
tasks['label_unplanned'] = "Unplanned Tasks"
|
114
135
|
self.plot_count += 1
|
115
136
|
return tasks
|
116
137
|
|
@@ -123,6 +144,7 @@ class Plot:
|
|
123
144
|
fast_lane['ymin'] = self.data.ymin
|
124
145
|
fast_lane['ymax'] = self.data.ymax
|
125
146
|
fast_lane['total'] = self.data.total_fast_lane
|
147
|
+
fast_lane['total_unplanned'] = self.data.total_unplanned_fast_lane
|
126
148
|
fast_lane['subplot'] = self.createSubplot()
|
127
149
|
fast_lane['plot_count'] = self.plot_count
|
128
150
|
self.plot_count += 1
|
Binary file
|
@@ -0,0 +1,94 @@
|
|
1
|
+
Copyright (c) 2009, Michael Ciuffo ch00f@ch00ftech.com,
|
2
|
+
|
3
|
+
|
4
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
5
|
+
This license is copied below, and is also available with a FAQ at:
|
6
|
+
http://scripts.sil.org/OFL
|
7
|
+
|
8
|
+
|
9
|
+
-----------------------------------------------------------
|
10
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
11
|
+
-----------------------------------------------------------
|
12
|
+
|
13
|
+
PREAMBLE
|
14
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
15
|
+
development of collaborative font projects, to support the font creation
|
16
|
+
efforts of academic and linguistic communities, and to provide a free and
|
17
|
+
open framework in which fonts may be shared and improved in partnership
|
18
|
+
with others.
|
19
|
+
|
20
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
21
|
+
redistributed freely as long as they are not sold by themselves. The
|
22
|
+
fonts, including any derivative works, can be bundled, embedded,
|
23
|
+
redistributed and/or sold with any software provided that any reserved
|
24
|
+
names are not used by derivative works. The fonts and derivatives,
|
25
|
+
however, cannot be released under any other type of license. The
|
26
|
+
requirement for fonts to remain under this license does not apply
|
27
|
+
to any document created using the fonts or their derivatives.
|
28
|
+
|
29
|
+
DEFINITIONS
|
30
|
+
"Font Software" refers to the set of files released by the Copyright
|
31
|
+
Holder(s) under this license and clearly marked as such. This may
|
32
|
+
include source files, build scripts and documentation.
|
33
|
+
|
34
|
+
"Reserved Font Name" refers to any names specified as such after the
|
35
|
+
copyright statement(s).
|
36
|
+
|
37
|
+
"Original Version" refers to the collection of Font Software components as
|
38
|
+
distributed by the Copyright Holder(s).
|
39
|
+
|
40
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
41
|
+
or substituting -- in part or in whole -- any of the components of the
|
42
|
+
Original Version, by changing formats or by porting the Font Software to a
|
43
|
+
new environment.
|
44
|
+
|
45
|
+
"Author" refers to any designer, engineer, programmer, technical
|
46
|
+
writer or other person who contributed to the Font Software.
|
47
|
+
|
48
|
+
PERMISSION & CONDITIONS
|
49
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
50
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
51
|
+
redistribute, and sell modified and unmodified copies of the Font
|
52
|
+
Software, subject to the following conditions:
|
53
|
+
|
54
|
+
1) Neither the Font Software nor any of its individual components,
|
55
|
+
in Original or Modified Versions, may be sold by itself.
|
56
|
+
|
57
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
58
|
+
redistributed and/or sold with any software, provided that each copy
|
59
|
+
contains the above copyright notice and this license. These can be
|
60
|
+
included either as stand-alone text files, human-readable headers or
|
61
|
+
in the appropriate machine-readable metadata fields within text or
|
62
|
+
binary files as long as those fields can be easily viewed by the user.
|
63
|
+
|
64
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
65
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
66
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
67
|
+
presented to the users.
|
68
|
+
|
69
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
70
|
+
Software shall not be used to promote, endorse or advertise any
|
71
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
72
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
73
|
+
permission.
|
74
|
+
|
75
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
76
|
+
must be distributed entirely under this license, and must not be
|
77
|
+
distributed under any other license. The requirement for fonts to
|
78
|
+
remain under this license does not apply to any document created
|
79
|
+
using the Font Software.
|
80
|
+
|
81
|
+
TERMINATION
|
82
|
+
This license becomes null and void if any of the above conditions are
|
83
|
+
not met.
|
84
|
+
|
85
|
+
DISCLAIMER
|
86
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
87
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
88
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
89
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
90
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
91
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
92
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
93
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
94
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
---
|
2
|
+
meta:
|
3
|
+
board_id: myboardid
|
4
|
+
not_done_columns:
|
5
|
+
- Sprint Backlog
|
6
|
+
- Doing
|
7
|
+
- QA
|
8
|
+
sprint: 2
|
9
|
+
total_days: 9
|
10
|
+
weekend_lines:
|
11
|
+
- 3.5
|
12
|
+
- 7.5
|
13
|
+
days:
|
14
|
+
- date: '2014-04-23'
|
15
|
+
updated_at: '2014-04-23T10:00:00+01:00'
|
16
|
+
story_points:
|
17
|
+
total: 30
|
18
|
+
open: 23
|
19
|
+
tasks:
|
20
|
+
total: 25
|
21
|
+
open: 21
|
22
|
+
- date: '2014-04-24'
|
23
|
+
updated_at: '2014-04-24T19:00:00+01:00'
|
24
|
+
story_points:
|
25
|
+
total: 30
|
26
|
+
open: 21
|
27
|
+
tasks:
|
28
|
+
total: 26
|
29
|
+
open: 19
|
30
|
+
story_points_extra:
|
31
|
+
done: 3
|
32
|
+
tasks_extra:
|
33
|
+
done: 2
|
@@ -0,0 +1,35 @@
|
|
1
|
+
---
|
2
|
+
meta:
|
3
|
+
board_id: myboardid
|
4
|
+
sprint: 2
|
5
|
+
total_days: 9
|
6
|
+
weekend_lines:
|
7
|
+
- 3.5
|
8
|
+
- 7.5
|
9
|
+
days:
|
10
|
+
- date: '2014-04-23'
|
11
|
+
updated_at: '2014-04-23T10:00:00+01:00'
|
12
|
+
story_points:
|
13
|
+
total: 30
|
14
|
+
open: 23
|
15
|
+
tasks:
|
16
|
+
total: 25
|
17
|
+
open: 21
|
18
|
+
- date: '2014-04-24'
|
19
|
+
updated_at: '2014-04-24T19:00:00+01:00'
|
20
|
+
story_points:
|
21
|
+
total: 30
|
22
|
+
open: 21
|
23
|
+
tasks:
|
24
|
+
total: 26
|
25
|
+
open: 19
|
26
|
+
story_points_extra:
|
27
|
+
done: 3
|
28
|
+
tasks_extra:
|
29
|
+
done: 2
|
30
|
+
story_points_unplanned:
|
31
|
+
total: 3
|
32
|
+
open: 1
|
33
|
+
tasks_unplanned:
|
34
|
+
total: 2
|
35
|
+
open: 1
|
@@ -0,0 +1,21 @@
|
|
1
|
+
---
|
2
|
+
meta:
|
3
|
+
board_id: "53186e8391ef8671265eba9d"
|
4
|
+
sprint: 2
|
5
|
+
total_days: 9
|
6
|
+
weekend_lines:
|
7
|
+
- 3.5
|
8
|
+
- 7.5
|
9
|
+
days:
|
10
|
+
- date: '2015-08-28'
|
11
|
+
updated_at: '2015-08-28T11:04:52+02:00'
|
12
|
+
story_points:
|
13
|
+
total: 24.0
|
14
|
+
open: 24.0
|
15
|
+
tasks:
|
16
|
+
total: 43
|
17
|
+
open: 28
|
18
|
+
story_points_extra:
|
19
|
+
done: 2.0
|
20
|
+
tasks_extra:
|
21
|
+
done: 5
|
@@ -0,0 +1,111 @@
|
|
1
|
+
---
|
2
|
+
meta:
|
3
|
+
board_id: myboardid
|
4
|
+
sprint: 8
|
5
|
+
total_days: 10
|
6
|
+
weekend_lines:
|
7
|
+
- 5
|
8
|
+
- 10
|
9
|
+
days:
|
10
|
+
- date: '2015-03-16'
|
11
|
+
story_points:
|
12
|
+
total: 25.0
|
13
|
+
open: 25.0
|
14
|
+
tasks:
|
15
|
+
total: 62.0
|
16
|
+
open: 26.0
|
17
|
+
fast_lane:
|
18
|
+
total: 2
|
19
|
+
open: 2
|
20
|
+
- date: '2015-03-17'
|
21
|
+
story_points:
|
22
|
+
total: 25.0
|
23
|
+
open: 25.0
|
24
|
+
tasks:
|
25
|
+
total: 62.0
|
26
|
+
open: 26.0
|
27
|
+
fast_lane:
|
28
|
+
total: 2
|
29
|
+
open: 2
|
30
|
+
- date: '2015-03-18'
|
31
|
+
story_points:
|
32
|
+
total: 25.0
|
33
|
+
open: 25.0
|
34
|
+
tasks:
|
35
|
+
total: 62.0
|
36
|
+
open: 13.0
|
37
|
+
fast_lane:
|
38
|
+
total: 2
|
39
|
+
open: 2
|
40
|
+
- date: '2015-03-19'
|
41
|
+
story_points:
|
42
|
+
total: 25.0
|
43
|
+
open: 25.0
|
44
|
+
tasks:
|
45
|
+
total: 65.0
|
46
|
+
open: 13.0
|
47
|
+
story_points_extra:
|
48
|
+
done: 2.0
|
49
|
+
fast_lane:
|
50
|
+
total: 4
|
51
|
+
open: 4
|
52
|
+
- date: '2015-03-20'
|
53
|
+
story_points:
|
54
|
+
total: 25.0
|
55
|
+
open: 25.0
|
56
|
+
tasks:
|
57
|
+
total: 65.0
|
58
|
+
open: 13.0
|
59
|
+
story_points_extra:
|
60
|
+
done: 2.0
|
61
|
+
fast_lane:
|
62
|
+
total: 4
|
63
|
+
open: 2
|
64
|
+
- date: '2015-03-23'
|
65
|
+
story_points:
|
66
|
+
total: 25.0
|
67
|
+
open: 20.0
|
68
|
+
tasks:
|
69
|
+
total: 66.0
|
70
|
+
open: 8.0
|
71
|
+
story_points_extra:
|
72
|
+
done: 2.0
|
73
|
+
fast_lane:
|
74
|
+
total: 6
|
75
|
+
open: 3
|
76
|
+
- date: '2015-03-24'
|
77
|
+
story_points:
|
78
|
+
total: 25.0
|
79
|
+
open: 20.0
|
80
|
+
tasks:
|
81
|
+
total: 70.0
|
82
|
+
open: 9.0
|
83
|
+
story_points_extra:
|
84
|
+
done: 2.0
|
85
|
+
fast_lane:
|
86
|
+
total: 7
|
87
|
+
open: 3
|
88
|
+
- date: '2015-03-25'
|
89
|
+
story_points:
|
90
|
+
total: 25.0
|
91
|
+
open: 10.0
|
92
|
+
tasks:
|
93
|
+
total: 75.0
|
94
|
+
open: 10.0
|
95
|
+
story_points_extra:
|
96
|
+
done: 2.0
|
97
|
+
fast_lane:
|
98
|
+
total: 7
|
99
|
+
open: 3
|
100
|
+
- date: '2015-03-27'
|
101
|
+
story_points:
|
102
|
+
total: 25.0
|
103
|
+
open: 8.0
|
104
|
+
tasks:
|
105
|
+
total: 75.0
|
106
|
+
open: 8.0
|
107
|
+
story_points_extra:
|
108
|
+
done: 2.0
|
109
|
+
fast_lane:
|
110
|
+
total: 7
|
111
|
+
open: 1
|
@@ -0,0 +1,85 @@
|
|
1
|
+
---
|
2
|
+
meta:
|
3
|
+
board_id: QI6wqucd
|
4
|
+
sprint: 23
|
5
|
+
total_days: 8
|
6
|
+
weekend_lines:
|
7
|
+
- 3.5
|
8
|
+
- 8.5
|
9
|
+
days:
|
10
|
+
- date: '2014-09-10'
|
11
|
+
story_points:
|
12
|
+
total: 24
|
13
|
+
open: 24
|
14
|
+
tasks:
|
15
|
+
total: 39
|
16
|
+
open: 39
|
17
|
+
- date: '2014-09-11'
|
18
|
+
story_points:
|
19
|
+
total: 26
|
20
|
+
open: 24
|
21
|
+
tasks:
|
22
|
+
total: 39
|
23
|
+
open: 39
|
24
|
+
- date: '2014-09-12'
|
25
|
+
story_points:
|
26
|
+
total: 24
|
27
|
+
open: 24
|
28
|
+
tasks:
|
29
|
+
total: 39
|
30
|
+
open: 22
|
31
|
+
- date: '2014-09-15'
|
32
|
+
story_points:
|
33
|
+
total: 24
|
34
|
+
open: 24
|
35
|
+
tasks:
|
36
|
+
total: 39
|
37
|
+
open: 21
|
38
|
+
story_points_extra:
|
39
|
+
done: 2
|
40
|
+
tasks_extra:
|
41
|
+
done: 2
|
42
|
+
- date: '2014-09-16'
|
43
|
+
story_points:
|
44
|
+
total: 28
|
45
|
+
open: 18
|
46
|
+
tasks:
|
47
|
+
total: 40
|
48
|
+
open: 15
|
49
|
+
story_points_extra:
|
50
|
+
done: 4
|
51
|
+
tasks_extra:
|
52
|
+
done: 4
|
53
|
+
- date: '2014-09-17'
|
54
|
+
story_points:
|
55
|
+
total: 28
|
56
|
+
open: 18
|
57
|
+
tasks:
|
58
|
+
total: 40
|
59
|
+
open: 11
|
60
|
+
story_points_extra:
|
61
|
+
done: 4
|
62
|
+
tasks_extra:
|
63
|
+
done: 4
|
64
|
+
- date: '2014-09-18'
|
65
|
+
story_points:
|
66
|
+
total: 28
|
67
|
+
open: 18
|
68
|
+
tasks:
|
69
|
+
total: 45
|
70
|
+
open: 15
|
71
|
+
story_points_extra:
|
72
|
+
done: 4
|
73
|
+
tasks_extra:
|
74
|
+
done: 4
|
75
|
+
- date: '2014-09-19'
|
76
|
+
story_points:
|
77
|
+
total: 27
|
78
|
+
open: 21
|
79
|
+
tasks:
|
80
|
+
total: 49
|
81
|
+
open: 10
|
82
|
+
story_points_extra:
|
83
|
+
done: 7
|
84
|
+
tasks_extra:
|
85
|
+
done: 8
|
@@ -0,0 +1,109 @@
|
|
1
|
+
---
|
2
|
+
meta:
|
3
|
+
board_id: myboardid
|
4
|
+
sprint: 31
|
5
|
+
total_days: 10
|
6
|
+
weekend_lines:
|
7
|
+
- 3.5
|
8
|
+
- 8.5
|
9
|
+
days:
|
10
|
+
- date: '2015-01-28'
|
11
|
+
story_points:
|
12
|
+
total: 28.0
|
13
|
+
open: 28.0
|
14
|
+
tasks:
|
15
|
+
total: 42.0
|
16
|
+
open: 29.0
|
17
|
+
tasks_extra:
|
18
|
+
done: 6.0
|
19
|
+
- date: '2015-01-29'
|
20
|
+
story_points:
|
21
|
+
total: 28.0
|
22
|
+
open: 25.0
|
23
|
+
tasks:
|
24
|
+
total: 42.0
|
25
|
+
open: 28.0
|
26
|
+
tasks_extra:
|
27
|
+
done: 6.0
|
28
|
+
- date: '2015-01-30'
|
29
|
+
story_points:
|
30
|
+
total: 28.0
|
31
|
+
open: 25.0
|
32
|
+
tasks:
|
33
|
+
total: 42.0
|
34
|
+
open: 22.0
|
35
|
+
tasks_extra:
|
36
|
+
done: 8.0
|
37
|
+
- date: '2015-02-02'
|
38
|
+
story_points:
|
39
|
+
total: 28.0
|
40
|
+
open: 20.0
|
41
|
+
tasks:
|
42
|
+
total: 43.0
|
43
|
+
open: 15.0
|
44
|
+
tasks_extra:
|
45
|
+
done: 9.0
|
46
|
+
- date: '2015-02-03'
|
47
|
+
story_points:
|
48
|
+
total: 28.0
|
49
|
+
open: 15.0
|
50
|
+
tasks:
|
51
|
+
total: 44.0
|
52
|
+
open: 11.0
|
53
|
+
tasks_extra:
|
54
|
+
done: 9.0
|
55
|
+
- date: '2015-02-04'
|
56
|
+
story_points:
|
57
|
+
total: 28.0
|
58
|
+
open: 15.0
|
59
|
+
tasks:
|
60
|
+
total: 44.0
|
61
|
+
open: 10.0
|
62
|
+
story_points_extra:
|
63
|
+
done: 2.0
|
64
|
+
tasks_extra:
|
65
|
+
done: 10.0
|
66
|
+
- date: '2015-02-05'
|
67
|
+
story_points:
|
68
|
+
total: 28.0
|
69
|
+
open: 11.0
|
70
|
+
tasks:
|
71
|
+
total: 44.0
|
72
|
+
open: 7.0
|
73
|
+
story_points_extra:
|
74
|
+
done: 3.0
|
75
|
+
tasks_extra:
|
76
|
+
done: 12.0
|
77
|
+
- date: '2015-02-06'
|
78
|
+
story_points:
|
79
|
+
total: 28.0
|
80
|
+
open: 6.0
|
81
|
+
tasks:
|
82
|
+
total: 47.0
|
83
|
+
open: 6.0
|
84
|
+
story_points_extra:
|
85
|
+
done: 5.0
|
86
|
+
tasks_extra:
|
87
|
+
done: 13.0
|
88
|
+
- date: '2015-02-09'
|
89
|
+
story_points:
|
90
|
+
total: 28.0
|
91
|
+
open: 3.0
|
92
|
+
tasks:
|
93
|
+
total: 47.0
|
94
|
+
open: 2.0
|
95
|
+
story_points_extra:
|
96
|
+
done: 5.0
|
97
|
+
tasks_extra:
|
98
|
+
done: 13.0
|
99
|
+
- date: '2015-02-10'
|
100
|
+
story_points:
|
101
|
+
total: 28.0
|
102
|
+
open: 3.0
|
103
|
+
tasks:
|
104
|
+
total: 48.0
|
105
|
+
open: 2.0
|
106
|
+
story_points_extra:
|
107
|
+
done: 5.0
|
108
|
+
tasks_extra:
|
109
|
+
done: 14.0
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
meta:
|
3
|
+
board_id: myboardid
|
4
|
+
sprint: 35
|
5
|
+
total_days: 10
|
6
|
+
weekend_lines:
|
7
|
+
- 3.5
|
8
|
+
- 8.5
|
9
|
+
days:
|
10
|
+
- date: '2015-03-25'
|
11
|
+
story_points:
|
12
|
+
total: 22.0
|
13
|
+
open: 22.0
|
14
|
+
tasks:
|
15
|
+
total: 35
|
16
|
+
open: 33
|
17
|
+
- date: '2015-03-26'
|
18
|
+
updated_at: '2015-03-26T14:13:25+01:00'
|
19
|
+
story_points:
|
20
|
+
total: 22.0
|
21
|
+
open: 21.0
|
22
|
+
tasks:
|
23
|
+
total: 35
|
24
|
+
open: 29
|
@@ -0,0 +1,65 @@
|
|
1
|
+
---
|
2
|
+
meta:
|
3
|
+
board_id: QI6wqucd
|
4
|
+
sprint: 42
|
5
|
+
total_days: 8
|
6
|
+
weekend_lines:
|
7
|
+
- 3.5
|
8
|
+
- 8.5
|
9
|
+
days:
|
10
|
+
- date: '2014-09-10'
|
11
|
+
story_points:
|
12
|
+
total: 24
|
13
|
+
open: 24
|
14
|
+
tasks:
|
15
|
+
total: 39
|
16
|
+
open: 39
|
17
|
+
- date: '2014-09-11'
|
18
|
+
story_points:
|
19
|
+
total: 24
|
20
|
+
open: 23
|
21
|
+
tasks:
|
22
|
+
total: 39
|
23
|
+
open: 28
|
24
|
+
- date: '2014-09-12'
|
25
|
+
story_points:
|
26
|
+
total: 24
|
27
|
+
open: 20
|
28
|
+
tasks:
|
29
|
+
total: 39
|
30
|
+
open: 22
|
31
|
+
- date: '2014-09-15'
|
32
|
+
story_points:
|
33
|
+
total: 24
|
34
|
+
open: 19
|
35
|
+
tasks:
|
36
|
+
total: 39
|
37
|
+
open: 21
|
38
|
+
- date: '2014-09-16'
|
39
|
+
story_points:
|
40
|
+
total: 24
|
41
|
+
open: 18
|
42
|
+
tasks:
|
43
|
+
total: 39
|
44
|
+
open: 15
|
45
|
+
- date: '2014-09-17'
|
46
|
+
story_points:
|
47
|
+
total: 24
|
48
|
+
open: 10
|
49
|
+
tasks:
|
50
|
+
total: 39
|
51
|
+
open: 11
|
52
|
+
- date: '2014-09-18'
|
53
|
+
story_points:
|
54
|
+
total: 24
|
55
|
+
open: 7
|
56
|
+
tasks:
|
57
|
+
total: 39
|
58
|
+
open: 7
|
59
|
+
- date: '2014-09-19'
|
60
|
+
story_points:
|
61
|
+
total: 24
|
62
|
+
open: 0
|
63
|
+
tasks:
|
64
|
+
total: 39
|
65
|
+
open: 0
|
@@ -0,0 +1,140 @@
|
|
1
|
+
---
|
2
|
+
meta:
|
3
|
+
board_id: QI6wqucd
|
4
|
+
not_done_columns:
|
5
|
+
- Sprint Backlog
|
6
|
+
- Doing
|
7
|
+
- QA
|
8
|
+
sprint: 56
|
9
|
+
total_days: 10
|
10
|
+
weekend_lines:
|
11
|
+
- 3.5
|
12
|
+
- 8.5
|
13
|
+
done_column_id: 5630c8074db53ef7499f7a02
|
14
|
+
days:
|
15
|
+
- date: '2016-01-27'
|
16
|
+
updated_at: '2016-01-28T10:14:11+01:00'
|
17
|
+
story_points:
|
18
|
+
total: 31.0
|
19
|
+
open: 31.0
|
20
|
+
tasks:
|
21
|
+
total: 53
|
22
|
+
open: 53
|
23
|
+
- date: '2016-01-28'
|
24
|
+
updated_at: '2016-01-28T11:54:44+01:00'
|
25
|
+
story_points:
|
26
|
+
total: 31.0
|
27
|
+
open: 26.0
|
28
|
+
tasks:
|
29
|
+
total: 53
|
30
|
+
open: 49
|
31
|
+
- date: '2016-01-29'
|
32
|
+
updated_at: '2016-01-29T12:48:01+01:00'
|
33
|
+
story_points:
|
34
|
+
total: 31.0
|
35
|
+
open: 26.0
|
36
|
+
tasks:
|
37
|
+
total: 53
|
38
|
+
open: 43
|
39
|
+
- date: '2016-02-01'
|
40
|
+
updated_at: '2016-02-01T12:47:59+01:00'
|
41
|
+
story_points:
|
42
|
+
total: 31.0
|
43
|
+
open: 24.0
|
44
|
+
tasks:
|
45
|
+
total: 54
|
46
|
+
open: 37
|
47
|
+
unplanned_story_points:
|
48
|
+
total: 1.0
|
49
|
+
open: 1.0
|
50
|
+
unplanned_tasks:
|
51
|
+
total: 9
|
52
|
+
open: 4
|
53
|
+
- date: '2016-02-02'
|
54
|
+
updated_at: '2016-02-02T14:52:11+01:00'
|
55
|
+
story_points:
|
56
|
+
total: 31.0
|
57
|
+
open: 24
|
58
|
+
tasks:
|
59
|
+
total: 56
|
60
|
+
open: 29
|
61
|
+
unplanned_story_points:
|
62
|
+
total: 1.0
|
63
|
+
open: 0.0
|
64
|
+
unplanned_tasks:
|
65
|
+
total: 8
|
66
|
+
open: 1
|
67
|
+
- date: '2016-02-03'
|
68
|
+
updated_at: '2016-02-03T17:10:50+01:00'
|
69
|
+
story_points:
|
70
|
+
total: 31.0
|
71
|
+
open: 15.5
|
72
|
+
tasks:
|
73
|
+
total: 56
|
74
|
+
open: 20
|
75
|
+
unplanned_story_points:
|
76
|
+
total: 4.0
|
77
|
+
open: 2.0
|
78
|
+
unplanned_tasks:
|
79
|
+
total: 9
|
80
|
+
open: 1
|
81
|
+
- date: '2016-02-04'
|
82
|
+
updated_at: '2016-02-04T11:43:31+01:00'
|
83
|
+
story_points:
|
84
|
+
total: 31.0
|
85
|
+
open: 15.5
|
86
|
+
tasks:
|
87
|
+
total: 56
|
88
|
+
open: 16
|
89
|
+
unplanned_story_points:
|
90
|
+
total: 4.0
|
91
|
+
open: 2.0
|
92
|
+
unplanned_tasks:
|
93
|
+
total: 9
|
94
|
+
open: 1
|
95
|
+
- date: '2016-02-05'
|
96
|
+
updated_at: '2016-02-05T13:19:02+01:00'
|
97
|
+
story_points:
|
98
|
+
total: 31.0
|
99
|
+
open: 8.5
|
100
|
+
tasks:
|
101
|
+
total: 56
|
102
|
+
open: 4
|
103
|
+
unplanned_story_points:
|
104
|
+
total: 4.0
|
105
|
+
open: 2.0
|
106
|
+
unplanned_tasks:
|
107
|
+
total: 9
|
108
|
+
open: 1
|
109
|
+
- date: '2016-02-08'
|
110
|
+
updated_at: '2016-02-08T13:28:28+01:00'
|
111
|
+
story_points:
|
112
|
+
total: 31.0
|
113
|
+
open: 8.5
|
114
|
+
tasks:
|
115
|
+
total: 56
|
116
|
+
open: 5
|
117
|
+
tasks_extra:
|
118
|
+
done: 1
|
119
|
+
unplanned_story_points:
|
120
|
+
total: 4.0
|
121
|
+
open: 2.0
|
122
|
+
unplanned_tasks:
|
123
|
+
total: 11
|
124
|
+
open: 3
|
125
|
+
- date: '2016-02-09'
|
126
|
+
updated_at: '2016-02-09T11:40:33+01:00'
|
127
|
+
story_points:
|
128
|
+
total: 31.0
|
129
|
+
open: 8.5
|
130
|
+
tasks:
|
131
|
+
total: 60
|
132
|
+
open: 3
|
133
|
+
tasks_extra:
|
134
|
+
done: 1
|
135
|
+
unplanned_story_points:
|
136
|
+
total: 4.0
|
137
|
+
open: 2.0
|
138
|
+
unplanned_tasks:
|
139
|
+
total: 11
|
140
|
+
open: 1
|
@@ -3,53 +3,59 @@ require_relative "integration_spec_helper"
|
|
3
3
|
include GivenFilesystemSpecHelpers
|
4
4
|
include CliTester
|
5
5
|
|
6
|
-
|
6
|
+
def run_helper(working_dir, sprint_number, extra_args = [])
|
7
|
+
helper_dir = File.expand_path("../../../scripts", __FILE__)
|
8
|
+
args = ["run"]
|
9
|
+
args += ["-v", "#{helper_dir}:/trollolo/helper"]
|
10
|
+
args += ["-v", "#{working_dir}:/trollolo/data"]
|
11
|
+
args += ["-w", "/trollolo/data"]
|
12
|
+
args += ["matplotlib"]
|
13
|
+
args += ["/trollolo/helper/create_burndown.py", sprint_number]
|
14
|
+
args += extra_args
|
15
|
+
run_command(cmd: "docker", args: args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def compare_images_for_sprint(sprint_number, extra_args = [])
|
19
|
+
@working_dir = given_directory do
|
20
|
+
given_file("burndown-data-#{sprint_number}.yaml", from: "create_burndown_helper/burndown-data-#{sprint_number}.yaml")
|
21
|
+
end
|
22
|
+
|
23
|
+
result = run_helper(@working_dir, sprint_number, extra_args)
|
24
|
+
expect(result).to exit_with_success("")
|
25
|
+
expect(File.join(@working_dir, "burndown-#{sprint_number}.png")).
|
26
|
+
to be_same_image_as("create_burndown_helper/burndown-#{sprint_number}.png")
|
27
|
+
end
|
7
28
|
|
8
29
|
describe "create_burndown.py" do
|
9
30
|
use_given_filesystem(keep_files: true)
|
10
31
|
|
11
|
-
|
12
|
-
|
13
|
-
|
32
|
+
before(:all) do
|
33
|
+
if `docker images -q trollolo-matplotlib`.empty?
|
34
|
+
raise "Required docker image 'trollolo-matplotlib' not found. Build it with 'docker build -t trollolo-matplotlib spec/containers/matplotlib'"
|
14
35
|
end
|
15
|
-
|
16
|
-
result = run_command(cmd: HELPER_SCRIPT, args: ["23", "--output=#{@working_dir}", "--no-head"])
|
17
|
-
expect(result).to exit_with_success("")
|
18
|
-
expect(File.join(@working_dir, "burndown-23.png")).
|
19
|
-
to be_same_image_as("create_burndown_helper/burndown-23.png")
|
20
36
|
end
|
21
37
|
|
22
|
-
it "creates burndown chart
|
23
|
-
|
24
|
-
|
25
|
-
end
|
38
|
+
it "creates burndown chart with varying number of total story points and tasks" do
|
39
|
+
compare_images_for_sprint("23")
|
40
|
+
end
|
26
41
|
|
27
|
-
|
28
|
-
|
29
|
-
expect(File.join(@working_dir, "burndown-31.png")).
|
30
|
-
to be_same_image_as("create_burndown_helper/burndown-31.png")
|
42
|
+
it "creates burndown chart with done tasks at the beginning" do
|
43
|
+
compare_images_for_sprint("31")
|
31
44
|
end
|
32
45
|
|
33
|
-
it "creates burndown chart
|
34
|
-
|
35
|
-
|
36
|
-
end
|
46
|
+
it "creates burndown chart of unfinished sprint" do
|
47
|
+
compare_images_for_sprint("35")
|
48
|
+
end
|
37
49
|
|
38
|
-
|
39
|
-
|
40
|
-
expect(File.join(@working_dir, "burndown-35.png")).
|
41
|
-
to be_same_image_as("create_burndown_helper/burndown-35.png")
|
50
|
+
it "creates burndown chart with fast lane and no tasks" do
|
51
|
+
compare_images_for_sprint("08", ["--no-tasks", "--with-fast-lane"])
|
42
52
|
end
|
43
53
|
|
44
|
-
it "creates burndown chart
|
45
|
-
|
46
|
-
|
47
|
-
end
|
54
|
+
it "creates perfect burndown chart" do
|
55
|
+
compare_images_for_sprint("42")
|
56
|
+
end
|
48
57
|
|
49
|
-
|
50
|
-
|
51
|
-
expect(result).to exit_with_success("")
|
52
|
-
expect(File.join(@working_dir, "burndown-08.png")).
|
53
|
-
to be_same_image_as("create_burndown_helper/burndown-08.png")
|
58
|
+
it "creates burndown chart with unplanned cards" do
|
59
|
+
compare_images_for_sprint("56")
|
54
60
|
end
|
55
61
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trollolo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cornelius Schumacher
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -79,6 +79,9 @@ files:
|
|
79
79
|
- scripts/create_burndown.py
|
80
80
|
- scripts/graph.py
|
81
81
|
- scripts/plot.py
|
82
|
+
- spec/containers/matplotlib/Dockerfile
|
83
|
+
- spec/containers/matplotlib/font/Humor-Sans-1.0.ttf
|
84
|
+
- spec/containers/matplotlib/font/Humor-SansOFL-1.0.txt
|
82
85
|
- spec/data/attachment-data
|
83
86
|
- spec/data/board-list.yaml
|
84
87
|
- spec/data/board.json
|
@@ -97,6 +100,8 @@ files:
|
|
97
100
|
- spec/data/create_burndown_helper/burndown-data-23.yaml
|
98
101
|
- spec/data/create_burndown_helper/burndown-data-31.yaml
|
99
102
|
- spec/data/create_burndown_helper/burndown-data-35.yaml
|
103
|
+
- spec/data/create_burndown_helper/burndown-data-42.yaml
|
104
|
+
- spec/data/create_burndown_helper/burndown-data-56.yaml
|
100
105
|
- spec/data/full-board.json
|
101
106
|
- spec/data/lists.json
|
102
107
|
- spec/data/trollolorc
|