cards 0.7 → 0.9

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.
Files changed (40) hide show
  1. data/CHANGES.txt +9 -0
  2. data/README.txt +1 -1
  3. data/Rakefile +7 -1
  4. data/examples/csv to dot/generate_card_walls +3 -3
  5. data/examples/custom csv builder/Stories.csv +16 -0
  6. data/examples/custom csv builder/Stories.tracker.csv +29 -0
  7. data/examples/custom csv builder/generate_card_wall +31 -0
  8. data/examples/excel to visio/Business Processes.xls +0 -0
  9. data/examples/excel to visio/TPS-small.png +0 -0
  10. data/examples/excel to visio/generate card walls.rb +10 -0
  11. data/examples/excel to visio/readme.html +104 -0
  12. data/examples/importing into tracker api/Stories.csv +16 -0
  13. data/examples/importing into tracker api/Voting Example.numbers +0 -0
  14. data/examples/importing into tracker api/appscript_example.rb +18 -0
  15. data/examples/importing into tracker api/from_numbers_09 +50 -0
  16. data/examples/importing into tracker api/generate_card_wall +32 -0
  17. data/examples/importing into tracker api/osa.rb +39 -0
  18. data/examples/numbers to omnigraffle/Voting Example.numbers +0 -0
  19. data/examples/numbers to omnigraffle/generate_card_walls +11 -9
  20. data/lib/cards.rb +3 -1
  21. data/lib/cards/csv_builder.rb +36 -34
  22. data/lib/cards/csv_parser.rb +6 -44
  23. data/lib/cards/numbers_parser.rb +43 -0
  24. data/lib/cards/swim_lanes.rb +1 -0
  25. data/lib/cards/tabular_parser.rb +78 -0
  26. data/lib/cards/tracker_csv.rb +15 -13
  27. data/lib/cards/writers/dot_writer.rb +6 -2
  28. data/spec/cards/csv_parser_spec.rb +22 -4
  29. data/spec/cards/numbers_parser_spec.rb +25 -0
  30. data/spec/fixtures/Voting Example.numbers +0 -0
  31. metadata +37 -17
  32. data/Manifest.txt +0 -40
  33. data/examples/csv to dot/Stories.dot +0 -63
  34. data/examples/numbers to omnigraffle/Voting Example.numbers/Contents/PkgInfo +0 -1
  35. data/examples/numbers to omnigraffle/Voting Example.numbers/QuickLook/Thumbnail.jpg +0 -0
  36. data/examples/numbers to omnigraffle/Voting Example.numbers/document-thumbnail.tiff +0 -0
  37. data/examples/numbers to omnigraffle/Voting Example.numbers/index.xml.gz +0 -0
  38. data/examples/numbers to omnigraffle/Voting Example/Cards-Goals.csv +0 -7
  39. data/examples/numbers to omnigraffle/Voting Example/Cards-Stories.csv +0 -16
  40. data/examples/numbers to omnigraffle/Voting Example/Cards-Workflow.csv +0 -7
data/CHANGES.txt CHANGED
@@ -1,5 +1,14 @@
1
1
  = Change Log
2
2
 
3
+ == Version 0.9
4
+
5
+ * example that directly imports into tracker api
6
+ * direct exports out of numbers '09
7
+
8
+ == Version 0.8
9
+
10
+ * example of custom csv builder
11
+
3
12
  == Version 0.7
4
13
 
5
14
  * fixing the dot writer
data/README.txt CHANGED
@@ -1,4 +1,4 @@
1
- = Project: CardWallGen
1
+ = Project: Cards
2
2
 
3
3
  == Description
4
4
 
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ require 'hoe'
11
11
  require 'lib/cards'
12
12
 
13
13
  desc "Default Task"
14
- task :default => [:spec]
14
+ task :default => [:generate_manifest, :spec]
15
15
 
16
16
  task :test => :spec
17
17
 
@@ -19,6 +19,12 @@ Spec::Rake::SpecTask.new(:spec) do |t|
19
19
  t.spec_files = FileList['spec/**/*_spec.rb']
20
20
  end
21
21
 
22
+ task :generate_manifest do
23
+ File.open("Manifest.txt", "w") do |f|
24
+ f << FileList["CHANGES.txt", "Rakefile", "README.txt", "examples/**/*", "lib/**/*", "spec/**/*"].to_a.join("\n")
25
+ end
26
+ end
27
+
22
28
  desc "export to dot file"
23
29
  task :run_dot do
24
30
  file = ENV["FILE"] || SAMPLE_FILE
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
-
3
2
  require 'rubygems'
3
+ gem 'cards'
4
4
  require 'cards'
5
-
6
5
  include Cards
7
6
 
7
+
8
8
  def csv_file(name)
9
9
  CsvParser.new(File.dirname(__FILE__) + "/#{name}.csv")
10
10
  end
@@ -22,4 +22,4 @@ end
22
22
  puts "generated Stories.dot"
23
23
 
24
24
  # uncomment this line if you want to see the generated file
25
- # `open "#{File.dirname(__FILE__)}/Stories.dot"`
25
+ # `open "#{File.dirname(__FILE__)}/Stories.dot"`
@@ -0,0 +1,16 @@
1
+ Activity,Task,Story,Note,Estimate,Priority
2
+ Voter,Vote on question,Ability to see question,,1,H
3
+ ,,Ability to see choices,,1,M
4
+ ,,Ability to choose,This is the server trip,2,H
5
+ ,Navigate questions,Ability to continue to next / prev,,1,H
6
+ ,,Ability to see list of questions,This includes ability to jump to that question,4,H
7
+ ,Submit,Ability to return to new screen,,1,M
8
+ Admin,Create questions,Ability to enter question,,4,H
9
+ ,,Ability to reorder question,,4,H
10
+ ,,Ability to enter multiple choice question,,1,L
11
+ ,,Ability to enter text question,,1,H
12
+ ,,Ability to enter long text question,,1,H
13
+ ,,Ability to enter list question,,2,L
14
+ ,Compile votes,Ability to see results in spreadsheet,,4,H
15
+ ,,Ability to see summary of results,This includes percentage of each question answering which answer,2,M
16
+ ,Announce winners,,,,
@@ -0,0 +1,29 @@
1
+ Story,Labels,Story Type,Estimate,Description,Priority
2
+ Ability to see question,"Vote on question, H",,1,"while Voter
3
+ ",2
4
+ Ability to see choices,"Vote on question, M",,1,"while Voter
5
+ ",1
6
+ Ability to choose,"Vote on question, H",,2,"while Voter
7
+ This is the server trip",2
8
+ Ability to continue to next / prev,"Navigate questions, H",,1,"while Voter
9
+ ",2
10
+ Ability to see list of questions,"Navigate questions, H",,4,"while Voter
11
+ This includes ability to jump to that question",2
12
+ Ability to return to new screen,"Submit, M",,1,"while Voter
13
+ ",1
14
+ Ability to enter question,"Create questions, H",,4,"while Admin
15
+ ",2
16
+ Ability to reorder question,"Create questions, H",,4,"while Admin
17
+ ",2
18
+ Ability to enter multiple choice question,"Create questions, L",,1,"while Admin
19
+ ",0
20
+ Ability to enter text question,"Create questions, H",,1,"while Admin
21
+ ",2
22
+ Ability to enter long text question,"Create questions, H",,1,"while Admin
23
+ ",2
24
+ Ability to enter list question,"Create questions, L",,2,"while Admin
25
+ ",0
26
+ Ability to see results in spreadsheet,"Compile votes, H",,4,"while Admin
27
+ ",2
28
+ Ability to see summary of results,"Compile votes, M",,2,"while Admin
29
+ This includes percentage of each question answering which answer",1
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ gem 'cards'
4
+ require 'cards'
5
+ include Cards
6
+
7
+
8
+ def csv_file(name)
9
+ CsvParser.new(File.dirname(__FILE__) + "/#{name}.csv")
10
+ end
11
+
12
+ class Tracker < CsvBuilder
13
+ PRIORITY = %w(L M H)
14
+
15
+ def header
16
+ ["Story", "Labels", "Story Type", "Estimate", "Description", "Priority"]
17
+ end
18
+
19
+ def add_story(name, row = {})
20
+ @csv << [
21
+ name,
22
+ "#{@task}, #{row[:priority]}",
23
+ nil,
24
+ row[:estimate],
25
+ "while #{@activity}\n#{row[:note]}",
26
+ PRIORITY.index(row[:priority])
27
+ ]
28
+ end
29
+ end
30
+
31
+ Tracker.from csv_file("Stories")
@@ -0,0 +1,10 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/CardWallGenerator/include.rb'
2
+
3
+
4
+ data = load_excel_data 'Business Processes.xls', 1
5
+ create_visio_document data, 'Caution.vsd'
6
+
7
+ data = load_excel_data 'Business Processes.xls', 2
8
+ create_visio_document data, 'TPS.vsd'
9
+
10
+
@@ -0,0 +1,104 @@
1
+ <html>
2
+ <head>
3
+ <title>Card Wall Generator</title>
4
+ </head>
5
+ <body>
6
+ <div align="center">
7
+ <div style="width:800;" align="left">
8
+ <h1>Card Wall Generator</h1>
9
+
10
+
11
+
12
+ <h2>What is it?</h2>
13
+ <hr/>
14
+
15
+ <p>
16
+ this tool will take an excel spreadsheet with a hierarchical story list in it, and generate
17
+ a "card wall" in visio from it.
18
+ </p>
19
+
20
+ <p style="text-align:right">
21
+ <img src="TPS-small.png"><br>
22
+ <i>a "card wall"</i>
23
+ </p>
24
+
25
+ <p>
26
+ the spreadsheet might look like - <a href="business processes.xls">business processes.xls</a>
27
+ </p>
28
+
29
+ <p>
30
+ and the card wall might look like - <a href="TPS.vsd">TPS.vsd</a><br>
31
+ </p>
32
+
33
+
34
+
35
+
36
+ <h2>Installation</h2>
37
+ <hr/>
38
+
39
+ <h3>You will need:</h3>
40
+
41
+ <ul>
42
+ <li>Windows - sorry only works on windows right now...
43
+ <li>Excel
44
+ <li>Visio
45
+ <li>Ruby - download it from here - <a href="http://rubyforge.org/projects/rubyinstaller/">http://rubyforge.org/projects/rubyinstaller/</a>
46
+ </ul>
47
+
48
+ <p>
49
+ Once you have all those installed, you can test your installation by double clicking the
50
+ "generate card walls.rb" file. It should open excel for a second, then open up visio and
51
+ generate 2 card walls.
52
+ </p>
53
+
54
+ <h3>The Driver File</h3>
55
+
56
+ <p>
57
+ What the card wall generator does is configured by the "generate card walls.rb" file. Open
58
+ it up and take a look. You should see stuff like :
59
+ </p>
60
+
61
+ <code><pre>
62
+ data = load_excel_data 'Business Processes.xls', 1
63
+ create_visio_document data, 'Caution.vsd'
64
+ </pre></code>
65
+
66
+ <p>You can customize this for your needs. Basically copy these 2 lines for every card wall
67
+ you want to create, and substitute these for the values :
68
+ <ul>
69
+ <li>excel file with the master story list - "Business Processes.xls"
70
+ <li>index of the sheet within the excel file - "1"
71
+ <li>visio file that will be created - "Caution.vsd"
72
+ </ul>
73
+ </p>
74
+
75
+
76
+
77
+ <h2>More Information</h2>
78
+ <hr/>
79
+
80
+ <p>Visit the project website at :</p>
81
+
82
+ <p><a href="http://rubyforge.org/projects/cardwallgen">http://rubyforge.org/projects/cardwallgen</a></p>
83
+
84
+ <p>The documentation is still pretty sparse. If you can find someone that knows ruby, have them poke
85
+ around, and they'll probably be able to figure out how to do most things. Otherwise, email the list
86
+ at cardwallgen-users@rubyforge.org, or sign up for the list at the project website.</p>
87
+
88
+
89
+
90
+
91
+ <h2>License</h2>
92
+ <hr/>
93
+
94
+ <p>
95
+ <i>Copyright (C) 2006 Jeremy Stell-Smith</i>
96
+ </p>
97
+
98
+ <p>
99
+ This software is distributed under the LGPL license.
100
+ </p>
101
+ </div>
102
+ </div>
103
+ </body>
104
+ </html>
@@ -0,0 +1,16 @@
1
+ Activity,Task,Story,Note,Estimate,Priority
2
+ Voter,Vote on question,Ability to see question,,1,H
3
+ ,,Ability to see choices,,1,M
4
+ ,,Ability to choose,This is the server trip,2,H
5
+ ,Navigate questions,Ability to continue to next / prev,,1,H
6
+ ,,Ability to see list of questions,This includes ability to jump to that question,3,H
7
+ ,Submit,Ability to return to new screen,,1,M
8
+ Admin,Create questions,Ability to enter question,,3,H
9
+ ,,Ability to reorder question,,3,H
10
+ ,,Ability to enter multiple choice question,,1,L
11
+ ,,Ability to enter text question,,1,H
12
+ ,,Ability to enter long text question,,1,H
13
+ ,,Ability to enter list question,,2,L
14
+ ,Compile votes,Ability to see results in spreadsheet,,3,H
15
+ ,,Ability to see summary of results,This includes percentage of each question answering which answer,2,M
16
+ ,Announce winners,,,,
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'appscript'
3
+
4
+ itunes = Appscript.app('iTunes')
5
+
6
+ track = itunes.current_track
7
+ p track # <OSA::Itunes::FileTrack:0x1495e20>
8
+ p track.name # "Over The Rainbow"
9
+ p track.artist # "Keith Jarrett"
10
+ p track.duration # 362.368988037109
11
+ p track.date_added.to_s # "2006-06-30"
12
+ p track.enabled? # true
13
+
14
+ # Play the selected track.
15
+ itunes.play
16
+
17
+ # Fade the volume.
18
+ 100.times { |i| itunes.sound_volume = i; sleep 0.1 }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require File.dirname(__FILE__) + "/../../lib/cards" # you probably want to include the gem version
4
+ require 'rest_client'
5
+
6
+ token = "d6b1b5ded944959aec78754ff9f018bd"
7
+ project_id = "9989"
8
+ priority = %w(L M H)
9
+
10
+ project = RestClient::Resource.new("http://www.pivotaltracker.com/services/v2/projects/#{project_id}",
11
+ :headers => { "X-TrackerToken" => token })
12
+
13
+ # puts project["stories?filter=type:bug"].get
14
+
15
+ require 'rbosa'
16
+ numbers = OSA.app('Numbers')
17
+
18
+ # itunes = OSA.app('iTunes')
19
+ # track = itunes.current_track
20
+ # p track # <OSA::Itunes::FileTrack:0x1495e20>
21
+ # p track.name # "Over The Rainbow"
22
+ # p track.artist # "Keith Jarrett"
23
+ # p track.duration # 362.368988037109
24
+ # p track.date_added.to_s # "2006-06-30"
25
+ # p track.enabled? # true
26
+ #
27
+
28
+
29
+
30
+
31
+
32
+
33
+ csv = Cards::CsvParser.new(File.dirname(__FILE__) + "/Stories.csv")
34
+ rows = csv.denormalized_rows(:story, [:activity, :task])
35
+ rows = rows.sort_by {|row| priority.index(row[:priority])}
36
+
37
+ rows.each do |row|
38
+ puts "adding #{row[:story]}"
39
+
40
+ params = {}
41
+ params[:name] = row[:story]
42
+ # params[:story_type] = "bug"
43
+ params[:current_state] = "unstarted"
44
+ params[:requested_by] = "Cards Test User"
45
+ params[:estimate] = row[:estimate]
46
+ params[:description] = "while #{row[:activity]}\n#{row[:note]}"
47
+
48
+ # project["stories"].post(:story => params)
49
+ end
50
+
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require File.dirname(__FILE__) + "/../../lib/cards" # you probably want to include the gem version
4
+ require 'rest_client'
5
+
6
+ token = "d6b1b5ded944959aec78754ff9f018bd"
7
+ project_id = "9989"
8
+ priority = %w(L M H)
9
+
10
+ project = RestClient::Resource.new("http://www.pivotaltracker.com/services/v2/projects/#{project_id}",
11
+ :headers => { "X-TrackerToken" => token })
12
+
13
+ # puts project["stories?filter=type:bug"].get
14
+
15
+ csv = Cards::CsvParser.new(File.dirname(__FILE__) + "/Stories.csv")
16
+ rows = csv.denormalized_rows(:story, [:activity, :task])
17
+ rows = rows.sort_by {|row| priority.index(row[:priority])}
18
+
19
+ rows.each do |row|
20
+ puts "adding #{row[:story]}"
21
+
22
+ params = {}
23
+ params[:name] = row[:story]
24
+ # params[:story_type] = "bug"
25
+ params[:current_state] = "unstarted"
26
+ params[:requested_by] = "Cards Test User"
27
+ params[:estimate] = row[:estimate]
28
+ params[:description] = "while #{row[:activity]}\n#{row[:note]}"
29
+
30
+ project["stories"].post(:story => params)
31
+ end
32
+
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'rbosa'
3
+
4
+ def find_table(document, name)
5
+ document.sheets.each do |sheet|
6
+ sheet.tables.each do |table|
7
+ return table
8
+ end
9
+ end
10
+ end
11
+
12
+ numbers = OSA.app('Numbers')
13
+ document = numbers.open(File.dirname(__FILE__) + "/Voting Example.numbers")
14
+ table = find_table(document, "Stories")
15
+
16
+ p table.name
17
+ table.rows.each do |row|
18
+ values = []
19
+ row.cells.map do |cell|
20
+ values << cell.name << cell.value
21
+ end
22
+ p values
23
+ end
24
+
25
+ # itunes = OSA.app('iTunes')
26
+ #
27
+ # track = itunes.current_track
28
+ # p track # <OSA::Itunes::FileTrack:0x1495e20>
29
+ # p track.name # "Over The Rainbow"
30
+ # p track.artist # "Keith Jarrett"
31
+ # p track.duration # 362.368988037109
32
+ # p track.date_added.to_s # "2006-06-30"
33
+ # p track.enabled? # true
34
+ #
35
+ # # Play the selected track.
36
+ # itunes.play
37
+ #
38
+ # # Fade the volume.
39
+ # 100.times { |i| itunes.sound_volume = i; sleep 0.1 }
@@ -1,29 +1,31 @@
1
1
  #!/usr/bin/env ruby
2
- # this doesn't actually read the numbers document, you must export csv from numbers as we've done here,
3
- # the csv can then be parsed and turned into omnigraffle
4
-
5
2
  require 'rubygems'
3
+ gem 'cards'
6
4
  require 'cards'
5
+ require 'cards/numbers_parser'
7
6
  include Cards
8
7
 
9
- def csv_file(name)
10
- CsvParser.new(File.dirname(__FILE__) + "/Voting Example/Cards-#{name}.csv")
8
+ # this requires numbers '09, before that you must export to csv and use the csv parser
9
+
10
+ def table(name)
11
+ file = File.dirname(__FILE__) + "/Voting Example.numbers"
12
+ NumbersParser.new(file, name)
11
13
  end
12
14
 
13
15
  CardWall.colors[:goal] = :green
14
16
  CardWall.writer = GraffleWriter
15
17
 
16
- CardWall.from csv_file("Goals") do
18
+ CardWall.from table("Goals") do
17
19
  column :goal
18
20
  end
19
21
 
20
- CardWall.from csv_file("Workflow") do
22
+ CardWall.from table("Workflow") do
21
23
  row :role
22
24
  column :task, :wrap_at => 5
23
25
  end
24
26
 
25
- CardWall.from csv_file("Stories") do
26
- row :role
27
+ CardWall.from table("Stories") do
28
+ row :activity
27
29
  row :task
28
30
  column :story, :wrap_at => 4 do |card, row|
29
31
  card.name = " #{row[:estimate]}\n\n#{card.name}" unless row[:estimate].blank?
data/lib/cards.rb CHANGED
@@ -9,9 +9,11 @@ require 'cards/card_wall'
9
9
  require 'cards/writers/dot_writer'
10
10
  require 'cards/writers/graffle_writer'
11
11
 
12
+ # csv builders
13
+ require 'cards/csv_builder'
12
14
  require 'cards/master_story_list'
13
15
  require 'cards/tracker_csv'
14
16
 
15
17
  module Cards
16
- VERSION = "0.7"
18
+ VERSION = "0.9"
17
19
  end
@@ -1,46 +1,48 @@
1
- class CsvBuilder
2
- def initialize(file_name)
3
- @file_name = file_name
4
- @file = File.open(file_name, "wb")
5
- @csv = CSV::Writer.create(@file)
6
- @csv << header
7
- end
1
+ module Cards
2
+ class CsvBuilder
3
+ def initialize(file_name)
4
+ @file_name = file_name
5
+ @file = File.open(file_name, "wb")
6
+ @csv = CSV::Writer.create(@file)
7
+ @csv << header
8
+ end
8
9
 
9
- def header
10
- raise "implement me"
11
- end
10
+ def header
11
+ raise "implement me"
12
+ end
12
13
 
13
- def self.from(parser)
14
- builder = self.new(parser.default_output_file + ".#{self.to_s.gsub("Cards::", "").downcase}.csv")
14
+ def self.from(parser)
15
+ builder = self.new(parser.default_output_file + ".#{self.to_s.gsub("Cards::", "").downcase}.csv")
15
16
 
16
- parser.each_row do |row|
17
- builder.process(row)
18
- end
17
+ parser.each_row do |row|
18
+ builder.process(row)
19
+ end
19
20
 
20
- builder.done
21
- end
21
+ builder.done
22
+ end
22
23
 
23
- def process(row)
24
- [:activity, :task, :story].each do |handler|
25
- send("add_#{handler}", row[handler], row) if row[handler]
24
+ def process(row)
25
+ [:activity, :task, :story].each do |handler|
26
+ send("add_#{handler}", row[handler], row) if row[handler]
27
+ end
26
28
  end
27
- end
28
29
 
29
- def add_activity(name, params = {})
30
- @activity = name
31
- end
30
+ def add_activity(name, params = {})
31
+ @activity = name
32
+ end
32
33
 
33
- def add_task(name, params = {})
34
- @task = name
35
- end
34
+ def add_task(name, params = {})
35
+ @task = name
36
+ end
36
37
 
37
- def add_story(name, params = {})
38
- raise "implement me"
39
- end
38
+ def add_story(name, params = {})
39
+ raise "implement me"
40
+ end
40
41
 
41
- def done
42
- @csv.close
43
- @file.close
44
- `open #{@file_name.gsub(' ', '\\ ')}`
42
+ def done
43
+ @csv.close
44
+ @file.close
45
+ `open #{@file_name.gsub(' ', '\\ ')}`
46
+ end
45
47
  end
46
48
  end
@@ -1,60 +1,22 @@
1
1
  require 'csv'
2
+ require 'cards/tabular_parser'
2
3
 
3
4
  module Cards
4
5
  class CsvParser
6
+ include TabularParser
7
+
5
8
  def initialize(file)
6
9
  @file = file
7
10
  end
8
11
 
9
- def rows
10
- rows = []
11
- each_row {|row| rows << row}
12
- rows
13
- end
14
-
15
- def each_row
16
- header = nil
12
+ def each_unparsed_row
17
13
  CSV.open(@file, 'r') do |row|
18
- if header.nil?
19
- header = define_header(row)
20
- else
21
- yield Row.new(header, row)
22
- end
14
+ yield row
23
15
  end
24
16
  end
25
-
17
+
26
18
  def default_output_file
27
19
  File.basename(@file).sub(/\..*/, '')
28
20
  end
29
-
30
- private
31
-
32
- def define_header(header_row)
33
- header = {}
34
- header_row.each_with_index do |name, i|
35
- header[name.downcase.gsub(' ', '_').to_sym] = i
36
- end
37
- return header
38
- end
39
-
40
- class Row
41
- def initialize(header, row)
42
- @header, @row = header, row
43
- end
44
-
45
- def [](key)
46
- index = @header[key] || raise("can't find column header #{key.inspect} in #{@header.inspect}")
47
- @row[index]
48
- end
49
-
50
- def to_h
51
- h = {}
52
- @header.each_key do |column_header|
53
- val = @row[@header[column_header]]
54
- h[column_header] = val unless val.blank?
55
- end
56
- h
57
- end
58
- end
59
21
  end
60
22
  end
@@ -0,0 +1,43 @@
1
+ require 'rbosa'
2
+ require 'cards/tabular_parser'
3
+
4
+ module Cards
5
+ class NumbersParser
6
+ include TabularParser
7
+
8
+ def initialize(file, table_name)
9
+ @file, @table_name = file, table_name
10
+ end
11
+
12
+ def each_unparsed_row
13
+ numbers = OSA.app('Numbers')
14
+ document = numbers.open(@file)
15
+ table = find_table(document, @table_name)
16
+
17
+ table.rows.each do |row|
18
+ values = []
19
+ row.cells.map do |cell|
20
+ value = cell.value
21
+ value = nil if value == 0
22
+ values << value
23
+ end
24
+ yield values
25
+ end
26
+ end
27
+
28
+ def default_output_file
29
+ @table_name
30
+ end
31
+
32
+ private
33
+
34
+ def find_table(document, name)
35
+ document.sheets.each do |sheet|
36
+ sheet.tables.each do |table|
37
+ return table if table.name.downcase == name.downcase
38
+ end
39
+ end
40
+ raise "table #{name} not found in #{document.name}"
41
+ end
42
+ end
43
+ end
@@ -1,5 +1,6 @@
1
1
  require 'cards/card_wall'
2
2
 
3
+ # todo ...
3
4
  module Cards
4
5
  class SwimLanes < CardWall
5
6
  def initialize
@@ -0,0 +1,78 @@
1
+ module Cards
2
+ module TabularParser
3
+ def rows
4
+ rows = []
5
+ each_row {|row| rows << row}
6
+ rows
7
+ end
8
+
9
+ def each_row
10
+ header = nil
11
+ each_unparsed_row do |row|
12
+ if header.nil?
13
+ header = define_header(row)
14
+ else
15
+ yield Row.new(header, row)
16
+ end
17
+ end
18
+ end
19
+
20
+ # this method will turn something of the form
21
+ #
22
+ # activity | task | story |
23
+ # a | | |
24
+ # | b | |
25
+ # | | c |
26
+ # | | d |
27
+ #
28
+ # into
29
+ #
30
+ # activity | task | story |
31
+ # a | b | c |
32
+ # a | b | d |
33
+ #
34
+ def denormalized_rows(key_column, columns_to_denormalize)
35
+ last_row = {}
36
+ rows = []
37
+ each_row do |row|
38
+ row = row.to_h
39
+ columns_to_denormalize.each do |c|
40
+ row[c] = last_row[c] if row[c].blank?
41
+ end
42
+ rows << row unless row[key_column].blank?
43
+ last_row = row
44
+ end
45
+ rows
46
+ end
47
+
48
+ private
49
+
50
+ def define_header(header_row)
51
+ header = {}
52
+ header_row.each_with_index do |name, i|
53
+ header[name.downcase.gsub(' ', '_').to_sym] = i
54
+ end
55
+ return header
56
+ end
57
+
58
+ class Row
59
+ def initialize(header, row)
60
+ @header, @row = header, row
61
+ end
62
+
63
+ def [](key)
64
+ index = @header[key] || raise("can't find column header #{key.inspect} in #{@header.inspect}")
65
+ @row[index]
66
+ end
67
+
68
+ def to_h
69
+ h = {}
70
+ @header.each_key do |column_header|
71
+ val = @row[@header[column_header]]
72
+ h[column_header] = val unless val.blank?
73
+ end
74
+ h
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,19 +1,21 @@
1
1
  require 'cards/csv_builder'
2
2
 
3
- class TrackerCsv < CsvBuilder
4
- def header
5
- ["Story", "Labels", "Story Type", "Estimate", "Description"]
6
- end
3
+ module Cards
4
+ class TrackerCsv < CsvBuilder
5
+ def header
6
+ ["Story", "Labels", "Story Type", "Estimate", "Description"]
7
+ end
7
8
 
8
- def add_story(name, params = {})
9
- return unless [nil, '', 'Story', 'Chore'].include?(params[:story_type])
9
+ def add_story(name, params = {})
10
+ return unless [nil, '', 'Story', 'Chore'].include?(params[:story_type])
10
11
 
11
- @csv << [
12
- name,
13
- @task,
14
- params[:story_type],
15
- nil, # params[:estimate],
16
- "#{@activity}\n#{params[:description]}"
17
- ]
12
+ @csv << [
13
+ name,
14
+ @task,
15
+ params[:story_type],
16
+ nil, # params[:estimate],
17
+ "#{@activity}\n#{params[:description]}"
18
+ ]
19
+ end
18
20
  end
19
21
  end
@@ -1,7 +1,11 @@
1
1
  module Cards
2
2
  class DotWriter
3
- def initialize(file_name)
4
- @file = File.open(file_name + ".dot", "w")
3
+ def initialize(file)
4
+ if file.respond_to? :puts
5
+ @file = file
6
+ else
7
+ @file = File.open(file + ".dot", "w")
8
+ end
5
9
  @node_color = {:green => "darkseagreen1", :blue => "lightblue", :red => "pink", :yellow => "lightyellow"}
6
10
  @nodes = []
7
11
 
@@ -7,22 +7,40 @@ include Cards
7
7
  describe CsvParser do
8
8
  include FileSandbox
9
9
 
10
- it "should parse csv" do
10
+ before do
11
11
  sandbox.new :file => 'tmp.csv', :with_contents =>
12
12
  "activity,task,story,estimate
13
13
  manage email,,
14
14
  ,read email,
15
15
  ,,display email body
16
- manage email,think about email,,1
16
+ manage email,think about email,mow the lawn,1
17
17
  ,read email,,2
18
18
  ,,display email body,3"
19
-
19
+ end
20
+
21
+ it "should parse csv" do
20
22
  rows = CsvParser.new(sandbox['tmp.csv'].path).rows
21
23
  rows[0].to_h.should == {:activity => 'manage email'}
22
24
  rows[1].to_h.should == {:task => 'read email'}
23
25
  rows[2].to_h.should == {:story => 'display email body'}
24
- rows[3].to_h.should == {:activity => 'manage email', :task => 'think about email', :estimate => "1"}
26
+ rows[3].to_h.should == {:activity => 'manage email', :task => 'think about email', :story => 'mow the lawn', :estimate => "1"}
25
27
  rows[4].to_h.should == {:task => 'read email', :estimate => "2"}
26
28
  rows[5].to_h.should == {:story => 'display email body', :estimate => "3"}
27
29
  end
30
+
31
+ it "should denormalize rows" do
32
+ rows = CsvParser.new(sandbox['tmp.csv'].path).denormalized_rows(:story, [:activity, :task])
33
+ rows.size.should == 3
34
+ rows[0].should == {:activity => 'manage email',
35
+ :task => 'read email',
36
+ :story => 'display email body'}
37
+ rows[1].should == {:activity => 'manage email',
38
+ :task => 'think about email',
39
+ :story => 'mow the lawn',
40
+ :estimate => "1"}
41
+ rows[2].should == {:activity => 'manage email',
42
+ :task => 'read email',
43
+ :story => 'display email body',
44
+ :estimate => '3'}
45
+ end
28
46
  end
@@ -0,0 +1,25 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'cards/numbers_parser'
3
+
4
+ # not the best test, but it will do
5
+ describe Cards::NumbersParser do
6
+ describe "each_row" do
7
+ it "should find all rows in Voting Example" do
8
+ file = File.expand_path(File.dirname(__FILE__) + "/../fixtures/Voting Example.numbers")
9
+ parser = Cards::NumbersParser.new(file, "Workflow")
10
+ rows = parser.rows
11
+ rows[0].to_h.should == {:role => "Voter", :task => "Vote on question"}
12
+ rows[1].to_h.should == {:task => "Navigate questions"}
13
+ rows[2].to_h.should == {:task => "Submit"}
14
+ rows[3].to_h.should == {:role => "Admin", :task => "Create questions"}
15
+ rows[4].to_h.should == {:task => "Compile votes"}
16
+ rows[5].to_h.should == {:task => "Announce winners"}
17
+ end
18
+ end
19
+
20
+ describe "default_output_file" do
21
+ it "should be the table name" do
22
+ Cards::NumbersParser.new("some file.numbers", "larry").default_output_file.should == "larry"
23
+ end
24
+ end
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cards
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.7"
4
+ version: "0.9"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Stell-Smith
@@ -9,17 +9,18 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-06-06 00:00:00 -07:00
12
+ date: 2009-03-05 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: hoe
17
+ type: :development
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
20
21
  - - ">="
21
22
  - !ruby/object:Gem::Version
22
- version: 1.5.3
23
+ version: 1.8.2
23
24
  version:
24
25
  description: CardWallGen is a program that reads a requirements map and generates a view of it. Typically it reads it from excel or csv and generates a graphical view in visio or omnigraffle, though it may also generate another csv view of it.
25
26
  email: jeremystellsmith@gmail.com
@@ -29,48 +30,67 @@ extensions: []
29
30
 
30
31
  extra_rdoc_files:
31
32
  - CHANGES.txt
32
- - Manifest.txt
33
33
  - README.txt
34
34
  files:
35
35
  - CHANGES.txt
36
- - Manifest.txt
37
- - README.txt
38
36
  - Rakefile
39
- - examples/csv to dot/Stories.csv
40
- - examples/csv to dot/Stories.dot
37
+ - README.txt
38
+ - examples/csv to dot
41
39
  - examples/csv to dot/generate_card_walls
42
- - examples/numbers to omnigraffle/Voting Example.numbers/Contents/PkgInfo
43
- - examples/numbers to omnigraffle/Voting Example.numbers/QuickLook/Thumbnail.jpg
44
- - examples/numbers to omnigraffle/Voting Example.numbers/document-thumbnail.tiff
45
- - examples/numbers to omnigraffle/Voting Example.numbers/index.xml.gz
46
- - examples/numbers to omnigraffle/Voting Example/Cards-Goals.csv
47
- - examples/numbers to omnigraffle/Voting Example/Cards-Stories.csv
48
- - examples/numbers to omnigraffle/Voting Example/Cards-Workflow.csv
40
+ - examples/csv to dot/Stories.csv
41
+ - examples/custom csv builder
42
+ - examples/custom csv builder/generate_card_wall
43
+ - examples/custom csv builder/Stories.csv
44
+ - examples/custom csv builder/Stories.tracker.csv
45
+ - examples/excel to visio
46
+ - examples/excel to visio/Business Processes.xls
47
+ - examples/excel to visio/generate card walls.rb
48
+ - examples/excel to visio/readme.html
49
+ - examples/excel to visio/TPS-small.png
50
+ - examples/importing into tracker api
51
+ - examples/importing into tracker api/appscript_example.rb
52
+ - examples/importing into tracker api/from_numbers_09
53
+ - examples/importing into tracker api/generate_card_wall
54
+ - examples/importing into tracker api/osa.rb
55
+ - examples/importing into tracker api/Stories.csv
56
+ - examples/importing into tracker api/Voting Example.numbers
57
+ - examples/numbers to omnigraffle
49
58
  - examples/numbers to omnigraffle/generate_card_walls
50
- - lib/cards.rb
59
+ - examples/numbers to omnigraffle/Voting Example.numbers
60
+ - lib/cards
51
61
  - lib/cards/builder.rb
52
62
  - lib/cards/card.rb
53
63
  - lib/cards/card_wall.rb
54
64
  - lib/cards/csv_builder.rb
55
65
  - lib/cards/csv_parser.rb
56
66
  - lib/cards/extensions.rb
67
+ - lib/cards/layouts
57
68
  - lib/cards/layouts/column_layout.rb
58
69
  - lib/cards/layouts/nil_layout.rb
59
70
  - lib/cards/layouts/row_layout.rb
60
71
  - lib/cards/master_story_list.rb
72
+ - lib/cards/numbers_parser.rb
61
73
  - lib/cards/swim_lanes.rb
74
+ - lib/cards/tabular_parser.rb
62
75
  - lib/cards/text.rb
63
76
  - lib/cards/tracker_csv.rb
77
+ - lib/cards/writers
64
78
  - lib/cards/writers/dot_writer.rb
65
79
  - lib/cards/writers/graffle_writer.rb
66
80
  - lib/cards/writers/text_writer.rb
81
+ - lib/cards.rb
82
+ - spec/cards
67
83
  - spec/cards/card_spec.rb
68
84
  - spec/cards/card_wall_spec.rb
69
85
  - spec/cards/csv_parser_spec.rb
70
86
  - spec/cards/extensions_spec.rb
87
+ - spec/cards/numbers_parser_spec.rb
71
88
  - spec/cards/swim_lanes_spec.rb
89
+ - spec/cards/writers
72
90
  - spec/cards/writers/dot_writer_spec.rb
73
91
  - spec/cards/writers/text_writer_spec.rb
92
+ - spec/fixtures
93
+ - spec/fixtures/Voting Example.numbers
74
94
  - spec/spec_helper.rb
75
95
  has_rdoc: true
76
96
  homepage: http://rubyforge.org/projects/cardwallgen
@@ -95,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
115
  requirements: []
96
116
 
97
117
  rubyforge_project: cardwallgen
98
- rubygems_version: 1.1.1
118
+ rubygems_version: 1.3.1
99
119
  signing_key:
100
120
  specification_version: 2
101
121
  summary: CardWallGen is a program that reads a requirements map and generates a view of it. Typically it reads it from excel or csv and generates a graphical view in visio or omnigraffle, though it may also generate another csv view of it.
data/Manifest.txt DELETED
@@ -1,40 +0,0 @@
1
- CHANGES.txt
2
- Manifest.txt
3
- README.txt
4
- Rakefile
5
- examples/csv to dot/Stories.csv
6
- examples/csv to dot/Stories.dot
7
- examples/csv to dot/generate_card_walls
8
- examples/numbers to omnigraffle/Voting Example.numbers/Contents/PkgInfo
9
- examples/numbers to omnigraffle/Voting Example.numbers/QuickLook/Thumbnail.jpg
10
- examples/numbers to omnigraffle/Voting Example.numbers/document-thumbnail.tiff
11
- examples/numbers to omnigraffle/Voting Example.numbers/index.xml.gz
12
- examples/numbers to omnigraffle/Voting Example/Cards-Goals.csv
13
- examples/numbers to omnigraffle/Voting Example/Cards-Stories.csv
14
- examples/numbers to omnigraffle/Voting Example/Cards-Workflow.csv
15
- examples/numbers to omnigraffle/generate_card_walls
16
- lib/cards.rb
17
- lib/cards/builder.rb
18
- lib/cards/card.rb
19
- lib/cards/card_wall.rb
20
- lib/cards/csv_builder.rb
21
- lib/cards/csv_parser.rb
22
- lib/cards/extensions.rb
23
- lib/cards/layouts/column_layout.rb
24
- lib/cards/layouts/nil_layout.rb
25
- lib/cards/layouts/row_layout.rb
26
- lib/cards/master_story_list.rb
27
- lib/cards/swim_lanes.rb
28
- lib/cards/text.rb
29
- lib/cards/tracker_csv.rb
30
- lib/cards/writers/dot_writer.rb
31
- lib/cards/writers/graffle_writer.rb
32
- lib/cards/writers/text_writer.rb
33
- spec/cards/card_spec.rb
34
- spec/cards/card_wall_spec.rb
35
- spec/cards/csv_parser_spec.rb
36
- spec/cards/extensions_spec.rb
37
- spec/cards/swim_lanes_spec.rb
38
- spec/cards/writers/dot_writer_spec.rb
39
- spec/cards/writers/text_writer_spec.rb
40
- spec/spec_helper.rb
@@ -1,63 +0,0 @@
1
- digraph G {
2
- graph [nodesep=.1, ranksep=.1, ordering=out];
3
- node [shape=box, color=black, style=filled, width=2, height=1.2, fixedsize=true, labeljust="l"];
4
- edge [style=invis, weight=1];
5
- card_0_0 [label="Voter",fillcolor=lightblue];
6
- card_0_1 [label="Vote on question",fillcolor=pink];
7
- card_0_2 [label="Ability to see\nquestion 1",fillcolor=lightyellow];
8
- card_0_3 [label="Ability to see\nchoices 1",fillcolor=lightyellow];
9
- card_0_4 [label="Ability to choose\nThis is the server\ntrip 2",fillcolor=lightyellow];
10
- card_1_1 [label="Navigate questions",fillcolor=pink];
11
- card_1_2 [label="Ability to continue\nto next / prev 1",fillcolor=lightyellow];
12
- card_1_3 [label="Ability to see list\nof questions This\nincludes ability to\njump to that\nquestion 4",fillcolor=lightyellow];
13
- card_2_1 [label="Submit",fillcolor=pink];
14
- card_2_2 [label="Ability to return to\nnew screen 1",fillcolor=lightyellow];
15
- card_3_0 [label="Admin",fillcolor=lightblue];
16
- card_3_1 [label="Create questions",fillcolor=pink];
17
- card_3_2 [label="Ability to enter\nquestion 4",fillcolor=lightyellow];
18
- card_3_3 [label="Ability to reorder\nquestion 4",fillcolor=lightyellow];
19
- card_3_4 [label="Ability to enter\nmultiple choice\nquestion 1",fillcolor=lightyellow];
20
- card_3_5 [label="Ability to enter\ntext question 1",fillcolor=lightyellow];
21
- card_4_2 [label="Ability to enter\nlong text question\n1",fillcolor=lightyellow];
22
- card_4_3 [label="Ability to enter\nlist question 2",fillcolor=lightyellow];
23
- card_5_1 [label="Compile votes",fillcolor=pink];
24
- card_5_2 [label="Ability to see\nresults in\nspreadsheet 4",fillcolor=lightyellow];
25
- card_5_3 [label="Ability to see\nsummary of results\nThis includes\npercentage of each\nquestion answering\nwhich answer 2",fillcolor=lightyellow];
26
- card_6_1 [label="Announce winners",fillcolor=pink];
27
- gap_1_0 [style=invis];
28
- gap_2_0 [style=invis];
29
- gap_4_0 [style=invis];
30
- gap_4_1 [style=invis];
31
- gap_5_0 [style=invis];
32
- gap_6_0 [style=invis];
33
- {rank=same; card_0_0; gap_1_0; gap_2_0; card_3_0; gap_4_0; gap_5_0; gap_6_0};
34
- {rank=same; card_0_1; card_1_1; card_2_1; card_3_1; gap_4_1; card_5_1; card_6_1};
35
- {rank=same; card_0_2; card_1_2; card_2_2; card_3_2; card_4_2; card_5_2};
36
- card_0_0 -> gap_1_0;
37
- gap_1_0 -> gap_2_0;
38
- gap_2_0 -> card_3_0;
39
- card_3_0 -> gap_4_0;
40
- gap_4_0 -> gap_5_0;
41
- gap_5_0 -> gap_6_0;
42
- card_0_0 -> card_0_1;
43
- card_0_1 -> card_0_2;
44
- card_0_2 -> card_0_3;
45
- card_0_3 -> card_0_4;
46
- gap_1_0 -> card_1_1;
47
- card_1_1 -> card_1_2;
48
- card_1_2 -> card_1_3;
49
- gap_2_0 -> card_2_1;
50
- card_2_1 -> card_2_2;
51
- card_3_0 -> card_3_1;
52
- card_3_1 -> card_3_2;
53
- card_3_2 -> card_3_3;
54
- card_3_3 -> card_3_4;
55
- card_3_4 -> card_3_5;
56
- gap_4_0 -> gap_4_1;
57
- gap_4_1 -> card_4_2;
58
- card_4_2 -> card_4_3;
59
- gap_5_0 -> card_5_1;
60
- card_5_1 -> card_5_2;
61
- card_5_2 -> card_5_3;
62
- gap_6_0 -> card_6_1;
63
- }
@@ -1,7 +0,0 @@
1
- Goal,Note
2
- Speed,
3
- Efficiency,For voter & admin
4
- Usability,
5
- Security,
6
- Traceability,
7
- Durability,We probably need to store results in a separate location
@@ -1,16 +0,0 @@
1
- Role,Task,Story,Note,Estimate
2
- Voter,Vote on question,Ability to see question,,1
3
- ,,Ability to see choices,,1
4
- ,,Ability to choose,This is the server trip,2
5
- ,Navigate questions,Ability to continue to next / prev,,1
6
- ,,Ability to see list of questions,This includes ability to jump to that question,4
7
- ,Submit,Ability to return to new screen,,1
8
- Admin,Create questions,Ability to enter question,,4
9
- ,,Ability to reorder question,,4
10
- ,,Ability to enter multiple choice question,,1
11
- ,,Ability to enter text question,,1
12
- ,,Ability to enter long text question,,1
13
- ,,Ability to enter list question,,2
14
- ,Compile votes,Ability to see results in spreadsheet,,4
15
- ,,Ability to see summary of results,This includes percentage of each question answering which answer,2
16
- ,Announce winners,,,
@@ -1,7 +0,0 @@
1
- Role,Task
2
- Voter,Vote on question
3
- ,Navigate questions
4
- ,Submit
5
- Admin,Create questions
6
- ,Compile votes
7
- ,Announce winners