tracco 0.0.14 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
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