trollolo 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +17 -0
- data/CONTRIBUTING.md +7 -0
- data/COPYING +674 -0
- data/Gemfile +11 -0
- data/README.md +92 -0
- data/Rakefile +12 -0
- data/bin/trollolo +33 -0
- data/lib/burndown_chart.rb +158 -0
- data/lib/burndown_data.rb +172 -0
- data/lib/card.rb +64 -0
- data/lib/cli.rb +251 -0
- data/lib/settings.rb +54 -0
- data/lib/trello.rb +66 -0
- data/lib/trollolo.rb +34 -0
- data/lib/version.rb +5 -0
- data/man/.gitignore +4 -0
- data/man/trollolo.1.md +152 -0
- data/scripts/create_burndown.py +149 -0
- data/spec/burndown_chart_spec.rb +307 -0
- data/spec/burndown_data_spec.rb +125 -0
- data/spec/card_spec.rb +15 -0
- data/spec/cli_spec.rb +18 -0
- data/spec/command_line_spec.rb +56 -0
- data/spec/data/burndown-data.yaml +26 -0
- data/spec/data/burndown_dir/burndown-data-01.yaml +9 -0
- data/spec/data/burndown_dir/burndown-data-02.yaml +9 -0
- data/spec/data/burndown_dir/create_burndown +142 -0
- data/spec/data/cards.json +1002 -0
- data/spec/data/lists.json +50 -0
- data/spec/data/trollolorc +2 -0
- data/spec/settings_spec.rb +39 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/trello_spec.rb +32 -0
- data/spec/wrapper/credentials_input_wrapper +19 -0
- data/spec/wrapper/empty_config_trollolo_wrapper +10 -0
- data/spec/wrapper/trollolo_wrapper +11 -0
- data/trollolo.gemspec +28 -0
- metadata +131 -0
data/man/.gitignore
ADDED
data/man/trollolo.1.md
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# Trollolo -- Trello command line client
|
2
|
+
|
3
|
+
## SYNOPSIS
|
4
|
+
|
5
|
+
`trollolo` [command] [options]
|
6
|
+
|
7
|
+
`trollolo` help [command]
|
8
|
+
|
9
|
+
|
10
|
+
## DESCRIPTION
|
11
|
+
|
12
|
+
**Trollolo** is a command line client for Trello. It supports fetching lists
|
13
|
+
and cards and has functionality for extracting data for burndown charts.
|
14
|
+
|
15
|
+
|
16
|
+
## GENERAL OPTIONS
|
17
|
+
|
18
|
+
* `--version`:
|
19
|
+
Give out version of trollolo tool. Exit when done.
|
20
|
+
|
21
|
+
* `--verbose`:
|
22
|
+
Run in verbose mode.
|
23
|
+
|
24
|
+
* `--board-id`:
|
25
|
+
Most commands take a `board-id` parameter. This is the id of the Trello
|
26
|
+
board. It is the cryptic part of the URL of the Trello board.
|
27
|
+
|
28
|
+
* `--raw`:
|
29
|
+
Some of the commands take a `raw` option. If this is provided the commands
|
30
|
+
put out the raw JSON returned by the server instead of processing it to
|
31
|
+
a more human-readable version.
|
32
|
+
|
33
|
+
|
34
|
+
## COMMANDS
|
35
|
+
|
36
|
+
### burndown-init -- Initialize burndown chart
|
37
|
+
|
38
|
+
`trollolo burndown-init --board-id=<board id> --output=<directory>`
|
39
|
+
|
40
|
+
Initialize the given directory for the generation of burndown charts. It stores
|
41
|
+
the given board id in the directory in a YAML file together with other
|
42
|
+
configuration data. The YAML file also is used to store the data for the
|
43
|
+
burndown charts. The `burndown` command can be used to update the file with
|
44
|
+
data from the specified Trello board.
|
45
|
+
|
46
|
+
The directory also gets a script to do the actual generation of the burndown
|
47
|
+
chart. Just run this script after each update of the data to get the latest
|
48
|
+
burndown chart.
|
49
|
+
|
50
|
+
### burndown -- Process data for burndown chart
|
51
|
+
|
52
|
+
`trollolo burndown --output=<directory>`
|
53
|
+
|
54
|
+
Update the burndown data in the given directory from the Trello board
|
55
|
+
specified in the YAML file in the directory. The given directory has to be
|
56
|
+
initialized before running the `burndown` command by running the
|
57
|
+
`burndown-init` command.
|
58
|
+
|
59
|
+
The actual generation of the burndown chart is done by running the script
|
60
|
+
which is put into the directory by the `burndown-init` command.
|
61
|
+
|
62
|
+
For correct generation of the burndown chart, the Trello board has to follow
|
63
|
+
a few convention. They are described in the section `CONVENTIONS for SCRUM
|
64
|
+
BOARDS`.
|
65
|
+
|
66
|
+
### plot -- Plot burndown chart
|
67
|
+
|
68
|
+
`trollolo plot <sprint-number>`
|
69
|
+
|
70
|
+
Plot the burndown chart for given sprint. This command assumes that you are in
|
71
|
+
the burndown directory (initially created with `burndown-init`) and that the
|
72
|
+
corresponding file `burndown-data-<sprint-number>.yaml` exists there.
|
73
|
+
|
74
|
+
### fetch-burndown-data -- Read data for burndown chart
|
75
|
+
|
76
|
+
`trollolo fetch-burndown-data --board-id=<board id>`
|
77
|
+
|
78
|
+
Reads data from the specified Trello board, extracts it according to the
|
79
|
+
conventions for Scrum boards, and reports burndown data.
|
80
|
+
|
81
|
+
### get-cards -- Get card data for a board
|
82
|
+
|
83
|
+
Read all card data for a given board.
|
84
|
+
|
85
|
+
### get-checklists -- Get checklist data for a board
|
86
|
+
|
87
|
+
Read all checklist data for a given board
|
88
|
+
|
89
|
+
### get-lists -- Get list data for a board
|
90
|
+
|
91
|
+
Read all list data for a given board.
|
92
|
+
|
93
|
+
|
94
|
+
## EXAMPLES
|
95
|
+
|
96
|
+
Fetch burndown data of a Trello board configured in the configuration file:
|
97
|
+
|
98
|
+
`trollolo fetch-burndown-data --board-id=CRdddpdy`
|
99
|
+
|
100
|
+
Fetch raw data of all cards of a Trello board:
|
101
|
+
|
102
|
+
`trollolo get-cards --raw --board-id=CRdddpdy`
|
103
|
+
|
104
|
+
|
105
|
+
## CONFIGURATION
|
106
|
+
|
107
|
+
Trollolo reads a configuration file `.trollolorc` in the home directory of the
|
108
|
+
user running the command line tool. It reads the data required to authenticate
|
109
|
+
with the Trello server from it. It's two values (the example shows random data):
|
110
|
+
|
111
|
+
```yaml
|
112
|
+
developer_public_key: 87349873487ef8732487234
|
113
|
+
member_token: 87345897238957a29835789b2374580927f3589072398579820345
|
114
|
+
```
|
115
|
+
|
116
|
+
These values have to be set with the personal access data for the Trello API
|
117
|
+
and the id of the board, which is processed.
|
118
|
+
|
119
|
+
For creating a developer key go to the
|
120
|
+
[Developer API Keys](https://trello.com/1/appKey/generate) page on Trello. It's
|
121
|
+
the key in the first box.
|
122
|
+
|
123
|
+
For creating a member token go follow the
|
124
|
+
[instructions](https://trello.com/docs/gettingstarted/index.html#getting-a-token-from-a-user)
|
125
|
+
in the Trello API documentation.
|
126
|
+
|
127
|
+
The board id is the cryptic string in the URL of your board.
|
128
|
+
|
129
|
+
|
130
|
+
## CONVENTIONS FOR SCRUM BOARDS
|
131
|
+
|
132
|
+
The burndown functionality expects the board to follow a certain naming scheme,
|
133
|
+
so that Trollolo can process it as a Scrum board.
|
134
|
+
|
135
|
+
It expects a list `Sprint Backlog` with open items, a list `Doing` with items in
|
136
|
+
progress, and a list `Done Sprint X` with done items, where `X` is the number
|
137
|
+
of the sprint. For burndown data calculation the list with the highest number
|
138
|
+
is taken.
|
139
|
+
|
140
|
+
On work item cards the tool takes a bracketed number as suffix as size of the
|
141
|
+
item in story points. E.g. a card with the title `(3) Build magic tool` would
|
142
|
+
have three story points.
|
143
|
+
|
144
|
+
Cards under the waterline not part of the actual sprint commitment are expected
|
145
|
+
to have a label with the name "Under waterline".
|
146
|
+
|
147
|
+
An example for a board which follow this conventions is the [Trollolo Testing
|
148
|
+
Board](https://trello.com/b/CRdddpdy/trollolo-testing-board).
|
149
|
+
|
150
|
+
## COPYRIGHT
|
151
|
+
|
152
|
+
Trollolo is Copyright (C) 2013-2014 SUSE LLC
|
@@ -0,0 +1,149 @@
|
|
1
|
+
#!/usr/bin/python
|
2
|
+
import matplotlib.pyplot as plt
|
3
|
+
import numpy as np
|
4
|
+
import sys
|
5
|
+
import yaml
|
6
|
+
|
7
|
+
if len(sys.argv) != 2:
|
8
|
+
print "Usage: machinery-burndown.py <sprint-number>"
|
9
|
+
sys.exit(1)
|
10
|
+
|
11
|
+
sprint = sys.argv[1]
|
12
|
+
|
13
|
+
with open('burndown-data-' + sprint + '.yaml', 'r') as f:
|
14
|
+
burndown = yaml.load(f)
|
15
|
+
|
16
|
+
meta = burndown["meta"]
|
17
|
+
|
18
|
+
total_days = meta["total_days"]
|
19
|
+
|
20
|
+
current_day = 1
|
21
|
+
x_days = []
|
22
|
+
y_open_story_points = []
|
23
|
+
y_open_tasks = []
|
24
|
+
total_tasks = []
|
25
|
+
total_story_points = []
|
26
|
+
x_days_extra = []
|
27
|
+
x_day_extra_start = []
|
28
|
+
y_story_points_done_extra = [0]
|
29
|
+
y_tasks_done_extra = [0]
|
30
|
+
|
31
|
+
for day in burndown["days"]:
|
32
|
+
x_days.append(current_day)
|
33
|
+
y_open_story_points.append(day["story_points"]["open"])
|
34
|
+
y_open_tasks.append(day["tasks"]["open"])
|
35
|
+
total_tasks.append(day["tasks"]["total"])
|
36
|
+
total_story_points.append(day["story_points"]["total"])
|
37
|
+
|
38
|
+
if "story_points_extra" in day or "tasks_extra" in day:
|
39
|
+
x_days_extra.append(current_day)
|
40
|
+
tasks = 0
|
41
|
+
if day.has_key("tasks_extra"):
|
42
|
+
tasks = -day["tasks_extra"]["done"]
|
43
|
+
y_tasks_done_extra.append(tasks)
|
44
|
+
points = 0
|
45
|
+
if day.has_key("story_points_extra"):
|
46
|
+
points = -day["story_points_extra"]["done"]
|
47
|
+
y_story_points_done_extra.append(points)
|
48
|
+
|
49
|
+
current_day += 1
|
50
|
+
|
51
|
+
# Add a day at the beginning of the extra days, so the curve starts at zero
|
52
|
+
if x_days_extra:
|
53
|
+
x_days_extra = [x_days_extra[0] - 1] + x_days_extra
|
54
|
+
|
55
|
+
scalefactor = float(total_tasks[0]) / float(y_open_story_points[0])
|
56
|
+
|
57
|
+
# Calculate minimum and maximum 'y' values for the axis
|
58
|
+
ymin_t_extra = 0
|
59
|
+
ymin_s_extra = 0
|
60
|
+
ymax = y_open_story_points[0] + 3
|
61
|
+
|
62
|
+
if len(y_tasks_done_extra) > 0:
|
63
|
+
ymin_t_extra = y_tasks_done_extra[len(y_tasks_done_extra) -1] -3
|
64
|
+
if len(y_story_points_done_extra) > 0:
|
65
|
+
ymin_s_extra = y_story_points_done_extra[len(y_story_points_done_extra) -1] -3
|
66
|
+
if ymin_t_extra < ymin_s_extra:
|
67
|
+
ymin = ymin_t_extra
|
68
|
+
else:
|
69
|
+
ymin = ymin_s_extra
|
70
|
+
if ymin_t_extra == 0 and ymin_s_extra == 0:
|
71
|
+
ymin = -3
|
72
|
+
|
73
|
+
# Plot in xkcd style
|
74
|
+
plt.xkcd()
|
75
|
+
|
76
|
+
plt.figure(1, figsize=(11, 6))
|
77
|
+
|
78
|
+
# Title of the burndown chart
|
79
|
+
plt.suptitle('Sprint ' + sprint, fontsize='large')
|
80
|
+
|
81
|
+
plt.xlabel('Days')
|
82
|
+
plt.axis([0, total_days + 1, ymin, ymax])
|
83
|
+
plt.plot([1, total_days] , [y_open_story_points[0], 0], color='grey')
|
84
|
+
plt.plot([0, total_days + 1], [0, 0], color='blue', linestyle=':')
|
85
|
+
|
86
|
+
# Weekend lines
|
87
|
+
for weekend_line in meta["weekend_lines"]:
|
88
|
+
plt.plot([weekend_line, weekend_line], [ymin+1, ymax-1], color='grey', linestyle=':')
|
89
|
+
|
90
|
+
# Story points
|
91
|
+
plt.ylabel('Story Points', color='black')
|
92
|
+
plt.plot(x_days, y_open_story_points, 'ko-', linewidth=2)
|
93
|
+
if x_days_extra:
|
94
|
+
plt.plot(x_days_extra, y_story_points_done_extra, 'ko-', linewidth=2)
|
95
|
+
|
96
|
+
# Tasks
|
97
|
+
plt.twinx()
|
98
|
+
plt.ylabel('Tasks', color='green')
|
99
|
+
plt.tick_params(axis='y', colors='green')
|
100
|
+
plt.axis([0, total_days + 1, ymin*scalefactor, ymax * scalefactor])
|
101
|
+
plt.plot(x_days, y_open_tasks, 'go-', linewidth=2)
|
102
|
+
if x_days_extra:
|
103
|
+
plt.plot(x_days_extra, y_tasks_done_extra, 'go-', linewidth=2)
|
104
|
+
|
105
|
+
# Calculation of new tasks
|
106
|
+
if len(total_tasks) > 1:
|
107
|
+
new_tasks = [0]
|
108
|
+
for i in range(1, len(total_tasks)):
|
109
|
+
new_tasks.append(total_tasks[i] - total_tasks[i - 1])
|
110
|
+
effective_new_tasks_days = []
|
111
|
+
effective_new_tasks = []
|
112
|
+
for i in range(len(new_tasks)):
|
113
|
+
if new_tasks[i] != 0:
|
114
|
+
effective_new_tasks_days.append(i - 0.25 + 1)
|
115
|
+
effective_new_tasks.append(new_tasks[i])
|
116
|
+
if len(effective_new_tasks) > 0:
|
117
|
+
plt.bar(effective_new_tasks_days, effective_new_tasks, .2, color='green')
|
118
|
+
|
119
|
+
# Calculation of new story points
|
120
|
+
if len(total_story_points) > 1:
|
121
|
+
new_story_points = [0]
|
122
|
+
for i in range(1, len(total_story_points)):
|
123
|
+
new_story_points.append(total_story_points[i] - total_story_points[i - 1])
|
124
|
+
effective_new_story_points_days = []
|
125
|
+
effective_new_story_points = []
|
126
|
+
for i in range(len(new_story_points)):
|
127
|
+
if new_story_points[i] != 0:
|
128
|
+
effective_new_story_points_days.append(i + 0.05 + 1)
|
129
|
+
effective_new_story_points.append(new_story_points[i])
|
130
|
+
if len(effective_new_story_points) > 0:
|
131
|
+
plt.bar(effective_new_story_points_days, effective_new_story_points, .2, color='black')
|
132
|
+
|
133
|
+
# Draw arrow showing already done tasks at begin of sprint
|
134
|
+
tasks_done = burndown["days"][0]["tasks"]["total"] - burndown["days"][0]["tasks"]["open"]
|
135
|
+
|
136
|
+
if tasks_done > 5:
|
137
|
+
plt.annotate("",
|
138
|
+
xy=(x_days[0], scalefactor * y_open_story_points[0] - 0.5 ), xycoords='data',
|
139
|
+
xytext=(x_days[0], y_open_tasks[0] + 0.5), textcoords='data',
|
140
|
+
arrowprops=dict(arrowstyle="<|-|>", connectionstyle="arc3", color='green')
|
141
|
+
)
|
142
|
+
|
143
|
+
plt.text(0.7, y_open_story_points[0], str(tasks_done) + " tasks done",
|
144
|
+
rotation='vertical', verticalalignment='center', color='green'
|
145
|
+
)
|
146
|
+
|
147
|
+
# Save the burndown chart
|
148
|
+
plt.savefig('burndown-' + sprint + '.png',bbox_inches='tight')
|
149
|
+
plt.show()
|
@@ -0,0 +1,307 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
include GivenFilesystemSpecHelpers
|
4
|
+
|
5
|
+
describe BurndownChart do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@settings = dummy_settings
|
9
|
+
@burndown_data = BurndownData.new(@settings)
|
10
|
+
@chart = BurndownChart.new(@settings)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "initializer" do
|
14
|
+
it "sets initial meta data" do
|
15
|
+
expect(@chart.data["meta"]["sprint"]).to eq 1
|
16
|
+
expect(@chart.data["meta"]["total_days"]).to eq 10
|
17
|
+
expect(@chart.data["meta"]["weekend_lines"]).to eq [3.5, 8.5]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "data" do
|
22
|
+
use_given_filesystem
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
@raw_data = [
|
26
|
+
{
|
27
|
+
"date" => '2014-04-23',
|
28
|
+
"story_points" =>
|
29
|
+
{
|
30
|
+
"total" => 30,
|
31
|
+
"open" => 23
|
32
|
+
},
|
33
|
+
"tasks" =>
|
34
|
+
{
|
35
|
+
"total" => 25,
|
36
|
+
"open" => 21
|
37
|
+
}
|
38
|
+
},
|
39
|
+
{
|
40
|
+
"date" => '2014-04-24',
|
41
|
+
"story_points" =>
|
42
|
+
{
|
43
|
+
"total" => 30,
|
44
|
+
"open" => 21
|
45
|
+
},
|
46
|
+
"tasks" =>
|
47
|
+
{
|
48
|
+
"total" => 26,
|
49
|
+
"open" => 19
|
50
|
+
},
|
51
|
+
"story_points_extra" =>
|
52
|
+
{
|
53
|
+
"done" => 3
|
54
|
+
},
|
55
|
+
"tasks_extra" =>
|
56
|
+
{
|
57
|
+
"done" => 2
|
58
|
+
}
|
59
|
+
}
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
it "creates first data entry" do
|
64
|
+
@burndown_data.story_points.open = 16
|
65
|
+
@burndown_data.story_points.done = 7
|
66
|
+
@burndown_data.tasks.open = 10
|
67
|
+
@burndown_data.tasks.done = 11
|
68
|
+
|
69
|
+
@chart.add_data(@burndown_data,Date.parse("2014-05-30"))
|
70
|
+
|
71
|
+
expect( @chart.data["days"].first["story_points"] ).to eq(
|
72
|
+
{
|
73
|
+
"total" => 23,
|
74
|
+
"open" => 16
|
75
|
+
} )
|
76
|
+
expect( @chart.data["days"].first["tasks"] ).to eq(
|
77
|
+
{
|
78
|
+
"total" => 21,
|
79
|
+
"open" => 10
|
80
|
+
} )
|
81
|
+
end
|
82
|
+
|
83
|
+
it "returns sprint number" do
|
84
|
+
expect(@chart.sprint).to eq 1
|
85
|
+
end
|
86
|
+
|
87
|
+
it "adds data" do
|
88
|
+
@chart.data["days"] = @raw_data
|
89
|
+
|
90
|
+
@burndown_data.story_points.open = 16
|
91
|
+
@burndown_data.story_points.done = 7
|
92
|
+
@burndown_data.tasks.open = 10
|
93
|
+
@burndown_data.tasks.done = 11
|
94
|
+
@burndown_data.extra_story_points.open = 2
|
95
|
+
@burndown_data.extra_story_points.done = 3
|
96
|
+
@burndown_data.extra_tasks.open = 5
|
97
|
+
@burndown_data.extra_tasks.done = 2
|
98
|
+
|
99
|
+
@chart.add_data(@burndown_data,Date.parse("2014-05-30"))
|
100
|
+
|
101
|
+
expect( @chart.data["days"].count ).to eq 3
|
102
|
+
expect( @chart.data["days"].last["date"] ).to eq ( "2014-05-30" )
|
103
|
+
expect( @chart.data["days"].last["story_points"] ).to eq ( {
|
104
|
+
"total" => 23,
|
105
|
+
"open" => 16
|
106
|
+
} )
|
107
|
+
expect( @chart.data["days"].last["tasks"] ).to eq ( {
|
108
|
+
"total" => 21,
|
109
|
+
"open" => 10
|
110
|
+
} )
|
111
|
+
expect( @chart.data["days"].last["story_points_extra"] ).to eq ( {
|
112
|
+
"done" => 3
|
113
|
+
} )
|
114
|
+
expect( @chart.data["days"].last["tasks_extra"] ).to eq ( {
|
115
|
+
"done" => 2
|
116
|
+
} )
|
117
|
+
end
|
118
|
+
|
119
|
+
it "replaces data of same day" do
|
120
|
+
@chart.data["days"] = @raw_data
|
121
|
+
|
122
|
+
@burndown_data.story_points.open = 16
|
123
|
+
@burndown_data.story_points.done = 7
|
124
|
+
@burndown_data.tasks.open = 10
|
125
|
+
@burndown_data.tasks.done = 11
|
126
|
+
|
127
|
+
@chart.add_data(@burndown_data,Date.parse("2014-05-30"))
|
128
|
+
|
129
|
+
expect( @chart.data["days"].count ).to eq 3
|
130
|
+
expect( @chart.data["days"].last["story_points"] ).to eq ( {
|
131
|
+
"total" => 23,
|
132
|
+
"open" => 16
|
133
|
+
} )
|
134
|
+
|
135
|
+
@burndown_data.story_points.done = 8
|
136
|
+
@chart.add_data(@burndown_data,Date.parse("2014-05-30"))
|
137
|
+
|
138
|
+
expect( @chart.data["days"].count ).to eq 3
|
139
|
+
expect( @chart.data["days"].last["story_points"] ).to eq ( {
|
140
|
+
"total" => 24,
|
141
|
+
"open" => 16
|
142
|
+
} )
|
143
|
+
end
|
144
|
+
|
145
|
+
it "reads data" do
|
146
|
+
@chart.read_data given_file('burndown-data.yaml')
|
147
|
+
|
148
|
+
expect(@chart.data["days"]).to eq @raw_data
|
149
|
+
end
|
150
|
+
|
151
|
+
it "writes data" do
|
152
|
+
read_path = given_file('burndown-data.yaml')
|
153
|
+
@chart.read_data(read_path)
|
154
|
+
|
155
|
+
write_path = given_dummy_file
|
156
|
+
@chart.write_data(write_path)
|
157
|
+
|
158
|
+
expect(File.read(write_path)).to eq File.read(read_path)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "doesn't write extra entries with 0 values" do
|
162
|
+
raw_data = [
|
163
|
+
{
|
164
|
+
"date" => '2014-04-24',
|
165
|
+
"story_points" =>
|
166
|
+
{
|
167
|
+
"total" => 30,
|
168
|
+
"open" => 21
|
169
|
+
},
|
170
|
+
"tasks" =>
|
171
|
+
{
|
172
|
+
"total" => 26,
|
173
|
+
"open" => 19
|
174
|
+
},
|
175
|
+
"story_points_extra" =>
|
176
|
+
{
|
177
|
+
"done" => 0
|
178
|
+
},
|
179
|
+
"tasks_extra" =>
|
180
|
+
{
|
181
|
+
"done" => 0
|
182
|
+
}
|
183
|
+
}
|
184
|
+
]
|
185
|
+
@chart.data["days"] = raw_data
|
186
|
+
@chart.data["meta"]["board_id"] = "1234"
|
187
|
+
|
188
|
+
write_path = given_dummy_file
|
189
|
+
@chart.write_data(write_path)
|
190
|
+
|
191
|
+
expected_file_content = <<EOT
|
192
|
+
---
|
193
|
+
meta:
|
194
|
+
board_id: '1234'
|
195
|
+
sprint: 1
|
196
|
+
total_days: 10
|
197
|
+
weekend_lines:
|
198
|
+
- 3.5
|
199
|
+
- 8.5
|
200
|
+
days:
|
201
|
+
- date: '2014-04-24'
|
202
|
+
story_points:
|
203
|
+
total: 30
|
204
|
+
open: 21
|
205
|
+
tasks:
|
206
|
+
total: 26
|
207
|
+
open: 19
|
208
|
+
EOT
|
209
|
+
expect(File.read(write_path)).to eq expected_file_content
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
|
214
|
+
describe "commands" do
|
215
|
+
use_given_filesystem(keep_files: true)
|
216
|
+
|
217
|
+
describe "setup" do
|
218
|
+
it "initializes new chart" do
|
219
|
+
path = given_directory
|
220
|
+
@chart.setup(path,"myboardid")
|
221
|
+
|
222
|
+
expect(File.exist?(File.join(path,"burndown-data-01.yaml"))).to be true
|
223
|
+
|
224
|
+
chart = BurndownChart.new(@settings)
|
225
|
+
chart.read_data(File.join(path,"burndown-data-01.yaml"))
|
226
|
+
|
227
|
+
expect(chart.board_id).to eq "myboardid"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe "update" do
|
232
|
+
it "updates chart with latest data" do
|
233
|
+
card_url_match = /https:\/\/trello.com\/1\/boards\/myboardid\/cards\?-*/
|
234
|
+
|
235
|
+
stub_request(:any,card_url_match).to_return(:status => 200,
|
236
|
+
:body => load_test_file("cards.json"), :headers => {})
|
237
|
+
|
238
|
+
list_url_match = /https:\/\/trello.com\/1\/boards\/myboardid\/lists\?-*/
|
239
|
+
|
240
|
+
stub_request(:any,list_url_match).to_return(:status => 200,
|
241
|
+
:body => load_test_file("lists.json"), :headers => {})
|
242
|
+
|
243
|
+
path = given_directory_from_data("burndown_dir")
|
244
|
+
|
245
|
+
before = BurndownChart.new(@settings)
|
246
|
+
before.read_data(File.join(path,'burndown-data-02.yaml'))
|
247
|
+
|
248
|
+
@chart.update(path)
|
249
|
+
|
250
|
+
after = BurndownChart.new(@settings)
|
251
|
+
after.read_data(File.join(path,'burndown-data-02.yaml'))
|
252
|
+
|
253
|
+
expect(after.days.size).to eq before.days.size + 1
|
254
|
+
end
|
255
|
+
|
256
|
+
it "overwrites data on same date" do
|
257
|
+
card_url_match = /https:\/\/trello.com\/1\/boards\/myboardid\/cards\?-*/
|
258
|
+
|
259
|
+
stub_request(:any,card_url_match).to_return(:status => 200,
|
260
|
+
:body => load_test_file("cards.json"), :headers => {})
|
261
|
+
|
262
|
+
list_url_match = /https:\/\/trello.com\/1\/boards\/myboardid\/lists\?-*/
|
263
|
+
|
264
|
+
stub_request(:any,list_url_match).to_return(:status => 200,
|
265
|
+
:body => load_test_file("lists.json"), :headers => {})
|
266
|
+
|
267
|
+
path = given_directory_from_data("burndown_dir")
|
268
|
+
|
269
|
+
before = BurndownChart.new(@settings)
|
270
|
+
before.read_data(File.join(path,'burndown-data-02.yaml'))
|
271
|
+
|
272
|
+
@chart.update(path)
|
273
|
+
@chart.update(path)
|
274
|
+
|
275
|
+
after = BurndownChart.new(@settings)
|
276
|
+
after.read_data(File.join(path,'burndown-data-02.yaml'))
|
277
|
+
|
278
|
+
expect(after.days.size).to eq before.days.size + 1
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
describe "create_next_sprint" do
|
283
|
+
it "create new sprint file" do
|
284
|
+
path = given_directory_from_data("burndown_dir")
|
285
|
+
chart = BurndownChart.new(@settings)
|
286
|
+
chart.create_next_sprint(path)
|
287
|
+
|
288
|
+
next_sprint_file = File.join(path, "burndown-data-03.yaml")
|
289
|
+
expect(File.exist?(next_sprint_file)).to be true
|
290
|
+
|
291
|
+
expected_file_content = <<EOT
|
292
|
+
---
|
293
|
+
meta:
|
294
|
+
board_id: myboardid
|
295
|
+
sprint: 3
|
296
|
+
total_days: 9
|
297
|
+
weekend_lines:
|
298
|
+
- 3.5
|
299
|
+
- 7.5
|
300
|
+
days: []
|
301
|
+
EOT
|
302
|
+
expect(File.read(next_sprint_file)).to eq expected_file_content
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|