trello_effort_tracker 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +25 -0
  2. data/.rspec +3 -0
  3. data/.rvmrc.template +2 -0
  4. data/.travis.yml +21 -0
  5. data/Gemfile +22 -0
  6. data/Gemfile.lock +101 -0
  7. data/LICENSE.txt +15 -0
  8. data/README.md +159 -0
  9. data/Rakefile +72 -0
  10. data/config/config.template.yml +7 -0
  11. data/config/mongoid.template.yml +24 -0
  12. data/lib/patches/trello/member.rb +20 -0
  13. data/lib/startup_trello.rb +2 -0
  14. data/lib/trello_effort_tracker/effort.rb +27 -0
  15. data/lib/trello_effort_tracker/estimate.rb +25 -0
  16. data/lib/trello_effort_tracker/google_docs_exporter.rb +67 -0
  17. data/lib/trello_effort_tracker/member.rb +46 -0
  18. data/lib/trello_effort_tracker/mongoid_helper.rb +13 -0
  19. data/lib/trello_effort_tracker/tracked_card.rb +103 -0
  20. data/lib/trello_effort_tracker/tracking.rb +130 -0
  21. data/lib/trello_effort_tracker/trello_authorize.rb +32 -0
  22. data/lib/trello_effort_tracker/trello_configuration.rb +33 -0
  23. data/lib/trello_effort_tracker/trello_tracker.rb +48 -0
  24. data/lib/trello_effort_tracker/version.rb +3 -0
  25. data/lib/trello_effort_tracker.rb +23 -0
  26. data/script/ci/before_script.sh +1 -0
  27. data/script/ci/run_build.sh +2 -0
  28. data/script/crontab.template +8 -0
  29. data/script/mate.sh +1 -0
  30. data/spec/effort_spec.rb +52 -0
  31. data/spec/estimate_spec.rb +38 -0
  32. data/spec/integration/trello_authorization_spec.rb +12 -0
  33. data/spec/member_spec.rb +45 -0
  34. data/spec/spec_helper.rb +21 -0
  35. data/spec/tracked_card_spec.rb +267 -0
  36. data/spec/tracking_spec.rb +236 -0
  37. data/spec/trello_authorize_spec.rb +71 -0
  38. data/spec/trello_configuration_spec.rb +43 -0
  39. data/trello_effort_tracker.gemspec +19 -0
  40. 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
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --format doc
3
+ --order rand
data/.rvmrc.template ADDED
@@ -0,0 +1,2 @@
1
+ rvm 1.9.3-p194@spikes --create
2
+ rvm wrapper 1.9.3-p194@spikes textmate
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,2 @@
1
+ include TrelloAuthorize
2
+ authorize_on_trello
@@ -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