doing 2.1.45 → 2.1.46

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/.irbrc +3 -0
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +1 -1
  6. data/bin/commands/reset.rb +6 -4
  7. data/docs/doc/Array.html +1 -1
  8. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  9. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  10. data/docs/doc/BooleanTermParser/Query.html +1 -1
  11. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  12. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  13. data/docs/doc/BooleanTermParser.html +1 -1
  14. data/docs/doc/Doing/ArrayCleanup.html +1 -1
  15. data/docs/doc/Doing/ArrayNestedHash.html +1 -1
  16. data/docs/doc/Doing/ArrayTags.html +1 -1
  17. data/docs/doc/Doing/CSVExport.html +1 -1
  18. data/docs/doc/Doing/CalendarImport.html +1 -1
  19. data/docs/doc/Doing/Change.html +1 -1
  20. data/docs/doc/Doing/Changes.html +1 -1
  21. data/docs/doc/Doing/ChronifyArray.html +1 -1
  22. data/docs/doc/Doing/ChronifyNumeric.html +1 -1
  23. data/docs/doc/Doing/ChronifyString.html +1 -1
  24. data/docs/doc/Doing/Color.html +1 -1
  25. data/docs/doc/Doing/Completion/BashCompletions.html +1 -1
  26. data/docs/doc/Doing/Completion/FishCompletions.html +1 -1
  27. data/docs/doc/Doing/Completion/StringUtils.html +1 -1
  28. data/docs/doc/Doing/Completion/ZshCompletions.html +1 -1
  29. data/docs/doc/Doing/Completion.html +1 -1
  30. data/docs/doc/Doing/Configuration.html +1 -1
  31. data/docs/doc/Doing/DayOneRenderer.html +1 -1
  32. data/docs/doc/Doing/DayoneExport.html +1 -1
  33. data/docs/doc/Doing/DoingImport.html +1 -1
  34. data/docs/doc/Doing/Entry.html +1 -1
  35. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  36. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  37. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  38. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  39. data/docs/doc/Doing/Errors/HistoryLimitError.html +1 -1
  40. data/docs/doc/Doing/Errors/InvalidPlugin.html +1 -1
  41. data/docs/doc/Doing/Errors/MissingBackupFile.html +1 -1
  42. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  43. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  44. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  45. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  46. data/docs/doc/Doing/Errors.html +1 -1
  47. data/docs/doc/Doing/HTMLExport.html +1 -1
  48. data/docs/doc/Doing/Hooks.html +1 -1
  49. data/docs/doc/Doing/Item.html +75 -36
  50. data/docs/doc/Doing/ItemDates.html +1 -1
  51. data/docs/doc/Doing/ItemQuery.html +1 -1
  52. data/docs/doc/Doing/ItemState.html +1 -1
  53. data/docs/doc/Doing/ItemTags.html +1 -1
  54. data/docs/doc/Doing/Items.html +129 -1
  55. data/docs/doc/Doing/JSONExport.html +1 -1
  56. data/docs/doc/Doing/Logger.html +1 -1
  57. data/docs/doc/Doing/MarkdownExport.html +1 -1
  58. data/docs/doc/Doing/Note.html +1 -1
  59. data/docs/doc/Doing/Pager.html +1 -1
  60. data/docs/doc/Doing/Plugins.html +1 -1
  61. data/docs/doc/Doing/Prompt.html +1 -1
  62. data/docs/doc/Doing/PromptChoose.html +1 -1
  63. data/docs/doc/Doing/PromptFZF.html +1 -1
  64. data/docs/doc/Doing/PromptInput.html +1 -1
  65. data/docs/doc/Doing/PromptSTD.html +1 -1
  66. data/docs/doc/Doing/PromptYN.html +1 -1
  67. data/docs/doc/Doing/Section.html +1 -1
  68. data/docs/doc/Doing/StringHighlight.html +1 -1
  69. data/docs/doc/Doing/StringNormalize.html +1 -1
  70. data/docs/doc/Doing/StringQuery.html +1 -1
  71. data/docs/doc/Doing/StringTags.html +1 -1
  72. data/docs/doc/Doing/StringTransform.html +1 -1
  73. data/docs/doc/Doing/StringTruncate.html +1 -1
  74. data/docs/doc/Doing/StringURL.html +1 -1
  75. data/docs/doc/Doing/SymbolNormalize.html +1 -1
  76. data/docs/doc/Doing/TaskPaperExport.html +1 -1
  77. data/docs/doc/Doing/TemplateExport.html +1 -1
  78. data/docs/doc/Doing/TemplateString.html +2 -2
  79. data/docs/doc/Doing/TimingImport.html +1 -1
  80. data/docs/doc/Doing/Types.html +1 -1
  81. data/docs/doc/Doing/Util/Backup.html +1 -1
  82. data/docs/doc/Doing/Util.html +2 -2
  83. data/docs/doc/Doing/Version.html +1 -1
  84. data/docs/doc/Doing/WWID.html +1 -1
  85. data/docs/doc/Doing.html +4 -4
  86. data/docs/doc/FalseClass.html +1 -1
  87. data/docs/doc/GLI/Commands/Help.html +1 -1
  88. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  89. data/docs/doc/GLI/Commands.html +1 -1
  90. data/docs/doc/GLI.html +1 -1
  91. data/docs/doc/Hash.html +1 -1
  92. data/docs/doc/Numeric.html +1 -1
  93. data/docs/doc/Object.html +1 -1
  94. data/docs/doc/PhraseParser/Operator.html +1 -1
  95. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  96. data/docs/doc/PhraseParser/Query.html +1 -1
  97. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  98. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  99. data/docs/doc/PhraseParser/TermClause.html +1 -1
  100. data/docs/doc/PhraseParser.html +1 -1
  101. data/docs/doc/Status.html +1 -1
  102. data/docs/doc/String.html +63 -1
  103. data/docs/doc/Symbol.html +1 -1
  104. data/docs/doc/Time.html +1 -1
  105. data/docs/doc/TrueClass.html +1 -1
  106. data/docs/doc/_index.html +8 -1
  107. data/docs/doc/class_list.html +1 -1
  108. data/docs/doc/file.README.html +2 -2
  109. data/docs/doc/index.html +2 -2
  110. data/docs/doc/method_list.html +409 -361
  111. data/docs/doc/top-level-namespace.html +1 -1
  112. data/doing.rdoc +3 -3
  113. data/lib/doing/item/item.rb +7 -8
  114. data/lib/doing/items/items.rb +24 -0
  115. data/lib/doing/plugins/export/json_export.rb +16 -2
  116. data/lib/doing/plugins/import/json_import.rb +93 -0
  117. data/lib/doing/string/string.rb +9 -0
  118. data/lib/doing/version.rb +1 -1
  119. data/lib/doing/wwid/filetools.rb +3 -2
  120. data/lib/doing/wwid/filter.rb +2 -2
  121. data/lib/doing.rb +1 -0
  122. metadata +3 -2
@@ -216,7 +216,7 @@
216
216
  </div>
217
217
 
218
218
  <div id="footer">
219
- Generated on Tue Mar 22 10:06:15 2022 by
219
+ Generated on Wed Mar 23 08:25:50 2022 by
220
220
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
221
221
  0.9.27 (ruby-3.0.1).
222
222
  </div>
data/doing.rdoc CHANGED
@@ -5,7 +5,7 @@ record of what you've been doing, complete with tag-based time tracking. The
5
5
  command line tool allows you to add entries, annotate with tags and notes, and
6
6
  view your entries with myriad options, with a focus on a "natural" language syntax.
7
7
 
8
- v2.1.45
8
+ v2.1.46
9
9
 
10
10
  === Global Options
11
11
  === --config_file arg
@@ -1055,7 +1055,7 @@ List commands one per line, to assist with shell completion
1055
1055
  ==== Command: <tt>import PATH</tt>
1056
1056
  Import entries from an external source
1057
1057
 
1058
- Imports entries from other sources. Available plugins: calendar, capturething, doing, timing
1058
+ Imports entries from other sources. Available plugins: calendar, capturething, doing, json, timing
1059
1059
  ===== Options
1060
1060
  ===== --after DATE_STRING
1061
1061
 
@@ -1123,7 +1123,7 @@ Tag all imported entries
1123
1123
 
1124
1124
  ===== --type TYPE
1125
1125
 
1126
- Import type (calendar|capturething|doing|timing)
1126
+ Import type (calendar|capturething|doing|json|timing)
1127
1127
 
1128
1128
  [Default Value] doing
1129
1129
 
@@ -15,7 +15,7 @@ module Doing
15
15
  include ItemState
16
16
  include ItemTags
17
17
 
18
- attr_accessor :date, :title, :section, :note
18
+ attr_accessor :date, :title, :section, :note, :id
19
19
 
20
20
  # attr_reader :id
21
21
 
@@ -31,19 +31,18 @@ module Doing
31
31
  ## the item belongs
32
32
  ## @param note [Array or String] The note
33
33
  ## (optional)
34
+ ## @param id MD5 identifier
34
35
  ##
35
- def initialize(date, title, section, note = nil)
36
+ def initialize(date, title, section, note = nil, id = nil)
36
37
  @date = date.is_a?(Time) ? date : Time.parse(date)
37
38
  @title = title
38
39
  @section = section
39
40
  @note = Note.new(note)
41
+ @id = id&.valid_id? ? id.strip : gen_id
40
42
  end
41
43
 
42
- # Generate a hash that represents the entry
43
- #
44
- # @return [String] entry hash
45
- def id
46
- @id ||= (@date.to_s + @title + @section).hash
44
+ def gen_id
45
+ Digest::MD5.hexdigest(to_s)
47
46
  end
48
47
 
49
48
  ##
@@ -91,7 +90,7 @@ module Doing
91
90
 
92
91
  # outputs item in Doing file format, including leading tab
93
92
  def to_s
94
- "\t- #{@date.strftime('%Y-%m-%d %H:%M')} | #{@title}#{@note.good? ? "\n#{@note}" : ''}"
93
+ "\t- #{@date.strftime('%Y-%m-%d %H:%M')} | #{@title} <#{@id}>#{@note.good? ? "\n#{@note}" : ''}"
95
94
  end
96
95
 
97
96
  ##
@@ -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/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '2.1.45'
2
+ VERSION = '2.1.46'
3
3
  end
@@ -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,8 +146,8 @@ module Doing
146
146
  end
147
147
 
148
148
  if keep && opt[:time_filter][0] || opt[:time_filter][1]
149
- opt[:time_filter][0] = '00:00' if opt[:time_filter][0] =~ /12 *am/i
150
- opt[:time_filter][1] = '00:00' if opt[:time_filter][1] =~ /12 *am/i
149
+ opt[:time_filter].map! { |v| v =~ /(12 *am|midnight)/i ? '00:00' : v }
150
+
151
151
  start_string = if opt[:time_filter][0].nil?
152
152
  "#{item.date.strftime('%Y-%m-%d')} 00:00"
153
153
  else
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'
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.45
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-22 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