lionel_richie 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +58 -0
- data/Rakefile +1 -0
- data/bin/authorize_lionel +61 -0
- data/bin/lionel +109 -0
- data/lib/lionel_richie/version.rb +3 -0
- data/lib/lionel_richie.rb +8 -0
- data/lionel_richie.gemspec +28 -0
- metadata +154 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Ross Kaffenberger
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# LionelRichie
|
2
|
+
|
3
|
+
Trello? Is it me you're looking for?
|
4
|
+
|
5
|
+
LionelRichie is a script for exporting Trello data to Google Docs. This gem is in its very early stages so its probably not useful to you yet.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'lionel_richie'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install lionel_richie
|
20
|
+
|
21
|
+
## Setup
|
22
|
+
|
23
|
+
First, you'll create a Google client app. Visit the [Google API console](https://code.google.com/apis/console). Create an app. Feel free to call it "Trello Export" or "Lionel Richie".
|
24
|
+
|
25
|
+
Click the tab "API Access", then "Create an OAuth 2.0 Client ID" to open up a modal. Set a client name, like "CLI" for command line interface and continue. Next the modal presents "Client ID Settings"; choose "Installed Application" for "Application type", and "Other" for "Installed application type". Now press "Create client ID".
|
26
|
+
|
27
|
+
From your newly created client settings, grab the client ID and client secret. Add them to your environment:
|
28
|
+
|
29
|
+
$ export GOOGLE_CLIENT_ID=your-google-client-id
|
30
|
+
$ export GOOGLE_CLIENT_SECRET=your-google-client-secret
|
31
|
+
|
32
|
+
Next, you'll need to authorize the lionel_richie CLI. Run the following:
|
33
|
+
|
34
|
+
$ authorize_lionel
|
35
|
+
|
36
|
+
Follow the instructions, which will include entering info on the command line from webpages on Trello and Google. If successful, you'll be able to export the following environment variables needed to run the export.
|
37
|
+
|
38
|
+
$ export TRELLO_KEY=your-trello-key
|
39
|
+
$ export TRELLO_TOKEN=your-trello-token
|
40
|
+
$ export GOOGLE_TOKEN=your-google-token
|
41
|
+
$ export GOOGLE_REFRESH_TOKEN=your-google-refresh-token
|
42
|
+
|
43
|
+
Finally, you need to set the trello board and the google doc you want to export. You can get these from the respective URLs for those resources.
|
44
|
+
|
45
|
+
$ export TRELLO_BOARD_ID=your-trello-board-id
|
46
|
+
$ export GOOGLE_DOC_ID=your-google-doc-id
|
47
|
+
|
48
|
+
You should now be ready to run the export:
|
49
|
+
|
50
|
+
$ lionel
|
51
|
+
|
52
|
+
## Contributing
|
53
|
+
|
54
|
+
1. Fork it
|
55
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
56
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
57
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
58
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
|
5
|
+
require 'bundler'
|
6
|
+
require 'lionel_richie'
|
7
|
+
Bundler.require(:development)
|
8
|
+
|
9
|
+
commands = []
|
10
|
+
|
11
|
+
puts "Authorize for Trello?"
|
12
|
+
if gets.strip =~ /\Ay/i
|
13
|
+
Launchy.open "https://trello.com/1/appKey/generate"
|
14
|
+
puts "Enter your trello key:"
|
15
|
+
trello_key = gets.strip
|
16
|
+
commands << "export TRELLO_KEY=#{trello_key}"
|
17
|
+
|
18
|
+
Launchy.open "https://trello.com/1/authorize?key=#{trello_key.strip}&name=LionelRichie&response_type=token&scope=read,write,account&expiration=never"
|
19
|
+
puts "Enter your trello token"
|
20
|
+
trello_token = gets.strip
|
21
|
+
commands << "export TRELLO_TOKEN=#{trello_token}"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Google Auth
|
25
|
+
puts "Authorize for Google?"
|
26
|
+
if gets.strip =~ /\Ay/i
|
27
|
+
client = OAuth2::Client.new(
|
28
|
+
ENV['GOOGLE_CLIENT_ID'],
|
29
|
+
ENV['GOOGLE_CLIENT_SECRET'],
|
30
|
+
:site => "https://accounts.google.com",
|
31
|
+
:token_url => "/o/oauth2/token",
|
32
|
+
:authorize_url => "/o/oauth2/auth")
|
33
|
+
|
34
|
+
if !(refresh_token = ENV['GOOGLE_REFRESH_TOKEN'])
|
35
|
+
google_auth_token = OAuth2::AccessToken.from_hash(client,
|
36
|
+
{:refresh_token => refresh_token, :expires_at => 7200})
|
37
|
+
google_auth_token = google_auth_token.refresh!
|
38
|
+
else
|
39
|
+
auth_url = client.auth_code.authorize_url(
|
40
|
+
:redirect_uri => "urn:ietf:wg:oauth:2.0:oob",
|
41
|
+
:scope =>
|
42
|
+
"https://docs.google.com/feeds/ " +
|
43
|
+
"https://docs.googleusercontent.com/ " +
|
44
|
+
"https://spreadsheets.google.com/feeds/")
|
45
|
+
|
46
|
+
# Redirect the user to auth_url and get authorization code from redirect URL.
|
47
|
+
Launchy.open auth_url
|
48
|
+
|
49
|
+
puts "Enter your google key:"
|
50
|
+
authorization_code = gets.strip
|
51
|
+
google_auth_token = client.auth_code.get_token(
|
52
|
+
authorization_code,
|
53
|
+
:redirect_uri => "urn:ietf:wg:oauth:2.0:oob")
|
54
|
+
end
|
55
|
+
|
56
|
+
commands << "export GOOGLE_TOKEN=#{google_auth_token.token}"
|
57
|
+
commands << "export GOOGLE_REFRESH_TOKEN=#{google_auth_token.refresh_token}"
|
58
|
+
end
|
59
|
+
|
60
|
+
puts "Run the following:\n"
|
61
|
+
puts commands
|
data/bin/lionel
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
|
5
|
+
require 'lionel_richie'
|
6
|
+
|
7
|
+
Trello.configure do |c|
|
8
|
+
c.developer_public_key = ENV['TRELLO_KEY']
|
9
|
+
c.member_token = ENV['TRELLO_TOKEN']
|
10
|
+
end
|
11
|
+
|
12
|
+
board_id = ENV['TRELLO_BOARD_ID']
|
13
|
+
board = Trello::Board.find(board_id)
|
14
|
+
|
15
|
+
cards = []
|
16
|
+
board.lists.each do |list|
|
17
|
+
list.cards.each do |card|
|
18
|
+
cards << card
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
session = GoogleDrive.login_with_oauth(ENV['GOOGLE_TOKEN'])
|
23
|
+
|
24
|
+
sp = session.spreadsheet_by_key(ENV['GOOGLE_DOC_ID'])
|
25
|
+
ws = sp.worksheets[0]
|
26
|
+
|
27
|
+
start_row = 2
|
28
|
+
rows = ws.rows.size
|
29
|
+
|
30
|
+
card_ids = cards.map(&:id)
|
31
|
+
|
32
|
+
def sync_row(ws, row, card)
|
33
|
+
puts "syncing row[#{row}] with #{card.name}"
|
34
|
+
ws["B#{row}"] = card.id
|
35
|
+
|
36
|
+
# Card link
|
37
|
+
ws["C#{row}"] = %Q[=HYPERLINK("#{card.url}", "#{card.name.gsub(/"/, "")}")]
|
38
|
+
|
39
|
+
actions = card.actions
|
40
|
+
actions = actions.select { |a| a.data["listBefore"].present? }
|
41
|
+
|
42
|
+
# Ready date
|
43
|
+
ws["D#{row}"] = action_date(actions) do |a|
|
44
|
+
(a.type == "createCard" && a.data["board"]["id"] == board_id) ||
|
45
|
+
a.data["listAfter"]["name"] =~ /ready/i
|
46
|
+
end
|
47
|
+
|
48
|
+
# In Progress date
|
49
|
+
ws["E#{row}"] = action_date(actions) { |a| a.data["listAfter"]["name"] =~ /^in progress/i }
|
50
|
+
|
51
|
+
# Code Review date
|
52
|
+
ws["F#{row}"] = action_date(actions) { |a| a.data["listAfter"]["name"] =~ /^code review/i }
|
53
|
+
|
54
|
+
# Review date
|
55
|
+
ws["G#{row}"] = action_date(actions) { |a| a.data["listAfter"]["name"] =~ /^review/i }
|
56
|
+
|
57
|
+
# Deploy date
|
58
|
+
ws["H#{row}"] = action_date(actions) { |a| a.data["listAfter"]["name"] =~ /^deploy/i }
|
59
|
+
|
60
|
+
# Completed date
|
61
|
+
ws["I#{row}"] = action_date(actions) { |a| a.data["listAfter"]["name"] =~ /^completed/i }
|
62
|
+
|
63
|
+
# Type
|
64
|
+
labels = card.labels.map(&:name)
|
65
|
+
type = labels.detect { |l| l.downcase =~ %r{bug|chore|task}i } || 'story'
|
66
|
+
ws["J#{row}"] = type
|
67
|
+
|
68
|
+
# Estimate
|
69
|
+
match = card.name.match(/\[(?<estimate>\w)\]/)
|
70
|
+
ws["L#{row}"] = match[:estimate] if match
|
71
|
+
|
72
|
+
rescue Trello::Error => e
|
73
|
+
puts e.inspect
|
74
|
+
puts card.inspect
|
75
|
+
end
|
76
|
+
|
77
|
+
def action_date(actions, &block)
|
78
|
+
actions = actions.select(&block)
|
79
|
+
return "" if actions.empty?
|
80
|
+
action = actions.sort { |a, b| a.date <=> b.date }.first
|
81
|
+
action.date.strftime("%m/%d/%Y")
|
82
|
+
end
|
83
|
+
|
84
|
+
card_rows = {}
|
85
|
+
|
86
|
+
# Find existing rows for current cards
|
87
|
+
(start_row..rows).each do |row|
|
88
|
+
cell_id = ws["B#{row}"]
|
89
|
+
next unless cell_id.present?
|
90
|
+
card = cards.find { |c| c.id == cell_id }
|
91
|
+
next unless card.present?
|
92
|
+
card_rows[row] = card
|
93
|
+
end
|
94
|
+
|
95
|
+
# Set available rows for new cards
|
96
|
+
new_cards = cards - card_rows.values
|
97
|
+
new_cards.each_with_index do |card, i|
|
98
|
+
row = rows + i + 1
|
99
|
+
card_rows[row] = card
|
100
|
+
end
|
101
|
+
|
102
|
+
card_rows.each do |row, card|
|
103
|
+
sleep 1
|
104
|
+
Timeout.timeout(5) { sync_row(ws, row, card) }
|
105
|
+
end
|
106
|
+
|
107
|
+
ws.save
|
108
|
+
|
109
|
+
puts "exiting"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'lionel_richie/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "lionel_richie"
|
8
|
+
spec.version = LionelRichie::VERSION
|
9
|
+
spec.authors = ["Ross Kaffenberger"]
|
10
|
+
spec.email = ["rosskaff@gmail.com"]
|
11
|
+
spec.description = %q{Export Trello to Google Docs}
|
12
|
+
spec.summary = %q{Export Trello to Google Docs}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "ruby-trello"
|
22
|
+
spec.add_dependency "google_drive"
|
23
|
+
spec.add_dependency "yajl-ruby"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
27
|
+
spec.add_development_dependency "launchy"
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lionel_richie
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ross Kaffenberger
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ruby-trello
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: google_drive
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: yajl-ruby
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.3'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.3'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '1.3'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.3'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: launchy
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: Export Trello to Google Docs
|
111
|
+
email:
|
112
|
+
- rosskaff@gmail.com
|
113
|
+
executables:
|
114
|
+
- authorize_lionel
|
115
|
+
- lionel
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- .gitignore
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- bin/authorize_lionel
|
125
|
+
- bin/lionel
|
126
|
+
- lib/lionel_richie.rb
|
127
|
+
- lib/lionel_richie/version.rb
|
128
|
+
- lionel_richie.gemspec
|
129
|
+
homepage: ''
|
130
|
+
licenses:
|
131
|
+
- MIT
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
none: false
|
144
|
+
requirements:
|
145
|
+
- - ! '>='
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 1.8.25
|
151
|
+
signing_key:
|
152
|
+
specification_version: 3
|
153
|
+
summary: Export Trello to Google Docs
|
154
|
+
test_files: []
|