doing 1.0.91 → 2.0.3.pre
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/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 +15 -699
 - data/Rakefile +79 -0
 - data/_config.yml +1 -0
 - data/bin/doing +1012 -486
 - 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 +1831 -2358
 - data/lib/doing/wwidfile.rb +117 -0
 - data/lib/doing.rb +44 -4
 - 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 -191
 
| 
         @@ -0,0 +1,208 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Doing
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Plugin handling
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Plugins
         
     | 
| 
      
 6 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def user_home
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @user_home ||= Util.user_home
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def plugins
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @plugins ||= {
         
     | 
| 
      
 13 
     | 
    
         
            +
                      import: {},
         
     | 
| 
      
 14 
     | 
    
         
            +
                      export: {}
         
     | 
| 
      
 15 
     | 
    
         
            +
                    }
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # Load plugins from plugins folder
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def load_plugins(add_dir = nil)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    plugins_path(add_dir).each do |plugin_search_path|
         
     | 
| 
      
 23 
     | 
    
         
            +
                      Dir.glob(File.join(plugin_search_path, '**', '*.rb')).sort.each do |plugin|
         
     | 
| 
      
 24 
     | 
    
         
            +
                        require plugin
         
     | 
| 
      
 25 
     | 
    
         
            +
                      end
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    plugins
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # Public: Setup the plugin search path
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # Returns an Array of plugin search paths
         
     | 
| 
      
 34 
     | 
    
         
            +
                  def plugins_path(add_dir = nil)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    paths = Array(File.join(File.dirname(__FILE__), 'plugins'))
         
     | 
| 
      
 36 
     | 
    
         
            +
                    paths << File.join(add_dir) if add_dir
         
     | 
| 
      
 37 
     | 
    
         
            +
                    paths.map { |d| File.expand_path(d) }
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # Register a plugin
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # param: +[String|Array]+ title  The name of the plugin (can be an array of names)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # param: +type+ The plugin type (:import, :export)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  #
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # param: +klass+ The class responding to :render or :import
         
     | 
| 
      
 48 
     | 
    
         
            +
                  #
         
     | 
| 
      
 49 
     | 
    
         
            +
                  #
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # returns: Success boolean
         
     | 
| 
      
 51 
     | 
    
         
            +
                  #
         
     | 
| 
      
 52 
     | 
    
         
            +
                  def register(title, type, klass)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    type = validate_plugin(title, type, klass)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    return unless type
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    if title.is_a?(Array)
         
     | 
| 
      
 57 
     | 
    
         
            +
                      title.each { |t| register(t, type, klass) }
         
     | 
| 
      
 58 
     | 
    
         
            +
                      return
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    settings = if klass.respond_to? :settings
         
     | 
| 
      
 62 
     | 
    
         
            +
                                 klass.settings
         
     | 
| 
      
 63 
     | 
    
         
            +
                               else
         
     | 
| 
      
 64 
     | 
    
         
            +
                                 { trigger: title.normalize_trigger, config: {} }
         
     | 
| 
      
 65 
     | 
    
         
            +
                               end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    plugins[type] ||= {}
         
     | 
| 
      
 68 
     | 
    
         
            +
                    plugins[type][title] = {
         
     | 
| 
      
 69 
     | 
    
         
            +
                      trigger: settings[:trigger].normalize_trigger || title.normalize_trigger,
         
     | 
| 
      
 70 
     | 
    
         
            +
                      class: klass,
         
     | 
| 
      
 71 
     | 
    
         
            +
                      templates: settings[:templates] || nil,
         
     | 
| 
      
 72 
     | 
    
         
            +
                      config: settings[:config] || {}
         
     | 
| 
      
 73 
     | 
    
         
            +
                    }
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    return unless ENV['DOING_PLUGIN_DEBUG']
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    Doing.logger.debug('Plugin Manager:', "Registered #{type} plugin \"#{title}\"")
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  def validate_plugin(title, type, klass)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    type = valid_type(type)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    if type == :import && !klass.respond_to?(:import)
         
     | 
| 
      
 83 
     | 
    
         
            +
                      raise Errors::PluginUncallable.new('Import plugins must respond to :import', type: type, plugin: title)
         
     | 
| 
      
 84 
     | 
    
         
            +
                    end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                    if type == :export && !klass.respond_to?(:render)
         
     | 
| 
      
 87 
     | 
    
         
            +
                      raise Errors::PluginUncallable.new('Export plugins must respond to :render', type: type, plugin: title)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                    type
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  def valid_type(type, default: nil)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    type ||= default
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                    t = type.to_s
         
     | 
| 
      
 97 
     | 
    
         
            +
                    type = case t
         
     | 
| 
      
 98 
     | 
    
         
            +
                           when /^i(m(p(o(r(t)?)?)?)?)?$/
         
     | 
| 
      
 99 
     | 
    
         
            +
                             :import
         
     | 
| 
      
 100 
     | 
    
         
            +
                           when /^e(x(p(o(r(t)?)?)?)?)?$/
         
     | 
| 
      
 101 
     | 
    
         
            +
                             :export
         
     | 
| 
      
 102 
     | 
    
         
            +
                           else
         
     | 
| 
      
 103 
     | 
    
         
            +
                             raise Errors::InvalidPluginType, 'Invalid plugin type'
         
     | 
| 
      
 104 
     | 
    
         
            +
                           end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                    type.to_sym
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 110 
     | 
    
         
            +
                  ## @brief      List available plugins to stdout
         
     | 
| 
      
 111 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 112 
     | 
    
         
            +
                  ## @param      options  { type, separator }
         
     | 
| 
      
 113 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 114 
     | 
    
         
            +
                  def list_plugins(options = {})
         
     | 
| 
      
 115 
     | 
    
         
            +
                    separator = options[:column] ? "\n" : "\t"
         
     | 
| 
      
 116 
     | 
    
         
            +
                    type = options[:type].nil? || options[:type] =~ /all/i ? 'all' : [valid_type(options[:type])]
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                    case type
         
     | 
| 
      
 119 
     | 
    
         
            +
                    when :import
         
     | 
| 
      
 120 
     | 
    
         
            +
                      puts plugin_names(type: :import, separator: separator)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    when :export
         
     | 
| 
      
 122 
     | 
    
         
            +
                      puts plugin_names(type: :export, separator: separator)
         
     | 
| 
      
 123 
     | 
    
         
            +
                    else
         
     | 
| 
      
 124 
     | 
    
         
            +
                      print 'Import plugins: '
         
     | 
| 
      
 125 
     | 
    
         
            +
                      puts plugin_names(type: :import, separator: ', ')
         
     | 
| 
      
 126 
     | 
    
         
            +
                      print 'Export plugins: '
         
     | 
| 
      
 127 
     | 
    
         
            +
                      puts plugin_names(type: :export, separator: ', ')
         
     | 
| 
      
 128 
     | 
    
         
            +
                    end
         
     | 
| 
      
 129 
     | 
    
         
            +
                  end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 132 
     | 
    
         
            +
                  ## @brief      Return array of available plugin names
         
     | 
| 
      
 133 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 134 
     | 
    
         
            +
                  ## @param      type  Plugin type (:import, :export)
         
     | 
| 
      
 135 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 136 
     | 
    
         
            +
                  ## @returns    [Array<String>] plugin names
         
     | 
| 
      
 137 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 138 
     | 
    
         
            +
                  def available_plugins(type: :export)
         
     | 
| 
      
 139 
     | 
    
         
            +
                    type = valid_type(type)
         
     | 
| 
      
 140 
     | 
    
         
            +
                    plugins[type].keys.sort
         
     | 
| 
      
 141 
     | 
    
         
            +
                  end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 144 
     | 
    
         
            +
                  ## @brief      Return string version of plugin names
         
     | 
| 
      
 145 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 146 
     | 
    
         
            +
                  ## @param      type       Plugin type (:import, :export)
         
     | 
| 
      
 147 
     | 
    
         
            +
                  ## @param      separator  The separator to join names with
         
     | 
| 
      
 148 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 149 
     | 
    
         
            +
                  ## @return     [String]   Plugin names
         
     | 
| 
      
 150 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 151 
     | 
    
         
            +
                  def plugin_names(type: :export, separator: '|')
         
     | 
| 
      
 152 
     | 
    
         
            +
                    type = valid_type(type)
         
     | 
| 
      
 153 
     | 
    
         
            +
                    available_plugins(type: type).join(separator)
         
     | 
| 
      
 154 
     | 
    
         
            +
                  end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 157 
     | 
    
         
            +
                  ## @brief      Return a regular expression of all
         
     | 
| 
      
 158 
     | 
    
         
            +
                  ##             plugin triggers for type
         
     | 
| 
      
 159 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 160 
     | 
    
         
            +
                  ## @param      type  The type :import or :export
         
     | 
| 
      
 161 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 162 
     | 
    
         
            +
                  def plugin_regex(type: :export)
         
     | 
| 
      
 163 
     | 
    
         
            +
                    type = valid_type(type)
         
     | 
| 
      
 164 
     | 
    
         
            +
                    pattern = []
         
     | 
| 
      
 165 
     | 
    
         
            +
                    plugins[type].each do |_, options|
         
     | 
| 
      
 166 
     | 
    
         
            +
                      pattern << options[:trigger].normalize_trigger
         
     | 
| 
      
 167 
     | 
    
         
            +
                    end
         
     | 
| 
      
 168 
     | 
    
         
            +
                    Regexp.new("^(?:#{pattern.join('|')})$", true)
         
     | 
| 
      
 169 
     | 
    
         
            +
                  end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                  def plugin_templates(type: :export)
         
     | 
| 
      
 172 
     | 
    
         
            +
                    type = valid_type(type)
         
     | 
| 
      
 173 
     | 
    
         
            +
                    templates = []
         
     | 
| 
      
 174 
     | 
    
         
            +
                    plugs = plugins[type].clone
         
     | 
| 
      
 175 
     | 
    
         
            +
                    plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options|
         
     | 
| 
      
 176 
     | 
    
         
            +
                      options[:templates].each do |t|
         
     | 
| 
      
 177 
     | 
    
         
            +
                        templates << t[:name]
         
     | 
| 
      
 178 
     | 
    
         
            +
                      end
         
     | 
| 
      
 179 
     | 
    
         
            +
                    end
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                    templates
         
     | 
| 
      
 182 
     | 
    
         
            +
                  end
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                  def template_regex(type: :export)
         
     | 
| 
      
 185 
     | 
    
         
            +
                    type = valid_type(type)
         
     | 
| 
      
 186 
     | 
    
         
            +
                    pattern = []
         
     | 
| 
      
 187 
     | 
    
         
            +
                    plugs = plugins[type].clone
         
     | 
| 
      
 188 
     | 
    
         
            +
                    plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options|
         
     | 
| 
      
 189 
     | 
    
         
            +
                      options[:templates].each do |t|
         
     | 
| 
      
 190 
     | 
    
         
            +
                        pattern << t[:trigger].normalize_trigger
         
     | 
| 
      
 191 
     | 
    
         
            +
                      end
         
     | 
| 
      
 192 
     | 
    
         
            +
                    end
         
     | 
| 
      
 193 
     | 
    
         
            +
                    Regexp.new("^(?:#{pattern.join('|')})$", true)
         
     | 
| 
      
 194 
     | 
    
         
            +
                  end
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                  def template_for_trigger(trigger, type: :export)
         
     | 
| 
      
 197 
     | 
    
         
            +
                    type = valid_type(type)
         
     | 
| 
      
 198 
     | 
    
         
            +
                    plugs = plugins[type].clone
         
     | 
| 
      
 199 
     | 
    
         
            +
                    plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options|
         
     | 
| 
      
 200 
     | 
    
         
            +
                      options[:templates].each do |t|
         
     | 
| 
      
 201 
     | 
    
         
            +
                        return options[:class].template(trigger) if trigger =~ /^(?:#{t[:trigger].normalize_trigger})$/
         
     | 
| 
      
 202 
     | 
    
         
            +
                      end
         
     | 
| 
      
 203 
     | 
    
         
            +
                    end
         
     | 
| 
      
 204 
     | 
    
         
            +
                    raise Errors::InvalidArgument, "No template type matched \"#{trigger}\""
         
     | 
| 
      
 205 
     | 
    
         
            +
                  end
         
     | 
| 
      
 206 
     | 
    
         
            +
                end
         
     | 
| 
      
 207 
     | 
    
         
            +
              end
         
     | 
| 
      
 208 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,48 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # title: CSV Export
         
     | 
| 
      
 4 
     | 
    
         
            +
            # description: Export CSV formatted data with header row
         
     | 
| 
      
 5 
     | 
    
         
            +
            # author: Brett Terpstra
         
     | 
| 
      
 6 
     | 
    
         
            +
            # url: https://brettterpstra.com
         
     | 
| 
      
 7 
     | 
    
         
            +
            module Doing
         
     | 
| 
      
 8 
     | 
    
         
            +
              ##
         
     | 
| 
      
 9 
     | 
    
         
            +
              ## @brief      CSV Export
         
     | 
| 
      
 10 
     | 
    
         
            +
              ##
         
     | 
| 
      
 11 
     | 
    
         
            +
              class CSVExport
         
     | 
| 
      
 12 
     | 
    
         
            +
                include Doing::Util
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def self.settings
         
     | 
| 
      
 15 
     | 
    
         
            +
                  {
         
     | 
| 
      
 16 
     | 
    
         
            +
                    trigger: 'csv'
         
     | 
| 
      
 17 
     | 
    
         
            +
                  }
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def self.render(wwid, items, variables: {})
         
     | 
| 
      
 21 
     | 
    
         
            +
                  return if items.nil?
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  opt = variables[:options]
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  output = [CSV.generate_line(%w[start end title note timer section])]
         
     | 
| 
      
 26 
     | 
    
         
            +
                  items.each do |i|
         
     | 
| 
      
 27 
     | 
    
         
            +
                    note = format_note(i.note)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end_date = i.end_date
         
     | 
| 
      
 29 
     | 
    
         
            +
                    interval = end_date && opt[:times] ? wwid.get_interval(i, formatted: false) : 0
         
     | 
| 
      
 30 
     | 
    
         
            +
                    output.push(CSV.generate_line([i.date, end_date, i.title, note, interval, i.section]))
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  Doing.logger.debug('CSV Export:', "#{items.count} items output to CSV")
         
     | 
| 
      
 33 
     | 
    
         
            +
                  output.join('')
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def self.format_note(note)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  out = ''
         
     | 
| 
      
 38 
     | 
    
         
            +
                  if note
         
     | 
| 
      
 39 
     | 
    
         
            +
                    arr = note.map(&:strip).delete_if { |e| e =~ /^\s*$/ }
         
     | 
| 
      
 40 
     | 
    
         
            +
                    out = arr.join("\n") unless arr.empty?
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  out
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                Doing::Plugins.register 'csv', :export, self
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,83 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # title: HTML Export
         
     | 
| 
      
 4 
     | 
    
         
            +
            # description: Export styled HTML view of data
         
     | 
| 
      
 5 
     | 
    
         
            +
            # author: Brett Terpstra
         
     | 
| 
      
 6 
     | 
    
         
            +
            # url: https://brettterpstra.com
         
     | 
| 
      
 7 
     | 
    
         
            +
            module Doing
         
     | 
| 
      
 8 
     | 
    
         
            +
              class HTMLExport
         
     | 
| 
      
 9 
     | 
    
         
            +
                include Doing::Util
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def self.settings
         
     | 
| 
      
 12 
     | 
    
         
            +
                  {
         
     | 
| 
      
 13 
     | 
    
         
            +
                    trigger: 'html?|web(?:page)?',
         
     | 
| 
      
 14 
     | 
    
         
            +
                    templates: [
         
     | 
| 
      
 15 
     | 
    
         
            +
                      { name: 'haml', trigger: 'h[ta]ml?|web' },
         
     | 
| 
      
 16 
     | 
    
         
            +
                      { name: 'css', trigger: 'css|styl(?:e|us)' }
         
     | 
| 
      
 17 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 18 
     | 
    
         
            +
                  }
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def self.template(trigger)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  if trigger =~ /^(css|sty)/
         
     | 
| 
      
 23 
     | 
    
         
            +
                    IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing.css'))
         
     | 
| 
      
 24 
     | 
    
         
            +
                  else
         
     | 
| 
      
 25 
     | 
    
         
            +
                    IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing.haml'))
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def self.render(wwid, items, variables: {})
         
     | 
| 
      
 30 
     | 
    
         
            +
                  return if items.nil?
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  opt = variables[:options]
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  items_out = []
         
     | 
| 
      
 35 
     | 
    
         
            +
                  items.each do |i|
         
     | 
| 
      
 36 
     | 
    
         
            +
                    # if i.has_key?('note')
         
     | 
| 
      
 37 
     | 
    
         
            +
                    #   note = '<span class="note">' + i.note.map{|n| n.strip }.join('<br>') + '</span>'
         
     | 
| 
      
 38 
     | 
    
         
            +
                    # else
         
     | 
| 
      
 39 
     | 
    
         
            +
                    #   note = ''
         
     | 
| 
      
 40 
     | 
    
         
            +
                    # end
         
     | 
| 
      
 41 
     | 
    
         
            +
                    if String.method_defined? :force_encoding
         
     | 
| 
      
 42 
     | 
    
         
            +
                      title = i.title.force_encoding('utf-8').link_urls
         
     | 
| 
      
 43 
     | 
    
         
            +
                      note = i.note.map { |line| line.force_encoding('utf-8').strip.link_urls } if i.note
         
     | 
| 
      
 44 
     | 
    
         
            +
                    else
         
     | 
| 
      
 45 
     | 
    
         
            +
                      title = i.title.link_urls
         
     | 
| 
      
 46 
     | 
    
         
            +
                      note = i.note.map { |line| line.strip.link_urls } if i.note
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    interval = wwid.get_interval(i) if i.title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
         
     | 
| 
      
 50 
     | 
    
         
            +
                    interval ||= false
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    items_out << {
         
     | 
| 
      
 53 
     | 
    
         
            +
                      date: i.date.strftime('%a %-I:%M%p'),
         
     | 
| 
      
 54 
     | 
    
         
            +
                      title: title.gsub(/(@[^ (]+(\(.*?\))?)/im, '<span class="tag">\1</span>').strip, #+ " #{note}"
         
     | 
| 
      
 55 
     | 
    
         
            +
                      note: note,
         
     | 
| 
      
 56 
     | 
    
         
            +
                      time: interval,
         
     | 
| 
      
 57 
     | 
    
         
            +
                      section: i.section
         
     | 
| 
      
 58 
     | 
    
         
            +
                    }
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  template = if wwid.config['export_templates']['haml'] && File.exist?(File.expand_path(wwid.config['export_templates']['haml']))
         
     | 
| 
      
 62 
     | 
    
         
            +
                               IO.read(File.expand_path(wwid.config['export_templates']['haml']))
         
     | 
| 
      
 63 
     | 
    
         
            +
                             else
         
     | 
| 
      
 64 
     | 
    
         
            +
                               self.template('html')
         
     | 
| 
      
 65 
     | 
    
         
            +
                             end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  style = if wwid.config['export_templates']['css'] && File.exist?(File.expand_path(wwid.config['export_templates']['css']))
         
     | 
| 
      
 68 
     | 
    
         
            +
                            IO.read(File.expand_path(wwid.config['export_templates']['css']))
         
     | 
| 
      
 69 
     | 
    
         
            +
                          else
         
     | 
| 
      
 70 
     | 
    
         
            +
                            self.template('css')
         
     | 
| 
      
 71 
     | 
    
         
            +
                          end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
         
     | 
| 
      
 74 
     | 
    
         
            +
                  engine = Haml::Engine.new(template)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  Doing.logger.debug('HTML Export:', "#{items_out.count} items output to HTML")
         
     | 
| 
      
 76 
     | 
    
         
            +
                  @out = engine.render(Object.new,
         
     | 
| 
      
 77 
     | 
    
         
            +
                                     { :@items => items_out, :@page_title => variables[:page_title], :@style => style, :@totals => totals })
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                Doing::Plugins.register 'html', :export, self
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
            end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,140 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # title: JSON Export
         
     | 
| 
      
 4 
     | 
    
         
            +
            # description: Export JSON-formatted data, including entry durations and tag totals
         
     | 
| 
      
 5 
     | 
    
         
            +
            # author: Brett Terpstra
         
     | 
| 
      
 6 
     | 
    
         
            +
            # url: https://brettterpstra.com
         
     | 
| 
      
 7 
     | 
    
         
            +
            module Doing
         
     | 
| 
      
 8 
     | 
    
         
            +
              class JSONExport
         
     | 
| 
      
 9 
     | 
    
         
            +
                include Doing::Util
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def self.settings
         
     | 
| 
      
 12 
     | 
    
         
            +
                  {
         
     | 
| 
      
 13 
     | 
    
         
            +
                    trigger: 'json|time(?:line)?'
         
     | 
| 
      
 14 
     | 
    
         
            +
                  }
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def self.render(wwid, items, variables: {})
         
     | 
| 
      
 18 
     | 
    
         
            +
                  return if items.nil?
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  opt = variables[:options]
         
     | 
| 
      
 21 
     | 
    
         
            +
                  opt[:output] =  case opt[:output]
         
     | 
| 
      
 22 
     | 
    
         
            +
                                  when /^t/
         
     | 
| 
      
 23 
     | 
    
         
            +
                                    'timeline'
         
     | 
| 
      
 24 
     | 
    
         
            +
                                  else
         
     | 
| 
      
 25 
     | 
    
         
            +
                                    'json'
         
     | 
| 
      
 26 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  items_out = []
         
     | 
| 
      
 28 
     | 
    
         
            +
                  last_date = items[-1].date + (60 * 60 * 24)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  max = last_date.strftime('%F')
         
     | 
| 
      
 30 
     | 
    
         
            +
                  min = items[0].date.strftime('%F')
         
     | 
| 
      
 31 
     | 
    
         
            +
                  items.each_with_index do |i, index|
         
     | 
| 
      
 32 
     | 
    
         
            +
                    if String.method_defined? :force_encoding
         
     | 
| 
      
 33 
     | 
    
         
            +
                      title = i.title.force_encoding('utf-8')
         
     | 
| 
      
 34 
     | 
    
         
            +
                      note = i.note.map { |line| line.force_encoding('utf-8').strip } if i.note
         
     | 
| 
      
 35 
     | 
    
         
            +
                    else
         
     | 
| 
      
 36 
     | 
    
         
            +
                      title = i.title
         
     | 
| 
      
 37 
     | 
    
         
            +
                      note = i.note.map { |line| line.strip } if i.note
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    end_date = i.end_date || ''
         
     | 
| 
      
 41 
     | 
    
         
            +
                    interval = wwid.get_interval(i, formatted: false) || 0
         
     | 
| 
      
 42 
     | 
    
         
            +
                    note ||= ''
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    tags = []
         
     | 
| 
      
 45 
     | 
    
         
            +
                    attributes = {}
         
     | 
| 
      
 46 
     | 
    
         
            +
                    skip_tags = %w[meanwhile done cancelled flagged]
         
     | 
| 
      
 47 
     | 
    
         
            +
                    i.title.scan(/@([^(\s]+)(?:\((.*?)\))?/).each do |tag|
         
     | 
| 
      
 48 
     | 
    
         
            +
                      tags.push(tag[0]) unless skip_tags.include?(tag[0])
         
     | 
| 
      
 49 
     | 
    
         
            +
                      attributes[tag[0]] = tag[1] if tag[1]
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    if opt[:output] == 'json'
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                      i = {
         
     | 
| 
      
 55 
     | 
    
         
            +
                        date: i.date,
         
     | 
| 
      
 56 
     | 
    
         
            +
                        end_date: end_date,
         
     | 
| 
      
 57 
     | 
    
         
            +
                        title: title.strip, #+ " #{note}"
         
     | 
| 
      
 58 
     | 
    
         
            +
                        note: note.instance_of?(Array) ? note.to_s : note,
         
     | 
| 
      
 59 
     | 
    
         
            +
                        time: '%02d:%02d:%02d' % wwid.fmt_time(interval),
         
     | 
| 
      
 60 
     | 
    
         
            +
                        tags: tags
         
     | 
| 
      
 61 
     | 
    
         
            +
                      }
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                      attributes.each { |attr, val| i[attr.to_sym] = val }
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                      items_out << i
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    elsif opt[:output] == 'timeline'
         
     | 
| 
      
 68 
     | 
    
         
            +
                      new_item = {
         
     | 
| 
      
 69 
     | 
    
         
            +
                        'id' => index + 1,
         
     | 
| 
      
 70 
     | 
    
         
            +
                        'content' => title.strip, #+ " #{note}"
         
     | 
| 
      
 71 
     | 
    
         
            +
                        'title' => title.strip + " (#{'%02d:%02d:%02d' % wwid.fmt_time(interval)})",
         
     | 
| 
      
 72 
     | 
    
         
            +
                        'start' => i.date.strftime('%F %T'),
         
     | 
| 
      
 73 
     | 
    
         
            +
                        'type' => 'box',
         
     | 
| 
      
 74 
     | 
    
         
            +
                        'style' => 'color:#4c566b;background-color:#d8dee9;'
         
     | 
| 
      
 75 
     | 
    
         
            +
                      }
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                      if interval && interval.to_i > 0
         
     | 
| 
      
 79 
     | 
    
         
            +
                        new_item['end'] = end_date.strftime('%F %T')
         
     | 
| 
      
 80 
     | 
    
         
            +
                        if interval.to_i > 3600
         
     | 
| 
      
 81 
     | 
    
         
            +
                          new_item['type'] = 'range'
         
     | 
| 
      
 82 
     | 
    
         
            +
                          new_item['style'] = 'color:white;background-color:#a2bf8a;'
         
     | 
| 
      
 83 
     | 
    
         
            +
                        end
         
     | 
| 
      
 84 
     | 
    
         
            +
                      end
         
     | 
| 
      
 85 
     | 
    
         
            +
                      new_item['style'] = 'color:white;background-color:#f7921e;' if i.tags?(wwid.config['marker_tag'])
         
     | 
| 
      
 86 
     | 
    
         
            +
                      items_out.push(new_item)
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
                  if opt[:output] == 'json'
         
     | 
| 
      
 90 
     | 
    
         
            +
                    Doing.logger.debug('JSON Export:', "#{items_out.count} items output to JSON")
         
     | 
| 
      
 91 
     | 
    
         
            +
                    JSON.pretty_generate({
         
     | 
| 
      
 92 
     | 
    
         
            +
                      'section' => variables[:page_title],
         
     | 
| 
      
 93 
     | 
    
         
            +
                      'items' => items_out,
         
     | 
| 
      
 94 
     | 
    
         
            +
                      'timers' => wwid.tag_times(format: :json, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order])
         
     | 
| 
      
 95 
     | 
    
         
            +
                    })
         
     | 
| 
      
 96 
     | 
    
         
            +
                  elsif opt[:output] == 'timeline'
         
     | 
| 
      
 97 
     | 
    
         
            +
                    template = <<~EOTEMPLATE
         
     | 
| 
      
 98 
     | 
    
         
            +
                                <!doctype html>
         
     | 
| 
      
 99 
     | 
    
         
            +
                                <html>
         
     | 
| 
      
 100 
     | 
    
         
            +
                                <head>
         
     | 
| 
      
 101 
     | 
    
         
            +
                                  <link href="https://unpkg.com/vis-timeline@7.4.9/dist/vis-timeline-graph2d.min.css" rel="stylesheet" type="text/css" />
         
     | 
| 
      
 102 
     | 
    
         
            +
                                  <script src="https://unpkg.com/vis-timeline@7.4.9/dist/vis-timeline-graph2d.min.js"></script>
         
     | 
| 
      
 103 
     | 
    
         
            +
                                </head>
         
     | 
| 
      
 104 
     | 
    
         
            +
                                <body>
         
     | 
| 
      
 105 
     | 
    
         
            +
                                  <div id="mytimeline"></div>
         
     | 
| 
      
 106 
     | 
    
         
            +
                      #{'          '}
         
     | 
| 
      
 107 
     | 
    
         
            +
                                  <script type="text/javascript">
         
     | 
| 
      
 108 
     | 
    
         
            +
                                    // DOM element where the Timeline will be attached
         
     | 
| 
      
 109 
     | 
    
         
            +
                                    var container = document.getElementById('mytimeline');
         
     | 
| 
      
 110 
     | 
    
         
            +
                      #{'          '}
         
     | 
| 
      
 111 
     | 
    
         
            +
                                    // Create a DataSet with data (enables two way data binding)
         
     | 
| 
      
 112 
     | 
    
         
            +
                                    var data = new vis.DataSet(#{items_out.to_json});
         
     | 
| 
      
 113 
     | 
    
         
            +
                      #{'          '}
         
     | 
| 
      
 114 
     | 
    
         
            +
                                    // Configuration for the Timeline
         
     | 
| 
      
 115 
     | 
    
         
            +
                                    var options = {
         
     | 
| 
      
 116 
     | 
    
         
            +
                                      width: '100%',
         
     | 
| 
      
 117 
     | 
    
         
            +
                                      height: '800px',
         
     | 
| 
      
 118 
     | 
    
         
            +
                                      margin: {
         
     | 
| 
      
 119 
     | 
    
         
            +
                                        item: 20
         
     | 
| 
      
 120 
     | 
    
         
            +
                                      },
         
     | 
| 
      
 121 
     | 
    
         
            +
                                      stack: true,
         
     | 
| 
      
 122 
     | 
    
         
            +
                                      min: '#{min}',
         
     | 
| 
      
 123 
     | 
    
         
            +
                                      max: '#{max}'
         
     | 
| 
      
 124 
     | 
    
         
            +
                                    };
         
     | 
| 
      
 125 
     | 
    
         
            +
                      #{'          '}
         
     | 
| 
      
 126 
     | 
    
         
            +
                                    // Create a Timeline
         
     | 
| 
      
 127 
     | 
    
         
            +
                                    var timeline = new vis.Timeline(container, data, options);
         
     | 
| 
      
 128 
     | 
    
         
            +
                                  </script>
         
     | 
| 
      
 129 
     | 
    
         
            +
                                </body>
         
     | 
| 
      
 130 
     | 
    
         
            +
                                </html>
         
     | 
| 
      
 131 
     | 
    
         
            +
                    EOTEMPLATE
         
     | 
| 
      
 132 
     | 
    
         
            +
                    Doing.logger.debug('Timeline Export:', "#{items_out.count} items output to Timeline")
         
     | 
| 
      
 133 
     | 
    
         
            +
                    template
         
     | 
| 
      
 134 
     | 
    
         
            +
                  end
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                Doing::Plugins.register 'timeline', :export, self
         
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
            end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,85 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # title: Markdown Export
         
     | 
| 
      
 4 
     | 
    
         
            +
            # description: Export GFM-style task list
         
     | 
| 
      
 5 
     | 
    
         
            +
            # author: Brett Terpstra
         
     | 
| 
      
 6 
     | 
    
         
            +
            # url: https://brettterpstra.com
         
     | 
| 
      
 7 
     | 
    
         
            +
            module Doing
         
     | 
| 
      
 8 
     | 
    
         
            +
              class MarkdownRenderer
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_accessor :items, :page_title, :totals
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(page_title, items, totals)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @page_title = page_title
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @items = items
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @totals = totals
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def get_binding
         
     | 
| 
      
 18 
     | 
    
         
            +
                  binding()
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              class MarkdownExport
         
     | 
| 
      
 23 
     | 
    
         
            +
                include Doing::Util
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def self.settings
         
     | 
| 
      
 26 
     | 
    
         
            +
                  {
         
     | 
| 
      
 27 
     | 
    
         
            +
                    trigger: 'markdown|mk?d|gfm',
         
     | 
| 
      
 28 
     | 
    
         
            +
                    templates: [{ name: 'markdown', trigger: 'mk?d|markdown' }]
         
     | 
| 
      
 29 
     | 
    
         
            +
                  }
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def self.template(_trigger)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing-markdown.erb'))
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def self.render(wwid, items, variables: {})
         
     | 
| 
      
 37 
     | 
    
         
            +
                  return if items.nil?
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  opt = variables[:options]
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  all_items = []
         
     | 
| 
      
 42 
     | 
    
         
            +
                  items.each do |i|
         
     | 
| 
      
 43 
     | 
    
         
            +
                    if String.method_defined? :force_encoding
         
     | 
| 
      
 44 
     | 
    
         
            +
                      title = i.title.force_encoding('utf-8').link_urls({format: :markdown})
         
     | 
| 
      
 45 
     | 
    
         
            +
                      note = i.note.map { |line| line.force_encoding('utf-8').strip.link_urls({format: :markdown}) } if i.note
         
     | 
| 
      
 46 
     | 
    
         
            +
                    else
         
     | 
| 
      
 47 
     | 
    
         
            +
                      title = i.title.link_urls({format: :markdown})
         
     | 
| 
      
 48 
     | 
    
         
            +
                      note = i.note.map { |line| line.strip.link_urls({format: :markdown}) } if i.note
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    title = "#{title} @project(#{i.section})" unless variables[:is_single]
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    interval = wwid.get_interval(i, record: true) if i.title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
         
     | 
| 
      
 54 
     | 
    
         
            +
                    interval ||= false
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    done = i.title =~ /(?<= |^)@done/ ? 'x' : ' '
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    all_items << {
         
     | 
| 
      
 59 
     | 
    
         
            +
                      date: i.date.strftime('%a %-I:%M%p'),
         
     | 
| 
      
 60 
     | 
    
         
            +
                      shortdate: i.date.relative_date,
         
     | 
| 
      
 61 
     | 
    
         
            +
                      done: done,
         
     | 
| 
      
 62 
     | 
    
         
            +
                      note: note,
         
     | 
| 
      
 63 
     | 
    
         
            +
                      section: i.section,
         
     | 
| 
      
 64 
     | 
    
         
            +
                      time: interval,
         
     | 
| 
      
 65 
     | 
    
         
            +
                      title: title.strip
         
     | 
| 
      
 66 
     | 
    
         
            +
                    }
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  template = if wwid.config['export_templates']['markdown'] && File.exist?(File.expand_path(wwid.config['export_templates']['markdown']))
         
     | 
| 
      
 70 
     | 
    
         
            +
                               IO.read(File.expand_path(wwid.config['export_templates']['markdown']))
         
     | 
| 
      
 71 
     | 
    
         
            +
                             else
         
     | 
| 
      
 72 
     | 
    
         
            +
                               self.template(nil)
         
     | 
| 
      
 73 
     | 
    
         
            +
                             end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  totals = opt[:totals] ? wwid.tag_times(format: :markdown, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  mdx = MarkdownRenderer.new(variables[:page_title], all_items, totals)
         
     | 
| 
      
 78 
     | 
    
         
            +
                  Doing.logger.debug('Markdown Export:', "#{all_items.count} items output to Markdown")
         
     | 
| 
      
 79 
     | 
    
         
            +
                  engine = ERB.new(template)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  @out = engine.result(mdx.get_binding)
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                Doing::Plugins.register 'markdown', :export, self
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # title: TaskPaper Export
         
     | 
| 
      
 4 
     | 
    
         
            +
            # description: Export TaskPaper-friendly data
         
     | 
| 
      
 5 
     | 
    
         
            +
            # author: Brett Terpstra
         
     | 
| 
      
 6 
     | 
    
         
            +
            # url: https://brettterpstra.com
         
     | 
| 
      
 7 
     | 
    
         
            +
            module Doing
         
     | 
| 
      
 8 
     | 
    
         
            +
              class TaskPaperExport
         
     | 
| 
      
 9 
     | 
    
         
            +
                include Doing::Util
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def self.settings
         
     | 
| 
      
 12 
     | 
    
         
            +
                  {
         
     | 
| 
      
 13 
     | 
    
         
            +
                    trigger: 'task(?:paper)?|tp'
         
     | 
| 
      
 14 
     | 
    
         
            +
                  }
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def self.render(wwid, items, variables: {})
         
     | 
| 
      
 18 
     | 
    
         
            +
                  return if items.nil?
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  options = variables[:options]
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  options[:highlight] = false
         
     | 
| 
      
 23 
     | 
    
         
            +
                  options[:wrap_width] = 0
         
     | 
| 
      
 24 
     | 
    
         
            +
                  options[:tags_color] = false
         
     | 
| 
      
 25 
     | 
    
         
            +
                  options[:output] = 'template'
         
     | 
| 
      
 26 
     | 
    
         
            +
                  options[:template] = '- %title @date(%date)%note'
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  Doing.logger.debug('TaskPaper Export:', "#{items.count} items output to TaskPaper format")
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @out = wwid.list_section(options)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                Doing::Plugins.register 'taskpaper', :export, self
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     |