tracco 0.0.14 → 0.0.15

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.
Files changed (45) hide show
  1. data/CHANGELOG +16 -0
  2. data/Gemfile.lock +1 -1
  3. data/README.md +4 -4
  4. data/lib/patches/trello/card.rb +3 -2
  5. data/lib/patches/trello/member.rb +4 -2
  6. data/lib/tasks/tasks.rake +2 -2
  7. data/lib/tracco/google_docs_exporter.rb +50 -47
  8. data/lib/tracco/models/effort.rb +40 -0
  9. data/lib/tracco/models/estimate.rb +29 -0
  10. data/lib/tracco/models/member.rb +64 -0
  11. data/lib/tracco/models/tracked_card.rb +148 -0
  12. data/lib/tracco/tracking/base.rb +68 -65
  13. data/lib/tracco/tracking/card_done_tracking.rb +9 -6
  14. data/lib/tracco/tracking/effort_tracking.rb +32 -29
  15. data/lib/tracco/tracking/estimate_tracking.rb +16 -13
  16. data/lib/tracco/tracking/factory.rb +18 -16
  17. data/lib/tracco/tracking/invalid_tracking.rb +15 -13
  18. data/lib/tracco/trello_tracker.rb +34 -30
  19. data/lib/tracco/version.rb +2 -2
  20. data/lib/tracco.rb +4 -4
  21. data/spec/factories/effort_factory.rb +2 -2
  22. data/spec/factories/estimate_factory.rb +1 -1
  23. data/spec/factories/tracked_card_factory.rb +1 -1
  24. data/spec/integration/trello_tracker_spec.rb +49 -47
  25. data/spec/models/effort_spec.rb +61 -0
  26. data/spec/models/estimate_spec.rb +40 -0
  27. data/spec/models/member_spec.rb +83 -0
  28. data/spec/models/tracked_card_spec.rb +467 -0
  29. data/spec/support/spec_helper_methods.rb +7 -1
  30. data/spec/tracking/card_done_tracking_spec.rb +11 -9
  31. data/spec/tracking/effort_tracking_spec.rb +77 -75
  32. data/spec/tracking/estimate_tracking_spec.rb +30 -28
  33. data/spec/tracking/factory_spec.rb +39 -0
  34. data/spec/trello_tracker_spec.rb +20 -18
  35. data/tracco.gemspec +1 -1
  36. metadata +12 -12
  37. data/lib/tracco/effort.rb +0 -37
  38. data/lib/tracco/estimate.rb +0 -26
  39. data/lib/tracco/member.rb +0 -61
  40. data/lib/tracco/tracked_card.rb +0 -145
  41. data/spec/effort_spec.rb +0 -59
  42. data/spec/estimate_spec.rb +0 -38
  43. data/spec/member_spec.rb +0 -81
  44. data/spec/tracked_card_spec.rb +0 -465
  45. data/spec/tracking_factory_spec.rb +0 -42
@@ -1,465 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe TrackedCard do
4
-
5
- before(:each) do
6
- Date.stub(:today).and_return(Date.parse("2012-11-05"))
7
- end
8
-
9
- subject(:card) { build(:tracked_card) }
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
-
16
- %w{piero tommaso michele}.each do |username|
17
- let(username.to_sym) { Member.new(username: username) }
18
- end
19
-
20
- describe "validations" do
21
- it "is not valid when a name is not given" do
22
- TrackedCard.new(trello_id: "123456789", short_id: 1234).should_not be_valid
23
- end
24
-
25
- it "is not valid when a short_id is not given" do
26
- TrackedCard.new(name: "any", trello_id: "123456789").should_not be_valid
27
- end
28
-
29
- it "is not valid when a trello_id is not given" do
30
- TrackedCard.new(name: "any", short_id: 1234).should_not be_valid
31
- end
32
-
33
- it "is valid when has a name, a short id and a trello id" do
34
- TrackedCard.new(name: "any", trello_id: "123456789", short_id: 1234).should be_valid
35
- end
36
- end
37
-
38
- describe ".find_by_trello_id" do
39
- it "finds a card given its Trello id" do
40
- card = create(:tracked_card, trello_id: "1")
41
- another_card = create(:tracked_card, trello_id: "2")
42
-
43
- TrackedCard.find_by_trello_id("1").should == card
44
- TrackedCard.find_by_trello_id("3").should == nil
45
- end
46
- end
47
-
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])])
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"))])
64
-
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]
73
- end
74
- end
75
-
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) }
80
-
81
- it "finds all tracked cards with a valid tracking" do
82
- TrackedCard.all_tracked_cards.should =~ [card, another_card]
83
- end
84
-
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")
88
-
89
- TrackedCard.all_tracked_cards(:sort_by => :name).should == [card, another_card]
90
- end
91
-
92
- it "applies an optional sorting order" do
93
- card.update_attributes(name: "AAA")
94
- another_card.update_attributes(name: "ZZZ")
95
-
96
- card_without_tracking = create(:tracked_card)
97
-
98
- TrackedCard.all_tracked_cards(:sort_by => :name, :order => :desc).should == [another_card, card]
99
- end
100
-
101
- it "uses the ascending order as default sorting order option" do
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)
105
-
106
- TrackedCard.all_tracked_cards(:sort_by => :short_id).should == [another_card, card]
107
- end
108
-
109
- end
110
-
111
- describe ".update_or_create_with" do
112
- let(:trello_card) { Trello::Card.new("id" => "ABC123", "name" => "a name", "idShort" => 1, "desc" => "any description") }
113
-
114
- before(:each) do
115
- Trello::Card.any_instance.stub(:in_done_column?)
116
- end
117
-
118
- it "creates a tracked card on a given trello card" do
119
- tracked_card = TrackedCard.update_or_create_with(trello_card)
120
-
121
- tracked_card.name.should == "a name"
122
- tracked_card.trello_id == "ABC123"
123
- tracked_card.short_id == 1
124
- end
125
-
126
- it "updates an existing tracked card on a given trello card" do
127
- existing_card = create(:tracked_card, name: "an old name", trello_id: trello_card.id)
128
-
129
- updated_card = TrackedCard.update_or_create_with(trello_card)
130
-
131
- updated_card.should == existing_card
132
- updated_card.name.should == "a name"
133
- end
134
-
135
- it "is nil when the trello card is not valid" do
136
- invalid_trello_card = Trello::Card.new("id" => nil, "name" => nil)
137
-
138
- TrackedCard.update_or_create_with(invalid_trello_card).should be_nil
139
- TrackedCard.all.should be_empty
140
- end
141
-
142
- it "tracks the card as done when the original trello card is moved in a DONE column" do
143
- trello_card.stub(:in_done_column?).and_return(true)
144
-
145
- tracked_card = TrackedCard.update_or_create_with(trello_card)
146
-
147
- tracked_card.should be_done
148
- end
149
-
150
- it "tracks the card as NOT done when the original trello card is moved in a column different from DONE" do
151
- trello_card.stub(:in_done_column?).and_return(false)
152
-
153
- tracked_card = TrackedCard.update_or_create_with(trello_card)
154
-
155
- tracked_card.should_not be_done
156
- end
157
-
158
- end
159
-
160
- describe ".build_from" do
161
- it "builds a TrackedCard from a Trello Card" do
162
- tracked_card = TrackedCard.build_from(Trello::Card.new("name" => "a name", "desc" => "any description"))
163
-
164
- tracked_card.name.should == "a name"
165
- tracked_card.description.should == "any description"
166
- end
167
-
168
- it "takes the Trello Card id and set it as trello_id" do
169
- tracked_card = TrackedCard.build_from(Trello::Card.new("id" => "abc123", "name" => "a name", "desc" => "any description"))
170
-
171
- tracked_card.id.should_not == "abc123"
172
- tracked_card.trello_id.should == "abc123"
173
- end
174
-
175
- end
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
-
198
- it "has no estimates and efforts initially" do
199
- card.estimates.should be_empty
200
- card.efforts.should be_empty
201
- end
202
-
203
- it "is possible to add estimates" do
204
- card.estimates << build(:estimate) << build(:estimate)
205
- card.estimates.should have(2).estimates
206
- end
207
-
208
- it "is possible to add efforts" do
209
- card.efforts << build(:effort) << build(:effort)
210
- card.efforts.should have(2).efforts
211
- end
212
-
213
- describe "#trello_notifications" do
214
- let(:first_notification) { stub("notification1", date: Date.yesterday) }
215
- let(:second_notification) { stub("notification1", date: Date.today) }
216
-
217
- it "fetches all the card notifications from trello" do
218
- card.estimates << Estimate.new(tracking_notification_id: "xyz987", amount: 5, date: Date.yesterday)
219
- card.efforts << Effort.new(tracking_notification_id: "abc123", amount: 3, date: Date.today, members: [piero])
220
-
221
- Trello::Notification.should_receive(:find).with("xyz987").and_return(second_notification)
222
- Trello::Notification.should_receive(:find).with("abc123").and_return(first_notification)
223
-
224
- card.trello_notifications.should == [first_notification, second_notification]
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
-
237
- end
238
-
239
- describe "equality" do
240
- it "is equal to another TrelloCard when the trello id is the same" do
241
- card = TrackedCard.new(name: "a name", trello_id: "123456789")
242
- same_card = TrackedCard.new(name: "a name", trello_id: "123456789")
243
- another_card = TrackedCard.new(name: "a name", trello_id: "987654321")
244
-
245
- card.should == same_card
246
- card.should_not == another_card
247
- end
248
- end
249
-
250
- describe "#add" do
251
- let(:card) { build(:tracked_card) }
252
- let(:estimate_tracking) { Tracking::EstimateTracking.new(create_notification(data: { 'text' => "@trackinguser [1h]" })) }
253
-
254
- it "adds an estimate from a tracking estimate notification" do
255
- card.add(estimate_tracking)
256
- card.estimates.should have(1).estimate
257
- end
258
-
259
- it "adds an estimate only once" do
260
- card.add(estimate_tracking)
261
- card.add(estimate_tracking)
262
-
263
- card.estimates.should have(1).estimate
264
- end
265
-
266
- it "is done when has a DONE notification" do
267
- card.should_not be_done
268
-
269
- card.add(Tracking::CardDoneTracking.new(stub(:notification)))
270
- card.should be_done
271
- end
272
-
273
- end
274
-
275
- describe "#add!" do
276
- let(:card) { build(:tracked_card) }
277
-
278
- it "saves the tracked card after adding the tracking" do
279
- any_tracking = Tracking::EstimateTracking.new(create_notification(data: { 'text' => "@trackinguser [1h]" }))
280
-
281
- card.add!(any_tracking)
282
- card.reload.should_not be_nil
283
- end
284
- end
285
-
286
- describe "#total_effort" do
287
- it "is zero when there's no effort" do
288
- card.total_effort.should == 0
289
- end
290
-
291
- it "computes the total effort on the card" do
292
- card.efforts << build(:effort, amount: 3)
293
- card.efforts << build(:effort, amount: 5)
294
-
295
- card.total_effort.should == 3+5
296
- end
297
- end
298
-
299
- describe "#last_estimate_error" do
300
-
301
- it "is nil when the card has no estimate" do
302
- card.efforts << build(:effort, amount: 5)
303
-
304
- card.last_estimate_error.should be_nil
305
- end
306
-
307
- it "is nil when the card has no effort" do
308
- card.efforts << build(:estimate)
309
-
310
- card.last_estimate_error.should be_nil
311
- end
312
-
313
- it "is zero when actual effort is equal to estimate" do
314
- card.estimates << build(:estimate, amount: 5)
315
- card.efforts << build(:effort, amount: 5)
316
-
317
- card.last_estimate_error.should == 0.0
318
- end
319
-
320
- it "is 100 when the actual effort is twice the given estimate" do
321
- card.estimates << build(:estimate, amount: 5)
322
- card.efforts << build(:effort, amount: 10)
323
-
324
- card.last_estimate_error.should == 100.0
325
- end
326
-
327
- it "is -50 when the actual effort is half of the given estimate" do
328
- card.estimates << build(:estimate, amount: 10)
329
- card.efforts << build(:effort, amount: 5)
330
-
331
- card.last_estimate_error.should == -50.0
332
- end
333
-
334
- it "is rounded with two decimal digits" do
335
- card.estimates << build(:estimate, amount: 3)
336
- card.efforts << build(:effort, amount: 5)
337
-
338
- card.last_estimate_error.should == 66.67
339
- end
340
-
341
- describe "#estimate_errors" do
342
-
343
- it "collects all the estimate errors against the actual effort" do
344
- card.estimates << Estimate.new(amount: 5, date: Date.yesterday)
345
- card.efforts << Effort.new(amount: 10, date: Date.yesterday, members: [tommaso])
346
-
347
- card.estimates << Estimate.new(amount: 10, date: Date.today)
348
- card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
349
-
350
- card.estimate_errors.should == [200.0, 50.0]
351
- end
352
- end
353
-
354
- end
355
-
356
- describe "#members" do
357
- it "lists all the members which spent some effort on the card" do
358
- card.efforts << build(:effort, members: [piero, tommaso])
359
- card.efforts << build(:effort, members: [tommaso])
360
- card.efforts << build(:effort, members: [tommaso, michele])
361
-
362
- card.members.should == [piero, tommaso, michele]
363
- end
364
- end
365
-
366
- describe "#working_start_date" do
367
-
368
- it "is the date of the first effort spent on the card" do
369
- card.efforts << build(:effort, date: Date.today)
370
- card.efforts << build(:effort, date: Date.yesterday)
371
- card.efforts << build(:effort, date: Date.tomorrow)
372
-
373
- card.working_start_date.should == Date.yesterday
374
- end
375
- end
376
-
377
- describe "#first_activity_date" do
378
- it "is the date of the first effort or estimate given on the card" do
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)
382
-
383
- card.efforts << build(:effort, date: Date.yesterday)
384
- card.efforts << build(:effort, date: Date.today)
385
-
386
- card.first_activity_date.should == Date.yesterday.prev_day
387
- end
388
- end
389
-
390
- describe "#first_estimate_date" do
391
- it "is the date of the first estimate given on the card" do
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)
395
-
396
- card.first_estimate_date.should == Date.yesterday.prev_day
397
- end
398
- end
399
-
400
- describe "#last_estimate_date" do
401
- it "is the date of the last estimate given on the card" do
402
- card.estimates << build(:estimate, date: Date.yesterday)
403
- card.estimates << build(:estimate, date: Date.tomorrow)
404
- card.estimates << build(:estimate, date: Date.today)
405
-
406
- card.last_estimate_date.should == Date.tomorrow
407
- end
408
- end
409
-
410
- describe "#no_tracking?" do
411
- it "is false when there's no effort or estimate tracked on the card" do
412
- card.no_tracking?.should be_true
413
-
414
- card.estimates << build(:estimate, date: Date.yesterday)
415
- card.no_tracking?.should be_false
416
- end
417
- end
418
-
419
- describe "#contains_effort?" do
420
- it "counts regular efforts" do
421
- effort = build(:effort, amount: 1, muted: false)
422
- card.efforts << effort
423
-
424
- card.contains_effort?(effort).should be_true
425
- end
426
-
427
- it "counts even muted efforts" do
428
- muted_effort = build(:effort, amount: 1, muted: true)
429
- card.efforts << muted_effort
430
-
431
- card.contains_effort?(muted_effort).should be_true
432
- end
433
-
434
- end
435
-
436
- describe "#to_s" do
437
- it "describes the card as a string" do
438
- card = TrackedCard.new(name: "A Story Name")
439
- card.estimates << Estimate.new(amount: 5, date: Date.today)
440
- card.efforts << Effort.new(amount: 3, date: Date.today, members: [Member.new(username: "piero"), Member.new(username: "tommaso")])
441
- card.efforts << Effort.new(amount: 6, date: Date.today, members: [Member.new(username: "piero"), Member.new(username: "tommaso")])
442
-
443
- card.to_s.should == %Q{[A Story Name]. Total effort: 9.0h. Estimates ["[2012-11-05] estimated 5.0 hours"]. Efforts: ["[2012-11-05] spent 3.0 hours by @piero, @tommaso", "[2012-11-05] spent 6.0 hours by @piero, @tommaso"]}
444
- end
445
- end
446
-
447
- describe "#status" do
448
- it "is done when is done" do
449
- done_card = TrackedCard.new(done: true)
450
- done_card.status.should == :done
451
-
452
- done_card.efforts << build(:effort)
453
- done_card.status.should == :done
454
- end
455
-
456
- it "is todo when no effort has been spent on the card" do
457
- card = TrackedCard.new
458
- card.status.should == :todo
459
-
460
- card.efforts << build(:effort)
461
- card.status.should == :in_progress
462
- end
463
- end
464
-
465
- end
@@ -1,42 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Tracking::Factory do
4
-
5
- TIME_MEASUREMENTS = {
6
- hours: 'h',
7
- days: 'd',
8
- giorni: 'g',
9
- pomodori: 'p'
10
- }
11
-
12
- context "unknown tracking format" do
13
- it "builds an invalid tracking instance" do
14
- Tracking::Factory.build_from(unrecognized_notification).class.should == Tracking::InvalidTracking
15
-
16
- with_message("@trackinguser +30m") { |tracking| tracking.class.should == Tracking::InvalidTracking }
17
- end
18
- end
19
-
20
- TIME_MEASUREMENTS.each_key do |time_measurement|
21
-
22
- context "estimate tracking notification in #{time_measurement}" do
23
- it "builds an estimate tracking instance" do
24
- Tracking::Factory.build_from(create_estimate(time_measurement)).class.should == Tracking::EstimateTracking
25
- end
26
- end
27
-
28
- context "effort tracking notification in #{time_measurement}" do
29
- it "builds an effort tracking instance" do
30
- Tracking::Factory.build_from(create_effort(time_measurement)).class.should == Tracking::EffortTracking
31
- end
32
- end
33
-
34
- end
35
-
36
- context "card done tracking notification" do
37
- it "builds a card done tracking instance" do
38
- with_message("@trackinguser DONE") { |tracking| tracking.class.should == Tracking::CardDoneTracking }
39
- end
40
- end
41
-
42
- end