journal-cli 1.0.22 → 1.0.23
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 +13 -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/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 +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94254a744cbef7c84b89dc49f3811a641e5ece9b81e813cef5085c6111673846
|
4
|
+
data.tar.gz: 06d736bceebf43d0505ce2f003a004a1dffc508d678f20ff1f1d8d2e06a51390
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 827d6165ddbcfb7752d0b91022b0c7779af14280503f915f007c1b3e937c4b6c989bdf0167b1d7399a0a2aaa9829528203784f80888c2ec9801d2ee614d653de
|
7
|
+
data.tar.gz: 5fb214409b54da6c11321bf0c6f77eb4221f54f0803474787afee2f696930fe6d025af8e8915cf9a0759cc4e50d9463d4e6d15c6e7fd271f24c01c385f2170e8
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
### 1.0.23
|
2
|
+
|
3
|
+
2023-09-20 08:30
|
4
|
+
|
5
|
+
#### NEW
|
6
|
+
|
7
|
+
- Question types weather.forecast and weather.current allow more specific weather entry types
|
8
|
+
- Time-based conditions for questions and sections (`condition: before noon` or `condition: < 12pm`)
|
9
|
+
|
10
|
+
#### FIXED
|
11
|
+
|
12
|
+
- Test for existence of dayone2 binary before attempting to write a Day One entry, provide error message
|
13
|
+
|
1
14
|
### 1.0.22
|
2
15
|
|
3
16
|
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
|
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.23
|
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
|