journal-cli 1.0.22 → 1.0.24
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/CHANGELOG.md +17 -0
- data/Gemfile.lock +1 -1
- data/README.md +17 -2
- data/bin/journal +5 -1
- data/lib/journal-cli/checkin.rb +36 -11
- data/lib/journal-cli/question.rb +10 -2
- data/lib/journal-cli/section.rb +4 -3
- data/lib/journal-cli/string.rb +27 -0
- data/lib/journal-cli/version.rb +1 -1
- data/lib/journal-cli/weather.rb +10 -2
- data/lib/journal-cli.rb +3 -0
- data/src/_README.md +17 -2
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 110b0fe8610e39052c3f85c417739821f66103cd0457a23a03188a158062962f
         | 
| 4 | 
            +
              data.tar.gz: b56dfb4649456dd53e22aa86fece669f8d162fd6048537e06ba8c6aeb0db61c1
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 237a1faa0d7d4acfb0bb1d0c3775b46055529e432dbba25e38b723cd5cab39e68db7796d824c870739f8fa9b28f2c1d040df1d4fb84bd3e57581cc18ba931ea7
         | 
| 7 | 
            +
              data.tar.gz: cc8e68cf73df3bf13a71b2eb9126a136dd9f989a869c519dece7f733e1a89dfd5552626fc249bf7c6bc2ea95ee2a1a23836a6c8ed410202b1d4578b6be8d655e
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,20 @@ | |
| 1 | 
            +
            ### 1.0.24
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            2023-09-20 09:12
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ### 1.0.23
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            2023-09-20 08:30
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            #### NEW
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            - Question types weather.forecast and weather.current allow more specific weather entry types
         | 
| 12 | 
            +
            - Time-based conditions for questions and sections (`condition: before noon` or `condition: < 12pm`)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            #### FIXED
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            - Test for existence of dayone2 binary before attempting to write a Day One entry, provide error message
         | 
| 17 | 
            +
             | 
| 1 18 | 
             
            ### 1.0.22
         | 
| 2 19 |  | 
| 3 20 | 
             
            2023-09-18 10:23
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -52,6 +52,10 @@ This file contains a YAML definition of your journal. Each journal gets a top-le | |
| 52 52 |  | 
| 53 53 | 
             
            You can include weather data automatically by setting a question type to 'weather'. In order for this to work, you'll need to define `zip` and `weather_api` keys. `zip` is just your zip code, and `weather_api` is a key from WeatherAPI.com. Sign up [here](https://www.weatherapi.com/) for a free plan, and then visit the [profile page](https://www.weatherapi.com/my/) to see your API key at the top.
         | 
| 54 54 |  | 
| 55 | 
            +
            If a question type is set to `weather.forecast`, only the predicted condition, high, and low will be included in the JSON data for the question. A full printout of hourly temps will be included in the Markdown/Day One output.
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            If the question type is `weather.current`, only the current condition and temperature will be recorded to the JSON, and a string containing "[TEMP] and [CONDITION]" (e.g. "64 and Sunny") will be recorded to Markdown/Day One for the question.
         | 
| 58 | 
            +
             | 
| 55 59 | 
             
            ### Journal Configuration
         | 
| 56 60 |  | 
| 57 61 | 
             
            Edit the file at `~/.config/journal/journals.yaml` following this structure:
         | 
| @@ -96,9 +100,12 @@ journals: # required key | |
| 96 100 | 
             
                  - title: Weather # Title of the section (will create template sections in Day One)
         | 
| 97 101 | 
             
                    key: weather # the key to use in the structured data, will contain all of the answers
         | 
| 98 102 | 
             
                    questions: # required key
         | 
| 99 | 
            -
                      - prompt: Current  | 
| 103 | 
            +
                      - prompt: Current Weather
         | 
| 104 | 
            +
                        key: weather.current
         | 
| 105 | 
            +
                        type: weather.current
         | 
| 106 | 
            +
                      - prompt: Weather Forecast # The prompt shown on the command line, will also become a header in the journal entries (Markdown, Day One)
         | 
| 100 107 | 
             
                        key: weather.forecast # if a key contains a dot, it will create nested data, e.g. `{ 'weather': { 'forecast': data } }`
         | 
| 101 | 
            -
                        type: weather # Set this to weather for weather data
         | 
| 108 | 
            +
                        type: weather.forecast # Set this to weather for weather data
         | 
| 102 109 | 
             
                  - title: Health # New section
         | 
| 103 110 | 
             
                    key: health 
         | 
| 104 111 | 
             
                    questions:
         | 
| @@ -141,10 +148,18 @@ A question `type` can be one of: | |
| 141 148 | 
             
            - `text` or `string` will request a single-line string, submitted on return
         | 
| 142 149 | 
             
            - `multiline` for multiline strings (opens a readline editor, use ctrl-d to save)
         | 
| 143 150 | 
             
            - `weather` will just insert current weather data with no prompt
         | 
| 151 | 
            +
              * `weather.forecast` will insert just the forecast
         | 
| 152 | 
            +
              * `weather.current` will insert just the current temperature and condition
         | 
| 144 153 | 
             
            - `number` or `float` will request numeric input, stored as a float (decimal)
         | 
| 145 154 | 
             
            - `integer` will convert numeric input to the nearest integer
         | 
| 146 155 | 
             
            - `date` will request a natural language date which will be parsed into a date object
         | 
| 147 156 |  | 
| 157 | 
            +
            ### Conditional Questions
         | 
| 158 | 
            +
             | 
| 159 | 
            +
            You can have a question only show up based on conditions. Currently the only condition is time based. Just add a key called `condition` to the question definition, then include a natural language string like `before noon` or `after 3pm`. If the condition is matched, then the question will be displayed, otherwise it will be skipped and its data entry in the JSON will be null.
         | 
| 160 | 
            +
             | 
| 161 | 
            +
            Conditions can be applied to individual questions, or to entire sections, depending on where the `condition` key is placed.
         | 
| 162 | 
            +
             | 
| 148 163 | 
             
            ### Naming Keys
         | 
| 149 164 |  | 
| 150 165 | 
             
            If you want data stored in a nested object, you can set a question type to `dictionary` and set the prompt to `null` (or just leave the key out), but give it a key that will serve as the parent in the object. Then in the nested questions, give them a key in the dot format `[PARENT_KEY].[CHILD_KEY]`. Section keys automatically nest their questions, but if you want to go deeper, you could have a question with the key `health` and type `dictionary`, then have questions with keys like `health.rating` and `health.notes`. If the section key was `status`, the resulting dictionary would look like this in the JSON:
         | 
    
        data/bin/journal
    CHANGED
    
    | @@ -4,6 +4,8 @@ $LOAD_PATH.unshift File.join(__dir__, '..', 'lib') | |
| 4 4 | 
             
            require 'journal-cli'
         | 
| 5 5 | 
             
            require 'optparse'
         | 
| 6 6 |  | 
| 7 | 
            +
            trap('SIGINT') { exit! }
         | 
| 8 | 
            +
             | 
| 7 9 | 
             
            module Journal
         | 
| 8 10 | 
             
              class << self
         | 
| 9 11 | 
             
                def usage
         | 
| @@ -33,8 +35,10 @@ module Journal | |
| 33 35 | 
             
                           Time.now
         | 
| 34 36 | 
             
                         end
         | 
| 35 37 |  | 
| 38 | 
            +
                  Journal.date = date
         | 
| 39 | 
            +
             | 
| 36 40 | 
             
                  if Journal.config['journals'].key?(journal)
         | 
| 37 | 
            -
                    checkin = Journal::Checkin.new(journal | 
| 41 | 
            +
                    checkin = Journal::Checkin.new(journal)
         | 
| 38 42 | 
             
                    checkin.go
         | 
| 39 43 | 
             
                  else
         | 
| 40 44 | 
             
                    puts "Journal #{journal} not found"
         | 
    
        data/lib/journal-cli/checkin.rb
    CHANGED
    
    | @@ -3,10 +3,10 @@ module Journal | |
| 3 3 | 
             
              class Checkin
         | 
| 4 4 | 
             
                attr_reader :key, :date, :data, :config, :journal, :sections, :title, :output
         | 
| 5 5 |  | 
| 6 | 
            -
                def initialize(journal | 
| 6 | 
            +
                def initialize(journal)
         | 
| 7 7 | 
             
                  @key = journal
         | 
| 8 8 | 
             
                  @output = []
         | 
| 9 | 
            -
                  @date = date
         | 
| 9 | 
            +
                  @date = Journal.date
         | 
| 10 10 | 
             
                  @date.localtime
         | 
| 11 11 |  | 
| 12 12 | 
             
                  raise StandardError, "No journal with key #{@key} found" unless Journal.config['journals'].key? @key
         | 
| @@ -36,7 +36,7 @@ module Journal | |
| 36 36 | 
             
                end
         | 
| 37 37 |  | 
| 38 38 | 
             
                def hr
         | 
| 39 | 
            -
                  @output << "\n | 
| 39 | 
            +
                  @output << "\n* * * * * *\n"
         | 
| 40 40 | 
             
                end
         | 
| 41 41 |  | 
| 42 42 | 
             
                def go
         | 
| @@ -58,6 +58,11 @@ module Journal | |
| 58 58 | 
             
                end
         | 
| 59 59 |  | 
| 60 60 | 
             
                def save_day_one_entry
         | 
| 61 | 
            +
                  unless TTY::Which.exist?('dayone2')
         | 
| 62 | 
            +
                    Journal.notify('{br}Day One CLI not installed, no Day One entry created')
         | 
| 63 | 
            +
                    return
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                  @date.localtime
         | 
| 61 66 | 
             
                  cmd = ['dayone2']
         | 
| 62 67 | 
             
                  cmd << %(-j "#{@journal['journal']}") if @journal.key?('journal')
         | 
| 63 68 | 
             
                  cmd << %(-t #{@journal['tags'].join(' ')}) if @journal.key?('tags')
         | 
| @@ -128,11 +133,17 @@ module Journal | |
| 128 133 | 
             
                end
         | 
| 129 134 |  | 
| 130 135 | 
             
                def print_answer(prompt, type, key, data)
         | 
| 136 | 
            +
                  return if data.nil? || !data.key?(key)
         | 
| 137 | 
            +
             | 
| 131 138 | 
             
                  case type
         | 
| 132 139 | 
             
                  when /^(weather|forecast)/
         | 
| 133 140 | 
             
                    header prompt
         | 
| 134 | 
            -
                    @output <<  | 
| 135 | 
            -
             | 
| 141 | 
            +
                    @output << case type
         | 
| 142 | 
            +
                               when /current$/
         | 
| 143 | 
            +
                                 data[key].current
         | 
| 144 | 
            +
                               else
         | 
| 145 | 
            +
                                 data[key].to_markdown
         | 
| 146 | 
            +
                               end
         | 
| 136 147 | 
             
                  when /^(int|num)/
         | 
| 137 148 | 
             
                    @output << "#{prompt}: #{data[key]}  " unless data[key].nil?
         | 
| 138 149 | 
             
                  when /^date/
         | 
| @@ -158,7 +169,14 @@ module Journal | |
| 158 169 | 
             
                      v.localtime
         | 
| 159 170 | 
             
                      data[k] = v.strftime('%Y-%m-%d %H:%M')
         | 
| 160 171 | 
             
                    when /Weather/
         | 
| 161 | 
            -
                      data[k] =  | 
| 172 | 
            +
                      data[k] = case k
         | 
| 173 | 
            +
                                when /current$/
         | 
| 174 | 
            +
                                  v.current
         | 
| 175 | 
            +
                                when /forecast$/
         | 
| 176 | 
            +
                                  data[k] = v.forecast
         | 
| 177 | 
            +
                                else
         | 
| 178 | 
            +
                                  data[k] = v.to_s
         | 
| 179 | 
            +
                                end
         | 
| 162 180 | 
             
                    else
         | 
| 163 181 | 
             
                      data[k] = v
         | 
| 164 182 | 
             
                    end
         | 
| @@ -240,11 +258,18 @@ module Journal | |
| 240 258 | 
             
                        v.each do |key, value|
         | 
| 241 259 | 
             
                          result = case value.class.to_s
         | 
| 242 260 | 
             
                                   when /Weather/
         | 
| 243 | 
            -
                                      | 
| 244 | 
            -
                                        | 
| 245 | 
            -
             | 
| 246 | 
            -
             | 
| 247 | 
            -
             | 
| 261 | 
            +
                                     if key =~ /current$/
         | 
| 262 | 
            +
                                       {
         | 
| 263 | 
            +
                                         'temp' => value.data[:temp],
         | 
| 264 | 
            +
                                         'condition' => value.data[:current_condition]
         | 
| 265 | 
            +
                                       }
         | 
| 266 | 
            +
                                     else
         | 
| 267 | 
            +
                                       {
         | 
| 268 | 
            +
                                         'high' => value.data[:high],
         | 
| 269 | 
            +
                                         'low' => value.data[:low],
         | 
| 270 | 
            +
                                         'condition' => value.data[:condition]
         | 
| 271 | 
            +
                                       }
         | 
| 272 | 
            +
                                     end
         | 
| 248 273 | 
             
                                   else
         | 
| 249 274 | 
             
                                     value
         | 
| 250 275 | 
             
                                   end
         | 
    
        data/lib/journal-cli/question.rb
    CHANGED
    
    | @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            module Journal
         | 
| 4 4 | 
             
              # Individual question
         | 
| 5 5 | 
             
              class Question
         | 
| 6 | 
            -
                attr_reader :key, :type, :min, :max, :prompt, :secondary_prompt, :gum
         | 
| 6 | 
            +
                attr_reader :key, :type, :min, :max, :prompt, :secondary_prompt, :gum, :condition
         | 
| 7 7 |  | 
| 8 8 | 
             
                ##
         | 
| 9 9 | 
             
                ## Initializes the given question.
         | 
| @@ -20,6 +20,7 @@ module Journal | |
| 20 20 | 
             
                  @prompt = question['prompt'] || nil
         | 
| 21 21 | 
             
                  @secondary_prompt = question['secondary_prompt'] || nil
         | 
| 22 22 | 
             
                  @gum = TTY::Which.exist?('gum')
         | 
| 23 | 
            +
                  @condition = question.key?('condition') ? question['condition'].parse_condition : true
         | 
| 23 24 | 
             
                end
         | 
| 24 25 |  | 
| 25 26 | 
             
                ##
         | 
| @@ -27,9 +28,11 @@ module Journal | |
| 27 28 | 
             
                ##
         | 
| 28 29 | 
             
                ## @return     [Number, String] the response based on @type
         | 
| 29 30 | 
             
                ##
         | 
| 30 | 
            -
                def ask
         | 
| 31 | 
            +
                def ask(condition)
         | 
| 31 32 | 
             
                  return nil if @prompt.nil?
         | 
| 32 33 |  | 
| 34 | 
            +
                  return nil unless @condition && condition
         | 
| 35 | 
            +
             | 
| 33 36 | 
             
                  case @type
         | 
| 34 37 | 
             
                  when /^int/i
         | 
| 35 38 | 
             
                    read_number(integer: true)
         | 
| @@ -126,6 +129,7 @@ module Journal | |
| 126 129 | 
             
                ##
         | 
| 127 130 | 
             
                ##
         | 
| 128 131 | 
             
                def read_number_gum
         | 
| 132 | 
            +
                  trap('SIGINT') { exit! }
         | 
| 129 133 | 
             
                  res = `gum input --placeholder "#{@min}-#{@max}"`.strip
         | 
| 130 134 | 
             
                  return nil if res.strip.empty?
         | 
| 131 135 |  | 
| @@ -140,6 +144,7 @@ module Journal | |
| 140 144 | 
             
                ## @return     [Number] integer response
         | 
| 141 145 | 
             
                ##
         | 
| 142 146 | 
             
                def read_line_tty
         | 
| 147 | 
            +
                  trap('SIGINT') { exit! }
         | 
| 143 148 | 
             
                  reader = TTY::Reader.new
         | 
| 144 149 | 
             
                  res = reader.read_line('>> ')
         | 
| 145 150 | 
             
                  return nil if res.strip.empty?
         | 
| @@ -155,6 +160,7 @@ module Journal | |
| 155 160 | 
             
                ## @return     [Number] integer response
         | 
| 156 161 | 
             
                ##
         | 
| 157 162 | 
             
                def read_line_gum(prompt)
         | 
| 163 | 
            +
                  trap('SIGINT') { exit! }
         | 
| 158 164 | 
             
                  `gum input --placeholder "#{prompt} (blank to end answer)"`
         | 
| 159 165 | 
             
                end
         | 
| 160 166 |  | 
| @@ -164,6 +170,7 @@ module Journal | |
| 164 170 | 
             
                ## @return     [string] multiline input
         | 
| 165 171 | 
             
                ##
         | 
| 166 172 | 
             
                def read_mutliline_tty
         | 
| 173 | 
            +
                  trap('SIGINT') { exit! }
         | 
| 167 174 | 
             
                  reader = TTY::Reader.new
         | 
| 168 175 | 
             
                  res = reader.read_multiline
         | 
| 169 176 | 
             
                  res.join("\n")
         | 
| @@ -175,6 +182,7 @@ module Journal | |
| 175 182 | 
             
                ## @return     [string] multiline input
         | 
| 176 183 | 
             
                ##
         | 
| 177 184 | 
             
                def read_multiline_gum(prompt)
         | 
| 185 | 
            +
                  trap('SIGINT') { exit! }
         | 
| 178 186 | 
             
                  `gum write --placeholder "#{prompt}" --width 80 --char-limit 0`
         | 
| 179 187 | 
             
                end
         | 
| 180 188 | 
             
              end
         | 
    
        data/lib/journal-cli/section.rb
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Journal
         | 
| 4 4 | 
             
              class Section
         | 
| 5 | 
            -
                attr_accessor :key, :title, :questions, :answers
         | 
| 5 | 
            +
                attr_accessor :key, :title, :questions, :answers, :condition
         | 
| 6 6 |  | 
| 7 7 | 
             
                ##
         | 
| 8 8 | 
             
                ## Initializes the given section.
         | 
| @@ -15,6 +15,7 @@ module Journal | |
| 15 15 | 
             
                def initialize(section)
         | 
| 16 16 | 
             
                  @key = section['key']
         | 
| 17 17 | 
             
                  @title = section['title']
         | 
| 18 | 
            +
                  @condition = section.key?('condition') ? section['condition'].parse_condition : true
         | 
| 18 19 | 
             
                  @questions = section['questions'].map { |question| Question.new(question) }
         | 
| 19 20 | 
             
                  @questions.delete_if { |q| q.prompt.nil? }
         | 
| 20 21 | 
             
                  @answers = {}
         | 
| @@ -38,9 +39,9 @@ module Journal | |
| 38 39 | 
             
                        res = res[key]
         | 
| 39 40 | 
             
                      end
         | 
| 40 41 |  | 
| 41 | 
            -
                      res[keys.last] = question.ask
         | 
| 42 | 
            +
                      res[keys.last] = question.ask(@condition)
         | 
| 42 43 | 
             
                    else
         | 
| 43 | 
            -
                      @answers[question.key] = question.ask
         | 
| 44 | 
            +
                      @answers[question.key] = question.ask(@condition)
         | 
| 44 45 | 
             
                    end
         | 
| 45 46 | 
             
                  end
         | 
| 46 47 | 
             
                end
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # String helpers
         | 
| 4 | 
            +
            class ::String
         | 
| 5 | 
            +
              def parse_condition
         | 
| 6 | 
            +
                condition = dup
         | 
| 7 | 
            +
                time_rx = /(?<comp>[<>=]{1,2}|before|after) +(?<time>(?:noon|midnight|[0-9]+) *(?:am|pm)?)$/i
         | 
| 8 | 
            +
                return true unless condition =~ time_rx
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                now = Journal.date
         | 
| 11 | 
            +
                m = condition.match(time_rx)
         | 
| 12 | 
            +
                time = Chronic.parse(m['time'])
         | 
| 13 | 
            +
                Journal.notify("{br}Invalid time string in question (#{m['time']})", exit_code: 4) unless time
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                case m['comp']
         | 
| 16 | 
            +
                when /^<=$/
         | 
| 17 | 
            +
                  now <= time
         | 
| 18 | 
            +
                when /^(<|bef)/i
         | 
| 19 | 
            +
                  now < time
         | 
| 20 | 
            +
                when /^>=/
         | 
| 21 | 
            +
                  now >= time
         | 
| 22 | 
            +
                when /^(>|aft)/i
         | 
| 23 | 
            +
                  now > time
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
                # TODO: Other condition types
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
    
        data/lib/journal-cli/version.rb
    CHANGED
    
    
    
        data/lib/journal-cli/weather.rb
    CHANGED
    
    | @@ -54,6 +54,14 @@ module Journal | |
| 54 54 | 
             
                  }
         | 
| 55 55 | 
             
                end
         | 
| 56 56 |  | 
| 57 | 
            +
                def current
         | 
| 58 | 
            +
                  "#{@data[:temp]} and #{@data[:current_condition]}"
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def forecast
         | 
| 62 | 
            +
                  "#{@data[:condition]} #{@data[:high]}/#{@data[:low]}"
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 57 65 | 
             
                def to_s
         | 
| 58 66 | 
             
                  "#{@data[:temp].round} and #{@data[:current_condition]} (#{@data[:high].round}/#{@data[:low].round})"
         | 
| 59 67 | 
             
                end
         | 
| @@ -61,8 +69,8 @@ module Journal | |
| 61 69 | 
             
                def to_markdown
         | 
| 62 70 | 
             
                  output = []
         | 
| 63 71 |  | 
| 64 | 
            -
                  output << "Forecast for #{@data[:day]}: #{ | 
| 65 | 
            -
                  output << "Currently: #{ | 
| 72 | 
            +
                  output << "Forecast for #{@data[:day]}: #{forecast}  "
         | 
| 73 | 
            +
                  output << "Currently: #{current}"
         | 
| 66 74 | 
             
                  output << ''
         | 
| 67 75 |  | 
| 68 76 | 
             
                  # Hours
         | 
    
        data/lib/journal-cli.rb
    CHANGED
    
    | @@ -11,6 +11,7 @@ require 'tty-which' | |
| 11 11 | 
             
            require 'tty-reader'
         | 
| 12 12 | 
             
            require_relative 'journal-cli/version'
         | 
| 13 13 | 
             
            require_relative 'journal-cli/color'
         | 
| 14 | 
            +
            require_relative 'journal-cli/string'
         | 
| 14 15 | 
             
            require_relative 'journal-cli/data'
         | 
| 15 16 | 
             
            require_relative 'journal-cli/weather'
         | 
| 16 17 | 
             
            require_relative 'journal-cli/checkin'
         | 
| @@ -21,6 +22,8 @@ require_relative 'journal-cli/question' | |
| 21 22 | 
             
            # Main Journal module
         | 
| 22 23 | 
             
            module Journal
         | 
| 23 24 | 
             
              class << self
         | 
| 25 | 
            +
                attr_accessor :date
         | 
| 26 | 
            +
             | 
| 24 27 | 
             
                def notify(string, debug: false, exit_code: nil)
         | 
| 25 28 | 
             
                  if debug
         | 
| 26 29 | 
             
                    $stderr.puts "{dw}#{string}{x}".x
         | 
    
        data/src/_README.md
    CHANGED
    
    | @@ -55,6 +55,10 @@ This file contains a YAML definition of your journal. Each journal gets a top-le | |
| 55 55 |  | 
| 56 56 | 
             
            You can include weather data automatically by setting a question type to 'weather'. In order for this to work, you'll need to define `zip` and `weather_api` keys. `zip` is just your zip code, and `weather_api` is a key from WeatherAPI.com. Sign up [here](https://www.weatherapi.com/) for a free plan, and then visit the [profile page](https://www.weatherapi.com/my/) to see your API key at the top.
         | 
| 57 57 |  | 
| 58 | 
            +
            If a question type is set to `weather.forecast`, only the predicted condition, high, and low will be included in the JSON data for the question. A full printout of hourly temps will be included in the Markdown/Day One output.
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            If the question type is `weather.current`, only the current condition and temperature will be recorded to the JSON, and a string containing "[TEMP] and [CONDITION]" (e.g. "64 and Sunny") will be recorded to Markdown/Day One for the question.
         | 
| 61 | 
            +
             | 
| 58 62 | 
             
            ### Journal Configuration
         | 
| 59 63 |  | 
| 60 64 | 
             
            Edit the file at `~/.config/journal/journals.yaml` following this structure:
         | 
| @@ -99,9 +103,12 @@ journals: # required key | |
| 99 103 | 
             
                  - title: Weather # Title of the section (will create template sections in Day One)
         | 
| 100 104 | 
             
                    key: weather # the key to use in the structured data, will contain all of the answers
         | 
| 101 105 | 
             
                    questions: # required key
         | 
| 102 | 
            -
                      - prompt: Current  | 
| 106 | 
            +
                      - prompt: Current Weather
         | 
| 107 | 
            +
                        key: weather.current
         | 
| 108 | 
            +
                        type: weather.current
         | 
| 109 | 
            +
                      - prompt: Weather Forecast # The prompt shown on the command line, will also become a header in the journal entries (Markdown, Day One)
         | 
| 103 110 | 
             
                        key: weather.forecast # if a key contains a dot, it will create nested data, e.g. `{ 'weather': { 'forecast': data } }`
         | 
| 104 | 
            -
                        type: weather # Set this to weather for weather data
         | 
| 111 | 
            +
                        type: weather.forecast # Set this to weather for weather data
         | 
| 105 112 | 
             
                  - title: Health # New section
         | 
| 106 113 | 
             
                    key: health 
         | 
| 107 114 | 
             
                    questions:
         | 
| @@ -144,10 +151,18 @@ A question `type` can be one of: | |
| 144 151 | 
             
            - `text` or `string` will request a single-line string, submitted on return
         | 
| 145 152 | 
             
            - `multiline` for multiline strings (opens a readline editor, use ctrl-d to save)
         | 
| 146 153 | 
             
            - `weather` will just insert current weather data with no prompt
         | 
| 154 | 
            +
              * `weather.forecast` will insert just the forecast
         | 
| 155 | 
            +
              * `weather.current` will insert just the current temperature and condition
         | 
| 147 156 | 
             
            - `number` or `float` will request numeric input, stored as a float (decimal)
         | 
| 148 157 | 
             
            - `integer` will convert numeric input to the nearest integer
         | 
| 149 158 | 
             
            - `date` will request a natural language date which will be parsed into a date object
         | 
| 150 159 |  | 
| 160 | 
            +
            ### Conditional Questions
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            You can have a question only show up based on conditions. Currently the only condition is time based. Just add a key called `condition` to the question definition, then include a natural language string like `before noon` or `after 3pm`. If the condition is matched, then the question will be displayed, otherwise it will be skipped and its data entry in the JSON will be null.
         | 
| 163 | 
            +
             | 
| 164 | 
            +
            Conditions can be applied to individual questions, or to entire sections, depending on where the `condition` key is placed.
         | 
| 165 | 
            +
             | 
| 151 166 | 
             
            ### Naming Keys
         | 
| 152 167 |  | 
| 153 168 | 
             
            If you want data stored in a nested object, you can set a question type to `dictionary` and set the prompt to `null` (or just leave the key out), but give it a key that will serve as the parent in the object. Then in the nested questions, give them a key in the dot format `[PARENT_KEY].[CHILD_KEY]`. Section keys automatically nest their questions, but if you want to go deeper, you could have a question with the key `health` and type `dictionary`, then have questions with keys like `health.rating` and `health.notes`. If the section key was `status`, the resulting dictionary would look like this in the JSON:
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: journal-cli
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.0. | 
| 4 | 
            +
              version: 1.0.24
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Brett Terpstra
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2023-09- | 
| 11 | 
            +
            date: 2023-09-20 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: tty-which
         | 
| @@ -238,6 +238,7 @@ files: | |
| 238 238 | 
             
            - lib/journal-cli/question.rb
         | 
| 239 239 | 
             
            - lib/journal-cli/section.rb
         | 
| 240 240 | 
             
            - lib/journal-cli/sections.rb
         | 
| 241 | 
            +
            - lib/journal-cli/string.rb
         | 
| 241 242 | 
             
            - lib/journal-cli/version.rb
         | 
| 242 243 | 
             
            - lib/journal-cli/weather.rb
         | 
| 243 244 | 
             
            - src/_README.md
         |