omnifocus 2.0.0 → 2.1.0

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 (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