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.
- data.tar.gz.sig +1 -4
- data/History.txt +9 -0
- data/lib/omnifocus.rb +233 -11
- metadata +13 -13
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
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.
|
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
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
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:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
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-
|
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:
|
81
|
+
hash: 7
|
82
82
|
segments:
|
83
|
-
-
|
84
|
-
-
|
85
|
-
version: "
|
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:
|
111
|
+
hash: 7
|
112
112
|
segments:
|
113
|
-
-
|
114
|
-
-
|
115
|
-
version: "
|
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.
|
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
|