omnifocus 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data.tar.gz.sig +1 -4
  2. data/History.txt +9 -0
  3. data/lib/omnifocus.rb +233 -11
  4. metadata +13 -13
  5. metadata.gz.sig +0 -0
data.tar.gz.sig CHANGED
@@ -1,4 +1 @@
1
- C��[V:I������=
2
- i�"R�♏��O��;�-�� e)I�z�֌掄��3���)
3
- SZ�EH��� m[]�����AG
4
- +�-哟�7�G5�@�
1
+ y���
data/History.txt CHANGED
@@ -1,3 +1,12 @@
1
+ === 2.1.0 / 2012-05-17
2
+
3
+ * 4 minor enhancements:
4
+
5
+ * Added "reschedule" command to evenly distribute review & release chaos.
6
+ * Added Project#review_interval=.
7
+ * Added Task#project.
8
+ * of fix_review_dates now fully re-distributes projects to be evenly spread out.
9
+
1
10
  === 2.0.0 / 2012-02-02
2
11
 
3
12
  * 2 minor enhancements:
data/lib/omnifocus.rb CHANGED
@@ -26,7 +26,7 @@ include Appscript
26
26
  # bts_id: a string uniquely identifying a task: SYSTEM(-projectname)?#id
27
27
 
28
28
  class OmniFocus
29
- VERSION = '2.0.0'
29
+ VERSION = '2.1.0'
30
30
 
31
31
  ##
32
32
  # bug_db = {
@@ -304,6 +304,10 @@ class OmniFocus
304
304
  }
305
305
  end
306
306
 
307
+ def add_hours t, n
308
+ t + (n * 3600).to_i
309
+ end
310
+
307
311
  def hour n
308
312
  t = Time.now
309
313
  midnight = Time.gm t.year, t.month, t.day
@@ -381,22 +385,211 @@ class OmniFocus
381
385
  print_aggregate_report cp.tasks, :long
382
386
  end
383
387
 
384
- def cmd_fix_review_dates args
388
+ def cmd_fix_review_dates args # TODO: merge into reschedule
389
+ skip = ARGV.first == "-n"
390
+
391
+ projs = Hash.new { |h,k| h[k] = [] }
392
+
393
+ all_projects.each do |proj|
394
+ name = proj.name
395
+ ri = proj.review_interval
396
+
397
+ projs[ri[:steps]] << proj
398
+ end
399
+
400
+ projs.each do |k, a|
401
+ # helps stabilize and prevent random shuffling
402
+ projs[k] = a.sort_by { |p| [p.next_review_date, p.name] }
403
+ end
404
+
405
+ now = hour 0
406
+ fri = if now.wday == 5 then
407
+ now
408
+ else
409
+ now - 86400 * (now.wday-5)
410
+ end
411
+
385
412
  no_autosave_during do
386
- projs = omnifocus.flattened_projects
387
-
388
- projs.get.each do |proj|
389
- nrd = proj.next_review_date.get
390
- wday = nrd.wday
391
- wday = 7 if wday == 0
392
- if nrd.wday != 5 then
393
- new_day = nrd - (86400*(wday-5))
394
- proj.next_review_date.set new_day
413
+ projs.each do |unit, a|
414
+ day = fri
415
+
416
+ steps = (a.size.to_f / unit).ceil
417
+
418
+ a.each_with_index do |proj, i|
419
+ if proj.next_review_date != day then
420
+ warn "Fixing #{unit} #{proj.name} to #{day}"
421
+ proj.thing.next_review_date.set day unless skip
422
+ end
423
+
424
+ day += 86400 * 7 if (i+1) % steps == 0
425
+ end
426
+ end
427
+ end
428
+ end
429
+
430
+ def distribute count, weeks
431
+ count = count.to_f
432
+ d = 5 * weeks
433
+ hits = (1..d).step(d/count).map(&:round)
434
+ (1..d).map { |n| hits.include?(n) ? weeks : nil }
435
+ end
436
+
437
+ def calculate_schedule projs
438
+ all = [
439
+ distribute(projs[1].size, 1),
440
+ distribute(projs[2].size, 2),
441
+ distribute(projs[3].size, 3),
442
+ distribute(projs[5].size, 5),
443
+ distribute(projs[7].size, 7),
444
+ ]
445
+
446
+ # [[1, 1, 1, 1, 1],
447
+ # [2, 2, 2, 2, 2, nil, 2, 2, 2, 2],
448
+ # [3, nil, 3, 3, nil, 3, 3, nil, 3, 3, nil, 3, 3, nil, 3],
449
+ # ...
450
+
451
+ all.map! { |a|
452
+ a.concat [nil] * (35-a.size)
453
+ a.each_slice(5).to_a
454
+ }
455
+
456
+ # [[[1, 1, 1, 1, 1], [nil, nil, nil, nil, nil], ...
457
+ # [[2, 2, 2, 2, 2], [nil, 2, 2, 2, 2], ...
458
+ # [[3, nil, 3, 3, nil], [3, 3, nil, 3, 3], ...
459
+ # ...
460
+
461
+ weeks = all.transpose.map { |a, *r|
462
+ a.zip(*r).map(&:compact)
463
+ }
464
+
465
+ # [[[1, 2, 3, 5, 7], [1, 2], [1, 2, 3], [1, 2, 3], [1, 2, 5]],
466
+ # [[3], [2, 3, 7], [2], [2, 3, 5], [2, 3]],
467
+ # ...
468
+
469
+ weeks
470
+ end
471
+
472
+ def aggregate_releases
473
+ rels = context "Releasing"
474
+
475
+ tasks = Hash.new { |h,k| h[k] = [] } # name => tasks
476
+ projs = Hash.new { |h,k| h[k] = [] } # step => projs
477
+
478
+ rels.tasks.each do |task|
479
+ proj = task.project
480
+ tasks[proj.name] << task
481
+ projs[proj.review_interval[:steps]] << proj
482
+ end
483
+
484
+ projs.each do |k, a|
485
+ # helps stabilize and prevent random shuffling
486
+ projs[k] = a.uniq_by { |p| p.name }.sort_by { |p|
487
+ tasks[p.name].map(&:name).min
488
+ }
489
+ end
490
+
491
+ return rels, tasks, projs
492
+ end
493
+
494
+ def fix_project_review_intervals rels, skip
495
+ rels.tasks.each do |task|
496
+ proj = task.project
497
+
498
+ t_ri = task.repetition[:steps]
499
+ p_ri = proj.review_interval[:steps]
500
+
501
+ if t_ri != p_ri then
502
+ warn "Fixing #{task.name} to #{p_ri} weeks"
503
+
504
+ rep = {
505
+ :recurrence => "FREQ=WEEKLY;INTERVAL=#{p_ri}",
506
+ :repetition_method => :fixed_repetition,
507
+ }
508
+
509
+ task.thing.repetition_rule.set :to => rep unless skip
510
+ end
511
+ end
512
+ end
513
+
514
+ def fix_release_task_names projs, tasks, skip
515
+ projs.each do |step, projects|
516
+ projects.each do |project|
517
+ tasks[project.name].each do |task|
518
+ if task.name =~ /^(\d+(\.\d+)?)/ then
519
+ if $1.to_i != step then
520
+ new_name = task.name.sub(/^(\d+(\.\d+)?)/, step.to_s)
521
+ puts "renaming to #{new_name}"
522
+ task.thing.name.set new_name unless skip
523
+ end
524
+ end
525
+ end
526
+ end
527
+ end
528
+ end
529
+
530
+ def fix_release_task_schedule projs, tasks, skip
531
+ weeks = calculate_schedule projs
532
+
533
+ now = hour 0
534
+ mon = if now.wday == 1 then
535
+ now
536
+ else
537
+ now - 86400 * (now.wday-1)
538
+ end
539
+
540
+ weeks.each_with_index do |week, wi|
541
+ week.each_with_index do |day, di|
542
+ next if day.empty?
543
+ delta = wi*7 + di
544
+ date = mon + 86400 * delta
545
+
546
+ day.each do |rank|
547
+ p = projs[rank].shift
548
+ t = tasks[p.name]
549
+
550
+ t.each do |task|
551
+ if task.start_date != date then
552
+ due_date1 = add_hours date, 16
553
+ due_date2 = add_hours date, 16.5
554
+
555
+ warn "Fixing #{p.name} to #{date.strftime "%Y-%m-%d"}"
556
+
557
+ next if skip
558
+
559
+ case task.name
560
+ when /Release/ then
561
+ task.start_date = date
562
+ task.due_date = due_date1
563
+ when /Triage/ then
564
+ task.start_date = date
565
+ task.due_date = due_date2
566
+ else
567
+ warn "Unknown task name: #{task.name}"
568
+ end
569
+ end
570
+ end
395
571
  end
396
572
  end
397
573
  end
398
574
  end
399
575
 
576
+ def cmd_reschedule args
577
+ skip = ARGV.first == "-n"
578
+
579
+ rels, tasks, projs = aggregate_releases
580
+
581
+ no_autosave_during do
582
+ warn "Checking project review intervals..."
583
+ fix_project_review_intervals rels, skip
584
+
585
+ warn "Checking releasing task numeric prefixes (if any)"
586
+ fix_release_task_names projs, tasks, skip
587
+
588
+ warn "Checking releasing task schedules"
589
+ fix_release_task_schedule projs, tasks, skip
590
+ end
591
+ end
592
+
400
593
  def cmd_time args
401
594
  m = 0
402
595
 
@@ -458,6 +651,10 @@ class OmniFocus
458
651
  thing.review_interval.get
459
652
  end
460
653
 
654
+ def review_interval= h
655
+ thing.review_interval.set :to => h
656
+ end
657
+
461
658
  def next_review_date
462
659
  thing.next_review_date.get
463
660
  end
@@ -468,6 +665,10 @@ class OmniFocus
468
665
  end
469
666
 
470
667
  class Task < Thingy
668
+ def project
669
+ Project.new omnifocus, thing.containing_project.get
670
+ end
671
+
471
672
  def start_date= t
472
673
  thing.start_date.set t
473
674
  end
@@ -645,3 +846,24 @@ class OmniFocus
645
846
  self.omnifocus.will_autosave.set true
646
847
  end
647
848
  end
849
+
850
+ class Array
851
+ def merge! o
852
+ o.each_with_index do |x, i|
853
+ self[i] << x
854
+ end
855
+ end
856
+ end
857
+
858
+ module Enumerable
859
+ def uniq_by
860
+ r, s = [], {}
861
+ each do |e|
862
+ v = yield(e)
863
+ next if s[v]
864
+ r << e
865
+ s[v] = true
866
+ end
867
+ r
868
+ end
869
+ end unless [].respond_to?(:uniq_by)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omnifocus
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 11
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
+ - 1
8
9
  - 0
9
- - 0
10
- version: 2.0.0
10
+ version: 2.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - aja
@@ -37,7 +37,7 @@ cert_chain:
37
37
  FBHgymkyj/AOSqKRIpXPhjC6
38
38
  -----END CERTIFICATE-----
39
39
 
40
- date: 2012-02-03 00:00:00 Z
40
+ date: 2012-05-18 00:00:00 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: rb-appscript
@@ -78,11 +78,11 @@ dependencies:
78
78
  requirements:
79
79
  - - ~>
80
80
  - !ruby/object:Gem::Version
81
- hash: 21
81
+ hash: 7
82
82
  segments:
83
- - 2
84
- - 11
85
- version: "2.11"
83
+ - 3
84
+ - 0
85
+ version: "3.0"
86
86
  type: :development
87
87
  version_requirements: *id003
88
88
  - !ruby/object:Gem::Dependency
@@ -108,11 +108,11 @@ dependencies:
108
108
  requirements:
109
109
  - - ~>
110
110
  - !ruby/object:Gem::Version
111
- hash: 25
111
+ hash: 7
112
112
  segments:
113
- - 2
114
- - 13
115
- version: "2.13"
113
+ - 3
114
+ - 0
115
+ version: "3.0"
116
116
  type: :development
117
117
  version_requirements: *id005
118
118
  description: Synchronizes bug tracking systems to omnifocus.
@@ -171,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
171
  requirements: []
172
172
 
173
173
  rubyforge_project: seattlerb
174
- rubygems_version: 1.8.12
174
+ rubygems_version: 1.8.17
175
175
  signing_key:
176
176
  specification_version: 3
177
177
  summary: Synchronizes bug tracking systems to omnifocus.
metadata.gz.sig CHANGED
Binary file