trello_effort_tracker 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +25 -0
- data/.rspec +3 -0
- data/.rvmrc.template +2 -0
- data/.travis.yml +21 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +101 -0
- data/LICENSE.txt +15 -0
- data/README.md +159 -0
- data/Rakefile +72 -0
- data/config/config.template.yml +7 -0
- data/config/mongoid.template.yml +24 -0
- data/lib/patches/trello/member.rb +20 -0
- data/lib/startup_trello.rb +2 -0
- data/lib/trello_effort_tracker/effort.rb +27 -0
- data/lib/trello_effort_tracker/estimate.rb +25 -0
- data/lib/trello_effort_tracker/google_docs_exporter.rb +67 -0
- data/lib/trello_effort_tracker/member.rb +46 -0
- data/lib/trello_effort_tracker/mongoid_helper.rb +13 -0
- data/lib/trello_effort_tracker/tracked_card.rb +103 -0
- data/lib/trello_effort_tracker/tracking.rb +130 -0
- data/lib/trello_effort_tracker/trello_authorize.rb +32 -0
- data/lib/trello_effort_tracker/trello_configuration.rb +33 -0
- data/lib/trello_effort_tracker/trello_tracker.rb +48 -0
- data/lib/trello_effort_tracker/version.rb +3 -0
- data/lib/trello_effort_tracker.rb +23 -0
- data/script/ci/before_script.sh +1 -0
- data/script/ci/run_build.sh +2 -0
- data/script/crontab.template +8 -0
- data/script/mate.sh +1 -0
- data/spec/effort_spec.rb +52 -0
- data/spec/estimate_spec.rb +38 -0
- data/spec/integration/trello_authorization_spec.rb +12 -0
- data/spec/member_spec.rb +45 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/tracked_card_spec.rb +267 -0
- data/spec/tracking_spec.rb +236 -0
- data/spec/trello_authorize_spec.rb +71 -0
- data/spec/trello_configuration_spec.rb +43 -0
- data/trello_effort_tracker.gemspec +19 -0
- metadata +86 -0
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
.DS_Store
|
2
|
+
*.gem
|
3
|
+
*.rbc
|
4
|
+
.bundle
|
5
|
+
.config
|
6
|
+
coverage
|
7
|
+
InstalledFiles
|
8
|
+
lib/bundler/man
|
9
|
+
pkg
|
10
|
+
rdoc
|
11
|
+
spec/reports
|
12
|
+
test/tmp
|
13
|
+
test/version_tmp
|
14
|
+
tmp
|
15
|
+
.tmtags
|
16
|
+
|
17
|
+
# YARD artifacts
|
18
|
+
.yardoc
|
19
|
+
_yardoc
|
20
|
+
doc/
|
21
|
+
|
22
|
+
config.yml
|
23
|
+
config.yml.piero
|
24
|
+
mongoid.yml
|
25
|
+
.rvmrc
|
data/.rspec
ADDED
data/.rvmrc.template
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
|
5
|
+
env:
|
6
|
+
global:
|
7
|
+
- developer_public_key: e/zPNGKOwapoxFWZs/CBTAhaoqrDTkqqz+InOmCUvGBcZBpUxH398nPLv8Hi\nNQSvWuspzqanyNbbQrTA/2XhsWF1gIFX+gNexYM7S+MxtRwUD3cpVT8DzVOS\nMuckdMTEAADVsm92vIX/bUs5igxD4+zfXlhzXUNDrzRlZnsUF2M=
|
8
|
+
- access_token_key: GG1Q8oq7GkaXjP0lbMgvQLLEi+LXOZlwmELqjPk39Exfy8114QUs2ki8nr9n\ndGO+tgOSZsCd/bt9IHxS3WWWU0INSYOTgp/prfsDDgosg7/Elk/b6w1OW0At\nX3VAjMeI0yAGloT6XB58LCFyrj6S2b4vCQqyaHR3tlTHai/5bMQ=
|
9
|
+
- developer_secret: jnWqZy2L5styLBGfgpZm9Unqog4KBlPTdtSHdFKG4I8PotrJEi6Kt2rSGljX\n8zFiRcnzelZSEEzGxa9z6pSMFuVz5Dkr3EK/Oa4+MpueMOXOoQA3zmty/865\n5Wokr1cEeJJOP/KXmnLgAkvPh9vOn7KEd8IlphsOkv5w+Rlmgb4=
|
10
|
+
- tracker_username=trackinguser
|
11
|
+
- MONGOID_ENV=test
|
12
|
+
|
13
|
+
services:
|
14
|
+
- mongodb
|
15
|
+
|
16
|
+
script: ./script/ci/run_build.sh
|
17
|
+
|
18
|
+
before_script:
|
19
|
+
- mongo trello_effort_tracker_test --eval 'db.addUser("travis", "test");'
|
20
|
+
- "./script/ci/before_script.sh"
|
21
|
+
|
data/Gemfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
gem 'trello_effort_tracker', :path => '.'
|
4
|
+
|
5
|
+
gem 'ruby-trello', :require => 'trello'
|
6
|
+
gem 'debugger'
|
7
|
+
gem 'rainbow'
|
8
|
+
gem 'chronic'
|
9
|
+
|
10
|
+
gem 'mongoid'
|
11
|
+
gem 'bson_ext'
|
12
|
+
|
13
|
+
# google docs exporter
|
14
|
+
gem 'google_drive'
|
15
|
+
gem 'highline'
|
16
|
+
|
17
|
+
group :test do
|
18
|
+
gem 'rake'
|
19
|
+
gem 'rspec'
|
20
|
+
gem 'rspec-mocks'
|
21
|
+
gem 'mongoid-rspec'
|
22
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
trello_effort_tracker (0.0.2)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
activemodel (3.2.11)
|
10
|
+
activesupport (= 3.2.11)
|
11
|
+
builder (~> 3.0.0)
|
12
|
+
activesupport (3.2.11)
|
13
|
+
i18n (~> 0.6)
|
14
|
+
multi_json (~> 1.0)
|
15
|
+
addressable (2.3.2)
|
16
|
+
bson (1.8.1)
|
17
|
+
bson_ext (1.8.1)
|
18
|
+
bson (~> 1.8.1)
|
19
|
+
builder (3.0.4)
|
20
|
+
chronic (0.9.0)
|
21
|
+
columnize (0.3.6)
|
22
|
+
debugger (1.2.3)
|
23
|
+
columnize (>= 0.3.1)
|
24
|
+
debugger-linecache (~> 1.1.1)
|
25
|
+
debugger-ruby_core_source (~> 1.1.5)
|
26
|
+
debugger-linecache (1.1.2)
|
27
|
+
debugger-ruby_core_source (>= 1.1.1)
|
28
|
+
debugger-ruby_core_source (1.1.6)
|
29
|
+
diff-lcs (1.1.3)
|
30
|
+
faraday (0.8.4)
|
31
|
+
multipart-post (~> 1.1)
|
32
|
+
google_drive (0.3.2)
|
33
|
+
nokogiri (>= 1.4.4, != 1.5.2, != 1.5.1)
|
34
|
+
oauth (>= 0.3.6)
|
35
|
+
oauth2 (>= 0.5.0)
|
36
|
+
highline (1.6.15)
|
37
|
+
httpauth (0.2.0)
|
38
|
+
i18n (0.6.1)
|
39
|
+
json (1.7.6)
|
40
|
+
jwt (0.1.5)
|
41
|
+
multi_json (>= 1.0)
|
42
|
+
mime-types (1.19)
|
43
|
+
mongoid (3.0.17)
|
44
|
+
activemodel (~> 3.1)
|
45
|
+
moped (~> 1.2)
|
46
|
+
origin (~> 1.0)
|
47
|
+
tzinfo (~> 0.3.22)
|
48
|
+
mongoid-rspec (1.5.6)
|
49
|
+
mongoid (>= 3.0.1)
|
50
|
+
rake
|
51
|
+
rspec (>= 2.9)
|
52
|
+
moped (1.3.2)
|
53
|
+
multi_json (1.5.0)
|
54
|
+
multipart-post (1.1.5)
|
55
|
+
nokogiri (1.5.6)
|
56
|
+
oauth (0.4.7)
|
57
|
+
oauth2 (0.8.0)
|
58
|
+
faraday (~> 0.8)
|
59
|
+
httpauth (~> 0.1)
|
60
|
+
jwt (~> 0.1.4)
|
61
|
+
multi_json (~> 1.0)
|
62
|
+
rack (~> 1.2)
|
63
|
+
origin (1.0.11)
|
64
|
+
rack (1.4.4)
|
65
|
+
rainbow (1.1.4)
|
66
|
+
rake (10.0.3)
|
67
|
+
rest-client (1.6.7)
|
68
|
+
mime-types (>= 1.16)
|
69
|
+
rspec (2.12.0)
|
70
|
+
rspec-core (~> 2.12.0)
|
71
|
+
rspec-expectations (~> 2.12.0)
|
72
|
+
rspec-mocks (~> 2.12.0)
|
73
|
+
rspec-core (2.12.2)
|
74
|
+
rspec-expectations (2.12.1)
|
75
|
+
diff-lcs (~> 1.1.3)
|
76
|
+
rspec-mocks (2.12.1)
|
77
|
+
ruby-trello (0.4.4.3)
|
78
|
+
activemodel
|
79
|
+
addressable (~> 2.3)
|
80
|
+
json
|
81
|
+
oauth (~> 0.4.5)
|
82
|
+
rest-client (~> 1.6.7)
|
83
|
+
tzinfo (0.3.35)
|
84
|
+
|
85
|
+
PLATFORMS
|
86
|
+
ruby
|
87
|
+
|
88
|
+
DEPENDENCIES
|
89
|
+
bson_ext
|
90
|
+
chronic
|
91
|
+
debugger
|
92
|
+
google_drive
|
93
|
+
highline
|
94
|
+
mongoid
|
95
|
+
mongoid-rspec
|
96
|
+
rainbow
|
97
|
+
rake
|
98
|
+
rspec
|
99
|
+
rspec-mocks
|
100
|
+
ruby-trello
|
101
|
+
trello_effort_tracker!
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Pietro Di Bello
|
2
|
+
2012
|
3
|
+
|
4
|
+
In the original BSD license, both occurrences of the phrase "COPYRIGHT HOLDERS AND CONTRIBUTORS" in the disclaimer read "REGENTS AND CONTRIBUTORS".
|
5
|
+
|
6
|
+
Here is the license template:
|
7
|
+
|
8
|
+
Copyright (c) 2012, Pietro Di Bello
|
9
|
+
All rights reserved.
|
10
|
+
|
11
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
12
|
+
|
13
|
+
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
14
|
+
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
[![Build Status](https://secure.travis-ci.org/xpepper/trello_effort_tracker.png)](http://travis-ci.org/xpepper/trello_effort_tracker)
|
2
|
+
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/xpepper/trello_effort_tracker)
|
3
|
+
|
4
|
+
# TrelloEffortTracker
|
5
|
+
|
6
|
+
The purpose of this tool is to extract and track estimates and actual efforts on Trello cards.
|
7
|
+
You just have to notify all of your estimates and efforts tracked on your Trello cards using a conventional format.
|
8
|
+
This tool will extract and store these estimates and actual efforts to let you extract useful key metrics (e.g. estimate errors, remaining efforts, pair programming frequencies, and so on).
|
9
|
+
|
10
|
+
The Trello API is used in readonly mode in this code, so all you need to access is your developer key.
|
11
|
+
TrelloEffortTracker uses the [Trello API Ruby wrapper](https://github.com/jeremytregunna/ruby-trello) for this purpose.
|
12
|
+
|
13
|
+
## Requirements
|
14
|
+
* [rvm](https://rvm.io/rvm/install/)
|
15
|
+
* [mongoDB](http://www.mongodb.org/) - mac users with homebrew will just run 'brew install mongodb' to have mongoDB installed on their machine.
|
16
|
+
|
17
|
+
## Setup
|
18
|
+
Copy the config template
|
19
|
+
|
20
|
+
cp config/config.template.yaml config/config.yml
|
21
|
+
|
22
|
+
and then fill the correct values in the placeholders in config.yml (see _"Where do I get an API key and API secret?"_ section).
|
23
|
+
|
24
|
+
Then copy the mongoid config template
|
25
|
+
|
26
|
+
cp config/mongoid.template.yaml config/mongoid.yml
|
27
|
+
|
28
|
+
and fill the correct values for the mongodb environments ([see here](http://mongoid.org/en/mongoid/docs/installation.html#configuration) to have more details).
|
29
|
+
|
30
|
+
Then run bundle to get all the required gems:
|
31
|
+
|
32
|
+
bundle install
|
33
|
+
|
34
|
+
### Where do I get an API key and API secret?
|
35
|
+
Log in as a Trello user and visit [this URL](https://trello.com/1/appKey/generate) to get your developer\_public\_key and the developer\_public\_key.
|
36
|
+
|
37
|
+
### Where do I get an API Access Token Key?
|
38
|
+
You will need an access token to use ruby-trello, which trello tracker depends on. To get it, you'll need to go to this URL:
|
39
|
+
|
40
|
+
https://trello.com/1/connect?key=<YOUR_DEVELOPER_PUBLIC_KEY>&name=Trello+Effort+Tracker&response_type=token&scope=read,write&expiration=never
|
41
|
+
|
42
|
+
At the end of this process, You'll be told to give some key to the app, this is what you want to put in the access\_token\_key yml prop file.
|
43
|
+
|
44
|
+
## Usage
|
45
|
+
The best way is to use one of the rake task defined, e.g.
|
46
|
+
|
47
|
+
rake 'run:today[test]' # will extract today's tracked data and store on the test db
|
48
|
+
|
49
|
+
rake run:today # will extract today's tracked data and store on the default (that is development) db
|
50
|
+
|
51
|
+
rake 'run:from_day[2012-11-1, production]' # will extract tracked data starting from November the 1st, 2012 and store them into the production db
|
52
|
+
|
53
|
+
Or you may just create a TrelloTracker instance and execute its track method.
|
54
|
+
|
55
|
+
tracker = TrelloTracker.new
|
56
|
+
tracker.track
|
57
|
+
|
58
|
+
You can set the Trello's auth params in three ways
|
59
|
+
|
60
|
+
* setting the three auth params via environment variables (ENV object)
|
61
|
+
* using the config.yml (which remains the default mode)
|
62
|
+
* passing into the constructor a hash containing the auth values, e.g.
|
63
|
+
|
64
|
+
tracker = TrelloTracker.new(
|
65
|
+
"developer_public_key" => "487635b55e6fe9021902fa763b4d101a",
|
66
|
+
"access_token_key" => "33bed56f2a12a49c9ba1c2d6ad3e2002e11a34358c3f3fe260d7fba746a06203",
|
67
|
+
"developer_secret" => "ab999c4396493dba4c04ade055eabfdfabdffd0ffd7c281a23234350a993524d")
|
68
|
+
|
69
|
+
tracker.track
|
70
|
+
|
71
|
+
### Console
|
72
|
+
You can open a irb console with the ruby-trello gem and this gem loaded, so that you can query the db or the Trello API and play with them
|
73
|
+
|
74
|
+
rake console
|
75
|
+
|
76
|
+
|
77
|
+
### Storage configuration
|
78
|
+
Tracking data collected from Trello are stored in a MongoDB, as configured in config/mongoid.yml.
|
79
|
+
To define which mongodb env is actually used, just set the MONGOID_ENV env variable. Development is the default mongo environment.
|
80
|
+
|
81
|
+
### Estimate format convention
|
82
|
+
To set an estimate on a card, a Trello user should send a notification from that card to the tracker username, e.g.
|
83
|
+
|
84
|
+
@trackinguser [15p]
|
85
|
+
@trackinguser [1.5d]
|
86
|
+
@trackinguser [12h]
|
87
|
+
|
88
|
+
estimates can be given in hours (h), days (d/g) or pomodori (p).
|
89
|
+
|
90
|
+
@trackinguser 22.11.2012 [4h]
|
91
|
+
|
92
|
+
will add the estimate (4 hours) in date 22.11.2012.
|
93
|
+
|
94
|
+
### Effort format convention
|
95
|
+
To set an effort in the current day on a card, a Trello user should send a notification from that card to the tracker username, e.g.
|
96
|
+
|
97
|
+
@trackinguser +6p
|
98
|
+
@trackinguser +4h
|
99
|
+
@trackinguser +0.5g
|
100
|
+
|
101
|
+
efforts can be given in hours (h), days (d/g) or pomodori (p).
|
102
|
+
|
103
|
+
### Tracking an effort in a specific date
|
104
|
+
To set an effort in a date different from the notification date, just add a date in the message
|
105
|
+
|
106
|
+
@trackinguser 23.10.2012 +6p
|
107
|
+
|
108
|
+
There's even a shortcut for efforts spent yesterday:
|
109
|
+
|
110
|
+
@trackinguser yesterday +6p
|
111
|
+
@trackinguser +6p yesterday
|
112
|
+
|
113
|
+
### Tracking an effort on more members
|
114
|
+
By default, the effort is tracked on the member which sends the tracking notification.
|
115
|
+
|
116
|
+
To set an effort for more than a Trello user (e.g. pair programming), just add the other user in the message, e.g.
|
117
|
+
|
118
|
+
@trackinguser +3p @alessandrodescovi
|
119
|
+
|
120
|
+
To set an effort just for other Trello users (excluding the current user), just include the users in round brackets, e.g.
|
121
|
+
|
122
|
+
@trackinguser +3p (@alessandrodescovi @michelevincenzi)
|
123
|
+
|
124
|
+
## Database import/export
|
125
|
+
To export the db you can execute something like:
|
126
|
+
|
127
|
+
mongoexport --db trello_effort_tracker_production --collection tracked_cards --out trello_effort_tracker_production.json
|
128
|
+
|
129
|
+
To reimport that db:
|
130
|
+
|
131
|
+
mongoimport --db trello_effort_tracker_production --collection tracked_cards --file trello_effort_tracker_production.json
|
132
|
+
|
133
|
+
## Google Docs exporter
|
134
|
+
To export all your tracked cards on a google docs named 'my_sheet' in the 'tracking' worksheet, run
|
135
|
+
|
136
|
+
rake "export:google_docs[my_sheet, tracking, production]"
|
137
|
+
|
138
|
+
The default env is development.
|
139
|
+
|
140
|
+
If you provide no name for the spreadsheet, a default name will be used.
|
141
|
+
If the spreadsheet name you provide does not exists, it will be created in you google drive account.
|
142
|
+
|
143
|
+
So, running simply
|
144
|
+
|
145
|
+
rake export:google_docs
|
146
|
+
|
147
|
+
will create (or update) a spreadsheet named "trello effort tracking" using the development db env.
|
148
|
+
|
149
|
+
|
150
|
+
## Roadmap and improvements
|
151
|
+
We develop Trello Effort Tracker using [Trello itself](https://trello.com/board/trello-effort-tracker-roadmap/509c3228dcb1ac3f1c018791).
|
152
|
+
|
153
|
+
## Contributing
|
154
|
+
|
155
|
+
Several ways you can contribute. Documentation, code, tests, feature requests, bug reports.
|
156
|
+
|
157
|
+
Pull requests are welcome :)
|
158
|
+
|
159
|
+
[@pierodibello](http://twitter.com/pierodibello)
|
data/Rakefile
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'trello_effort_tracker'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
task :specs => :spec
|
8
|
+
task :c => :console
|
9
|
+
|
10
|
+
desc "Open an irb session preloaded with this library, e.g. rake 'console[production]' will open a irb session with the production db env"
|
11
|
+
task :console, [:db_env] do |t, args|
|
12
|
+
args.with_defaults(db_env: "development")
|
13
|
+
sh "export MONGOID_ENV=#{args.db_env}; irb -rubygems -I lib -r trello_effort_tracker.rb -r startup_trello.rb"
|
14
|
+
end
|
15
|
+
|
16
|
+
namespace :spec do
|
17
|
+
desc "Run fast specs"
|
18
|
+
RSpec::Core::RakeTask.new(:fast) do |t|
|
19
|
+
t.rspec_opts = '--tag ~needs_valid_configuration'
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Run slow specs"
|
23
|
+
RSpec::Core::RakeTask.new(:slow) do |t|
|
24
|
+
t.rspec_opts = '--tag needs_valid_configuration'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
namespace :run do
|
29
|
+
include TrelloConfiguration
|
30
|
+
|
31
|
+
desc "Run on the cards tracked starting from a given day, e.g. rake 'run:from_day[2012-11-1]'"
|
32
|
+
task :from_day, [:starting_date, :db_env] => [:ensure_environment] do |t, args|
|
33
|
+
args.with_defaults(starting_date: Date.today.to_s, db_env: "development")
|
34
|
+
TrelloConfiguration::Database.load_env(args.db_env)
|
35
|
+
|
36
|
+
tracker = TrelloTracker.new
|
37
|
+
tracker.track(Date.parse(args.starting_date))
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Run on the cards tracked today, #{Date.today}"
|
41
|
+
task :today, [:db_env] => [:ensure_environment] do |t, args|
|
42
|
+
args.with_defaults(db_env: "development")
|
43
|
+
Rake.application.invoke_task("run:from_day[#{Date.today.to_s}, #{args.db_env}]")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
namespace :export do
|
48
|
+
desc "Export all cards to a google docs spreadsheet, e.g. rake \"export:google_docs[my_sheet,tracking,production]\""
|
49
|
+
task :google_docs, [:spreadsheet, :worksheet, :db_env] => [:ensure_environment] do |t, args|
|
50
|
+
args.with_defaults(db_env: "development")
|
51
|
+
TrelloConfiguration::Database.load_env(args.db_env)
|
52
|
+
|
53
|
+
exporter = GoogleDocsExporter.new(args.spreadsheet, args.worksheet)
|
54
|
+
spreadsheet_url = exporter.export
|
55
|
+
|
56
|
+
puts "[DONE]".color(:green)
|
57
|
+
puts "Go to #{spreadsheet_url}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
task :ensure_environment do
|
62
|
+
%w{developer_public_key developer_secret access_token_key}.each do |each_name|
|
63
|
+
unless ENV[each_name] || authorization_params_from_config_file[each_name]
|
64
|
+
puts "ERROR: Missing <#{each_name}> environment variable."
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
unless tracker_username
|
69
|
+
puts "ERROR: Missing <tracker_username> environment variable."
|
70
|
+
exit 1
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
trello:
|
2
|
+
developer_public_key: <here put the key taken from https://trello.com/1/appKey/generate>
|
3
|
+
access_token_key: <here put the token taken in the 'Reading Private Data' section from https://trello.com/1/appKey/generate>
|
4
|
+
developer_secret: <here put the Secret (for OAuth signing) taken from https://trello.com/1/appKey/generate>
|
5
|
+
|
6
|
+
tracker_username: <here put the Trello member used as recipient of the tracking notifications>
|
7
|
+
google_docs_username: <here put your Google Docs username>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
development:
|
2
|
+
sessions:
|
3
|
+
default:
|
4
|
+
database: trello_effort_tracker_dev
|
5
|
+
hosts:
|
6
|
+
- localhost:27017
|
7
|
+
|
8
|
+
test:
|
9
|
+
sessions:
|
10
|
+
default:
|
11
|
+
database: trello_effort_tracker_test
|
12
|
+
hosts:
|
13
|
+
- localhost:27017
|
14
|
+
|
15
|
+
|
16
|
+
production:
|
17
|
+
autocreate_indexes: true
|
18
|
+
persist_in_safe_mode: true
|
19
|
+
|
20
|
+
sessions:
|
21
|
+
default:
|
22
|
+
database: trello_effort_tracker_production
|
23
|
+
hosts:
|
24
|
+
- localhost:27017
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Trello
|
2
|
+
|
3
|
+
class Member
|
4
|
+
def notifications_from(from_date)
|
5
|
+
notifications(limit:1000).select(&greater_than_or_equal_to(from_date)).select(&tracking_notification?)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def greater_than_or_equal_to(from_date)
|
11
|
+
lambda { |notification| Chronic.parse(notification.date) >= from_date }
|
12
|
+
end
|
13
|
+
|
14
|
+
def tracking_notification?
|
15
|
+
lambda { |notification| notification.type == "mentionedOnCard" }
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Effort
|
2
|
+
include Mongoid::Document
|
3
|
+
include Mongoid::Timestamps
|
4
|
+
|
5
|
+
field :amount, type: BigDecimal
|
6
|
+
field :date, type: Date
|
7
|
+
field :tracking_notification_id
|
8
|
+
|
9
|
+
embeds_many :members
|
10
|
+
embedded_in :tracked_card
|
11
|
+
|
12
|
+
default_scope asc(:date)
|
13
|
+
|
14
|
+
validates_presence_of :amount, :date, :members
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
return true if other.equal?(self)
|
18
|
+
return false unless other.kind_of?(self.class)
|
19
|
+
|
20
|
+
amount == other.amount && date == other.date && Set.new(members) == Set.new(other.members)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"[#{date}] spent #{amount} hours by #{members.map(&:at_username).join(", ")}"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Estimate
|
2
|
+
include Mongoid::Document
|
3
|
+
include Mongoid::Timestamps
|
4
|
+
|
5
|
+
field :amount, type: BigDecimal
|
6
|
+
field :date, type: Date
|
7
|
+
field :tracking_notification_id
|
8
|
+
|
9
|
+
embedded_in :tracked_card
|
10
|
+
|
11
|
+
default_scope asc(:date)
|
12
|
+
|
13
|
+
validates_presence_of :amount, :date
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
return true if other.equal?(self)
|
17
|
+
return false unless other.kind_of?(self.class)
|
18
|
+
|
19
|
+
amount == other.amount && date == other.date
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"[#{date}] estimated #{amount} hours"
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "google_drive"
|
2
|
+
require 'highline/import'
|
3
|
+
|
4
|
+
class GoogleDocsExporter
|
5
|
+
include TrelloConfiguration
|
6
|
+
|
7
|
+
def initialize(spreadsheet_name, worksheet_name)
|
8
|
+
@spreadsheet_name = spreadsheet_name || "trello effort tracking"
|
9
|
+
@worksheet_name = worksheet_name || "tracking"
|
10
|
+
end
|
11
|
+
|
12
|
+
def export
|
13
|
+
Trello.logger.info "Running exporter from db env '#{db_environment}' to google docs '#{@spreadsheet_name.color(:green)}##{@worksheet_name.color(:green)}'..."
|
14
|
+
|
15
|
+
spreadsheet = google_docs_session.spreadsheet_by_title(@spreadsheet_name) || google_docs_session.create_spreadsheet(@spreadsheet_name)
|
16
|
+
worksheet = spreadsheet.worksheet_by_title(@worksheet_name) || spreadsheet.add_worksheet(@worksheet_name)
|
17
|
+
|
18
|
+
create_header(worksheet)
|
19
|
+
index = 2 # skip the header
|
20
|
+
|
21
|
+
cards = TrackedCard.all.reject(&:no_tracking?).sort_by(&:first_activity_date).reverse
|
22
|
+
cards.each do |card|
|
23
|
+
print ".".color(:green)
|
24
|
+
worksheet[index, columns[:user_story_id]] = card.short_id
|
25
|
+
worksheet[index, columns[:user_story_name]] = card.name
|
26
|
+
worksheet[index, columns[:start_date]] = card.working_start_date
|
27
|
+
worksheet[index, columns[:total_effort]] = card.total_effort
|
28
|
+
worksheet[index, columns[:last_estimate_error]] = card.last_estimate_error
|
29
|
+
card.estimates.each_with_index do |estimate, i|
|
30
|
+
worksheet[index, columns[:estimate]+i] = estimate.amount
|
31
|
+
end
|
32
|
+
index += 1
|
33
|
+
end
|
34
|
+
|
35
|
+
saved = worksheet.save
|
36
|
+
spreadsheet.human_url if saved
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def google_docs_session(email=configuration["google_docs_username"])
|
42
|
+
@session ||= login(email)
|
43
|
+
end
|
44
|
+
|
45
|
+
def login(email)
|
46
|
+
username = ask("Enter your google docs username: ") { |q| q.default = email }
|
47
|
+
password = ask("Enter your google docs password: ") { |q| q.echo = false }
|
48
|
+
|
49
|
+
GoogleDrive.login(username, password)
|
50
|
+
end
|
51
|
+
|
52
|
+
def columns
|
53
|
+
@columns ||= {
|
54
|
+
user_story_id: 1,
|
55
|
+
user_story_name: 2,
|
56
|
+
start_date: 3,
|
57
|
+
total_effort: 4,
|
58
|
+
last_estimate_error: 5,
|
59
|
+
estimate: 6,
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_header(worksheet)
|
64
|
+
worksheet.update_cells(1,1, [["ID", "Story Name", "Start Date", "Total Effort (hours)", "Last estimate error (%)", "First Estimate", "2nd estimate", "3rd estimate"]])
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Member
|
2
|
+
include Mongoid::Document
|
3
|
+
include Mongoid::Timestamps
|
4
|
+
|
5
|
+
field :trello_id
|
6
|
+
field :username
|
7
|
+
field :full_name
|
8
|
+
field :avatar_id
|
9
|
+
field :bio
|
10
|
+
field :url
|
11
|
+
|
12
|
+
embedded_in :effort
|
13
|
+
|
14
|
+
validates_presence_of :username
|
15
|
+
|
16
|
+
def self.build_from(trello_member)
|
17
|
+
trello_member_id = trello_member.id
|
18
|
+
trello_member.attributes.delete(:id)
|
19
|
+
new(trello_member.attributes.merge(trello_id: trello_member_id))
|
20
|
+
end
|
21
|
+
|
22
|
+
def at_username
|
23
|
+
"@#{username}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def avatar_url
|
27
|
+
"https://trello-avatars.s3.amazonaws.com/#{avatar_id}/30.png"
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(other)
|
31
|
+
return true if other.equal?(self)
|
32
|
+
return false unless other.kind_of?(self.class)
|
33
|
+
|
34
|
+
username == other.username
|
35
|
+
end
|
36
|
+
|
37
|
+
def eql?(other)
|
38
|
+
return false unless other.instance_of?(self.class)
|
39
|
+
username == other.username
|
40
|
+
end
|
41
|
+
|
42
|
+
def hash
|
43
|
+
username.hash
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module MongoidHelper
|
2
|
+
|
3
|
+
def without_mongo_raising_errors(&block)
|
4
|
+
original_value = Mongoid.raise_not_found_error
|
5
|
+
Mongoid.raise_not_found_error = false
|
6
|
+
begin
|
7
|
+
block.call if block
|
8
|
+
ensure
|
9
|
+
Mongoid.raise_not_found_error = original_value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|