tracco 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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