doing 1.0.65 → 1.0.69
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.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/bin/doing +113 -40
- data/lib/doing.rb +1 -0
- data/lib/doing/helpers.rb +121 -0
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +18 -125
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf5235acbab516c74ea7ff28c610dcbc49b9d9f108056c1b3c60ac0100bd9ec5
|
4
|
+
data.tar.gz: ca6abc5bd66a950d78ab16aabc781660b0ea5ee3f6dc50bb8a9fd0b986cce21e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9f62baa0a4348212c54c1f9f3aa5bca041d81bc5061221310a85747b05c0b9b7af9036d309fcffd78e192a7a415f462ceeb96f8c276cef320249651f4890d35
|
7
|
+
data.tar.gz: cc61feaf78e9a3d6865bbb3d5fecdff00659b9b00a7430971012bfca0856e45ee87efd864dde603a7a6e6abf6a571f01f0e8d60afbbb7ebe060364f65c908b6b
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ _Side note:_ I actually use the library behind this utility as part of another s
|
|
29
29
|
|
30
30
|
## Installation
|
31
31
|
|
32
|
-
The current version of `doing` is <!--VER-->1.0.
|
32
|
+
The current version of `doing` is <!--VER-->1.0.68<!--END VER-->.
|
33
33
|
|
34
34
|
$ [sudo] gem install doing
|
35
35
|
|
@@ -464,6 +464,7 @@ You can also include a `--no-date` switch to add `@done` without a finish date,
|
|
464
464
|
|
465
465
|
By default `doing finish` works on a single entry, the last entry or the most recent entry matching a `--tag` or `--search` query. Specifying `doing finish 10` would finish any unfinished entries within the last 10 entries. In the case of `--tag` or `--search` queries, the count serves as the maximum number of matches doing will act on, sorted in reverse date order (most recent first). A count of 0 will disable the limit entirely, acting on all matching entries.
|
466
466
|
|
467
|
+
Both `finish` and `cancel` accept `--unfinished` as an argument. This causes them to act on the last entry not already marked @done, no matter how far back it's dated or how many @done entries come after it. You can use `doing finish --unfinished X -s SECTION` to finish the last X unfinished entries in SECTION.
|
467
468
|
|
468
469
|
##### Tagging and Autotagging
|
469
470
|
|
data/bin/doing
CHANGED
@@ -52,7 +52,7 @@ flag %i[f doing_file]
|
|
52
52
|
|
53
53
|
desc 'Add an entry'
|
54
54
|
arg_name 'ENTRY'
|
55
|
-
command [
|
55
|
+
command %i[now next] do |c|
|
56
56
|
c.desc 'Section'
|
57
57
|
c.arg_name 'NAME'
|
58
58
|
c.flag %i[s section], default_value: wwid.current_section
|
@@ -105,7 +105,8 @@ command [:now, :next] do |c|
|
|
105
105
|
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
|
106
106
|
wwid.write(wwid.doing_file)
|
107
107
|
elsif $stdin.stat.size.positive?
|
108
|
-
|
108
|
+
input = $stdin.read
|
109
|
+
title, note = wwid.format_input(input)
|
109
110
|
note.push(options[:n]) if options[:n]
|
110
111
|
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
|
111
112
|
wwid.write(wwid.doing_file)
|
@@ -239,7 +240,7 @@ long_desc %(
|
|
239
240
|
|
240
241
|
Example `doing template HAML > ~/styles/my_doing.haml`
|
241
242
|
)
|
242
|
-
arg_name 'TYPE', must_match: /^(html|haml|css)/i
|
243
|
+
arg_name 'TYPE', must_match: /^(?:html|haml|css)/i
|
243
244
|
command :template do |c|
|
244
245
|
c.action do |_global_options, options, args|
|
245
246
|
raise 'No type specified, use `doing template [HAML|CSS]`' if args.empty?
|
@@ -443,7 +444,10 @@ command :cancel do |c|
|
|
443
444
|
|
444
445
|
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
445
446
|
c.arg_name 'BOOLEAN'
|
446
|
-
c.flag [:bool], must_match:
|
447
|
+
c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
|
448
|
+
|
449
|
+
c.desc 'Cancel last entry (or entries) not already marked @done'
|
450
|
+
c.switch %i[u unfinished], negatable: false, default_value: false
|
447
451
|
|
448
452
|
c.action do |_global_options, options, args|
|
449
453
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
@@ -452,7 +456,16 @@ command :cancel do |c|
|
|
452
456
|
tags = []
|
453
457
|
else
|
454
458
|
tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
|
455
|
-
options[:bool] =
|
459
|
+
options[:bool] = case options[:bool]
|
460
|
+
when /(and|all)/i
|
461
|
+
'AND'
|
462
|
+
when /(any|or)/i
|
463
|
+
'OR'
|
464
|
+
when /(not|none)/i
|
465
|
+
'NOT'
|
466
|
+
else
|
467
|
+
'AND'
|
468
|
+
end
|
456
469
|
end
|
457
470
|
|
458
471
|
raise 'Only one argument allowed' if args.length > 1
|
@@ -468,7 +481,8 @@ command :cancel do |c|
|
|
468
481
|
sequential: false,
|
469
482
|
tag: tags,
|
470
483
|
tag_bool: options[:bool],
|
471
|
-
tags: ['done']
|
484
|
+
tags: ['done'],
|
485
|
+
unfinished: options[:unfinished]
|
472
486
|
}
|
473
487
|
wwid.tag_last(opts)
|
474
488
|
end
|
@@ -500,7 +514,10 @@ command :finish do |c|
|
|
500
514
|
|
501
515
|
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
502
516
|
c.arg_name 'BOOLEAN'
|
503
|
-
c.flag [:bool], must_match:
|
517
|
+
c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
|
518
|
+
|
519
|
+
c.desc 'Finish last entry (or entries) not already marked @done'
|
520
|
+
c.switch %i[u unfinished], negatable: false, default_value: false
|
504
521
|
|
505
522
|
c.desc %(Auto-generate finish dates from next entry's start time.
|
506
523
|
Automatically generate completion dates 1 minute before next start date.
|
@@ -537,7 +554,16 @@ command :finish do |c|
|
|
537
554
|
tags = []
|
538
555
|
else
|
539
556
|
tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
|
540
|
-
options[:bool] =
|
557
|
+
options[:bool] = case options[:bool]
|
558
|
+
when /(and|all)/i
|
559
|
+
'AND'
|
560
|
+
when /(any|or)/i
|
561
|
+
'OR'
|
562
|
+
when /(not|none)/i
|
563
|
+
'NOT'
|
564
|
+
else
|
565
|
+
'AND'
|
566
|
+
end
|
541
567
|
end
|
542
568
|
|
543
569
|
raise 'Only one argument allowed' if args.length > 1
|
@@ -555,7 +581,8 @@ command :finish do |c|
|
|
555
581
|
sequential: options[:auto],
|
556
582
|
tag: tags,
|
557
583
|
tag_bool: options[:bool],
|
558
|
-
tags: ['done']
|
584
|
+
tags: ['done'],
|
585
|
+
unfinished: options[:unfinished]
|
559
586
|
}
|
560
587
|
wwid.tag_last(opts)
|
561
588
|
end
|
@@ -582,7 +609,7 @@ command [:again, :resume] do |c|
|
|
582
609
|
|
583
610
|
c.desc 'Boolean used to combine multiple tags'
|
584
611
|
c.arg_name 'BOOLEAN'
|
585
|
-
c.flag [:bool], must_match:
|
612
|
+
c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
|
586
613
|
|
587
614
|
c.desc 'Note'
|
588
615
|
c.arg_name 'TEXT'
|
@@ -590,6 +617,16 @@ command [:again, :resume] do |c|
|
|
590
617
|
|
591
618
|
c.action do |_global_options, options, _args|
|
592
619
|
tags = options[:tag].nil? ? [] : options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }
|
620
|
+
options[:bool] = case options[:bool]
|
621
|
+
when /(and|all)/i
|
622
|
+
'AND'
|
623
|
+
when /(any|or)/i
|
624
|
+
'OR'
|
625
|
+
when /(not|none)/i
|
626
|
+
'NOT'
|
627
|
+
else
|
628
|
+
'AND'
|
629
|
+
end
|
593
630
|
opts = {
|
594
631
|
in: options[:in],
|
595
632
|
note: options[:n],
|
@@ -619,6 +656,9 @@ command :tag do |c|
|
|
619
656
|
c.desc 'Remove given tag(s)'
|
620
657
|
c.switch %i[r remove], negatable: false, default_value: false
|
621
658
|
|
659
|
+
c.desc 'Tag last entry (or entries) not marked @done'
|
660
|
+
c.switch %i[u unfinished], negatable: false, default_value: false
|
661
|
+
|
622
662
|
c.desc 'Autotag entries based on autotag configuration in ~/.doingrc'
|
623
663
|
c.switch %i[a autotag], negatable: false, default_value: false
|
624
664
|
|
@@ -662,7 +702,8 @@ command :tag do |c|
|
|
662
702
|
date: options[:date],
|
663
703
|
remove: options[:r],
|
664
704
|
section: section,
|
665
|
-
tags: tags
|
705
|
+
tags: tags,
|
706
|
+
unfinished: options[:unfinished]
|
666
707
|
}
|
667
708
|
wwid.tag_last(opts)
|
668
709
|
end
|
@@ -677,9 +718,17 @@ command [:mark, :flag] do |c|
|
|
677
718
|
c.desc 'Remove mark'
|
678
719
|
c.switch %i[r remove], negatable: false, default_value: false
|
679
720
|
|
721
|
+
c.desc 'Mark last entry not marked @done'
|
722
|
+
c.switch %i[u unfinished], negatable: false, default_value: false
|
723
|
+
|
680
724
|
c.action do |_global_options, options, _args|
|
681
725
|
mark = wwid.config['marker_tag'] || 'flagged'
|
682
|
-
wwid.tag_last({
|
726
|
+
wwid.tag_last({
|
727
|
+
remove: options[:r],
|
728
|
+
section: options[:s],
|
729
|
+
tags: [mark],
|
730
|
+
unfinished: options[:unfinished]
|
731
|
+
})
|
683
732
|
end
|
684
733
|
end
|
685
734
|
|
@@ -694,9 +743,9 @@ command :show do |c|
|
|
694
743
|
c.arg_name 'TAG'
|
695
744
|
c.flag [:tag]
|
696
745
|
|
697
|
-
c.desc 'Tag boolean (AND,OR,
|
746
|
+
c.desc 'Tag boolean (AND,OR,NOT)'
|
698
747
|
c.arg_name 'BOOLEAN'
|
699
|
-
c.flag %i[b bool], must_match:
|
748
|
+
c.flag %i[b bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'OR'
|
700
749
|
|
701
750
|
c.desc 'Max count to show'
|
702
751
|
c.arg_name 'MAX'
|
@@ -708,7 +757,7 @@ command :show do |c|
|
|
708
757
|
|
709
758
|
c.desc 'Sort order (asc/desc)'
|
710
759
|
c.arg_name 'ORDER'
|
711
|
-
c.flag %i[s sort], must_match: /^
|
760
|
+
c.flag %i[s sort], must_match: /^[ad].*/i, default_value: 'ASC'
|
712
761
|
|
713
762
|
c.desc %(
|
714
763
|
Date range to show, or a single day to filter date on.
|
@@ -728,14 +777,14 @@ command :show do |c|
|
|
728
777
|
default = 'time'
|
729
778
|
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
730
779
|
c.arg_name 'KEY'
|
731
|
-
c.flag [:tag_sort], must_match: /^(name|time)/i, default_value: default
|
780
|
+
c.flag [:tag_sort], must_match: /^(?:name|time)/i, default_value: default
|
732
781
|
|
733
782
|
c.desc 'Only show items with recorded time intervals'
|
734
783
|
c.switch [:only_timed], default_value: false, negatable: false
|
735
784
|
|
736
785
|
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
737
786
|
c.arg_name 'FORMAT'
|
738
|
-
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
787
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
739
788
|
c.action do |_global_options, options, args|
|
740
789
|
tag_filter = false
|
741
790
|
tags = []
|
@@ -757,13 +806,9 @@ command :show do |c|
|
|
757
806
|
end
|
758
807
|
if args.length.positive?
|
759
808
|
args.each do |arg|
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
end
|
764
|
-
else
|
765
|
-
tags.push(arg.strip.sub(/^@/, ''))
|
766
|
-
end
|
809
|
+
arg.split(/,/).each do |tag|
|
810
|
+
tags.push(tag.strip.sub(/^@/, ''))
|
811
|
+
end
|
767
812
|
end
|
768
813
|
end
|
769
814
|
else
|
@@ -771,11 +816,21 @@ command :show do |c|
|
|
771
816
|
end
|
772
817
|
|
773
818
|
tags.concat(options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if options[:tag]
|
819
|
+
options[:bool] = case options[:bool]
|
820
|
+
when /(and|all)/i
|
821
|
+
'AND'
|
822
|
+
when /(any|or)/i
|
823
|
+
'OR'
|
824
|
+
when /(not|none)/i
|
825
|
+
'NOT'
|
826
|
+
else
|
827
|
+
'AND'
|
828
|
+
end
|
774
829
|
|
775
830
|
unless tags.empty?
|
776
831
|
tag_filter = {
|
777
832
|
'tags' => tags,
|
778
|
-
'bool' => options[:
|
833
|
+
'bool' => options[:bool]
|
779
834
|
}
|
780
835
|
end
|
781
836
|
|
@@ -832,7 +887,7 @@ command [:grep, :search] do |c|
|
|
832
887
|
|
833
888
|
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
834
889
|
c.arg_name 'FORMAT'
|
835
|
-
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
890
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
836
891
|
|
837
892
|
c.desc 'Show time intervals on @done tasks'
|
838
893
|
c.switch %i[t times], default_value: true
|
@@ -844,7 +899,7 @@ command [:grep, :search] do |c|
|
|
844
899
|
default = 'time'
|
845
900
|
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
846
901
|
c.arg_name 'KEY'
|
847
|
-
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
902
|
+
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
848
903
|
|
849
904
|
c.desc 'Only show items with recorded time intervals'
|
850
905
|
c.switch [:only_timed], default_value: false, negatable: false
|
@@ -891,7 +946,7 @@ command :recent do |c|
|
|
891
946
|
default = 'time'
|
892
947
|
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
893
948
|
c.arg_name 'KEY'
|
894
|
-
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
949
|
+
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
895
950
|
|
896
951
|
c.action do |global_options, options, args|
|
897
952
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
@@ -931,11 +986,11 @@ command :today do |c|
|
|
931
986
|
default = 'time'
|
932
987
|
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
933
988
|
c.arg_name 'KEY'
|
934
|
-
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
989
|
+
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
935
990
|
|
936
991
|
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
937
992
|
c.arg_name 'FORMAT'
|
938
|
-
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
993
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
939
994
|
|
940
995
|
c.action do |_global_options, options, _args|
|
941
996
|
options[:t] = true if options[:totals]
|
@@ -966,11 +1021,11 @@ command :on do |c|
|
|
966
1021
|
default = 'time'
|
967
1022
|
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
968
1023
|
c.arg_name 'KEY'
|
969
|
-
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
1024
|
+
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
970
1025
|
|
971
1026
|
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
972
1027
|
c.arg_name 'FORMAT'
|
973
|
-
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
1028
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
974
1029
|
|
975
1030
|
c.action do |_global_options, options, args|
|
976
1031
|
exit_now! 'Missing date argument' if args.empty?
|
@@ -1008,7 +1063,7 @@ command :yesterday do |c|
|
|
1008
1063
|
|
1009
1064
|
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1010
1065
|
c.arg_name 'FORMAT'
|
1011
|
-
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
1066
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1012
1067
|
|
1013
1068
|
c.desc 'Show time intervals on @done tasks'
|
1014
1069
|
c.switch %i[t times], default_value: true
|
@@ -1020,7 +1075,7 @@ command :yesterday do |c|
|
|
1020
1075
|
default = 'time'
|
1021
1076
|
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
1022
1077
|
c.arg_name 'KEY'
|
1023
|
-
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
1078
|
+
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
1024
1079
|
|
1025
1080
|
c.action do |_global_options, options, _args|
|
1026
1081
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
@@ -1044,7 +1099,7 @@ command :last do |c|
|
|
1044
1099
|
|
1045
1100
|
c.desc 'Tag boolean'
|
1046
1101
|
c.arg_name 'BOOLEAN'
|
1047
|
-
c.flag [:bool], must_match: /(and|or|not)/i, default_value: 'AND'
|
1102
|
+
c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
|
1048
1103
|
|
1049
1104
|
c.desc 'Search filter, surround with slashes for regex (/query/)'
|
1050
1105
|
c.arg_name 'QUERY'
|
@@ -1057,7 +1112,15 @@ command :last do |c|
|
|
1057
1112
|
tags = []
|
1058
1113
|
else
|
1059
1114
|
tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
|
1060
|
-
options[:bool] =
|
1115
|
+
options[:bool] = case options[:bool]
|
1116
|
+
when /(any|or)/i
|
1117
|
+
:or
|
1118
|
+
when /(not|none)/i
|
1119
|
+
:not
|
1120
|
+
else
|
1121
|
+
:and
|
1122
|
+
end
|
1123
|
+
|
1061
1124
|
end
|
1062
1125
|
|
1063
1126
|
if options[:e]
|
@@ -1130,7 +1193,7 @@ command :view do |c|
|
|
1130
1193
|
|
1131
1194
|
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1132
1195
|
c.arg_name 'FORMAT'
|
1133
|
-
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
1196
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1134
1197
|
|
1135
1198
|
c.desc 'Show time intervals on @done tasks'
|
1136
1199
|
c.switch %i[t times], default_value: true
|
@@ -1145,7 +1208,7 @@ command :view do |c|
|
|
1145
1208
|
default = 'time'
|
1146
1209
|
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
1147
1210
|
c.arg_name 'KEY'
|
1148
|
-
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
1211
|
+
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
1149
1212
|
|
1150
1213
|
c.desc 'Only show items with recorded time intervals'
|
1151
1214
|
c.switch [:only_timed], default_value: false, negatable: true
|
@@ -1178,7 +1241,7 @@ command :view do |c|
|
|
1178
1241
|
else
|
1179
1242
|
view['tags'].gsub(/[, ]+/, ' ').split(' ').map(&:strip)
|
1180
1243
|
end
|
1181
|
-
tag_filter['bool'] = view.key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].
|
1244
|
+
tag_filter['bool'] = view.key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].normalize_bool : :or
|
1182
1245
|
end
|
1183
1246
|
|
1184
1247
|
# If the -o/--output flag was specified, override any default in the view template
|
@@ -1257,7 +1320,7 @@ command :archive do |c|
|
|
1257
1320
|
|
1258
1321
|
c.desc 'Tag boolean (AND|OR|NOT)'
|
1259
1322
|
c.arg_name 'BOOLEAN'
|
1260
|
-
c.flag [:bool], must_match: /(and|or|not)/i, default_value: 'AND'
|
1323
|
+
c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
|
1261
1324
|
|
1262
1325
|
c.desc 'Search filter'
|
1263
1326
|
c.arg_name 'QUERY'
|
@@ -1281,6 +1344,16 @@ command :archive do |c|
|
|
1281
1344
|
|
1282
1345
|
tags.concat(options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if options[:tag]
|
1283
1346
|
|
1347
|
+
options[:bool] = case options[:bool]
|
1348
|
+
when /(and|all)/i
|
1349
|
+
'AND'
|
1350
|
+
when /(any|or)/i
|
1351
|
+
'OR'
|
1352
|
+
when /(not|none)/i
|
1353
|
+
'NOT'
|
1354
|
+
else
|
1355
|
+
'AND'
|
1356
|
+
end
|
1284
1357
|
opts = {
|
1285
1358
|
bool: options[:bool],
|
1286
1359
|
destination: options[:to],
|
data/lib/doing.rb
CHANGED
@@ -0,0 +1,121 @@
|
|
1
|
+
##
|
2
|
+
## @brief Hash helpers
|
3
|
+
##
|
4
|
+
class ::Hash
|
5
|
+
def has_tags?(tags, bool = :and)
|
6
|
+
tags = tags.split(/ *, */) if tags.is_a? String
|
7
|
+
bool = bool.normalize_bool if bool.is_a? String
|
8
|
+
item = self
|
9
|
+
tags.map! {|t| t.strip.sub(/^@/, '')}
|
10
|
+
case bool
|
11
|
+
when :and
|
12
|
+
result = true
|
13
|
+
tags.each do |tag|
|
14
|
+
unless item['title'] =~ /@#{tag}/
|
15
|
+
result = false
|
16
|
+
break
|
17
|
+
end
|
18
|
+
end
|
19
|
+
result
|
20
|
+
when :not
|
21
|
+
result = true
|
22
|
+
tags.each do |tag|
|
23
|
+
if item['title'] =~ /@#{tag}/
|
24
|
+
result = false
|
25
|
+
break
|
26
|
+
end
|
27
|
+
end
|
28
|
+
result
|
29
|
+
else
|
30
|
+
result = false
|
31
|
+
tags.each do |tag|
|
32
|
+
if item['title'] =~ /@#{tag}/
|
33
|
+
result = true
|
34
|
+
break
|
35
|
+
end
|
36
|
+
end
|
37
|
+
result
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def matches_search?(search)
|
42
|
+
item = self
|
43
|
+
text = item['note'] ? item['title'] + item['note'].join(' ') : item['title']
|
44
|
+
pattern = if search.strip =~ %r{^/.*?/$}
|
45
|
+
search.sub(%r{/(.*?)/}, '\1')
|
46
|
+
else
|
47
|
+
search.split('').join('.{0,3}')
|
48
|
+
end
|
49
|
+
text =~ /#{pattern}/i ? true : false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
## @brief String helpers
|
55
|
+
##
|
56
|
+
class ::String
|
57
|
+
def cap_first
|
58
|
+
sub(/^\w/) do |m|
|
59
|
+
m.upcase
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
##
|
65
|
+
## @brief Convert a boolean string to a symbol
|
66
|
+
##
|
67
|
+
## @return Symbol :and, :or, or :not
|
68
|
+
##
|
69
|
+
def normalize_bool!
|
70
|
+
replace normalize_bool
|
71
|
+
end
|
72
|
+
|
73
|
+
def normalize_bool(default = :and)
|
74
|
+
case self
|
75
|
+
when /(and|all)/i
|
76
|
+
:and
|
77
|
+
when /(any|or)/i
|
78
|
+
:or
|
79
|
+
when /(not|none)/i
|
80
|
+
:not
|
81
|
+
else
|
82
|
+
default.is_a?(Symbol) ? default : default.normalize_bool
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
##
|
88
|
+
## @brief Turn raw urls into HTML links
|
89
|
+
##
|
90
|
+
## @param opt (Hash) Additional Options
|
91
|
+
##
|
92
|
+
def link_urls(opt = {})
|
93
|
+
opt[:format] ||= :html
|
94
|
+
if opt[:format] == :html
|
95
|
+
gsub(%r{(?mi)((http|https)://)?([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&:/~+#]*[\w\-@^=%&/~+#])?}) do |_match|
|
96
|
+
m = Regexp.last_match
|
97
|
+
proto = m[1].nil? ? 'http://' : ''
|
98
|
+
%(<a href="#{proto}#{m[0]}" title="Link to #{m[0]}">[#{m[3]}]</a>)
|
99
|
+
end.gsub(/<(\w+:.*?)>/) do |match|
|
100
|
+
m = Regexp.last_match
|
101
|
+
if m[1] =~ /<a href/
|
102
|
+
match
|
103
|
+
else
|
104
|
+
%(<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
else
|
108
|
+
self
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class ::Symbol
|
114
|
+
def normalize_bool!
|
115
|
+
replace normalize_bool
|
116
|
+
end
|
117
|
+
|
118
|
+
def normalize_bool
|
119
|
+
to_s.normalize_bool
|
120
|
+
end
|
121
|
+
end
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid.rb
CHANGED
@@ -4,92 +4,6 @@ require 'deep_merge'
|
|
4
4
|
require 'open3'
|
5
5
|
require 'pp'
|
6
6
|
|
7
|
-
##
|
8
|
-
## @brief Hash helpers
|
9
|
-
##
|
10
|
-
class ::Hash
|
11
|
-
def has_tags?(tags, bool = 'AND')
|
12
|
-
tags = tags.split(/ *, */) if tags.is_a? String
|
13
|
-
item = self
|
14
|
-
case bool
|
15
|
-
when 'AND'
|
16
|
-
result = true
|
17
|
-
tags.each do |tag|
|
18
|
-
unless item['title'] =~ /@#{tag}/
|
19
|
-
result = false
|
20
|
-
break
|
21
|
-
end
|
22
|
-
end
|
23
|
-
result
|
24
|
-
when 'NOT'
|
25
|
-
result = true
|
26
|
-
tags.each do |tag|
|
27
|
-
if item['title'] =~ /@#{tag}/
|
28
|
-
result = false
|
29
|
-
break
|
30
|
-
end
|
31
|
-
end
|
32
|
-
result
|
33
|
-
else
|
34
|
-
result = false
|
35
|
-
tags.each do |tag|
|
36
|
-
if item['title'] =~ /@#{tag}/
|
37
|
-
result = true
|
38
|
-
break
|
39
|
-
end
|
40
|
-
end
|
41
|
-
result
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def matches_search?(search)
|
46
|
-
item = self
|
47
|
-
text = item['note'] ? item['title'] + item['note'].join(' ') : item['title']
|
48
|
-
pattern = if search.strip =~ %r{^/.*?/$}
|
49
|
-
search.sub(%r{/(.*?)/}, '\1')
|
50
|
-
else
|
51
|
-
search.split('').join('.{0,3}')
|
52
|
-
end
|
53
|
-
text =~ /#{pattern}/i ? true : false
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
##
|
58
|
-
## @brief String helpers
|
59
|
-
##
|
60
|
-
class String
|
61
|
-
def cap_first
|
62
|
-
sub(/^\w/) do |m|
|
63
|
-
m.upcase
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
##
|
68
|
-
## @brief Turn raw urls into HTML links
|
69
|
-
##
|
70
|
-
## @param opt (Hash) Additional Options
|
71
|
-
##
|
72
|
-
def link_urls(opt = {})
|
73
|
-
opt[:format] ||= :html
|
74
|
-
if opt[:format] == :html
|
75
|
-
gsub(%r{(?mi)((http|https)://)?([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&:/~+#]*[\w\-@^=%&/~+#])?}) do |_match|
|
76
|
-
m = Regexp.last_match
|
77
|
-
proto = m[1].nil? ? 'http://' : ''
|
78
|
-
%(<a href="#{proto}#{m[0]}" title="Link to #{m[0]}">[#{m[3]}]</a>)
|
79
|
-
end.gsub(/<(\w+:.*?)>/) do |match|
|
80
|
-
m = Regexp.last_match
|
81
|
-
if m[1] =~ /<a href/
|
82
|
-
match
|
83
|
-
else
|
84
|
-
%(<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
else
|
88
|
-
self
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
7
|
##
|
94
8
|
## @brief Main "What Was I Doing" methods
|
95
9
|
##
|
@@ -791,7 +705,7 @@ class WWID
|
|
791
705
|
opt[:section] ||= 'all'
|
792
706
|
opt[:note] ||= []
|
793
707
|
opt[:tag] ||= []
|
794
|
-
opt[:tag_bool] ||=
|
708
|
+
opt[:tag_bool] ||= :and
|
795
709
|
|
796
710
|
last = last_entry(opt)
|
797
711
|
if last.nil?
|
@@ -817,7 +731,7 @@ class WWID
|
|
817
731
|
## @param opt (Hash) Additional Options
|
818
732
|
##
|
819
733
|
def last_entry(opt = {})
|
820
|
-
opt[:tag_bool] ||=
|
734
|
+
opt[:tag_bool] ||= :and
|
821
735
|
opt[:section] ||= @current_section
|
822
736
|
|
823
737
|
sec_arr = []
|
@@ -867,6 +781,7 @@ class WWID
|
|
867
781
|
opt[:autotag] ||= false
|
868
782
|
opt[:back] ||= false
|
869
783
|
opt[:took] ||= nil
|
784
|
+
opt[:unfinished] ||= false
|
870
785
|
|
871
786
|
sec_arr = []
|
872
787
|
|
@@ -902,10 +817,11 @@ class WWID
|
|
902
817
|
items.map! do |item|
|
903
818
|
break if idx == count
|
904
819
|
|
820
|
+
finished = opt[:unfinished] && item.has_tags?('done', :and)
|
905
821
|
tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.has_tags?(opt[:tag], opt[:tag_bool])
|
906
822
|
search_match = opt[:search].nil? || opt[:search].empty? ? true : item.matches_search?(opt[:search])
|
907
823
|
|
908
|
-
if tag_match && search_match
|
824
|
+
if tag_match && search_match && !finished
|
909
825
|
if opt[:autotag]
|
910
826
|
new_title = autotag(item['title']) if @auto_tag
|
911
827
|
if new_title == item['title']
|
@@ -1308,7 +1224,7 @@ class WWID
|
|
1308
1224
|
@content.each do |_k, v|
|
1309
1225
|
combined['items'] += v['items']
|
1310
1226
|
end
|
1311
|
-
section = if opt[:tag_filter] && opt[:tag_filter]['bool'] !=
|
1227
|
+
section = if opt[:tag_filter] && opt[:tag_filter]['bool'].normalize_bool != :not
|
1312
1228
|
opt[:tag_filter]['tags'].map do |tag|
|
1313
1229
|
"@#{tag}"
|
1314
1230
|
end.join(' + ')
|
@@ -1339,31 +1255,7 @@ class WWID
|
|
1339
1255
|
end
|
1340
1256
|
|
1341
1257
|
if opt[:tag_filter] && !opt[:tag_filter]['tags'].empty?
|
1342
|
-
items.
|
1343
|
-
case opt[:tag_filter]['bool']
|
1344
|
-
when /(AND|ALL)/
|
1345
|
-
del = false
|
1346
|
-
opt[:tag_filter]['tags'].each do |tag|
|
1347
|
-
unless item['title'] =~ /@#{tag}/
|
1348
|
-
del = true
|
1349
|
-
break
|
1350
|
-
end
|
1351
|
-
end
|
1352
|
-
del
|
1353
|
-
when /NONE/
|
1354
|
-
del = false
|
1355
|
-
opt[:tag_filter]['tags'].each do |tag|
|
1356
|
-
del = true if item['title'] =~ /@#{tag}/
|
1357
|
-
end
|
1358
|
-
del
|
1359
|
-
when /(OR|ANY)/
|
1360
|
-
del = true
|
1361
|
-
opt[:tag_filter]['tags'].each do |tag|
|
1362
|
-
del = false if item['title'] =~ /@#{tag}/
|
1363
|
-
end
|
1364
|
-
del
|
1365
|
-
end
|
1366
|
-
end
|
1258
|
+
items.select! { |item| item.has_tags?(opt[:tag_filter]['tags'], opt[:tag_filter]['bool']) }
|
1367
1259
|
end
|
1368
1260
|
|
1369
1261
|
if opt[:search]
|
@@ -1655,7 +1547,7 @@ class WWID
|
|
1655
1547
|
count = options[:keep] || 0
|
1656
1548
|
destination = options[:destination] || 'Archive'
|
1657
1549
|
tags = options[:tags] || []
|
1658
|
-
bool = options[:bool] ||
|
1550
|
+
bool = options[:bool] || :and
|
1659
1551
|
|
1660
1552
|
section = choose_section if section.nil? || section =~ /choose/i
|
1661
1553
|
archive_all = section =~ /^all$/i # && !(tags.nil? || tags.empty?)
|
@@ -1683,7 +1575,7 @@ class WWID
|
|
1683
1575
|
def do_archive(sect, destination, opt = {})
|
1684
1576
|
count = opt[:count] || 0
|
1685
1577
|
tags = opt[:tags] || []
|
1686
|
-
bool = opt[:bool] ||
|
1578
|
+
bool = opt[:bool] || :and
|
1687
1579
|
label = opt[:label] || true
|
1688
1580
|
|
1689
1581
|
if sect =~ /^all$/i
|
@@ -1696,7 +1588,7 @@ class WWID
|
|
1696
1588
|
counter = 0
|
1697
1589
|
|
1698
1590
|
all_sections.each do |section|
|
1699
|
-
items = @content[section]['items']
|
1591
|
+
items = @content[section]['items'].dup
|
1700
1592
|
|
1701
1593
|
moved_items = []
|
1702
1594
|
if !tags.empty? || opt[:search]
|
@@ -1720,13 +1612,7 @@ class WWID
|
|
1720
1612
|
@content[destination]['items'].concat(moved_items)
|
1721
1613
|
@results.push("Archived #{moved_items.length} items from #{section} to #{destination}")
|
1722
1614
|
else
|
1723
|
-
count = items.length if
|
1724
|
-
|
1725
|
-
@content[section]['items'] = if count.zero?
|
1726
|
-
[]
|
1727
|
-
else
|
1728
|
-
items[0..count - 1]
|
1729
|
-
end
|
1615
|
+
count = items.length if items.length < count
|
1730
1616
|
|
1731
1617
|
items.map! do |item|
|
1732
1618
|
if label && section != 'Currently'
|
@@ -1735,12 +1621,19 @@ class WWID
|
|
1735
1621
|
end
|
1736
1622
|
item
|
1737
1623
|
end
|
1624
|
+
|
1738
1625
|
if items.count > count
|
1739
1626
|
@content[destination]['items'].concat(items[count..-1])
|
1740
1627
|
else
|
1741
1628
|
@content[destination]['items'].concat(items)
|
1742
1629
|
end
|
1743
1630
|
|
1631
|
+
@content[section]['items'] = if count.zero?
|
1632
|
+
[]
|
1633
|
+
else
|
1634
|
+
items[0..count - 1]
|
1635
|
+
end
|
1636
|
+
|
1744
1637
|
@results.push("Archived #{items.length - count} items from #{section} to #{destination}")
|
1745
1638
|
end
|
1746
1639
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: doing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.69
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -165,6 +165,7 @@ files:
|
|
165
165
|
- README.md
|
166
166
|
- bin/doing
|
167
167
|
- lib/doing.rb
|
168
|
+
- lib/doing/helpers.rb
|
168
169
|
- lib/doing/version.rb
|
169
170
|
- lib/doing/wwid.rb
|
170
171
|
- lib/templates/doing.css
|