doing 1.0.90 → 2.0.2.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/AUTHORS +19 -0
- data/CHANGELOG.md +590 -0
- data/COMMANDS.md +1181 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +110 -0
- data/LICENSE +23 -0
- data/README.md +14 -697
- data/Rakefile +79 -0
- data/_config.yml +1 -0
- data/bin/doing +1037 -481
- data/doing.fish +278 -0
- data/doing.gemspec +34 -0
- data/doing.rdoc +1759 -0
- data/example_plugin.rb +209 -0
- data/generate_completions.sh +4 -0
- data/img/doing-colors.jpg +0 -0
- data/img/doing-printf-wrap-800.jpg +0 -0
- data/img/doing-show-note-formatting-800.jpg +0 -0
- data/lib/completion/_doing.zsh +151 -0
- data/lib/completion/doing.bash +416 -0
- data/lib/completion/doing.fish +278 -0
- data/lib/doing/array.rb +8 -0
- data/lib/doing/cli_status.rb +66 -0
- data/lib/doing/colors.rb +136 -0
- data/lib/doing/configuration.rb +310 -0
- data/lib/doing/errors.rb +102 -0
- data/lib/doing/hash.rb +31 -0
- data/lib/doing/hooks.rb +59 -0
- data/lib/doing/item.rb +155 -0
- data/lib/doing/log_adapter.rb +342 -0
- data/lib/doing/markdown_document_listener.rb +174 -0
- data/lib/doing/note.rb +59 -0
- data/lib/doing/pager.rb +95 -0
- data/lib/doing/plugin_manager.rb +208 -0
- data/lib/doing/plugins/export/csv_export.rb +48 -0
- data/lib/doing/plugins/export/html_export.rb +83 -0
- data/lib/doing/plugins/export/json_export.rb +140 -0
- data/lib/doing/plugins/export/markdown_export.rb +85 -0
- data/lib/doing/plugins/export/taskpaper_export.rb +34 -0
- data/lib/doing/plugins/export/template_export.rb +141 -0
- data/lib/doing/plugins/import/cal_to_json.scpt +0 -0
- data/lib/doing/plugins/import/calendar_import.rb +76 -0
- data/lib/doing/plugins/import/doing_import.rb +144 -0
- data/lib/doing/plugins/import/timing_import.rb +78 -0
- data/lib/doing/string.rb +346 -0
- data/lib/doing/symbol.rb +16 -0
- data/lib/doing/time.rb +18 -0
- data/lib/doing/util.rb +186 -0
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +1838 -2266
- data/lib/doing/wwidfile.rb +117 -0
- data/lib/doing.rb +43 -2
- data/lib/examples/commands/wiki.rb +80 -0
- data/lib/examples/plugins/hooks.rb +22 -0
- data/lib/examples/plugins/say_export.rb +202 -0
- data/lib/examples/plugins/templates/wiki.css +169 -0
- data/lib/examples/plugins/templates/wiki.haml +27 -0
- data/lib/examples/plugins/templates/wiki_index.haml +18 -0
- data/lib/examples/plugins/wiki_export.rb +87 -0
- data/lib/templates/doing-markdown.erb +5 -0
- data/man/doing.1 +964 -0
- data/man/doing.1.html +711 -0
- data/man/doing.1.ronn +600 -0
- data/package-lock.json +3 -0
- data/rdoc_to_mmd.rb +42 -0
- data/rdocfixer.rb +13 -0
- data/scripts/generate_bash_completions.rb +210 -0
- data/scripts/generate_fish_completions.rb +201 -0
- data/scripts/generate_zsh_completions.rb +164 -0
- metadata +82 -6
- data/lib/doing/helpers.rb +0 -121
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# title: Template Export
|
4
|
+
# description: Default export option using configured template placeholders
|
5
|
+
# author: Brett Terpstra
|
6
|
+
# url: https://brettterpstra.com
|
7
|
+
module Doing
|
8
|
+
class TemplateExport
|
9
|
+
include Doing::Color
|
10
|
+
include Doing::Util
|
11
|
+
|
12
|
+
def self.settings
|
13
|
+
{
|
14
|
+
trigger: 'template|doing'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.render(wwid, items, variables: {})
|
19
|
+
return if items.nil?
|
20
|
+
|
21
|
+
opt = variables[:options]
|
22
|
+
|
23
|
+
out = ''
|
24
|
+
items.each do |item|
|
25
|
+
if opt[:highlight] && item.title =~ /@#{wwid.config['marker_tag']}\b/i
|
26
|
+
flag = Doing::Color.send(wwid.config['marker_color'])
|
27
|
+
reset = Doing::Color.default
|
28
|
+
else
|
29
|
+
flag = ''
|
30
|
+
reset = ''
|
31
|
+
end
|
32
|
+
|
33
|
+
if (!item.note.empty?) && wwid.config['include_notes']
|
34
|
+
note = item.note.map(&:strip).delete_if(&:empty?)
|
35
|
+
note.map! { |line| "#{line.sub(/^\t*/, '')} " }
|
36
|
+
|
37
|
+
if opt[:wrap_width]&.positive?
|
38
|
+
width = opt[:wrap_width]
|
39
|
+
note.map! { |line| line.chomp.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n") }
|
40
|
+
note = note.join("\n").split(/\n/).delete_if(&:empty?)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
note = []
|
44
|
+
end
|
45
|
+
output = opt[:template].dup
|
46
|
+
|
47
|
+
output.gsub!(/%[a-z]+/) do |m|
|
48
|
+
if Doing::Color.respond_to?(m.sub(/^%/, ''))
|
49
|
+
Doing::Color.send(m.sub(/^%/, ''))
|
50
|
+
else
|
51
|
+
m
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
output.sub!(/%(\d+)?date/) do
|
56
|
+
pad = Regexp.last_match(1).to_i
|
57
|
+
format("%#{pad}s", item.date.strftime(opt[:format]))
|
58
|
+
end
|
59
|
+
|
60
|
+
interval = wwid.get_interval(item, record: true) if opt[:times]
|
61
|
+
|
62
|
+
interval ||= ''
|
63
|
+
output.sub!(/%interval/, interval)
|
64
|
+
|
65
|
+
output.sub!(/%(\d+)?shortdate/) do
|
66
|
+
pad = Regexp.last_match(1) || 13
|
67
|
+
format("%#{pad}s", item.date.relative_date)
|
68
|
+
end
|
69
|
+
|
70
|
+
output.sub!(/%section/, item.section) if item.section
|
71
|
+
|
72
|
+
title_offset = Doing::Color.uncolor(output).match(/%(-?\d+)?([ _t]\d+)?title/).begin(0)
|
73
|
+
output.sub!(/%(-?\d+)?(([ _t])(\d+))?title(.*?)$/) do
|
74
|
+
m = Regexp.last_match
|
75
|
+
pad = m[1].to_i
|
76
|
+
indent = ''
|
77
|
+
if m[2]
|
78
|
+
char = m[3] =~ /t/ ? "\t" : " "
|
79
|
+
indent = char * m[4].to_i
|
80
|
+
end
|
81
|
+
after = m[5]
|
82
|
+
if opt[:wrap_width]&.positive? || pad.positive?
|
83
|
+
width = pad.positive? ? pad : opt[:wrap_width]
|
84
|
+
item.title.wrap(width, pad: pad, indent: indent, offset: title_offset, prefix: flag, after: after, reset: reset)
|
85
|
+
# flag + item.title.gsub(/(.{#{opt[:wrap_width]}})(?=\s+|\Z)/, "\\1\n ").sub(/\s*$/, '') + reset
|
86
|
+
else
|
87
|
+
format("%s%#{pad}s%s%s", flag, item.title.sub(/\s*$/, ''), reset, after)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# output.sub!(/(?i-m)^([\s\S]*?)(%(?:[io]d|(?:\^[\s\S])?(?:(?:[ _t]|[^a-z0-9])?\d+)?(?:[\s\S][ _t]?)?)?note)([\s\S]*?)$/, '\1\3\2')
|
92
|
+
if opt[:tags_color]
|
93
|
+
output.highlight_tags!(opt[:tags_color])
|
94
|
+
end
|
95
|
+
|
96
|
+
if note.empty?
|
97
|
+
output.gsub!(/%([io]d|(\^.)?(([ _t]|[^a-z0-9])?\d+)?(.[ _t]?)?)?note/, '')
|
98
|
+
else
|
99
|
+
output.sub!(/%note/, "\n#{note.map { |l| "\t#{l.strip} " }.join("\n")}")
|
100
|
+
output.sub!(/%idnote/, "\n#{note.map { |l| "\t\t#{l.strip} " }.join("\n")}")
|
101
|
+
output.sub!(/%odnote/, "\n#{note.map { |l| "#{l.strip} " }.join("\n")}")
|
102
|
+
output.sub!(/(?mi)%(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])?(?<icount>\d+))?(?<prefix>.[ _t]?)?note/) do
|
103
|
+
m = Regexp.last_match
|
104
|
+
mark = m['mchar'] || ''
|
105
|
+
indent = if m['ichar']
|
106
|
+
char = m['ichar'] =~ /t/ ? "\t" : ' '
|
107
|
+
char * m['icount'].to_i
|
108
|
+
else
|
109
|
+
''
|
110
|
+
end
|
111
|
+
prefix = m['prefix'] || ''
|
112
|
+
"\n#{note.map { |l| "#{mark}#{indent}#{prefix}#{l.strip} " }.join("\n")}"
|
113
|
+
end
|
114
|
+
output.sub!(/%chompnote/) do |_m|
|
115
|
+
chomp_note = note.map do |l|
|
116
|
+
l.gsub(/\n+/, ' ').gsub(/(^\s*|\s*$)/, '').gsub(/\s+/, ' ')
|
117
|
+
end
|
118
|
+
chomp_note.join(' ')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
output.gsub!(/%hr(_under)?/) do |_m|
|
123
|
+
o = ''
|
124
|
+
`tput cols`.to_i.times do
|
125
|
+
o += Regexp.last_match(1).nil? ? '-' : '_'
|
126
|
+
end
|
127
|
+
o
|
128
|
+
end
|
129
|
+
output.gsub!(/%n/, "\n")
|
130
|
+
output.gsub!(/%t/, "\t")
|
131
|
+
|
132
|
+
out += "#{output}\n"
|
133
|
+
end
|
134
|
+
# Doing.logger.debug('Template Export:', "#{items.count} items output to template #{opt[:template]}")
|
135
|
+
out += wwid.tag_times(format: :text, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) if opt[:totals]
|
136
|
+
out
|
137
|
+
end
|
138
|
+
|
139
|
+
Doing::Plugins.register ['template', 'doing'], :export, self
|
140
|
+
end
|
141
|
+
end
|
Binary file
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# title: Calendar.app Import
|
4
|
+
# description: Import entries from a Calendar.app calendar
|
5
|
+
# author: Brett Terpstra
|
6
|
+
# url: https://brettterpstra.com
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
module Doing
|
10
|
+
##
|
11
|
+
## @brief Plugin for importing from Calendar.app on macOS
|
12
|
+
##
|
13
|
+
class CalendarImport
|
14
|
+
include Doing::Util
|
15
|
+
|
16
|
+
def self.settings
|
17
|
+
{
|
18
|
+
trigger: 'i?cal(?:endar)?'
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.import(wwid, _path, options: {})
|
23
|
+
limit_start = options[:start].to_i
|
24
|
+
limit_end = options[:end].to_i
|
25
|
+
|
26
|
+
section = options[:section] || wwid.config['current_section']
|
27
|
+
options[:no_overlap] ||= false
|
28
|
+
options[:autotag] ||= wwid.auto_tag
|
29
|
+
|
30
|
+
wwid.add_section(section) unless wwid.content.key?(section)
|
31
|
+
|
32
|
+
tags = options[:tag] ? options[:tag].split(/[ ,]+/).map { |t| t.sub(/^@?/, '') } : []
|
33
|
+
prefix = options[:prefix] || '[Calendar.app]'
|
34
|
+
|
35
|
+
script = File.join(File.dirname(__FILE__), 'cal_to_json.scpt')
|
36
|
+
res = `/usr/bin/osascript "#{script}" #{limit_start} #{limit_end}`.strip
|
37
|
+
data = JSON.parse(res)
|
38
|
+
|
39
|
+
new_items = []
|
40
|
+
data.each do |entry|
|
41
|
+
# Only process entries with a start and end date
|
42
|
+
next unless entry.key?('start') && entry.key?('end')
|
43
|
+
|
44
|
+
# Round down seconds and convert UTC to local time
|
45
|
+
start_time = Time.parse(entry['start']).getlocal
|
46
|
+
end_time = Time.parse(entry['end']).getlocal
|
47
|
+
next unless start_time && end_time
|
48
|
+
|
49
|
+
title = "#{prefix} "
|
50
|
+
title += entry['name']
|
51
|
+
tags.each do |tag|
|
52
|
+
if title =~ /\b#{tag}\b/i
|
53
|
+
title.sub!(/\b#{tag}\b/i, "@#{tag}")
|
54
|
+
else
|
55
|
+
title += " @#{tag}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
title = wwid.autotag(title) if options[:autotag]
|
59
|
+
title += " @done(#{end_time.strftime('%Y-%m-%d %H:%M')})"
|
60
|
+
title.gsub!(/ +/, ' ')
|
61
|
+
title.strip!
|
62
|
+
new_entry = { 'title' => title, 'date' => start_time, 'section' => section }
|
63
|
+
new_entry.note = entry['notes'].split(/\n/).map(&:chomp) if entry.key?('notes')
|
64
|
+
new_items.push(new_entry)
|
65
|
+
end
|
66
|
+
total = new_items.count
|
67
|
+
new_items = wwid.dedup(new_items, options[:no_overlap])
|
68
|
+
dups = total - new_items.count
|
69
|
+
Doing.logger.info(%(Skipped #{dups} items with overlapping times)) if dups.positive?
|
70
|
+
wwid.content[section][:items].concat(new_items)
|
71
|
+
Doing.logger.info(%(Imported #{new_items.count} items to #{section}))
|
72
|
+
end
|
73
|
+
|
74
|
+
Doing::Plugins.register 'calendar', :import, self
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# title: Doing Format Import
|
4
|
+
# description: Import entries from a Doing-formatted file
|
5
|
+
# author: Brett Terpstra
|
6
|
+
# url: https://brettterpstra.com
|
7
|
+
module Doing
|
8
|
+
class DoingImport
|
9
|
+
include Doing::Util
|
10
|
+
|
11
|
+
def self.settings
|
12
|
+
{
|
13
|
+
trigger: 'doing'
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
## @brief Imports a Doing file
|
19
|
+
##
|
20
|
+
## @param wwid WWID object
|
21
|
+
## @param path (String) Path to Doing file
|
22
|
+
## @param options (Hash) Additional Options
|
23
|
+
##
|
24
|
+
## @return Nothing
|
25
|
+
##
|
26
|
+
def self.import(wwid, path, options: {})
|
27
|
+
exit_now! 'Path to Doing file required' if path.nil?
|
28
|
+
|
29
|
+
exit_now! 'File not found' unless File.exist?(File.expand_path(path))
|
30
|
+
|
31
|
+
options[:no_overlap] ||= false
|
32
|
+
options[:autotag] ||= wwid.auto_tag
|
33
|
+
|
34
|
+
tags = options[:tag] ? options[:tag].split(/[ ,]+/).map { |t| t.sub(/^@?/, '') } : []
|
35
|
+
prefix = options[:prefix] || ''
|
36
|
+
|
37
|
+
@old_items = []
|
38
|
+
|
39
|
+
wwid.content.each { |_, v| @old_items.concat(v[:items]) }
|
40
|
+
|
41
|
+
new_items = read_doing_file(path)
|
42
|
+
|
43
|
+
if options[:date_filter]
|
44
|
+
new_items = wwid.filter_items(new_items, opt: { count: 0, date_filter: options[:date_filter] })
|
45
|
+
end
|
46
|
+
|
47
|
+
if options[:before] || options[:after]
|
48
|
+
new_items = wwid.filter_items(new_items, opt: { count: 0, before: options[:before], after: options[:after] })
|
49
|
+
end
|
50
|
+
|
51
|
+
imported = []
|
52
|
+
|
53
|
+
new_items.each do |item|
|
54
|
+
next if duplicate?(item)
|
55
|
+
|
56
|
+
title = "#{prefix} #{item.title}"
|
57
|
+
tags.each do |tag|
|
58
|
+
if title =~ /\b#{tag}\b/i
|
59
|
+
title.sub!(/\b#{tag}\b/i, "@#{tag}")
|
60
|
+
else
|
61
|
+
title += " @#{tag}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
title = wwid.autotag(title) if options[:autotag]
|
65
|
+
title.gsub!(/ +/, ' ')
|
66
|
+
title.strip!
|
67
|
+
section = options[:section] || item.section
|
68
|
+
section ||= wwid.config['current_section']
|
69
|
+
|
70
|
+
new_item = Item.new(item.date, title, section)
|
71
|
+
new_item.note = item.note
|
72
|
+
|
73
|
+
imported.push(new_item)
|
74
|
+
end
|
75
|
+
|
76
|
+
dups = new_items.count - imported.count
|
77
|
+
Doing.logger.info('Skipped:', %(#{dups} duplicate items)) if dups.positive?
|
78
|
+
|
79
|
+
imported = wwid.dedup(imported, !options[:overlap])
|
80
|
+
overlaps = new_items.count - imported.count - dups
|
81
|
+
Doing.logger.debug('Skipped:', "#{overlaps} items with overlapping times") if overlaps.positive?
|
82
|
+
|
83
|
+
imported.each do |item|
|
84
|
+
wwid.add_section(item.section) unless wwid.content.key?(item.section)
|
85
|
+
wwid.content[item.section][:items].push(item)
|
86
|
+
end
|
87
|
+
|
88
|
+
Doing.logger.info('Imported:', "#{imported.count} items")
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.duplicate?(item)
|
92
|
+
@old_items.each do |oi|
|
93
|
+
return true if item.equal?(oi)
|
94
|
+
end
|
95
|
+
|
96
|
+
false
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.read_doing_file(path)
|
100
|
+
doing_file = File.expand_path(path)
|
101
|
+
|
102
|
+
return nil unless File.exist?(doing_file) && File.file?(doing_file) && File.stat(doing_file).size.positive?
|
103
|
+
|
104
|
+
input = IO.read(doing_file)
|
105
|
+
input = input.force_encoding('utf-8') if input.respond_to? :force_encoding
|
106
|
+
|
107
|
+
lines = input.split(/[\n\r]/)
|
108
|
+
current = 0
|
109
|
+
|
110
|
+
items = []
|
111
|
+
section = ''
|
112
|
+
|
113
|
+
lines.each do |line|
|
114
|
+
next if line =~ /^\s*$/
|
115
|
+
|
116
|
+
case line
|
117
|
+
when /^(\S[\S ]+):\s*(@\S+\s*)*$/
|
118
|
+
section = Regexp.last_match(1)
|
119
|
+
current = 0
|
120
|
+
when /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*)/
|
121
|
+
date = Regexp.last_match(1).strip
|
122
|
+
title = Regexp.last_match(2).strip
|
123
|
+
item = Item.new(date, title, section)
|
124
|
+
items.push(item)
|
125
|
+
current += 1
|
126
|
+
when /^\S/
|
127
|
+
next
|
128
|
+
else
|
129
|
+
next if current.zero?
|
130
|
+
|
131
|
+
prev_item = items[current - 1]
|
132
|
+
prev_item.note = Note.new unless prev_item.note
|
133
|
+
|
134
|
+
prev_item.note.add(line)
|
135
|
+
# end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
items
|
140
|
+
end
|
141
|
+
|
142
|
+
Doing::Plugins.register 'doing', :import, self
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# title: Timing.app Import
|
4
|
+
# description: Import entries from a Timing.app report (JSON)
|
5
|
+
# author: Brett Terpstra
|
6
|
+
# url: https://brettterpstra.com
|
7
|
+
module Doing
|
8
|
+
class TimingImport
|
9
|
+
include Doing::Util
|
10
|
+
|
11
|
+
def self.settings
|
12
|
+
{
|
13
|
+
trigger: 'tim(?:ing)?'
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
## @brief Imports a Timing report
|
19
|
+
##
|
20
|
+
## @param path (String) Path to JSON report file
|
21
|
+
## @param options (Hash) Additional Options
|
22
|
+
##
|
23
|
+
def self.import(wwid, path, options: {})
|
24
|
+
exit_now! 'Path to JSON report required' if path.nil?
|
25
|
+
section = options[:section] || wwid.config['current_section']
|
26
|
+
options[:no_overlap] ||= false
|
27
|
+
options[:autotag] ||= wwid.auto_tag
|
28
|
+
wwid.add_section(section) unless wwid.content.key?(section)
|
29
|
+
|
30
|
+
add_tags = options[:tag] ? options[:tag].split(/[ ,]+/).map { |t| t.sub(/^@?/, '') } : []
|
31
|
+
prefix = options[:prefix] || '[Timing.app]'
|
32
|
+
exit_now! 'File not found' unless File.exist?(File.expand_path(path))
|
33
|
+
|
34
|
+
data = JSON.parse(IO.read(File.expand_path(path)))
|
35
|
+
new_items = []
|
36
|
+
data.each do |entry|
|
37
|
+
# Only process task entries
|
38
|
+
next if entry.key?('activityType') && entry['activityType'] != 'Task'
|
39
|
+
# Only process entries with a start and end date
|
40
|
+
next unless entry.key?('startDate') && entry.key?('endDate')
|
41
|
+
|
42
|
+
# Round down seconds and convert UTC to local time
|
43
|
+
start_time = Time.parse(entry['startDate'].sub(/:\d\dZ$/, ':00Z')).getlocal
|
44
|
+
end_time = Time.parse(entry['endDate'].sub(/:\d\dZ$/, ':00Z')).getlocal
|
45
|
+
next unless start_time && end_time
|
46
|
+
|
47
|
+
tags = entry['project'].split(/ ▸ /).map { |proj| proj.gsub(/[^a-z0-9]+/i, '').downcase }
|
48
|
+
tags.concat(add_tags)
|
49
|
+
title = "#{prefix} "
|
50
|
+
title += entry.key?('activityTitle') && entry['activityTitle'] != '(Untitled Task)' ? entry['activityTitle'] : 'Working on'
|
51
|
+
tags.each do |tag|
|
52
|
+
if title =~ /\b#{tag}\b/i
|
53
|
+
title.sub!(/\b#{tag}\b/i, "@#{tag}")
|
54
|
+
else
|
55
|
+
title += " @#{tag}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
title = wwid.autotag(title) if options[:autotag]
|
59
|
+
title += " @done(#{end_time.strftime('%Y-%m-%d %H:%M')})"
|
60
|
+
title.gsub!(/ +/, ' ')
|
61
|
+
title.strip!
|
62
|
+
new_item = Item.new(start_time, title, section)
|
63
|
+
new_item.note.add(entry['notes']) if entry.key?('notes')
|
64
|
+
new_items.push(new_item)
|
65
|
+
end
|
66
|
+
total = new_items.count
|
67
|
+
skipped = data.count - total
|
68
|
+
Doing.logger.debug('Skipped:' , %(#{skipped} items, invalid type or no time interval)) if skipped.positive?
|
69
|
+
new_items = wwid.dedup(new_items, options[:no_overlap])
|
70
|
+
dups = total - new_items.count
|
71
|
+
Doing.logger.debug('Skipped:' , %(#{dups} items with overlapping times)) if dups.positive?
|
72
|
+
wwid.content[section][:items].concat(new_items)
|
73
|
+
Doing.logger.info('Imported:', %(#{new_items.count} items to #{section}))
|
74
|
+
end
|
75
|
+
|
76
|
+
Doing::Plugins.register 'timing', :import, self
|
77
|
+
end
|
78
|
+
end
|