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 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