trollolo 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 822cffbe55d2f6d6003d3efe7e5df4afd16a9bc1
4
- data.tar.gz: 48ba1dc0d72deb98df2620cb09ac3413a64a682b
3
+ metadata.gz: e965a5dd1f21efe913566b48fdd34366c96a281d
4
+ data.tar.gz: 3ceddeef3b6156896c240274603422f456b4a774
5
5
  SHA512:
6
- metadata.gz: 80cf5c482a836eba4ed4743693cfb7c0acc459e955aa17c7e33e1b3ab3ade4fb0d2f8babb077f86829aa08ad9821256865c47b28f8ac421f14de4ccf04550753
7
- data.tar.gz: 7974a309702986ff401670b20f24cb12c55d23e644e50bce8bbf806ae089bd0b385c6043ca64aab6598770003a5500e47bc28e04d2f6497dda5c26e246e49261
6
+ metadata.gz: a4a388af4ab07d713d63fcdc1b1ef70601de633e6755a462988893f9cd42cfbf4401fe02a24af1869ef1124f1e09291a711a0f9abb311e0c01e33933b9b07beb
7
+ data.tar.gz: dc7bd9ccb6dcca2f136163929e68b6842e5cfd1f88da59f5450547a8af4d5a238826b9a1050b880942956b3cc67da2b1789ad568340082b09c3be3932d56b2fd
data/.gitignore CHANGED
@@ -3,4 +3,3 @@ Gemfile.lock
3
3
  tmp
4
4
  coverage
5
5
  *.png
6
- *.yaml
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
@@ -1,5 +1,5 @@
1
1
  module Trollolo
2
2
 
3
- VERSION = "0.0.7"
3
+ VERSION = "0.0.8"
4
4
 
5
5
  end
@@ -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.calculateYRange(self.max_story_points, self.bonus_tasks_done, self.bonus_story_points_done)
16
- self.setScaleFactor(self.total_tasks[0], self.max_story_points)
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 calculateYRange(self, max_story_points, bonus_tasks_done, bonus_story_points_done):
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
- if ymin_bonus_tasks == 0 and ymin_bonus_story_points == 0:
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, total_tasks, max_story_points):
111
- self.scalefactor = float(total_tasks) / float(max_story_points)
171
+ def setScaleFactor(self, max_tasks, max_story_points):
172
+ self.scalefactor = float(max_tasks) / float(max_story_points)
112
173
  return
113
174
 
@@ -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.drawBars(color)
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 drawBars(self, color):
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 = -width
73
- new = [0, 0]
74
- for i in range(1, len(self.total)):
75
- new.append(self.total[i] - self.total[i - 1])
76
- additional_days = []
88
+ offset += width
89
+
90
+ for i in range(len(days)):
91
+ days[i] = days[i] - offset
92
+
77
93
  additional = []
78
- for i in range(len(new)):
79
- if new[i] != 0:
80
- additional_days.append(i + offset)
81
- additional.append(new[i])
82
- if len(additional) > 0:
83
- self.subplot.bar(additional_days, additional, width, color=color)
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
@@ -0,0 +1,6 @@
1
+ FROM opensuse:42.1
2
+ MAINTAINER Cornelius Schumacher <cschum@suse.de>
3
+
4
+ COPY font/Humor-Sans-1.0.ttf /usr/share/fonts/truetype/
5
+
6
+ RUN zypper --non-interactive install python-matplotlib python-matplotlib-tk python-PyYAML
@@ -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,4 @@
1
+ orange:
2
+ boardid: 53186e8391ef8671265eba9d
3
+ blue:
4
+ boardid: 53186e8391ef8671265eba9d
@@ -0,0 +1,9 @@
1
+ ---
2
+ meta:
3
+ board_id: "myboardid"
4
+ sprint: 10
5
+ total_days: 9
6
+ weekend_lines:
7
+ - 3.5
8
+ - 8.5
9
+ days: []
@@ -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,9 @@
1
+ ---
2
+ meta:
3
+ board_id: "53186e8391ef8671265eba9d"
4
+ sprint: 1
5
+ total_days: 9
6
+ weekend_lines:
7
+ - 3.5
8
+ - 7.5
9
+ days: []
@@ -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
- HELPER_SCRIPT = File.expand_path("../../../scripts/create_burndown.py", __FILE__)
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
- it "creates burndown chart for sprint 23" do
12
- @working_dir = given_directory do
13
- given_file("burndown-data-23.yaml", from: "create_burndown_helper/burndown-data-23.yaml")
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 for sprint 31" do
23
- @working_dir = given_directory do
24
- given_file("burndown-data-31.yaml", from: "create_burndown_helper/burndown-data-31.yaml")
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
- result = run_command(cmd: HELPER_SCRIPT, args: ["31", "--output=#{@working_dir}", "--no-head"])
28
- expect(result).to exit_with_success("")
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 for sprint 35" do
34
- @working_dir = given_directory do
35
- given_file("burndown-data-35.yaml", from: "create_burndown_helper/burndown-data-35.yaml")
36
- end
46
+ it "creates burndown chart of unfinished sprint" do
47
+ compare_images_for_sprint("35")
48
+ end
37
49
 
38
- result = run_command(cmd: HELPER_SCRIPT, args: ["35", "--output=#{@working_dir}", "--no-head"])
39
- expect(result).to exit_with_success("")
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 for sprint 8" do
45
- @working_dir = given_directory do
46
- given_file("burndown-data-08.yaml", from: "create_burndown_helper/burndown-data-08.yaml")
47
- end
54
+ it "creates perfect burndown chart" do
55
+ compare_images_for_sprint("42")
56
+ end
48
57
 
49
- result = run_command(cmd: HELPER_SCRIPT,
50
- args: ["08", "--output=#{@working_dir}", "--no-tasks", "--with-fast-lane", "--no-head"])
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.7
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-01-08 00:00:00.000000000 Z
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