trollolo 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +5 -1
  4. data/CHANGELOG.md +29 -0
  5. data/Gemfile +7 -2
  6. data/README.md +19 -0
  7. data/bin/trollolo +1 -1
  8. data/lib/array.rb +6 -0
  9. data/lib/backup.rb +67 -0
  10. data/lib/burndown_chart.rb +96 -67
  11. data/lib/burndown_data.rb +62 -123
  12. data/lib/card.rb +74 -30
  13. data/lib/cli.rb +131 -9
  14. data/lib/column.rb +61 -0
  15. data/lib/result.rb +0 -0
  16. data/lib/scrum_board.rb +104 -0
  17. data/lib/settings.rb +9 -4
  18. data/lib/trello_wrapper.rb +62 -0
  19. data/lib/trollolo.rb +10 -7
  20. data/lib/version.rb +1 -1
  21. data/scripts/.gitignore +1 -0
  22. data/scripts/burndowndata.py +113 -0
  23. data/scripts/create_burndown.py +111 -146
  24. data/scripts/graph.py +116 -0
  25. data/scripts/plot.py +131 -0
  26. data/spec/data/board.json +63 -0
  27. data/spec/data/burndown-data.yaml +3 -0
  28. data/spec/data/burndown_dir/burndown-data-01.yaml +1 -1
  29. data/spec/data/burndown_dir/burndown-data-02.yaml +1 -1
  30. data/spec/data/card.json +61 -0
  31. data/spec/data/full-board.json +1626 -0
  32. data/spec/data/lists.json +25 -25
  33. data/spec/data/trollolorc +5 -0
  34. data/spec/{command_line_spec.rb → integration/command_line_spec.rb} +1 -4
  35. data/spec/integration/create_burndown_spec.rb +57 -0
  36. data/spec/integration/integration_spec_helper.rb +10 -0
  37. data/spec/integration/support/aruba_hook.rb +11 -0
  38. data/spec/integration/support/custom_matchers.rb +13 -0
  39. data/spec/{wrapper → integration/wrapper}/credentials_input_wrapper +2 -2
  40. data/spec/{wrapper → integration/wrapper}/empty_config_trollolo_wrapper +2 -2
  41. data/spec/integration/wrapper/trollolo_wrapper +10 -0
  42. data/spec/unit/backup_spec.rb +107 -0
  43. data/spec/unit/burndown_chart_spec.rb +396 -0
  44. data/spec/unit/burndown_data_spec.rb +118 -0
  45. data/spec/unit/card_spec.rb +79 -0
  46. data/spec/unit/cli_spec.rb +38 -0
  47. data/spec/unit/retrieve_data_spec.rb +54 -0
  48. data/spec/unit/scrum_board_spec.rb +18 -0
  49. data/spec/{settings_spec.rb → unit/settings_spec.rb} +1 -1
  50. data/spec/{spec_helper.rb → unit/spec_helper.rb} +4 -12
  51. data/spec/unit/support/test_data_operations.rb +7 -0
  52. data/spec/unit/support/update_webmock_data +17 -0
  53. data/spec/unit/support/webmocks.rb +52 -0
  54. data/spec/unit/trello_wrapper_spec.rb +47 -0
  55. data/trollolo.gemspec +10 -11
  56. metadata +54 -37
  57. data/lib/trello.rb +0 -66
  58. data/spec/burndown_chart_spec.rb +0 -307
  59. data/spec/burndown_data_spec.rb +0 -125
  60. data/spec/card_spec.rb +0 -15
  61. data/spec/cli_spec.rb +0 -18
  62. data/spec/data/cards.json +0 -1002
  63. data/spec/trello_spec.rb +0 -32
  64. data/spec/wrapper/trollolo_wrapper +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c781701661ccd9a1f161f56cd7c07c6b4b0cf7e0
4
- data.tar.gz: a4615b48df363c710ff45788c9fb72c963690ed3
3
+ metadata.gz: 5adc2629b2f526c70851abc5ba72765860d73e2f
4
+ data.tar.gz: d31159f96937237011765b1df87036d90a94fede
5
5
  SHA512:
6
- metadata.gz: 0772e65840bbc21aff6cd757adace1938fbca7b4094c78a550d1d7c2bb4069ff66e3af37e17b55618b259a62ee0ce7c0726415d6f2f593f813f22ece587fc528
7
- data.tar.gz: 321418dcee6afb7c3283ffb13370c287367270887fd8b8a316ff2d00fc384805762b68a1cb1c4b8d3bb43bb8407a8f3df3794d84b03f5f4e42cdf5cc88d1fb16
6
+ metadata.gz: 7eb4de3cb4a144902d467e40471236ca54c8992228fc5b1b6531148887512609bb5e56f2b43abb646d4eca512008e9c6b7d3c228073c3f1bb2a94cd6a22532e9
7
+ data.tar.gz: 7df063a8b64b2e8eea6118115d1e0ab8b7653783c972abcd046b0c25b1a6951ac39147b2d4e01f4e1c1dfc7724f5554347cc85570695b5026310a6f746b9ab6f
data/.gitignore CHANGED
@@ -2,3 +2,5 @@
2
2
  Gemfile.lock
3
3
  tmp
4
4
  coverage
5
+ *.png
6
+ *.yaml
@@ -1,8 +1,12 @@
1
1
  language: ruby
2
+ cache: bundler
3
+ before_install:
4
+ - gem update --system
5
+ - gem --version
2
6
  rvm:
3
7
  - "2.0.0"
4
8
  - "2.1.1"
5
- script: bundle exec rspec spec
9
+ script: bundle exec rspec spec/unit
6
10
 
7
11
  addons:
8
12
  code_climate:
@@ -1,5 +1,34 @@
1
1
  # Trollolo Changelog
2
2
 
3
+ ## Version 0.0.4
4
+
5
+ * Allow to story points anywhere in the card name
6
+ * Read columns considered as not done from sprint yaml. This makes it possible
7
+ to configure additional work in progress columns.
8
+ * Add `burndowns` command to update multiple charts at once
9
+ * Add option `--no-tasks` to not show tasks part of the graph
10
+ * Add option `--with-fast-lane` to separately plot cards which have a
11
+ `Fast Lane` label.
12
+ * Store date and time when chart was updated
13
+ * Exclude checlists named "Feedback" from the tasks calculation
14
+ * Add commands to show organization data:
15
+ * The command `organization` shows basic info about the organization.
16
+ * The command `organization_members` lists all members.
17
+ * Add command to get raw JSON from Trello API
18
+ * Add handling of done tasks under the waterline at the beginning of a new
19
+ sprint
20
+ * Add `--plot` option to `burndown` command to immediately show chart
21
+ * Add `--output` option to `plot` command to specify the directory it uses
22
+ * Save date and time of fetching burndown data
23
+ * Optionally fetch general meta data for burndown chart from special card
24
+ * Include list named "Blocked" in burndown calculation
25
+ * Implement basic backup function:
26
+ * The command `backup` creates a backup of a board identified by its id
27
+ * The command `show_backup` shows the content of the backup
28
+ * The command `list_backups` shows the list of backups which have been made
29
+ * The backups are stored to the directory `~/.trollolo/backups` in JSON
30
+ format
31
+
3
32
  ## Version 0.0.3
4
33
 
5
34
  * Document burndown generation work flow
data/Gemfile CHANGED
@@ -1,11 +1,16 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
5
  group :test do
6
6
  gem 'codeclimate-test-reporter'
7
- gem 'rspec', "~>3"
7
+ gem 'rspec', '~> 3'
8
8
  gem 'aruba'
9
9
  gem 'webmock'
10
10
  gem 'given_filesystem'
11
+ gem 'byebug'
12
+ gem 'awesome_print'
13
+ gem 'pry'
14
+ gem 'ronn', '>=0.7.3'
15
+ gem 'rake'
11
16
  end
data/README.md CHANGED
@@ -19,6 +19,15 @@ functions work see the Trollolo man page. There is an
19
19
  [example Trello board](https://trello.com/b/CRdddpdy/trollolo-testing-board)
20
20
  which demonstrates the expected structure.
21
21
 
22
+ ## Installation
23
+
24
+ You can install Trollolo as gem with `gem install trollolo`.
25
+
26
+ For the chart generation you will need a working matplotlib installation and
27
+ the python module to read YAML. On openSUSE you can get that with
28
+
29
+ zypper install python-matplotlib python-matplotlib-tk python-PyYAML
30
+
22
31
  ## Configuration
23
32
 
24
33
  Trollolo reads a configuration file `.trollolorc` in the home directory of the
@@ -85,8 +94,18 @@ To generate the actual burndown chart, go to the working directory and call:
85
94
 
86
95
  trollolo plot SPRINT_NUMBER
87
96
 
97
+ or fetch and plot data in one step with:
98
+
99
+ trollolo burndown --plot
100
+
88
101
  This will take the data from the file `burndown-data-SPRINT_NUMBER.yaml` and
89
102
  create a nice chart from it. It will show the chart and also create a file
90
103
  `burndown-SPRINT_NUMBER.png` you can upload as cover graphics to a card on your
91
104
  Trello board.
92
105
 
106
+ Some more info can be found in the command line help with `trollolo help` and
107
+ `trollolo help burndown`.
108
+
109
+ ### Example
110
+
111
+ ![Burndown example](https://raw.githubusercontent.com/openSUSE/trollolo/master/examples/burndown-26.png)
@@ -26,7 +26,7 @@ Cli.settings = Settings.new(config_path)
26
26
  ENV["THOR_DEBUG"] = "1"
27
27
  begin
28
28
  Cli.check_unknown_options!
29
- result = Cli.start ARGV
29
+ Cli.start
30
30
  rescue Thor::Error => e
31
31
  STDERR.puts e
32
32
  exit 1
@@ -0,0 +1,6 @@
1
+ class Array
2
+ def sum
3
+ return 0 if empty?
4
+ inject(:+)
5
+ end
6
+ end
@@ -0,0 +1,67 @@
1
+ class Backup
2
+
3
+ attr_accessor :directory
4
+
5
+ def initialize settings
6
+ @settings = settings
7
+ @directory = File.expand_path("~/.trollolo/backup")
8
+ end
9
+
10
+ def backup(board_id)
11
+ backup_path = File.join(@directory, board_id)
12
+ FileUtils.mkdir_p(backup_path)
13
+
14
+ trello = TrelloWrapper.new(@settings)
15
+
16
+ data = trello.backup(board_id)
17
+
18
+ File.open(File.join(backup_path, "board.json"), "w") do |f|
19
+ f.write(data)
20
+ end
21
+ end
22
+
23
+ def list
24
+ Dir.entries(@directory).reject { |d| d =~ /^\./ }
25
+ end
26
+
27
+ def show(board_id, options = {})
28
+ out = options[:output] || STDOUT
29
+
30
+ backup_path = File.join(@directory, board_id)
31
+
32
+ board = JSON.parse(File.read(File.join(backup_path, "board.json")))
33
+
34
+ out.puts board["name"]
35
+
36
+ lists = {}
37
+ board["lists"].each do |list|
38
+ lists[list["id"]] = []
39
+ end
40
+ board["cards"].each do |card|
41
+ if lists[card["idList"]]
42
+ lists[card["idList"]].push(card)
43
+ end
44
+ end
45
+
46
+ board["lists"].each do |list|
47
+ out.puts " #{list['name']}"
48
+ lists[list["id"]].each do |card|
49
+ out.puts " " + card["name"]
50
+ if options["show-descriptions"]
51
+ if !card["desc"].empty?
52
+ out.puts " Description"
53
+ out.puts " " + card["desc"]
54
+ end
55
+ end
56
+ card["checklists"].each do |checklist|
57
+ out.puts " " + checklist["name"]
58
+ checklist["checkItems"].each do |checklist_item|
59
+ out.puts " " + checklist_item["name"] + " (" +
60
+ checklist_item["state"] + ")"
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ end
@@ -14,11 +14,10 @@
14
14
  #
15
15
  # To contact SUSE about this file by physical or electronic mail,
16
16
  # you may find current contact information at www.suse.com
17
-
18
17
  class BurndownChart
19
18
 
20
19
  attr_accessor :data
21
-
20
+
22
21
  def initialize(settings)
23
22
  @settings = settings
24
23
  @burndown_data = BurndownData.new settings
@@ -33,65 +32,63 @@ class BurndownChart
33
32
  "days" => []
34
33
  }
35
34
  end
36
-
35
+
37
36
  def sprint
38
37
  @data["meta"]["sprint"]
39
38
  end
40
-
39
+
41
40
  def sprint= s
42
41
  @data["meta"]["sprint"] = s
43
42
  end
44
-
43
+
45
44
  def board_id
46
45
  @data["meta"]["board_id"]
47
46
  end
48
-
47
+
49
48
  def board_id= id
50
49
  @data["meta"]["board_id"] = id
51
50
  end
52
-
51
+
53
52
  def days
54
53
  @data["days"]
55
54
  end
56
-
57
- def add_data(burndown_data, date)
58
- new_entry = {
59
- "date" => date.to_s,
60
- "story_points" => {
61
- "total" => burndown_data.story_points.total,
62
- "open" => burndown_data.story_points.open
63
- },
64
- "tasks" => {
65
- "total" => burndown_data.tasks.total,
66
- "open" => burndown_data.tasks.open
67
- },
68
- "story_points_extra" => {
69
- "done" => burndown_data.extra_story_points.done
70
- },
71
- "tasks_extra" => {
72
- "done" => burndown_data.extra_tasks.done
73
- }
74
- }
75
- new_days = Array.new
76
- replaced_entry = false
77
- @data["days"].each do |entry|
78
- if entry["date"] == date.to_s
79
- new_days.push(new_entry)
80
- replaced_entry = true
81
- else
82
- new_days.push(entry)
55
+
56
+ def merge_meta_data_from_board(burndown_data)
57
+ if burndown_data.meta
58
+ m = burndown_data.meta
59
+ if m["sprint"] == @data["meta"]["sprint"].to_i
60
+ @data["meta"] = @data["meta"].merge(m)
83
61
  end
84
62
  end
85
- if !replaced_entry
86
- new_days.push(new_entry)
63
+ end
64
+
65
+ def replace_entry(date, new_entry)
66
+ days.each_with_index do |entry, idx|
67
+ days[idx] = new_entry if entry["date"] == date.to_s
68
+ end
69
+ end
70
+
71
+ def entry_exists?(date)
72
+ days.any? { |entry| entry["date"] == date.to_s }
73
+ end
74
+
75
+ def add_data(burndown_data)
76
+ new_entry = burndown_data.to_hash
77
+ if entry_exists?(burndown_data.date_time.to_date)
78
+ replace_entry(burndown_data.date_time.to_date, new_entry)
79
+ else
80
+ days.push(new_entry)
87
81
  end
88
- @data["days"] = new_days
89
82
  end
90
-
83
+
91
84
  def read_data filename
92
85
  @data = YAML.load_file filename
86
+ not_done_columns = @data["meta"]["not_done_columns"]
87
+ if not_done_columns
88
+ @settings.not_done_columns = not_done_columns
89
+ end
93
90
  end
94
-
91
+
95
92
  def write_data filename
96
93
  @data["days"].each do |day|
97
94
  [ "story_points_extra", "tasks_extra" ].each do |key|
@@ -101,58 +98,90 @@ class BurndownChart
101
98
  end
102
99
  end
103
100
 
104
- File.open( filename, "w" ) do |file|
105
- file.write @data.to_yaml
101
+ begin
102
+ File.open( filename, "w" ) do |file|
103
+ file.write @data.to_yaml
104
+ end
105
+ rescue Errno::ENOENT
106
+ raise TrolloloError.new( "'#{filename}' not found" )
106
107
  end
107
108
  end
108
-
109
+
109
110
  def burndown_data_filename
110
111
  "burndown-data-#{sprint.to_s.rjust(2,"0")}.yaml"
111
112
  end
112
-
113
+
113
114
  def setup(burndown_dir, board_id)
114
115
  self.board_id = board_id
115
116
  FileUtils.mkdir_p burndown_dir
116
117
  write_data File.join(burndown_dir, burndown_data_filename)
117
118
  end
118
-
119
- def update(burndown_dir)
120
- Dir.glob("#{burndown_dir}/burndown-data-*.yaml").each do |file|
121
- file =~ /burndown-data-(.*).yaml/
122
- current_sprint = $1.to_i
123
- if current_sprint > sprint
124
- self.sprint = current_sprint
125
- end
119
+
120
+ class << self
121
+
122
+ def plot_helper
123
+ File.expand_path('../../scripts/create_burndown.py', __FILE__ )
126
124
  end
127
- burndown_data_path = File.join(burndown_dir, burndown_data_filename)
128
- begin
129
- read_data burndown_data_path
130
- @burndown_data.board_id = board_id
131
- @burndown_data.fetch
132
- add_data(@burndown_data, Date.today)
133
- write_data burndown_data_path
134
- puts "Updated data for sprint #{self.sprint}"
135
- rescue Errno::ENOENT
136
- raise TrolloloError.new( "'#{burndown_data_path}' not found" )
125
+
126
+ def plot(sprint_number, options)
127
+ sprint_number = sprint_number.to_s.rjust(2, '0')
128
+ cli_switches = process_options(options)
129
+ system "python #{plot_helper} #{sprint_number} #{cli_switches.join(' ')}"
137
130
  end
131
+
132
+ private
133
+
134
+ def process_options(hash)
135
+ return [] unless hash
136
+ [].tap do |cli_switches|
137
+ cli_switches << '--no-tasks' if hash['no-tasks']
138
+ cli_switches << '--with-fast-lane' if hash['with-fast-lane']
139
+ cli_switches << "--output #{hash['output']}" if hash['output']
140
+ cli_switches << '--verbose' if hash['verbose']
141
+ end
142
+ end
143
+
138
144
  end
139
145
 
140
- def create_next_sprint(burndown_dir)
146
+ def last_sprint(burndown_dir)
147
+ last_sprint = sprint
141
148
  Dir.glob("#{burndown_dir}/burndown-data-*.yaml").each do |file|
142
149
  file =~ /burndown-data-(.*).yaml/
143
150
  current_sprint = $1.to_i
144
- if current_sprint > sprint
145
- self.sprint = current_sprint
151
+ if current_sprint > last_sprint
152
+ last_sprint = current_sprint
146
153
  end
147
154
  end
155
+ last_sprint
156
+ end
157
+
158
+ def load_last_sprint(burndown_dir)
159
+ self.sprint = last_sprint(burndown_dir)
148
160
  burndown_data_path = File.join(burndown_dir, burndown_data_filename)
149
161
  begin
150
162
  read_data burndown_data_path
151
- self.sprint = self.sprint + 1
152
- @data["days"] = []
153
- write_data File.join(burndown_dir, burndown_data_filename)
154
163
  rescue Errno::ENOENT
155
164
  raise TrolloloError.new( "'#{burndown_data_path}' not found" )
156
165
  end
166
+ return burndown_data_path
157
167
  end
168
+
169
+ def update(options)
170
+ burndown_data_path = load_last_sprint(options['output'] || Dir.pwd)
171
+ @burndown_data.board_id = board_id
172
+ @burndown_data.fetch
173
+ add_data(@burndown_data)
174
+ write_data burndown_data_path
175
+ if options[:plot]
176
+ BurndownChart.plot(self.sprint, options)
177
+ end
178
+ end
179
+
180
+ def create_next_sprint(burndown_dir)
181
+ load_last_sprint(burndown_dir)
182
+ self.sprint = self.sprint + 1
183
+ @data["days"] = []
184
+ write_data File.join(burndown_dir, burndown_data_filename)
185
+ end
186
+
158
187
  end
@@ -20,153 +20,92 @@
20
20
  # or interaction with the files used to generate burndown charts.
21
21
  class BurndownData
22
22
 
23
- attr_accessor :story_points, :tasks, :extra_story_points, :extra_tasks
24
- attr_accessor :board_id
25
-
26
23
  class Result
27
24
  attr_accessor :open, :done
28
-
25
+
29
26
  def initialize
30
27
  @open = 0
31
28
  @done = 0
32
29
  end
33
-
30
+
34
31
  def total
35
32
  @open + @done
36
33
  end
37
34
  end
38
35
 
39
- def initialize settings
40
- @settings = settings
41
-
42
- @story_points = Result.new
43
- @tasks = Result.new
36
+ attr_accessor :story_points, :tasks, :extra_story_points, :extra_tasks,
37
+ :board_id, :fast_lane_cards, :date_time
38
+ attr_reader :meta
44
39
 
40
+ def initialize(settings)
41
+ @settings = settings
42
+ @story_points = Result.new
43
+ @tasks = Result.new
45
44
  @extra_story_points = Result.new
46
- @extra_tasks = Result.new
47
- end
48
-
49
- def trello
50
- Trello.new(board_id: @board_id, developer_public_key: @settings.developer_public_key, member_token: @settings.member_token)
45
+ @extra_tasks = Result.new
46
+ @fast_lane_cards = Result.new
51
47
  end
52
48
 
53
- def fetch_todo_list_id
54
- lists = trello.lists
55
- lists.each do |l|
56
- if l["name"] =~ /^Sprint Backlog$/
57
- return l["id"]
58
- end
49
+ def to_hash
50
+ base = {
51
+ "date" => date_time.to_date.to_s,
52
+ "updated_at" => date_time.to_s,
53
+ "story_points" => {
54
+ "total" => story_points.total,
55
+ "open" => story_points.open
56
+ },
57
+ "tasks" => {
58
+ "total" => tasks.total,
59
+ "open" => tasks.open
60
+ },
61
+ "story_points_extra" => {
62
+ "done" => extra_story_points.done
63
+ },
64
+ "tasks_extra" => {
65
+ "done" => extra_tasks.done
66
+ }
67
+ }
68
+ if fast_lane_cards.total > 0
69
+ base.merge("fast_lane" => {
70
+ "total" => fast_lane_cards.total,
71
+ "open" => fast_lane_cards.open
72
+ })
73
+ else
74
+ base
59
75
  end
60
-
61
- raise "Unable to find sprint backlog column on sprint board"
62
76
  end
63
77
 
64
- def fetch_doing_list_id
65
- lists = trello.lists
66
- lists.each do |l|
67
- if l["name"] =~ /^Doing$/
68
- return l["id"]
69
- end
70
- end
71
-
72
- raise "Unable to find doing column on sprint board"
78
+ def trello
79
+ @trello ||= TrelloWrapper.new(@settings)
73
80
  end
74
81
 
75
- def fetch_done_list_id
76
- lists = trello.lists
77
- last_sprint = nil
78
- lists.each do |l|
79
- if l["name"] =~ /^Done Sprint (.*)$/
80
- sprint = $1.to_i
81
- if !last_sprint || sprint > last_sprint[:number]
82
- last_sprint = { :number => sprint, :id => l["id"] }
83
- end
84
- end
85
- end
86
-
87
- id = last_sprint[:id]
88
- if !id
89
- raise "Unable to find done column on sprint board"
90
- end
91
- id
82
+ def board
83
+ trello.board(@board_id)
92
84
  end
93
-
94
- def fetch
95
- cards = trello.cards
96
-
97
- todo_list_id = fetch_todo_list_id
98
- doing_list_id = fetch_doing_list_id
99
- done_list_id = fetch_done_list_id
100
-
101
- if @settings.verbose
102
- puts "Todo list: #{todo_list_id}"
103
- puts "Doing list: #{doing_list_id}"
104
- puts "Done list: #{done_list_id}"
105
- end
106
-
107
- sp_total = 0
108
- sp_done = 0
109
-
110
- extra_sp_total = 0
111
- extra_sp_done = 0
112
85
 
113
- tasks_total = 0
114
- tasks_done = 0
115
-
116
- extra_tasks_total = 0
117
- extra_tasks_done = 0
118
-
119
- cards.each do |c|
120
- card = Card.parse c
121
-
122
- list_id = c["idList"]
86
+ def fetch
87
+ get_meta
88
+ @story_points.done = board.done_story_points
89
+ @story_points.open = board.open_story_points
90
+ @tasks.open = board.tasks - board.closed_tasks
91
+ @tasks.done = board.closed_tasks
92
+ @extra_story_points.done = board.extra_done_story_points
93
+ @extra_story_points.open = board.extra_open_story_points
94
+ @extra_tasks.done = board.extra_closed_tasks
95
+ @extra_tasks.open = board.extra_tasks - board.extra_closed_tasks
96
+ @fast_lane_cards.done = board.done_fast_lane_cards_count
97
+ @fast_lane_cards.open = board.open_fast_lane_cards_count
98
+ @date_time = DateTime.now
99
+ end
123
100
 
124
- if list_id == todo_list_id || list_id == doing_list_id
125
- if card.has_sp?
126
- if card.extra?
127
- extra_sp_total += card.sp
128
- else
129
- sp_total += card.sp
130
- end
131
- end
132
- if card.extra?
133
- extra_tasks_total += card.tasks
134
- extra_tasks_done += card.tasks_done
135
- else
136
- tasks_total += card.tasks
137
- tasks_done += card.tasks_done
138
- end
139
- elsif list_id == done_list_id
140
- if card.has_sp?
141
- if card.extra?
142
- extra_sp_total += card.sp
143
- extra_sp_done += card.sp
144
- else
145
- sp_total += card.sp
146
- sp_done += card.sp
147
- end
148
- end
149
- if card.extra?
150
- extra_tasks_total += card.tasks
151
- extra_tasks_done += card.tasks_done
152
- else
153
- tasks_total += card.tasks
154
- tasks_done += card.tasks_done
155
- end
156
- end
157
- end
158
-
159
- @story_points.done = sp_done
160
- @story_points.open = sp_total - sp_done
161
-
162
- @tasks.done = tasks_done
163
- @tasks.open = tasks_total - tasks_done
101
+ private
164
102
 
165
- @extra_story_points.done = extra_sp_done
166
- @extra_story_points.open = extra_sp_total - extra_sp_done
167
-
168
- @extra_tasks.done = extra_tasks_done
169
- @extra_tasks.open = extra_tasks_total - extra_tasks_done
103
+ def get_meta
104
+ meta_cards = board.meta_cards
105
+ return unless meta_cards.any?
106
+ current_sprint_meta_card = meta_cards.max_by(&:sprint_number)
107
+ @meta = Card.parse_yaml_from_description(current_sprint_meta_card.desc)
108
+ @meta['sprint'] = current_sprint_meta_card.sprint_number
170
109
  end
171
110
 
172
111
  end