doing 2.1.43 → 2.1.46

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/.irbrc +3 -0
  3. data/CHANGELOG.md +40 -0
  4. data/Gemfile.lock +4 -4
  5. data/README.md +1 -1
  6. data/bin/commands/grep.rb +10 -2
  7. data/bin/commands/on.rb +6 -1
  8. data/bin/commands/recent.rb +1 -0
  9. data/bin/commands/reset.rb +6 -4
  10. data/bin/commands/show.rb +6 -1
  11. data/bin/commands/since.rb +5 -2
  12. data/bin/commands/today.rb +1 -0
  13. data/bin/commands/views.rb +81 -20
  14. data/bin/doing +24 -1
  15. data/docs/doc/Array.html +12 -2
  16. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  17. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  18. data/docs/doc/BooleanTermParser/Query.html +1 -1
  19. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  20. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  21. data/docs/doc/BooleanTermParser.html +1 -1
  22. data/docs/doc/Doing/ArrayCleanup.html +1 -1
  23. data/docs/doc/Doing/ArrayNestedHash.html +1 -1
  24. data/docs/doc/Doing/ArrayTags.html +1 -1
  25. data/docs/doc/Doing/CSVExport.html +1 -1
  26. data/docs/doc/Doing/CalendarImport.html +1 -1
  27. data/docs/doc/Doing/Change.html +1 -1
  28. data/docs/doc/Doing/Changes.html +1 -1
  29. data/docs/doc/Doing/ChronifyArray.html +1 -1
  30. data/docs/doc/Doing/ChronifyNumeric.html +1 -1
  31. data/docs/doc/Doing/ChronifyString.html +1 -1
  32. data/docs/doc/Doing/Color.html +1 -1
  33. data/docs/doc/Doing/Completion/BashCompletions.html +1 -1
  34. data/docs/doc/Doing/Completion/FishCompletions.html +1 -1
  35. data/docs/doc/Doing/Completion/StringUtils.html +1 -1
  36. data/docs/doc/Doing/Completion/ZshCompletions.html +1 -1
  37. data/docs/doc/Doing/Completion.html +1 -1
  38. data/docs/doc/Doing/Configuration.html +1 -1
  39. data/docs/doc/Doing/DayOneRenderer.html +1 -1
  40. data/docs/doc/Doing/DayoneExport.html +1 -1
  41. data/docs/doc/Doing/DoingImport.html +1 -1
  42. data/docs/doc/Doing/Entry.html +1 -1
  43. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  44. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  45. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  46. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  47. data/docs/doc/Doing/Errors/HistoryLimitError.html +1 -1
  48. data/docs/doc/Doing/Errors/InvalidPlugin.html +1 -1
  49. data/docs/doc/Doing/Errors/MissingBackupFile.html +1 -1
  50. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  51. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  52. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  53. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  54. data/docs/doc/Doing/Errors.html +1 -1
  55. data/docs/doc/Doing/HTMLExport.html +1 -1
  56. data/docs/doc/Doing/Hooks.html +1 -1
  57. data/docs/doc/Doing/Item.html +75 -36
  58. data/docs/doc/Doing/ItemDates.html +1 -1
  59. data/docs/doc/Doing/ItemQuery.html +1 -1
  60. data/docs/doc/Doing/ItemState.html +1 -1
  61. data/docs/doc/Doing/ItemTags.html +1 -1
  62. data/docs/doc/Doing/Items.html +129 -1
  63. data/docs/doc/Doing/JSONExport.html +1 -1
  64. data/docs/doc/Doing/Logger.html +1 -1
  65. data/docs/doc/Doing/MarkdownExport.html +1 -1
  66. data/docs/doc/Doing/Note.html +1 -1
  67. data/docs/doc/Doing/Pager.html +1 -1
  68. data/docs/doc/Doing/Plugins.html +1 -1
  69. data/docs/doc/Doing/Prompt.html +1 -1
  70. data/docs/doc/Doing/PromptChoose.html +1 -1
  71. data/docs/doc/Doing/PromptFZF.html +1 -1
  72. data/docs/doc/Doing/PromptInput.html +1 -1
  73. data/docs/doc/Doing/PromptSTD.html +1 -1
  74. data/docs/doc/Doing/PromptYN.html +1 -1
  75. data/docs/doc/Doing/Section.html +1 -1
  76. data/docs/doc/Doing/StringHighlight.html +1 -1
  77. data/docs/doc/Doing/StringNormalize.html +1 -1
  78. data/docs/doc/Doing/StringQuery.html +1 -1
  79. data/docs/doc/Doing/StringTags.html +1 -1
  80. data/docs/doc/Doing/StringTransform.html +1 -1
  81. data/docs/doc/Doing/StringTruncate.html +1 -1
  82. data/docs/doc/Doing/StringURL.html +1 -1
  83. data/docs/doc/Doing/SymbolNormalize.html +1 -1
  84. data/docs/doc/Doing/TaskPaperExport.html +1 -1
  85. data/docs/doc/Doing/TemplateExport.html +1 -1
  86. data/docs/doc/Doing/TemplateString.html +2 -2
  87. data/docs/doc/Doing/TimingImport.html +1 -1
  88. data/docs/doc/Doing/Types.html +23 -18
  89. data/docs/doc/Doing/Util/Backup.html +1 -1
  90. data/docs/doc/Doing/Util.html +2 -2
  91. data/docs/doc/Doing/Version.html +1 -1
  92. data/docs/doc/Doing/WWID.html +1 -1
  93. data/docs/doc/Doing.html +4 -4
  94. data/docs/doc/FalseClass.html +11 -1
  95. data/docs/doc/GLI/Commands/Help.html +1 -1
  96. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  97. data/docs/doc/GLI/Commands.html +1 -1
  98. data/docs/doc/GLI.html +1 -1
  99. data/docs/doc/Hash.html +1 -1
  100. data/docs/doc/Numeric.html +1 -1
  101. data/docs/doc/Object.html +1 -1
  102. data/docs/doc/PhraseParser/Operator.html +1 -1
  103. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  104. data/docs/doc/PhraseParser/Query.html +1 -1
  105. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  106. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  107. data/docs/doc/PhraseParser/TermClause.html +1 -1
  108. data/docs/doc/PhraseParser.html +1 -1
  109. data/docs/doc/Status.html +1 -1
  110. data/docs/doc/String.html +65 -3
  111. data/docs/doc/Symbol.html +1 -1
  112. data/docs/doc/Time.html +68 -3
  113. data/docs/doc/TrueClass.html +11 -1
  114. data/docs/doc/_index.html +8 -1
  115. data/docs/doc/class_list.html +1 -1
  116. data/docs/doc/file.README.html +2 -2
  117. data/docs/doc/index.html +2 -2
  118. data/docs/doc/method_list.html +496 -440
  119. data/docs/doc/top-level-namespace.html +1 -1
  120. data/doing.rdoc +66 -5
  121. data/lib/completion/_doing.zsh +10 -10
  122. data/lib/completion/doing.bash +18 -18
  123. data/lib/completion/doing.fish +9 -0
  124. data/lib/doing/add_options.rb +6 -2
  125. data/lib/doing/configuration.rb +4 -0
  126. data/lib/doing/good.rb +18 -1
  127. data/lib/doing/item/item.rb +7 -8
  128. data/lib/doing/items/items.rb +24 -0
  129. data/lib/doing/plugins/export/json_export.rb +16 -2
  130. data/lib/doing/plugins/import/json_import.rb +93 -0
  131. data/lib/doing/string/string.rb +9 -0
  132. data/lib/doing/types.rb +9 -8
  133. data/lib/doing/version.rb +1 -1
  134. data/lib/doing/wwid/display.rb +4 -1
  135. data/lib/doing/wwid/filetools.rb +3 -2
  136. data/lib/doing/wwid/filter.rb +6 -3
  137. data/lib/doing.rb +10 -0
  138. metadata +3 -2
@@ -35,6 +35,30 @@ module Doing
35
35
  includes
36
36
  end
37
37
 
38
+ # Find an item by ID
39
+ #
40
+ # @param id The identifier to match
41
+ #
42
+ def find_id(id)
43
+ select { |item| item.id == id }[0]
44
+ end
45
+
46
+ ##
47
+ ## Return the index for an entry matching ID
48
+ ##
49
+ ## @param id The identifier to match
50
+ ##
51
+ def index_for_id(id)
52
+ i = nil
53
+ each_with_index do |item, idx|
54
+ if item.id == id
55
+ i = idx
56
+ break
57
+ end
58
+ end
59
+ i
60
+ end
61
+
38
62
  # Output sections and items in Doing file format
39
63
  def to_s
40
64
  out = []
@@ -15,7 +15,18 @@ module Doing
15
15
  end
16
16
 
17
17
  def self.render(wwid, items, variables: {})
18
- return if items.nil?
18
+ if items.nil? || items.empty?
19
+ return case variables[:options][:output]
20
+ when 'json'
21
+ {
22
+ 'section' => '',
23
+ 'items' => [],
24
+ 'timers' => ""
25
+ }.to_json
26
+ when 'timeline'
27
+ "<html></html>"
28
+ end
29
+ end
19
30
 
20
31
  opt = variables[:options]
21
32
  opt[:output] = case opt[:output]
@@ -25,6 +36,7 @@ module Doing
25
36
  'json'
26
37
  end
27
38
  items_out = []
39
+
28
40
  last_date = items[-1].date + (60 * 60 * 24)
29
41
  max = last_date.strftime('%F')
30
42
  min = items[0].date.strftime('%F')
@@ -51,10 +63,12 @@ module Doing
51
63
  date: i.date,
52
64
  end_date: end_date,
53
65
  title: title.strip, #+ " #{note}"
66
+ section: i.section,
54
67
  note: note.to_s(prefix: ''),
55
68
  time: interval.time_string(format: :clock),
56
69
  duration: duration.time_string(format: :clock),
57
- tags: tags
70
+ tags: tags,
71
+ id: i.id
58
72
  }
59
73
 
60
74
  attributes.each { |attr, val| i[attr.to_sym] = val }
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ # title: JSON Import
4
+ # description: Import entries from a Doing JSON export
5
+ # author: Brett Terpstra
6
+ # url: https://brettterpstra.com
7
+ module Doing
8
+ class JSONImport
9
+ include Doing::Util
10
+
11
+ def self.settings
12
+ {
13
+ trigger: 'json'
14
+ }
15
+ end
16
+
17
+ ##
18
+ ## Imports a Timing report
19
+ ##
20
+ ## @param wwid [WWID] The wwid object
21
+ ## @param path [String] Path to JSON report
22
+ ## file
23
+ ## @param options [Hash] Additional Options
24
+ ##
25
+ def self.import(wwid, path, options: {})
26
+ exit_now! 'Path to JSON export required' if path.nil?
27
+ options[:no_overlap] ||= false
28
+ options[:autotag] ||= Doing.auto_tag
29
+
30
+ exit_now! 'File not found' unless File.exist?(File.expand_path(path))
31
+
32
+ updated = 0
33
+ added = 0
34
+ skipped = 0
35
+
36
+ data = JSON.parse(IO.read(File.expand_path(path)))
37
+ new_items = []
38
+ new_section = options[:section] || Doing.setting('current_section')
39
+
40
+ data['items'].each do |entry|
41
+ title = entry['title']
42
+ date = Time.parse(entry['date'])
43
+ date ||= entry['date'].chronify
44
+ note = Doing::Note.new(entry['note'])
45
+ section = if entry['section'].empty?
46
+ new_section
47
+ else
48
+ entry['section']
49
+ end
50
+ id = entry.key?('id') ? entry['id'] : nil
51
+
52
+ new_item = Doing::Item.new(date, title, section, note, id)
53
+
54
+ is_match = true
55
+
56
+ if options[:search]
57
+ is_match = new_item.search(options[:search], case_type: options[:case], negate: options[:not])
58
+ end
59
+
60
+ if is_match && options[:date_filter]
61
+ is_match = start_time > options[:date_filter][0] && start_time < options[:date_filter][1]
62
+ is_match = options[:not] ? !is_match : is_match
63
+ end
64
+
65
+ unless is_match
66
+ skipped += 1
67
+ next
68
+
69
+ end
70
+
71
+ if wwid.content.find_id(new_item.id)
72
+ old_index = wwid.content.index_for_id(entry['id'])
73
+ old_item = wwid.content[old_index].clone
74
+ wwid.content[old_index] = new_item
75
+ Hooks.trigger :post_entry_updated, self, new_item, old_item
76
+ updated += 1
77
+ else
78
+ Hooks.trigger :pre_entry_add, self, item
79
+ wwid.content << new_entry
80
+ Hooks.trigger :post_entry_added, self, item
81
+ added += 1
82
+ end
83
+ end
84
+ total = new_items.count
85
+ skipped = data.count - total
86
+ Doing.logger.debug('Skipped:', %(#{skipped} items)) if skipped.positive?
87
+ Doing.logger.info('Updated:', %(#{updated} items))
88
+ Doing.logger.info('Imported:', %(#{added} new items to #{new_section}))
89
+ end
90
+
91
+ Doing::Plugins.register 'json', :import, self
92
+ end
93
+ end
@@ -16,6 +16,15 @@ class ::String
16
16
  include Doing::StringTruncate
17
17
  include Doing::StringURL
18
18
 
19
+ ##
20
+ ## Test if string is a valid 32-character MD5 id
21
+ ##
22
+ ## @return [Boolean] string is valid identifier
23
+ ##
24
+ def valid_id?
25
+ strip =~ /^[a-z0-9]{32}$/ ? true : false
26
+ end
27
+
19
28
  ##
20
29
  ## Force UTF-8 encoding if available
21
30
  ##
data/lib/doing/types.rb CHANGED
@@ -17,18 +17,19 @@ module Doing
17
17
  InvalidExportType = Class.new(RuntimeError)
18
18
  MissingConfigFile = Class.new(RuntimeError)
19
19
 
20
- TagArray = Class.new(Array)
21
- TemplateName = Class.new(String)
20
+ AgeSymbol = Class.new(String)
21
+ BooleanSymbol = Class.new(Symbol)
22
+ CaseSymbol = Class.new(Symbol)
22
23
  DateBeginString = Class.new(DateTime)
23
24
  DateEndString = Class.new(DateTime)
24
- DateRangeString = Class.new(Array)
25
- DateRangeOptionalString = Class.new(Array)
26
25
  DateIntervalString = Class.new(DateTime)
27
- BooleanSymbol = Class.new(Symbol)
28
- CaseSymbol = Class.new(Symbol)
29
- AgeSymbol = Class.new(String)
26
+ DateRangeOptionalString = Class.new(Array)
27
+ DateRangeString = Class.new(Array)
28
+ ExportTemplate = Class.new(String)
29
+ MatchingSymbol = Class.new(Symbol)
30
30
  OrderSymbol = Class.new(Symbol)
31
+ TagArray = Class.new(Array)
31
32
  TagSortSymbol = Class.new(Symbol)
32
- MatchingSymbol = Class.new(Symbol)
33
+ TemplateName = Class.new(String)
33
34
  end
34
35
  end
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '2.1.43'
2
+ VERSION = '2.1.46'
3
3
  end
@@ -357,7 +357,10 @@ module Doing
357
357
  opt ||= {}
358
358
  out = nil
359
359
 
360
- raise InvalidArgument, 'Unknown output format' unless opt[:output] =~ Plugins.plugin_regex(type: :export)
360
+ unless opt[:output] =~ Plugins.plugin_regex(type: :export)
361
+ raise InvalidPlugin.new('Unknown output format', opt[:output])
362
+
363
+ end
361
364
 
362
365
  export_options = { page_title: title, is_single: is_single, options: opt }
363
366
 
@@ -40,7 +40,7 @@ module Doing
40
40
  if line =~ /^(\S[\S ]+):\s*(@[\w\-_.]+\s*)*$/
41
41
  section = Regexp.last_match(1)
42
42
  @content.add_section(Section.new(section, original: line), log: false)
43
- elsif line =~ /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*)/
43
+ elsif line =~ /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*?)(?: +<([a-z0-9]{32})>)? *$/
44
44
  if section.nil?
45
45
  section = 'Uncategorized'
46
46
  @content.add_section(Section.new(section, original: 'Uncategorized:'), log: false)
@@ -48,7 +48,8 @@ module Doing
48
48
 
49
49
  date = Regexp.last_match(1).strip
50
50
  title = Regexp.last_match(2).strip
51
- item = Item.new(date, title, section)
51
+ id = Regexp.last_match(3) || nil
52
+ item = Item.new(date, title, section, [], id)
52
53
  @content.push(item)
53
54
  elsif @content.count.zero?
54
55
  # if content[section].items.length - 1 == current
@@ -146,21 +146,24 @@ module Doing
146
146
  end
147
147
 
148
148
  if keep && opt[:time_filter][0] || opt[:time_filter][1]
149
+ opt[:time_filter].map! { |v| v =~ /(12 *am|midnight)/i ? '00:00' : v }
150
+
149
151
  start_string = if opt[:time_filter][0].nil?
150
- "#{item.date.strftime('%Y-%m-%d')} 12am"
152
+ "#{item.date.strftime('%Y-%m-%d')} 00:00"
151
153
  else
152
154
  "#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][0]}"
153
155
  end
154
156
  start_time = start_string.chronify(guess: :begin)
155
157
 
156
158
  end_string = if opt[:time_filter][1].nil?
157
- "#{item.date.to_datetime.next_day.strftime('%Y-%m-%d')} 12am"
159
+ "#{item.date.to_datetime.next_day.strftime('%Y-%m-%d')} 00:00"
158
160
  else
159
161
  "#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][1]}"
160
162
  end
161
- end_time = end_string.chronify(guess: :end)
163
+ end_time = end_string.chronify(guess: :end) || Time.now
162
164
 
163
165
  in_time_range = item.date >= start_time && item.date <= end_time
166
+
164
167
  keep = false unless in_time_range
165
168
  keep = opt[:not] ? !keep : keep
166
169
  end
data/lib/doing.rb CHANGED
@@ -16,6 +16,7 @@ require 'json'
16
16
  require 'logger'
17
17
  require 'safe_yaml/load'
18
18
  require 'fcntl'
19
+ require 'digest'
19
20
 
20
21
  require 'chronic'
21
22
  require 'tty-link'
@@ -85,6 +86,15 @@ module Doing
85
86
  config.settings
86
87
  end
87
88
 
89
+ def original_options
90
+ @original_options ||= {
91
+ date_begin: nil,
92
+ date_end: nil,
93
+ date_range: nil,
94
+ date_interval: nil
95
+ }
96
+ end
97
+
88
98
  ##
89
99
  ## Fetch a config setting using a dot-separated keypath
90
100
  ## or array of keys
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: 2.1.43
4
+ version: 2.1.46
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-20 00:00:00.000000000 Z
11
+ date: 2022-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: github-markup
@@ -677,6 +677,7 @@ files:
677
677
  - lib/doing/plugins/import/cal_to_json.scpt
678
678
  - lib/doing/plugins/import/calendar_import.rb
679
679
  - lib/doing/plugins/import/doing_import.rb
680
+ - lib/doing/plugins/import/json_import.rb
680
681
  - lib/doing/plugins/import/timing_import.rb
681
682
  - lib/doing/prompt/choose.rb
682
683
  - lib/doing/prompt/fzf.rb