tracco 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,42 +1,59 @@
1
+ 0.0.13 / 2013-02-27
2
+ ==================
3
+ * adding muted status on Effort to be able to 'turn off' a card's specific effort
4
+ * adding a TrackedCard.efforts_between method to fetch all the cards with an effort spent in a given date range
5
+ * improving TrackedCard#trello_notifications
6
+
1
7
  0.0.12
8
+ ======
2
9
  - Adding a TrackedCard#trello_notifications to fetch all the notifications belonging to the card
3
10
  - Introducing FactoryGirl as a factory gem to simplify specs
4
11
 
5
12
  0.0.11
13
+ ======
6
14
  - Adding a TrackedCard.all_tracked_cards method to fetch all cards with a valid tracking. Moreover, sorting_options can be passed to sort using a method e.g. TrackedCard.all_tracked_cards(:method => :name, :order => :desc)
7
15
 
8
16
  0.0.10
17
+ ======
9
18
  - Including Mongoid::MultiParameterAttributes in Effort and Estimate to enable date params to be collected correctly by Rails app (e.g. using the Rails' date_select form helper)
10
19
 
11
20
  0.0.8 - 0.0.9
21
+ =============
12
22
  - Gem rename :)
13
23
 
14
24
  0.0.7
25
+ =====
15
26
  - Minor refactorings on the rake tasks
16
27
 
17
28
  0.0.6
29
+ =====
18
30
  - Added TrackedCard#status to track card status: :todo if no effort has been spent, :done if the card is in a DONE column, :in_progress otherwise
19
31
  - Added a Members#effort_spent and #effort_spent_since to extract the overall effort tracked by a member since a given date
20
32
  - Re-merged in the trello_effort_app branch to bring on the Rails app
21
33
 
22
34
  0.0.5
35
+ =====
23
36
  - Improved docs
24
37
  - Improved use of this tool as a gem
25
38
 
26
39
  0.0.4
40
+ =====
27
41
  - A card moved into a DONE column is closed
28
42
 
29
43
  0.0.3
44
+ =====
30
45
  - When I send a tracking with "DONE" then the card is "closed" (aka "finished")
31
46
  - Export all cards in a Google Docs spreadsheet
32
47
  - When I card change its name, the tracked card should be updated too
33
48
  - A card should tells its estimate error (3)
34
49
 
35
50
  0.0.2
51
+ =====
36
52
  - Very simple Rails app on a separate branch, still experimenting
37
53
  - Should be able to open a Trello console with a rake task
38
54
  - Avoid running the script if already run for that period
39
55
  - Tracking data is now persisted in a mongo database
40
56
 
41
57
  0.0.1
58
+ =====
42
59
  - Initial Release.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tracco (0.0.12)
4
+ tracco (0.0.13)
5
5
  bson_ext
6
6
  chronic
7
7
  google_drive
@@ -19,7 +19,7 @@ GEM
19
19
  activesupport (3.2.12)
20
20
  i18n (~> 0.6)
21
21
  multi_json (~> 1.0)
22
- addressable (2.3.2)
22
+ addressable (2.3.3)
23
23
  bson (1.8.2)
24
24
  bson_ext (1.8.2)
25
25
  bson (~> 1.8.2)
@@ -1,10 +1,10 @@
1
1
  module Trello
2
2
  class Member
3
3
  # Reopening the Trello::Member class to add a notifications helper method
4
- def notifications_from(starting_date)
4
+ def notifications_since(starting_date)
5
5
  notifications(limit:1000).select(&greater_than_or_equal_to(starting_date)).select(&tracking_notification?)
6
6
  end
7
-
7
+
8
8
  private
9
9
 
10
10
  def greater_than_or_equal_to(starting_date)
@@ -14,7 +14,7 @@ module Trello
14
14
  def tracking_notification?
15
15
  lambda { |notification| notification.type == "mentionedOnCard" }
16
16
  end
17
-
17
+
18
18
  end
19
19
 
20
20
  end
data/lib/tracco/effort.rb CHANGED
@@ -6,11 +6,12 @@ class Effort
6
6
  field :amount, type: BigDecimal
7
7
  field :date, type: Date
8
8
  field :tracking_notification_id
9
+ field :muted, type: Boolean, default: false
9
10
 
10
11
  embeds_many :members
11
12
  embedded_in :tracked_card
12
13
 
13
- default_scope asc(:date)
14
+ default_scope where(muted: false).asc(:date)
14
15
 
15
16
  validates_presence_of :amount, :date, :members
16
17
 
@@ -18,7 +18,7 @@ class GoogleDocsExporter
18
18
  create_header(worksheet)
19
19
  index = 2 # skip the header
20
20
 
21
- cards = TrackedCard.all.reject(&:no_tracking?).sort_by(&:first_activity_date).reverse
21
+ cards = TrackedCard.all_tracked_cards(:method => :first_activity_date, :order => :desc)
22
22
  cards.each do |card|
23
23
  print ".".color(:green)
24
24
  worksheet[index, columns[:user_story_id]] = card.short_id
data/lib/tracco/member.rb CHANGED
@@ -28,7 +28,7 @@ class Member
28
28
  end
29
29
 
30
30
  def effort_spent(from_date=nil)
31
- cards = TrackedCard.where("efforts.members.username" => username)
31
+ cards = TrackedCard.with_effort_spent_by(username)
32
32
  efforts = cards.map(&:efforts).compact.flatten
33
33
  efforts = efforts.select {|e| e.date >= from_date} if from_date
34
34
  efforts.select { |effort| effort.include?(self) }.inject(0) { |total, effort| total + effort.amount_per_member }
@@ -19,6 +19,8 @@ class TrackedCard
19
19
  validates_presence_of :name, :short_id, :trello_id
20
20
  validates_numericality_of :short_id
21
21
 
22
+ scope :with_effort_spent_by, ->(username){ where("efforts.members.username" => username) }
23
+
22
24
  def self.find_by_trello_id(trello_id)
23
25
  without_mongo_raising_errors do
24
26
  find_by(trello_id: trello_id)
@@ -35,10 +37,9 @@ class TrackedCard
35
37
 
36
38
  def self.all_tracked_cards(sorting_options = {})
37
39
  cards = all.reject(&:no_tracking?)
38
-
39
- cards = cards.sort_by(&sorting_options[:method].to_sym) if sorting_options[:method]
40
-
41
- sorting_options[:order] == :desc ? cards.reverse : cards
40
+ cards.sort_by!(&sorting_options[:sort_by].to_sym) if sorting_options[:sort_by]
41
+ cards.reverse! if sorting_options[:order] == :desc
42
+ return cards
42
43
  end
43
44
 
44
45
  def self.build_from(trello_card)
@@ -47,6 +48,15 @@ class TrackedCard
47
48
  new(trello_card.attributes.merge(trello_id: trello_card_id))
48
49
  end
49
50
 
51
+ def self.efforts_between(search_options)
52
+ condition = {}
53
+ {'$gte' => :from_date, '$lte' => :to_date}.each do |selection, option_key|
54
+ condition[selection] = search_options[option_key] if search_options[option_key]
55
+ end
56
+
57
+ where("efforts.date" => condition)
58
+ end
59
+
50
60
  def status
51
61
  if done?
52
62
  :done
@@ -118,7 +128,7 @@ class TrackedCard
118
128
 
119
129
  def trello_notifications
120
130
  notification_ids = efforts.map(&:tracking_notification_id) | estimates.map(&:tracking_notification_id)
121
- notification_ids.map { |each_id| Trello::Notification.find(each_id) }.sort_by(&:date)
131
+ notification_ids.map { |id| Trello::Notification.find(id) rescue nil }.compact.sort_by(&:date)
122
132
  end
123
133
 
124
134
  def to_s
@@ -2,39 +2,36 @@ module Tracking
2
2
  class EffortTracking
3
3
  include Base
4
4
 
5
- #TODO: rename to 'amount' ?
6
- #TODO: avoid recomputing effort every time using a lazy instance variable
7
- def effort
8
- effort_amount = convert_to_hours(raw_effort)
9
- if effort_amount
10
- total_effort = effort_amount * effort_members.size
11
- Effort.new(amount: total_effort, date: date, members: effort_members, tracking_notification_id: tracking_notification.id)
12
- end
13
- end
14
-
15
5
  def add_to(card)
16
6
  card.efforts << effort unless card.contains_effort?(effort)
17
7
  end
18
8
 
9
+ def effort
10
+ @effort ||= Effort.new(amount: total_effort_from(raw_effort), date: date, members: effort_members, tracking_notification_id: tracking_notification.id)
11
+ end
12
+
19
13
  private
20
14
 
15
+ def total_effort_from(raw_effort)
16
+ effort_amount = convert_to_hours(raw_effort)
17
+ total_effort = effort_amount * effort_members.size
18
+ end
19
+
21
20
  def raw_effort
22
21
  extract_match_from_raw_tracking(/\+#{DURATION_REGEXP}/)
23
22
  end
24
23
 
25
24
  def effort_members
26
- @effort_members ||= users_involved_in_the_effort.map do |username|
25
+ @effort_members ||= members_involved_in_the_effort.map do |username|
27
26
  Member.build_from(Trello::Member.find(username))
28
27
  end
29
-
30
- @effort_members
31
28
  end
32
29
 
33
- def users_involved_in_the_effort
34
- users_involved_in_the_effort = raw_tracking.scan(/@(\w+)/).flatten
35
- users_involved_in_the_effort << notifier.username unless should_count_only_listed_members?
30
+ def members_involved_in_the_effort
31
+ members_involved_in_the_effort = raw_tracking.scan(/@(\w+)/).flatten
32
+ members_involved_in_the_effort << notifier.username unless should_count_only_listed_members?
36
33
 
37
- users_involved_in_the_effort
34
+ members_involved_in_the_effort
38
35
  end
39
36
 
40
37
  def should_count_only_listed_members?
@@ -2,11 +2,8 @@ module Tracking
2
2
  class EstimateTracking
3
3
  include Base
4
4
 
5
- #TODO: rename to 'amount' ?
6
- #TODO: avoid recomputing estimate every time using a lazy instance variable
7
5
  def estimate
8
- estimate = convert_to_hours(raw_estimate)
9
- Estimate.new(amount: estimate, date: date, tracking_notification_id: tracking_notification.id)
6
+ @estimate ||= Estimate.new(amount: convert_to_hours(raw_estimate), date: date, tracking_notification_id: tracking_notification.id)
10
7
  end
11
8
 
12
9
  def add_to(card)
@@ -0,0 +1,26 @@
1
+ module Tracking
2
+ class Factory
3
+
4
+ DURATION_REGEXP = '(\d+\.?\d*[phdg])'
5
+
6
+ @match_pairs = {}
7
+
8
+ def self.build_from(tracking_notification)
9
+ matching_pair = @match_pairs.find { |regexp, tracking_class| tracking_notification.data['text'] =~ regexp }
10
+
11
+ tracking_class = matching_pair ? matching_pair.last : Tracking::InvalidTracking
12
+ tracking_class.new(tracking_notification)
13
+ end
14
+
15
+ private
16
+
17
+ def self.match(match_pair)
18
+ @match_pairs.merge!(match_pair)
19
+ end
20
+
21
+ match /\[#{DURATION_REGEXP}\]/ => Tracking::EstimateTracking
22
+ match /\+#{DURATION_REGEXP}/ => Tracking::EffortTracking
23
+ match /DONE/ => Tracking::CardDoneTracking
24
+
25
+ end
26
+ end
@@ -10,13 +10,13 @@ class TrelloTracker
10
10
  end
11
11
 
12
12
  def track(starting_date=Date.today)
13
- notifications = tracker.notifications_from(starting_date)
13
+ notifications = tracker.notifications_since(starting_date)
14
14
 
15
15
  oldest, latest = boundary_dates_in(notifications)
16
16
  Trello.logger.info "Processing #{notifications.size} tracking notifications (from #{oldest} to #{latest}) starting from #{starting_date}..."
17
17
 
18
18
  notifications.each do |notification|
19
- tracking = TrackingFactory.build_from(notification)
19
+ tracking = Tracking::Factory.build_from(notification)
20
20
  begin
21
21
  tracked_card = TrackedCard.update_or_create_with(notification.card)
22
22
  tracked_card.add!(tracking)
@@ -1,3 +1,3 @@
1
1
  class TrelloEffortTracker
2
- VERSION = '0.0.12'
2
+ VERSION = '0.0.13'
3
3
  end
data/lib/tracco.rb CHANGED
@@ -18,7 +18,7 @@ require 'tracco/tracking/estimate_tracking'
18
18
  require 'tracco/tracking/effort_tracking'
19
19
  require 'tracco/tracking/card_done_tracking'
20
20
  require 'tracco/tracking/invalid_tracking'
21
- require 'tracco/tracking_factory'
21
+ require 'tracco/tracking/factory'
22
22
  require 'tracco/trello_tracker'
23
23
  require 'tracco/google_docs_exporter'
24
24
 
data/spec/effort_spec.rb CHANGED
@@ -3,7 +3,7 @@ require 'mongoid-rspec'
3
3
 
4
4
  describe Effort do
5
5
 
6
- it { should have_fields(:amount, :date) }
6
+ it { should have_fields(:amount, :date, :muted, :tracking_notification_id) }
7
7
  it { should be_embedded_in(:tracked_card) }
8
8
  it { should embed_many(:members) }
9
9
 
@@ -0,0 +1,11 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :effort do
4
+ amount 4
5
+ date Date.today
6
+ members [Member.new(username: "any_name")]
7
+ sequence(:tracking_notification_id, 1000)
8
+ muted false
9
+ end
10
+
11
+ end
@@ -0,0 +1,9 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :estimate do
4
+ amount 42
5
+ date Date.today
6
+ sequence(:tracking_notification_id, 1000)
7
+ end
8
+
9
+ end
@@ -1,13 +1,12 @@
1
1
  FactoryGirl.define do
2
+
2
3
  factory :tracked_card do
3
- sequence(:name) { |n| "any_card_#{n}" }
4
- description "any description"
5
- sequence(:short_id, 1000) { |n| n }
4
+ sequence(:short_id, 1000)
6
5
  sequence(:trello_id, 100000) { |n| "xyz#{n}" }
7
- done false
6
+ sequence(:name) { |n| "any_card_#{n}" }
7
+ description "any description"
8
+ done false
8
9
  closed false
9
-
10
- # embeds_many :estimates
11
- # embeds_many :efforts
12
10
  end
11
+
13
12
  end
data/spec/spec_helper.rb CHANGED
@@ -14,8 +14,6 @@ rescue Bundler::GemNotFound => e
14
14
  exit!
15
15
  end
16
16
 
17
- Bundler.require(:spec)
18
-
19
17
  require 'tracco'
20
18
 
21
19
  require 'factory_girl'
@@ -32,39 +30,3 @@ end
32
30
 
33
31
  # force test env for the mongodb configuration
34
32
  TrelloConfiguration::Database.load_env("test")
35
-
36
-
37
- ## Spec Helper Methods (TODO: should we move them in a separate file?)
38
-
39
- def unrecognized_notification
40
- create_notification(data: { 'text' => '@trackinguser hi there!' })
41
- end
42
-
43
- def notification_with_message(message)
44
- create_notification(data: { 'text' => message })
45
- end
46
-
47
- def create_estimate(time_measurement)
48
- create_notification(data: { 'text' => "@trackinguser [1.5#{TIME_MEASUREMENTS[time_measurement]}]" })
49
- end
50
-
51
- def create_effort(time_measurement)
52
- create_notification(data: { 'text' => "@trackinguser +4.5#{TIME_MEASUREMENTS[time_measurement]}]" })
53
- end
54
-
55
- def with(notification)
56
- tracking = TrackingFactory.build_from(notification)
57
- yield(tracking)
58
- end
59
-
60
- def with_message(notification_message, &block)
61
- with(notification_with_message(notification_message), &block)
62
- end
63
-
64
- def create_notification(custom_params)
65
- params = { data: { 'text' => "@trackinguser +2h" }, date: "2012-10-28T21:06:14.801Z", member_creator: stub(username: "pietrodibello") }
66
- params.merge!(custom_params)
67
-
68
- stub(data: params[:data], date: params[:date], member_creator: params[:member_creator]).as_null_object
69
- end
70
-
@@ -0,0 +1,32 @@
1
+
2
+ def unrecognized_notification
3
+ create_notification(data: { 'text' => '@trackinguser hi there!' })
4
+ end
5
+
6
+ def notification_with_message(message)
7
+ create_notification(data: { 'text' => message })
8
+ end
9
+
10
+ def create_estimate(time_measurement)
11
+ create_notification(data: { 'text' => "@trackinguser [1.5#{TIME_MEASUREMENTS[time_measurement]}]" })
12
+ end
13
+
14
+ def create_effort(time_measurement)
15
+ create_notification(data: { 'text' => "@trackinguser +4.5#{TIME_MEASUREMENTS[time_measurement]}]" })
16
+ end
17
+
18
+ def with(notification)
19
+ tracking = Tracking::Factory.build_from(notification)
20
+ yield(tracking)
21
+ end
22
+
23
+ def with_message(notification_message, &block)
24
+ with(notification_with_message(notification_message), &block)
25
+ end
26
+
27
+ def create_notification(custom_params)
28
+ params = { data: { 'text' => "@trackinguser +2h" }, date: "2012-10-28T21:06:14.801Z", member_creator: stub(username: "pietrodibello") }
29
+ params.merge!(custom_params)
30
+
31
+ stub(data: params[:data], date: params[:date], member_creator: params[:member_creator]).as_null_object
32
+ end
@@ -8,6 +8,11 @@ describe TrackedCard do
8
8
 
9
9
  subject(:card) { build(:tracked_card) }
10
10
 
11
+ before(:each) do
12
+ # adding a muted effort to check that won't be counted
13
+ card.efforts << build(:effort, amount: 1000, muted: true)
14
+ end
15
+
11
16
  %w{piero tommaso michele}.each do |username|
12
17
  let(username.to_sym) { Member.new(username: username) }
13
18
  end
@@ -40,60 +45,67 @@ describe TrackedCard do
40
45
  end
41
46
  end
42
47
 
43
- describe ".all_tracked_cards" do
44
- it "finds all tracked cards with a valid tracking" do
45
- # TODO introduce a factory on estimate and effort
46
- card = create(:tracked_card, :estimates => [Estimate.new(amount: 5, date: Date.today)],
47
- :efforts => [Effort.new(amount: 3, date: Date.today, members: [piero])])
48
- card_without_tracking = create(:tracked_card)
48
+ describe ".with_effort_spent_by" do
49
+ it "finds all the cards worked by a given member" do
50
+ card = create(:tracked_card, efforts: [build(:effort, members: [piero, tommaso])])
51
+ another_card = create(:tracked_card, efforts: [build(:effort, members: [piero, michele])])
49
52
 
53
+ TrackedCard.with_effort_spent_by("piero").should =~ [card, another_card]
54
+ TrackedCard.with_effort_spent_by("tommaso").should == [card]
55
+ TrackedCard.with_effort_spent_by("michele").should == [another_card]
56
+ end
57
+ end
58
+
59
+ describe ".efforts_between" do
60
+ it "finds all the cards worked in a given date range" do
61
+ a_card = create(:tracked_card, efforts: [build(:effort, date: Date.parse("2013-01-02"))])
62
+ another_card = create(:tracked_card, efforts: [build(:effort, date: Date.parse("2013-11-03"))])
63
+ a_very_old_card = create(:tracked_card, efforts: [build(:effort, date: Date.parse("2009-11-03"))])
50
64
 
51
- TrackedCard.all_tracked_cards.should == [card]
65
+ TrackedCard.efforts_between(from_date: Date.parse("2012-01-01")).should =~ [a_card, another_card]
66
+ TrackedCard.efforts_between(from_date: Date.parse("2013-01-01")).should =~ [a_card, another_card]
67
+ TrackedCard.efforts_between(from_date: Date.parse("2013-01-22")).should == [another_card]
68
+ TrackedCard.efforts_between(from_date: Date.parse("2014-01-22")).should == []
69
+
70
+ TrackedCard.efforts_between(from_date: Date.parse("2012-01-01"), to_date: Date.parse("2012-11-01")).should == []
71
+ TrackedCard.efforts_between(from_date: Date.parse("2012-01-01"), to_date: Date.parse("2013-02-02")).should == [a_card]
72
+ TrackedCard.efforts_between(from_date: Date.parse("2012-01-01"), to_date: Date.parse("2014-02-02")).should =~ [a_card, another_card]
52
73
  end
74
+ end
53
75
 
54
- it "optionally sorts the cards using a given sorting method" do
55
- card = create(:tracked_card, name: "AAA")
56
- card.estimates << Estimate.new(amount: 5, date: Date.today)
57
- card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero])
76
+ describe ".all_tracked_cards" do
77
+ let!(:card) { create(:tracked_card, name: "AAA", estimates: [build(:estimate)], efforts: [build(:effort)]) }
78
+ let!(:another_card) { create(:tracked_card, name: "ZZZ", estimates: [build(:estimate)], efforts: [build(:effort)]) }
79
+ let!(:card_without_tracking) { create(:tracked_card) }
58
80
 
59
- another_card = create(:tracked_card, name: "ZZZ")
60
- another_card.estimates << Estimate.new(amount: 5, date: Date.today)
61
- another_card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero])
81
+ it "finds all tracked cards with a valid tracking" do
82
+ TrackedCard.all_tracked_cards.should =~ [card, another_card]
83
+ end
62
84
 
63
- card_without_tracking = create(:tracked_card)
85
+ it "optionally sorts the cards using a given sorting method" do
86
+ card.update_attributes(name: "AAA")
87
+ another_card.update_attributes(name: "ZZZ")
64
88
 
65
- TrackedCard.all_tracked_cards(:method => :name).should == [card, another_card]
89
+ TrackedCard.all_tracked_cards(:sort_by => :name).should == [card, another_card]
66
90
  end
67
91
 
68
92
  it "applies an optional sorting order" do
69
- card = create(:tracked_card, name: "AAA")
70
- card.estimates << Estimate.new(amount: 5, date: Date.today)
71
- card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero])
72
-
73
- another_card = create(:tracked_card, name: "ZZZ")
74
- another_card.estimates << Estimate.new(amount: 5, date: Date.today)
75
- another_card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero])
93
+ card.update_attributes(name: "AAA")
94
+ another_card.update_attributes(name: "ZZZ")
76
95
 
77
96
  card_without_tracking = create(:tracked_card)
78
97
 
79
- TrackedCard.all_tracked_cards(:method => :name, :order => :desc).should == [another_card, card]
98
+ TrackedCard.all_tracked_cards(:sort_by => :name, :order => :desc).should == [another_card, card]
80
99
  end
81
100
 
82
101
  it "uses the ascending order as default sorting order option" do
83
- card = create(:tracked_card, name: "AAA", short_id: 44)
84
- card.estimates << Estimate.new(amount: 5, date: Date.today)
85
- card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero])
86
-
87
- another_card = create(:tracked_card, name: "ZZZ", short_id: 12)
88
- another_card.estimates << Estimate.new(amount: 5, date: Date.today)
89
- another_card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero])
102
+ card.update_attributes(name: "AAA", short_id: 44)
103
+ another_card.update_attributes(name: "ZZZ", short_id: 11)
104
+ card_without_tracking.update_attributes(short_id: 3456)
90
105
 
91
- card_without_tracking = create(:tracked_card, short_id: 3456)
92
-
93
- TrackedCard.all_tracked_cards(:method => :short_id).should == [another_card, card]
106
+ TrackedCard.all_tracked_cards(:sort_by => :short_id).should == [another_card, card]
94
107
  end
95
108
 
96
-
97
109
  end
98
110
 
99
111
  describe ".update_or_create_with" do
@@ -162,22 +174,39 @@ describe TrackedCard do
162
174
 
163
175
  end
164
176
 
177
+ describe "card with muted effort" do
178
+ it "fetches only non-muted efforts" do
179
+ card = create(:tracked_card, efforts: [build(:effort, muted: false)])
180
+ card_with_muted_effort = create(:tracked_card, efforts: [build(:effort, muted: true)])
181
+
182
+ TrackedCard.should have(2).cards
183
+
184
+ card.efforts.should have(1).effort
185
+
186
+ card_with_muted_effort.efforts.should be_empty
187
+ card_with_muted_effort.efforts.unscoped.should have(1).effort
188
+ end
189
+
190
+ it "skips muted effort when computing the total effort on the card" do
191
+ card.efforts << build(:effort, amount: 3, muted: true)
192
+ card.efforts << build(:effort, amount: 5, muted: false)
193
+
194
+ card.total_effort.should == 5
195
+ end
196
+ end
197
+
165
198
  it "has no estimates and efforts initially" do
166
199
  card.estimates.should be_empty
167
200
  card.efforts.should be_empty
168
201
  end
169
202
 
170
203
  it "is possible to add estimates" do
171
- card.estimates << Estimate.new(amount: 5, date: Date.yesterday)
172
- card.estimates << Estimate.new(amount: 12, date: Date.today)
173
-
204
+ card.estimates << build(:estimate) << build(:estimate)
174
205
  card.estimates.should have(2).estimates
175
206
  end
176
207
 
177
208
  it "is possible to add efforts" do
178
- card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero, tommaso])
179
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
180
-
209
+ card.efforts << build(:effort) << build(:effort)
181
210
  card.efforts.should have(2).efforts
182
211
  end
183
212
 
@@ -185,14 +214,26 @@ describe TrackedCard do
185
214
  let(:first_notification) { stub("notification1", date: Date.yesterday) }
186
215
  let(:second_notification) { stub("notification1", date: Date.today) }
187
216
 
188
- it "fetch all the card notifications from trello" do
217
+ it "fetches all the card notifications from trello" do
189
218
  card.estimates << Estimate.new(tracking_notification_id: "xyz987", amount: 5, date: Date.yesterday)
190
219
  card.efforts << Effort.new(tracking_notification_id: "abc123", amount: 3, date: Date.today, members: [piero])
191
220
 
192
221
  Trello::Notification.should_receive(:find).with("xyz987").and_return(second_notification)
193
222
  Trello::Notification.should_receive(:find).with("abc123").and_return(first_notification)
223
+
194
224
  card.trello_notifications.should == [first_notification, second_notification]
195
225
  end
226
+
227
+ it "skips the notifications not found" do
228
+ card.estimates << build(:estimate, tracking_notification_id: "unexisting_id")
229
+ card.efforts << build(:effort, tracking_notification_id: "first_notification_id")
230
+
231
+ Trello::Notification.should_receive(:find).with("unexisting_id").and_raise(Trello::Error)
232
+ Trello::Notification.should_receive(:find).with("first_notification_id").and_return(first_notification)
233
+
234
+ card.trello_notifications.should == [first_notification]
235
+ end
236
+
196
237
  end
197
238
 
198
239
  describe "equality" do
@@ -207,7 +248,7 @@ describe TrackedCard do
207
248
  end
208
249
 
209
250
  describe "#add" do
210
- let(:card) { TrackedCard.new(name: "a name", trello_id: "123456789") }
251
+ let(:card) { build(:tracked_card) }
211
252
  let(:estimate_tracking) { Tracking::EstimateTracking.new(create_notification(data: { 'text' => "@trackinguser [1h]" })) }
212
253
 
213
254
  it "adds an estimate from a tracking estimate notification" do
@@ -232,7 +273,7 @@ describe TrackedCard do
232
273
  end
233
274
 
234
275
  describe "#add!" do
235
- let(:card) { TrackedCard.new(name: "a name", trello_id: "123456789", short_id: "123") }
276
+ let(:card) { build(:tracked_card) }
236
277
 
237
278
  it "saves the tracked card after adding the tracking" do
238
279
  any_tracking = Tracking::EstimateTracking.new(create_notification(data: { 'text' => "@trackinguser [1h]" }))
@@ -248,50 +289,51 @@ describe TrackedCard do
248
289
  end
249
290
 
250
291
  it "computes the total effort on the card" do
251
- card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero, tommaso])
252
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
292
+ card.efforts << build(:effort, amount: 3)
293
+ card.efforts << build(:effort, amount: 5)
253
294
 
254
295
  card.total_effort.should == 3+5
255
296
  end
256
297
  end
257
298
 
258
299
  describe "#last_estimate_error" do
300
+
259
301
  it "is nil when the card has no estimate" do
260
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
302
+ card.efforts << build(:effort, amount: 5)
261
303
 
262
304
  card.last_estimate_error.should be_nil
263
305
  end
264
306
 
265
307
  it "is nil when the card has no effort" do
266
- card.efforts << Estimate.new(amount: 5, date: Date.today)
308
+ card.efforts << build(:estimate)
267
309
 
268
310
  card.last_estimate_error.should be_nil
269
311
  end
270
312
 
271
313
  it "is zero when actual effort is equal to estimate" do
272
- card.estimates << Estimate.new(amount: 5, date: Date.today)
273
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
314
+ card.estimates << build(:estimate, amount: 5)
315
+ card.efforts << build(:effort, amount: 5)
274
316
 
275
317
  card.last_estimate_error.should == 0.0
276
318
  end
277
319
 
278
320
  it "is 100 when the actual effort is twice the given estimate" do
279
- card.estimates << Estimate.new(amount: 5, date: Date.today)
280
- card.efforts << Effort.new(amount: 10, date: Date.today, members: [tommaso])
321
+ card.estimates << build(:estimate, amount: 5)
322
+ card.efforts << build(:effort, amount: 10)
281
323
 
282
324
  card.last_estimate_error.should == 100.0
283
325
  end
284
326
 
285
327
  it "is -50 when the actual effort is half of the given estimate" do
286
- card.estimates << Estimate.new(amount: 10, date: Date.today)
287
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
328
+ card.estimates << build(:estimate, amount: 10)
329
+ card.efforts << build(:effort, amount: 5)
288
330
 
289
331
  card.last_estimate_error.should == -50.0
290
332
  end
291
333
 
292
334
  it "is rounded with two decimal digits" do
293
- card.estimates << Estimate.new(amount: 3, date: Date.today)
294
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
335
+ card.estimates << build(:estimate, amount: 3)
336
+ card.efforts << build(:effort, amount: 5)
295
337
 
296
338
  card.last_estimate_error.should == 66.67
297
339
  end
@@ -313,9 +355,9 @@ describe TrackedCard do
313
355
 
314
356
  describe "#members" do
315
357
  it "lists all the members which spent some effort on the card" do
316
- card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero, tommaso])
317
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
318
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso, michele])
358
+ card.efforts << build(:effort, members: [piero, tommaso])
359
+ card.efforts << build(:effort, members: [tommaso])
360
+ card.efforts << build(:effort, members: [tommaso, michele])
319
361
 
320
362
  card.members.should == [piero, tommaso, michele]
321
363
  end
@@ -324,9 +366,9 @@ describe TrackedCard do
324
366
  describe "#working_start_date" do
325
367
 
326
368
  it "is the date of the first effort spent on the card" do
327
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
328
- card.efforts << Effort.new(amount: 3, date: Date.yesterday, members: [tommaso])
329
- card.efforts << Effort.new(amount: 5, date: Date.tomorrow, members: [tommaso])
369
+ card.efforts << build(:effort, date: Date.today)
370
+ card.efforts << build(:effort, date: Date.yesterday)
371
+ card.efforts << build(:effort, date: Date.tomorrow)
330
372
 
331
373
  card.working_start_date.should == Date.yesterday
332
374
  end
@@ -334,12 +376,12 @@ describe TrackedCard do
334
376
 
335
377
  describe "#first_activity_date" do
336
378
  it "is the date of the first effort or estimate given on the card" do
337
- card.estimates << Estimate.new(amount: 5, date: Date.yesterday)
338
- card.estimates << Estimate.new(amount: 12, date: Date.yesterday.prev_day)
339
- card.estimates << Estimate.new(amount: 12, date: Date.tomorrow)
379
+ card.estimates << build(:estimate, date: Date.yesterday)
380
+ card.estimates << build(:estimate, date: Date.yesterday.prev_day)
381
+ card.estimates << build(:estimate, date: Date.tomorrow)
340
382
 
341
- card.efforts << Effort.new(amount: 3, date: Date.yesterday, members: [tommaso])
342
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
383
+ card.efforts << build(:effort, date: Date.yesterday)
384
+ card.efforts << build(:effort, date: Date.today)
343
385
 
344
386
  card.first_activity_date.should == Date.yesterday.prev_day
345
387
  end
@@ -347,9 +389,9 @@ describe TrackedCard do
347
389
 
348
390
  describe "#first_estimate_date" do
349
391
  it "is the date of the first estimate given on the card" do
350
- card.estimates << Estimate.new(amount: 5, date: Date.tomorrow)
351
- card.estimates << Estimate.new(amount: 12, date: Date.yesterday.prev_day)
352
- card.estimates << Estimate.new(amount: 12, date: Date.today)
392
+ card.estimates << build(:estimate, date: Date.tomorrow)
393
+ card.estimates << build(:estimate, date: Date.yesterday.prev_day)
394
+ card.estimates << build(:estimate, date: Date.today)
353
395
 
354
396
  card.first_estimate_date.should == Date.yesterday.prev_day
355
397
  end
@@ -357,9 +399,9 @@ describe TrackedCard do
357
399
 
358
400
  describe "#last_estimate_date" do
359
401
  it "is the date of the last estimate given on the card" do
360
- card.estimates << Estimate.new(amount: 5, date: Date.yesterday)
361
- card.estimates << Estimate.new(amount: 12, date: Date.tomorrow)
362
- card.estimates << Estimate.new(amount: 12, date: Date.today)
402
+ card.estimates << build(:estimate, date: Date.yesterday)
403
+ card.estimates << build(:estimate, date: Date.tomorrow)
404
+ card.estimates << build(:estimate, date: Date.today)
363
405
 
364
406
  card.last_estimate_date.should == Date.tomorrow
365
407
  end
@@ -369,7 +411,7 @@ describe TrackedCard do
369
411
  it "is false when there's no effort or estimate tracked on the card" do
370
412
  card.no_tracking?.should be_true
371
413
 
372
- card.estimates << Estimate.new(amount: 5, date: Date.yesterday)
414
+ card.estimates << build(:estimate, date: Date.yesterday)
373
415
  card.no_tracking?.should be_false
374
416
  end
375
417
  end
@@ -387,10 +429,10 @@ describe TrackedCard do
387
429
 
388
430
  describe "#status" do
389
431
  it "is done when is done" do
390
- done_card = TrackedCard.new(done:true)
432
+ done_card = TrackedCard.new(done: true)
391
433
  done_card.status.should == :done
392
434
 
393
- done_card.efforts << Effort.new(amount: 3, date: Date.today, members: [Member.new(username: "any")])
435
+ done_card.efforts << build(:effort)
394
436
  done_card.status.should == :done
395
437
  end
396
438
 
@@ -398,7 +440,7 @@ describe TrackedCard do
398
440
  card = TrackedCard.new
399
441
  card.status.should == :todo
400
442
 
401
- card.efforts << Effort.new(amount: 3, date: Date.today, members: [Member.new(username: "any")])
443
+ card.efforts << build(:effort)
402
444
  card.status.should == :in_progress
403
445
  end
404
446
  end
@@ -7,7 +7,7 @@ module Tracking
7
7
  it "marks the card as done" do
8
8
  card = TrackedCard.new
9
9
 
10
- done_tracking = TrackingFactory.build_from(notification_with_message("DONE"))
10
+ done_tracking = Tracking::Factory.build_from(notification_with_message("DONE"))
11
11
  done_tracking.add_to(card)
12
12
 
13
13
  card.done?.should be_true
@@ -28,7 +28,7 @@ module Tracking
28
28
  date: "2012-10-28T21:06:14.801Z",
29
29
  member_creator: stub(username: "michelepangrazzi"))
30
30
 
31
- TrackingFactory.build_from(raw_data).effort.should == Effort.new(amount: 2.0, date: Date.parse('2012-10-28'), members: [michelepangrazzi])
31
+ Tracking::Factory.build_from(raw_data).effort.should == Effort.new(amount: 2.0, date: Date.parse('2012-10-28'), members: [michelepangrazzi])
32
32
  end
33
33
 
34
34
  it "converts the effort in hours when the notification contains an effort in days" do
@@ -87,7 +87,7 @@ module Tracking
87
87
  it "tracks the effort with the date given in the notification text, not the actual notification date" do
88
88
  raw_data = create_notification(data: { 'text' => "@trackinguser 22.11.2012 +6p" }, date: "2012-09-19T12:46:13.713Z")
89
89
 
90
- tracking = TrackingFactory.build_from(raw_data)
90
+ tracking = Tracking::Factory.build_from(raw_data)
91
91
 
92
92
  tracking.effort.date.should == Date.parse('2012-11-22')
93
93
  end
@@ -95,7 +95,7 @@ module Tracking
95
95
  it "tracks the effort to yesterday when the keyword 'yesterday' is present before the effort amount" do
96
96
  raw_data = create_notification(data: { 'text' => "@trackinguser yesterday +6p" }, date: "2012-09-19T12:46:13.713Z")
97
97
 
98
- tracking = TrackingFactory.build_from(raw_data)
98
+ tracking = Tracking::Factory.build_from(raw_data)
99
99
 
100
100
  tracking.effort.date.should == Date.parse('2012-09-18')
101
101
  end
@@ -103,7 +103,7 @@ module Tracking
103
103
  it "tracks the effort to yesterday when the keyword 'yesterday' is present before the effort amount" do
104
104
  raw_data = create_notification(data: { 'text' => "@trackinguser +6p yesterday" }, date: "2012-09-19T12:46:13.713Z")
105
105
 
106
- tracking = TrackingFactory.build_from(raw_data)
106
+ tracking = Tracking::Factory.build_from(raw_data)
107
107
 
108
108
  tracking.effort.date.should == Date.parse('2012-09-18')
109
109
  end
@@ -6,34 +6,34 @@ module Tracking
6
6
  describe "#estimate" do
7
7
 
8
8
  it "is nil when the notification does not contain an estimate" do
9
- TrackingFactory.build_from(unrecognized_notification).estimate.should be_nil
9
+ Tracking::Factory.build_from(unrecognized_notification).estimate.should be_nil
10
10
  end
11
11
 
12
12
  it "is the hour-based estimate when the notification contains an estimate in hours" do
13
13
  raw_data = create_notification(data: { 'text' => "@trackinguser [2h]" }, date: "2012-10-28T21:06:14.801Z")
14
14
 
15
- TrackingFactory.build_from(raw_data).estimate.should == Estimate.new(amount: 2.0, date: Date.parse('2012-10-28'))
15
+ Tracking::Factory.build_from(raw_data).estimate.should == Estimate.new(amount: 2.0, date: Date.parse('2012-10-28'))
16
16
  end
17
17
 
18
18
  it "converts the estimate in hours when the notification contains an estimate in days" do
19
- TrackingFactory.build_from(create_notification(data: { 'text' => "@trackinguser [1.5d]" })).estimate.amount.should == 8+4
20
- TrackingFactory.build_from(create_notification(data: { 'text' => "@trackinguser [1.5g]" })).estimate.amount.should == 8+4
19
+ Tracking::Factory.build_from(create_notification(data: { 'text' => "@trackinguser [1.5d]" })).estimate.amount.should == 8+4
20
+ Tracking::Factory.build_from(create_notification(data: { 'text' => "@trackinguser [1.5g]" })).estimate.amount.should == 8+4
21
21
  end
22
22
 
23
23
  it "converts the estimate in hours when the notification contains an estimate in pomodori" do
24
24
  raw_data = create_notification(data: { 'text' => "@trackinguser [10p]" })
25
- TrackingFactory.build_from(raw_data).estimate.amount.should == 5
25
+ Tracking::Factory.build_from(raw_data).estimate.amount.should == 5
26
26
  end
27
27
 
28
28
  it "fetch the estimate from a complex estimate message" do
29
29
  raw_data = create_notification(data: { 'text' => "@maxmazza Dobbiamo ancora lavorarci.\n@trackinguser ristimo ancora [3h] per il fix" })
30
- TrackingFactory.build_from(raw_data).estimate.amount.should == 3.0
30
+ Tracking::Factory.build_from(raw_data).estimate.amount.should == 3.0
31
31
  end
32
32
 
33
33
  it "tracks the effort with the date given in the notification text, not the actual notification date" do
34
34
  raw_data = create_notification( data: { 'text' => "@trackinguser 22.11.2012 [6p]" }, date: "2012-09-19T12:46:13.713Z")
35
35
 
36
- tracking = TrackingFactory.build_from(raw_data)
36
+ tracking = Tracking::Factory.build_from(raw_data)
37
37
 
38
38
  tracking.estimate.date.should == Date.parse('2012-11-22')
39
39
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe TrackingFactory do
3
+ describe Tracking::Factory do
4
4
 
5
5
  TIME_MEASUREMENTS = {
6
6
  hours: 'h',
@@ -11,7 +11,7 @@ describe TrackingFactory do
11
11
 
12
12
  context "unknown tracking format" do
13
13
  it "builds an invalid tracking instance" do
14
- TrackingFactory.build_from(unrecognized_notification).class.should == Tracking::InvalidTracking
14
+ Tracking::Factory.build_from(unrecognized_notification).class.should == Tracking::InvalidTracking
15
15
 
16
16
  with_message("@trackinguser +30m") { |tracking| tracking.class.should == Tracking::InvalidTracking }
17
17
  end
@@ -21,13 +21,13 @@ describe TrackingFactory do
21
21
 
22
22
  context "estimate tracking notification in #{time_measurement}" do
23
23
  it "builds an estimate tracking instance" do
24
- TrackingFactory.build_from(create_estimate(time_measurement)).class.should == Tracking::EstimateTracking
24
+ Tracking::Factory.build_from(create_estimate(time_measurement)).class.should == Tracking::EstimateTracking
25
25
  end
26
26
  end
27
27
 
28
28
  context "effort tracking notification in #{time_measurement}" do
29
29
  it "builds an effort tracking instance" do
30
- TrackingFactory.build_from(create_effort(time_measurement)).class.should == Tracking::EffortTracking
30
+ Tracking::Factory.build_from(create_effort(time_measurement)).class.should == Tracking::EffortTracking
31
31
  end
32
32
  end
33
33
 
@@ -3,7 +3,7 @@
3
3
  [
4
4
  {
5
5
  "path": ".",
6
- "folder_exclude_patterns": ["tmp", "log", "coverage"],
6
+ "folder_exclude_patterns": ["pkg", "tmp", "log", "coverage"],
7
7
  "file_exclude_patterns": ["tracco.sublime-workspace"]
8
8
  }
9
9
  ]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tracco
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.13
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-20 00:00:00.000000000 Z
12
+ date: 2013-02-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ruby-trello
@@ -272,8 +272,8 @@ files:
272
272
  - lib/tracco/tracking/card_done_tracking.rb
273
273
  - lib/tracco/tracking/effort_tracking.rb
274
274
  - lib/tracco/tracking/estimate_tracking.rb
275
+ - lib/tracco/tracking/factory.rb
275
276
  - lib/tracco/tracking/invalid_tracking.rb
276
- - lib/tracco/tracking_factory.rb
277
277
  - lib/tracco/trello_authorize.rb
278
278
  - lib/tracco/trello_configuration.rb
279
279
  - lib/tracco/trello_tracker.rb
@@ -284,6 +284,8 @@ files:
284
284
  - script/mate.sh
285
285
  - spec/effort_spec.rb
286
286
  - spec/estimate_spec.rb
287
+ - spec/factories/effort_factory.rb
288
+ - spec/factories/estimate_factory.rb
287
289
  - spec/factories/tracked_card_factory.rb
288
290
  - spec/integration/trello_authorization_spec.rb
289
291
  - spec/integration/trello_tracker_spec.rb
@@ -291,6 +293,7 @@ files:
291
293
  - spec/patches/trello/card_spec.rb
292
294
  - spec/spec_helper.rb
293
295
  - spec/support/database_cleaner.rb
296
+ - spec/support/spec_helper_methods.rb
294
297
  - spec/tracked_card_spec.rb
295
298
  - spec/tracking/card_done_tracking_spec.rb
296
299
  - spec/tracking/effort_tracking_spec.rb
@@ -315,7 +318,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
315
318
  version: '0'
316
319
  segments:
317
320
  - 0
318
- hash: 2200448488814002736
321
+ hash: 1645942165596305334
319
322
  required_rubygems_version: !ruby/object:Gem::Requirement
320
323
  none: false
321
324
  requirements:
@@ -1,22 +0,0 @@
1
- class TrackingFactory
2
-
3
- DURATION_REGEXP = '(\d+\.?\d*[phdg])'
4
-
5
- @match_pairs = {}
6
-
7
- def self.match(match_pair)
8
- @match_pairs.merge!(match_pair)
9
- end
10
-
11
- def self.build_from(tracking_notification)
12
- matching_pair = @match_pairs.find { |regexp, tracking_class| tracking_notification.data['text'] =~ regexp }
13
-
14
- tracking_class = matching_pair ? matching_pair.last : Tracking::InvalidTracking
15
- tracking_class.new(tracking_notification)
16
- end
17
-
18
- match /\[#{DURATION_REGEXP}\]/ => Tracking::EstimateTracking
19
- match /\+#{DURATION_REGEXP}/ => Tracking::EffortTracking
20
- match /DONE/ => Tracking::CardDoneTracking
21
-
22
- end