todo-jsonl 0.1.8 → 0.1.14
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/bin/todo.rb +70 -31
- 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: c84036e07afa19297ed5bf2e0967d7e715dba649ba5a8bd84bacfe2936ea1d1f
|
4
|
+
data.tar.gz: '0291fca44038ce3674086f8078348c1a44d40a2755dbc36479eeabf223abe2db'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 398dbcea18e7730823ffc9dc65192187b5f0eb223aff1402c6d3089b3a479cdbeebf01f6969970fa4dfc046aad26921367ec67bd032c09fedadc680f01e4aca0
|
7
|
+
data.tar.gz: 3987df17cc1cbeb03c8cad2a3dc15f3ff867ff1213b80352a0745d48b0e7b506e32f6b820e25dd2019f7870efb196b207fa1cbede3367faa426ebbbe5d03a715
|
data/bin/todo.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# todo.rb - todo list manager inspired by todo.txt using the jsonl format.
|
4
4
|
#
|
5
|
-
# Copyright (c) 2020 Gabor Bata
|
5
|
+
# Copyright (c) 2020-2021 Gabor Bata
|
6
6
|
#
|
7
7
|
# Permission is hereby granted, free of charge, to any person
|
8
8
|
# obtaining a copy of this software and associated documentation files
|
@@ -27,8 +27,6 @@
|
|
27
27
|
require 'json'
|
28
28
|
require 'date'
|
29
29
|
|
30
|
-
DATE_FORMAT = '%Y-%m-%d'
|
31
|
-
|
32
30
|
COLOR_CODES = {
|
33
31
|
black: 30,
|
34
32
|
red: 31,
|
@@ -64,21 +62,27 @@ COLORS = {
|
|
64
62
|
'default' => :magenta
|
65
63
|
}
|
66
64
|
|
67
|
-
QUERIES = {
|
68
|
-
':active' => 'state=(new|started|blocked)',
|
69
|
-
':done' => 'state=done',
|
70
|
-
':blocked' => 'state=blocked',
|
71
|
-
':started' => 'state=started',
|
72
|
-
':new' => 'state=new',
|
73
|
-
':all' => 'state=\w+'
|
74
|
-
}
|
75
|
-
|
76
65
|
TODAY = DateTime.now
|
66
|
+
NEXT_7_DAYS = (0..6).map do |day| (TODAY.to_date + day) end
|
67
|
+
DATE_FORMAT = '%Y-%m-%d'
|
68
|
+
DUE_DATE_DAYS = NEXT_7_DAYS.map do |day| day.strftime('%A').downcase end
|
69
|
+
DUE_DATES_FOR_QUERIES = NEXT_7_DAYS.map do |day| day.strftime(DATE_FORMAT) end
|
70
|
+
DUE_DATE_DAYS_SIMPLE = ['today', 'tomorrow']
|
77
71
|
|
78
|
-
|
79
|
-
(
|
80
|
-
|
81
|
-
|
72
|
+
DUE_DATE_TAG_PATTERN = /(^| )due:([a-zA-Z0-9-]+)/
|
73
|
+
CONTEXT_TAG_PATTERN = /(^| )[@+][\w-]+/
|
74
|
+
|
75
|
+
QUERIES = {
|
76
|
+
':active' => 'state=(new|started|blocked)',
|
77
|
+
':done' => 'state=done',
|
78
|
+
':blocked' => 'state=blocked',
|
79
|
+
':started' => 'state=started',
|
80
|
+
':new' => 'state=new',
|
81
|
+
':all' => 'state=\w+',
|
82
|
+
':today' => "due=#{DUE_DATES_FOR_QUERIES[0]}",
|
83
|
+
':tomorrow' => "due=#{DUE_DATES_FOR_QUERIES[1]}",
|
84
|
+
':next7days' => "due=(#{DUE_DATES_FOR_QUERIES.join('|')})"
|
85
|
+
}
|
82
86
|
|
83
87
|
PRIORITY_FLAG = '*'
|
84
88
|
|
@@ -90,12 +94,12 @@ def usage
|
|
90
94
|
|
91
95
|
Commands:
|
92
96
|
* add <text> add new task
|
93
|
-
* start <tasknumber>
|
94
|
-
* done <tasknumber>
|
95
|
-
* block <tasknumber>
|
96
|
-
* reset <tasknumber>
|
97
|
+
* start <tasknumber> [text] mark task as started, with optional note
|
98
|
+
* done <tasknumber> [text] mark task as completed, with optional note
|
99
|
+
* block <tasknumber> [text] mark task as blocked, with optional note
|
100
|
+
* reset <tasknumber> [text] reset task to new state, with optional note
|
97
101
|
* prio <tasknumber> toggle high priority flag
|
98
|
-
* due <tasknumber>
|
102
|
+
* due <tasknumber> [date] set/unset due date (in YYYY-MM-DD format)
|
99
103
|
|
100
104
|
* append <tasknumber> <text> append text to task title
|
101
105
|
* rename <tasknumber> <text> rename task
|
@@ -111,8 +115,12 @@ def usage
|
|
111
115
|
With list command the following pre-defined regex patterns can be also used:
|
112
116
|
#{QUERIES.keys.join(', ')}
|
113
117
|
|
118
|
+
Due dates can be also added via tags in task title: "due:YYYY-MM-DD"
|
119
|
+
|
114
120
|
Legend:
|
115
121
|
#{STATES.select { |k, v| k != 'default' }.map { |k, v| "#{k} #{v}" }.join(', ') }, priority #{PRIORITY_FLAG}
|
122
|
+
|
123
|
+
Todo file: #{TODO_FILE}
|
116
124
|
USAGE
|
117
125
|
end
|
118
126
|
|
@@ -142,12 +150,22 @@ def write_tasks(tasks)
|
|
142
150
|
end
|
143
151
|
end
|
144
152
|
|
153
|
+
def postprocess_tags(task)
|
154
|
+
title = task[:title]
|
155
|
+
match_data = title.match(DUE_DATE_TAG_PATTERN)
|
156
|
+
if match_data
|
157
|
+
task[:title] = title.gsub(DUE_DATE_TAG_PATTERN, '')
|
158
|
+
task[:due] = convert_due_date(match_data[2])
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
145
162
|
def add(text)
|
146
163
|
task = {
|
147
164
|
state: 'new',
|
148
165
|
title: text,
|
149
166
|
modified: Time.now.strftime(DATE_FORMAT)
|
150
167
|
}
|
168
|
+
postprocess_tags(task)
|
151
169
|
File.open(TODO_FILE, 'a:UTF-8') do |file|
|
152
170
|
file.write(JSON.generate(task) + "\n")
|
153
171
|
end
|
@@ -158,6 +176,7 @@ def append(item, text = '')
|
|
158
176
|
tasks = load_tasks(item)
|
159
177
|
tasks[item][:title] = [tasks[item][:title], text].join(' ')
|
160
178
|
tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
|
179
|
+
postprocess_tags(tasks[item])
|
161
180
|
write_tasks(tasks)
|
162
181
|
list(tasks)
|
163
182
|
end
|
@@ -166,6 +185,7 @@ def rename(item, text)
|
|
166
185
|
tasks = load_tasks(item)
|
167
186
|
tasks[item][:title] = text
|
168
187
|
tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
|
188
|
+
postprocess_tags(tasks[item])
|
169
189
|
write_tasks(tasks)
|
170
190
|
list(tasks)
|
171
191
|
end
|
@@ -177,10 +197,14 @@ def delete(item)
|
|
177
197
|
list
|
178
198
|
end
|
179
199
|
|
180
|
-
def change_state(item, state)
|
200
|
+
def change_state(item, state, note = nil)
|
181
201
|
tasks = load_tasks(item)
|
182
202
|
tasks[item][:state] = state
|
183
203
|
tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
|
204
|
+
if !note.nil? && !note.empty?
|
205
|
+
tasks[item][:note] ||= []
|
206
|
+
tasks[item][:note].push(note)
|
207
|
+
end
|
184
208
|
write_tasks(tasks)
|
185
209
|
list(tasks)
|
186
210
|
end
|
@@ -193,9 +217,9 @@ def set_priority(item)
|
|
193
217
|
list(tasks)
|
194
218
|
end
|
195
219
|
|
196
|
-
def due_date(item, date = '')
|
220
|
+
def due_date(item, date = '', task = nil)
|
197
221
|
tasks = load_tasks(item)
|
198
|
-
tasks[item][:due] =
|
222
|
+
tasks[item][:due] = convert_due_date(date)
|
199
223
|
tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
|
200
224
|
write_tasks(tasks)
|
201
225
|
list(tasks)
|
@@ -207,7 +231,7 @@ def list(tasks = nil, patterns = nil)
|
|
207
231
|
task_indent = [tasks.keys.max.to_s.size, 4].max
|
208
232
|
patterns = patterns.nil? || patterns.empty? ? [QUERIES[':active']] : patterns
|
209
233
|
tasks.each do |num, task|
|
210
|
-
normalized_task = "state=#{task[:state]} #{task[:title]}"
|
234
|
+
normalized_task = "state=#{task[:state]} due=#{task[:due]} #{task[:title]}"
|
211
235
|
match = true
|
212
236
|
patterns.each do |pattern|
|
213
237
|
match = false unless /#{QUERIES[pattern] || pattern}/ix.match(normalized_task)
|
@@ -221,7 +245,9 @@ def list(tasks = nil, patterns = nil)
|
|
221
245
|
state = task[:state] || 'default'
|
222
246
|
color = COLORS[state]
|
223
247
|
display_state = colorize(STATES[state], color)
|
224
|
-
title = task[:title].gsub(
|
248
|
+
title = task[:title].gsub(CONTEXT_TAG_PATTERN) do |tag|
|
249
|
+
(tag.start_with?(' ') ? ' ' : '') + colorize(tag.strip, :cyan)
|
250
|
+
end
|
225
251
|
priority_flag = task[:priority] ? colorize(PRIORITY_FLAG, :red) : ' '
|
226
252
|
due_date = ''
|
227
253
|
if task[:due] && state != 'done'
|
@@ -229,7 +255,7 @@ def list(tasks = nil, patterns = nil)
|
|
229
255
|
if date_diff < 0
|
230
256
|
due_date = colorize("(#{date_diff.abs}d overdue)", :red)
|
231
257
|
elsif date_diff == 0 || date_diff == 1
|
232
|
-
due_date = colorize("(#{
|
258
|
+
due_date = colorize("(#{DUE_DATE_DAYS_SIMPLE[date_diff]})", :yellow)
|
233
259
|
else
|
234
260
|
due_date = colorize("(#{DUE_DATE_DAYS[date_diff] || task[:due]})", :magenta) if date_diff > 1
|
235
261
|
end
|
@@ -282,6 +308,19 @@ def colorize(text, color)
|
|
282
308
|
"\e[#{COLOR_CODES[color]}m#{text}\e[0m"
|
283
309
|
end
|
284
310
|
|
311
|
+
def convert_due_date(date = '')
|
312
|
+
due = nil
|
313
|
+
day_index = DUE_DATE_DAYS.index(date.to_s.downcase) ||
|
314
|
+
DUE_DATE_DAYS_SIMPLE.index(date.to_s.downcase) ||
|
315
|
+
DUE_DATE_DAYS.map do |day| day[0..2] end.index(date.to_s.downcase)
|
316
|
+
if day_index
|
317
|
+
due = (TODAY.to_date + day_index).strftime(DATE_FORMAT)
|
318
|
+
else
|
319
|
+
due = date.nil? || date.empty? ? nil : Date.parse(date).strftime(DATE_FORMAT)
|
320
|
+
end
|
321
|
+
return due
|
322
|
+
end
|
323
|
+
|
285
324
|
def read(arguments)
|
286
325
|
begin
|
287
326
|
action = arguments.first
|
@@ -291,13 +330,13 @@ def read(arguments)
|
|
291
330
|
raise action + ' command requires at least one parameter' if args.nil? || args.empty?
|
292
331
|
add(args.join(' '))
|
293
332
|
when 'start'
|
294
|
-
args.length
|
333
|
+
args.length > 0 ? change_state(args.first.to_i, 'started', args[1..-1].join(' ')) : list(nil, [':started'])
|
295
334
|
when 'done'
|
296
|
-
args.length
|
335
|
+
args.length > 0 ? change_state(args.first.to_i, 'done', args[1..-1].join(' ')) : list(nil, [':done'])
|
297
336
|
when 'block'
|
298
|
-
args.length
|
337
|
+
args.length > 0 ? change_state(args.first.to_i, 'blocked', args[1..-1].join(' ')) : list(nil, [':blocked'])
|
299
338
|
when 'reset'
|
300
|
-
args.length
|
339
|
+
args.length > 0 ? change_state(args.first.to_i, 'new', args[1..-1].join(' ')) : list(nil, [':new'])
|
301
340
|
when 'prio'
|
302
341
|
raise action + ' command requires exactly one parameter' if args.length != 1
|
303
342
|
set_priority(args.first.to_i)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: todo-jsonl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabor Bata
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|