done_log 1.0.0
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 +7 -0
- data/README.md +89 -0
- data/bin/done_log +14 -0
- data/lib/done_log.rb +176 -0
- data/lib/done_log/ansi_colors.rb +38 -0
- data/lib/done_log/git_repo.rb +65 -0
- data/lib/done_log/options.rb +67 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fa9087eac5309de9a13842028ba9d4414a31019914c288a4f6708b20a77fe12e
|
4
|
+
data.tar.gz: c95a79c7b8a1ebc63f831829f387d509f3872004b78defc139e84f985cddacd0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 50203c3aafcd8719de41afda937b5859bd6b27d730d9688b57dd291e03a411b351c975ec124fe6d415af707a4bf9702053b27a6d8218b282a99aa1a9afe0370a
|
7
|
+
data.tar.gz: a4471793fc7b925e611957c8b81c28c77771c4ac42377e2b21202d2096c7d9fe51272be5029bbd409affa00a153d17a9bea0f4f081b10b33fc08d08d3fc1ab13
|
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
## Done
|
2
|
+
|
3
|
+
This is a dumb Ruby script to help manage daily work logs.
|
4
|
+
|
5
|
+
Keeps each day in a separate file.
|
6
|
+
|
7
|
+
Keeps the files in a Git repository.
|
8
|
+
|
9
|
+
Edits the logs via `vim`.
|
10
|
+
|
11
|
+
Trailing newlines stripped automatically from logs.
|
12
|
+
|
13
|
+
Optionally pushes to a remote Git repository.
|
14
|
+
|
15
|
+
### Installing
|
16
|
+
|
17
|
+
This is a Ruby program, so you'll need [a Ruby](https://www.ruby-lang.org/) first.
|
18
|
+
|
19
|
+
Via RubyGems:
|
20
|
+
|
21
|
+
gem install done_log
|
22
|
+
|
23
|
+
From source:
|
24
|
+
|
25
|
+
git clone https://github.com/presidentbeef/done_log.git
|
26
|
+
gem build done_log.gemspec
|
27
|
+
gem install done_log*.gem
|
28
|
+
|
29
|
+
### Configuration
|
30
|
+
|
31
|
+
Logs are kept in `~/.done/log/`.
|
32
|
+
|
33
|
+
This directory is created automatically.
|
34
|
+
|
35
|
+
Configuration is read from JSON file `~/.done/config`.
|
36
|
+
|
37
|
+
A default configuration file is created automatically.
|
38
|
+
|
39
|
+
Options are:
|
40
|
+
|
41
|
+
* `git_repo` which can be set to a remote Git repository.
|
42
|
+
* `date_color` which sets the color used for dates when viewing logs.
|
43
|
+
|
44
|
+
[Possible colors listed here](https://github.com/presidentbeef/done_log/blob/master/lib/done_log/ansi_colors.rb#L4-L12),
|
45
|
+
plus they can be prefixed by `bright_`.
|
46
|
+
|
47
|
+
### Running
|
48
|
+
|
49
|
+
`done_log` edits the log for the current day.
|
50
|
+
|
51
|
+
`done_log -[N]` edits the log for the previous `N` day. `-1` is yesterday, `-2` is two days ago, etc.
|
52
|
+
|
53
|
+
`done_log -d DATE` edits the log for the specified date.
|
54
|
+
|
55
|
+
`done_log -s -d DATE` shows the specified log.
|
56
|
+
|
57
|
+
`done_log --sprint` shows the last 14 days of logs.
|
58
|
+
|
59
|
+
### Logging
|
60
|
+
|
61
|
+
#### Start a Log
|
62
|
+
|
63
|
+
To enter a log for today, run `done_log`.
|
64
|
+
|
65
|
+
To enter a log for yesterday, run `done_log -1`
|
66
|
+
|
67
|
+
To enter a log for a specific date, run `done_log -d DATE`.
|
68
|
+
|
69
|
+
#### Edit a Log
|
70
|
+
|
71
|
+
When editing, `vim` is opened to a new/existing document. When `vim` exits, the log is committed and pushed to the Git repo.
|
72
|
+
|
73
|
+
To cancel a new log, delete _everything_ in the file and save.
|
74
|
+
|
75
|
+
To cancel edits to an existing log, close `vim` without saving.
|
76
|
+
|
77
|
+
#### Review a Log
|
78
|
+
|
79
|
+
To view an existing log, use `-s` or `--show`.
|
80
|
+
|
81
|
+
Examples:
|
82
|
+
|
83
|
+
* `done_log --show --date DATE`
|
84
|
+
* `done_log -s -d DATE`
|
85
|
+
* `done_log -s -1` (Show yesterday's log)
|
86
|
+
|
87
|
+
### License
|
88
|
+
|
89
|
+
MIT
|
data/bin/done_log
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/bin/env ruby
|
2
|
+
require 'date'
|
3
|
+
require_relative '../lib/done_log.rb'
|
4
|
+
|
5
|
+
options = Done::Options.parse!(ARGV)
|
6
|
+
|
7
|
+
case options[:action]
|
8
|
+
when :edit
|
9
|
+
Done::Log.edit_logs(options[:time_period])
|
10
|
+
when :show
|
11
|
+
Done::Log.show_logs(options[:time_period], options[:date_color])
|
12
|
+
else
|
13
|
+
abort "Need to either --edit or --show logs"
|
14
|
+
end
|
data/lib/done_log.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'json'
|
6
|
+
require_relative 'done_log/git_repo'
|
7
|
+
require_relative 'done_log/options'
|
8
|
+
require_relative 'done_log/ansi_colors'
|
9
|
+
|
10
|
+
module Done
|
11
|
+
class Log
|
12
|
+
attr_reader :date, :dir, :git, :log_file
|
13
|
+
|
14
|
+
def initialize date:, date_color: nil
|
15
|
+
@date = date
|
16
|
+
@dir = Done::Log.default_log_dir
|
17
|
+
year = date.year.to_s
|
18
|
+
month = date.month.to_s.rjust(2, "0")
|
19
|
+
@log_file = File.join(dir, year, month, date_string)
|
20
|
+
|
21
|
+
config = Done::Log.config
|
22
|
+
@git = GitRepo.new(config[:git_repo], dir, log_file)
|
23
|
+
@date_color = date_color || config[:date_color]
|
24
|
+
end
|
25
|
+
|
26
|
+
def log
|
27
|
+
ensure_directory
|
28
|
+
git.pull
|
29
|
+
create_log
|
30
|
+
vim_edit
|
31
|
+
strip_log
|
32
|
+
git.add
|
33
|
+
git.commit
|
34
|
+
git.push
|
35
|
+
end
|
36
|
+
|
37
|
+
def show pull = true
|
38
|
+
ensure_directory
|
39
|
+
git.pull if pull
|
40
|
+
|
41
|
+
if File.exist? log_file
|
42
|
+
log = File.read(log_file)
|
43
|
+
if $stdout.tty?
|
44
|
+
puts log.sub(/^(\d{4}-\d{2}-\d{2})$/) { |date| Done::ANSIColors.colorize(date, @date_color) }
|
45
|
+
else
|
46
|
+
puts log
|
47
|
+
end
|
48
|
+
|
49
|
+
puts
|
50
|
+
else
|
51
|
+
puts <<~LOG
|
52
|
+
#{Done::ANSIColors.colorize(date_string, @date_color)}
|
53
|
+
|
54
|
+
[No log]
|
55
|
+
|
56
|
+
LOG
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def ensure_directory
|
61
|
+
FileUtils.mkdir_p(File.dirname(log_file))
|
62
|
+
git.init
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_log
|
66
|
+
unless File.exist? log_file
|
67
|
+
File.open(log_file, "w") do |f|
|
68
|
+
f.puts <<~LOG
|
69
|
+
#{date_string}
|
70
|
+
|
71
|
+
LOG
|
72
|
+
end
|
73
|
+
|
74
|
+
git.add
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def strip_log
|
79
|
+
log = File.read(log_file).strip
|
80
|
+
|
81
|
+
if log.empty?
|
82
|
+
File.delete(log_file)
|
83
|
+
else
|
84
|
+
File.open(log_file, "w") do |f|
|
85
|
+
f.puts log
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def vim_edit
|
91
|
+
system 'vim',
|
92
|
+
'+ normal Go', # Move to end of file, start new line
|
93
|
+
'+startinsert', # Switch to insert mode
|
94
|
+
log_file
|
95
|
+
end
|
96
|
+
|
97
|
+
def date_string
|
98
|
+
date.iso8601.freeze
|
99
|
+
end
|
100
|
+
|
101
|
+
class << self
|
102
|
+
def default_dir
|
103
|
+
File.join(Dir.home, ".done").freeze
|
104
|
+
end
|
105
|
+
|
106
|
+
def default_log_dir
|
107
|
+
File.join(Dir.home, ".done", "log").freeze
|
108
|
+
end
|
109
|
+
|
110
|
+
def logs
|
111
|
+
Dir[self.default_dir].sort
|
112
|
+
end
|
113
|
+
|
114
|
+
def config config_file = default_config_file
|
115
|
+
if File.exist? config_file
|
116
|
+
begin
|
117
|
+
JSON.parse(File.read(config_file), symbolize_names: true)
|
118
|
+
rescue JSON::ParserError => e
|
119
|
+
warn "Unable to parse config file: #{e.inspect}"
|
120
|
+
{}
|
121
|
+
end
|
122
|
+
else
|
123
|
+
FileUtils.mkdir_p(default_dir)
|
124
|
+
|
125
|
+
config = default_config
|
126
|
+
puts "What Git URL (e.g. git@github.com:user_name/example_log.git) should we use?\nLeave blank to skip."
|
127
|
+
print "? "
|
128
|
+
url = gets.strip
|
129
|
+
|
130
|
+
unless url.empty?
|
131
|
+
config[:git_repo] = url
|
132
|
+
end
|
133
|
+
|
134
|
+
File.open(config_file, "w") do |f|
|
135
|
+
f.puts(JSON.pretty_generate(config))
|
136
|
+
end
|
137
|
+
|
138
|
+
config
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def default_config
|
143
|
+
{
|
144
|
+
git_repo: nil,
|
145
|
+
date_color: :cyan
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
def default_config_file
|
150
|
+
File.join(default_dir, "config")
|
151
|
+
end
|
152
|
+
|
153
|
+
def edit_logs time_period
|
154
|
+
if time_period.is_a? Date
|
155
|
+
Done::Log.new(date: time_period).log
|
156
|
+
else
|
157
|
+
time_period.each do |d|
|
158
|
+
Done::Log.new(date: d).log
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def show_logs time_period, date_color = nil
|
164
|
+
if time_period.is_a? Date
|
165
|
+
Done::Log.new(date: time_period, date_color: date_color).show
|
166
|
+
else
|
167
|
+
pull = true
|
168
|
+
time_period.each do |d|
|
169
|
+
Done::Log.new(date: d, date_color: date_color).show(pull)
|
170
|
+
pull = false
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Done
|
2
|
+
module ANSIColors
|
3
|
+
Colors = {
|
4
|
+
none: 0,
|
5
|
+
black: 30,
|
6
|
+
red: 31,
|
7
|
+
green: 32,
|
8
|
+
yellow: 33,
|
9
|
+
blue: 34,
|
10
|
+
magenta: 35,
|
11
|
+
cyan: 36,
|
12
|
+
white: 37,
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def self.colorize input, color
|
16
|
+
return input unless color
|
17
|
+
|
18
|
+
color = color.to_sym
|
19
|
+
|
20
|
+
if color == :none
|
21
|
+
return input
|
22
|
+
end
|
23
|
+
|
24
|
+
color_code = if color.start_with? 'bright'
|
25
|
+
code = Colors[color.to_s.split('_').last.to_sym]
|
26
|
+
"#{code};1"
|
27
|
+
else
|
28
|
+
Colors[color.to_sym]
|
29
|
+
end
|
30
|
+
|
31
|
+
"\e[#{color_code}m#{input}\e[0m"
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.colors
|
35
|
+
@colors ||= Colors.keys.concat(Colors.keys.map { |c| "bright_#{c}".to_sym })
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Done
|
6
|
+
class GitRepo
|
7
|
+
attr_reader :git_url, :log_dir, :log_file
|
8
|
+
|
9
|
+
def initialize git_url, log_dir, log_file
|
10
|
+
@git_url = git_url
|
11
|
+
@log_dir = log_dir
|
12
|
+
@log_file = log_file
|
13
|
+
end
|
14
|
+
|
15
|
+
def add
|
16
|
+
unless File.empty? log_file
|
17
|
+
run "git add #{log_file}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def commit
|
22
|
+
unless run "git diff --no-patch --cached --exit-code" # Check if there are any changes
|
23
|
+
unless run "git commit #{log_file} -m 'Update #{File.basename log_file}'"
|
24
|
+
raise 'Could not commit update to git'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_remote?
|
30
|
+
run "git remote -v | grep -q -F #{git_url.inspect}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def init
|
34
|
+
if git_url and not Dir.exist? log_dir
|
35
|
+
run "git clone -q #{git_url} #{log_dir}"
|
36
|
+
else
|
37
|
+
run "git init -q #{log_dir}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def pull
|
42
|
+
return unless git_url
|
43
|
+
return unless has_remote?
|
44
|
+
|
45
|
+
unless run 'git pull -q'
|
46
|
+
raise 'Could not pull from git'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def push
|
51
|
+
return unless git_url
|
52
|
+
return unless has_remote?
|
53
|
+
|
54
|
+
unless run 'git push'
|
55
|
+
raise 'Could not push to git'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def run cmd
|
60
|
+
FileUtils.cd(log_dir) do
|
61
|
+
system cmd
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'optparse/date'
|
5
|
+
require_relative 'ansi_colors'
|
6
|
+
|
7
|
+
module Done
|
8
|
+
class Options
|
9
|
+
def self.parse!(args)
|
10
|
+
options = {}
|
11
|
+
|
12
|
+
days = args.grep(/^-\d+$/)
|
13
|
+
|
14
|
+
unless days.empty?
|
15
|
+
if days.length > 1
|
16
|
+
raise "Expected only one day, got #{days.inspect}"
|
17
|
+
end
|
18
|
+
|
19
|
+
days = days.first
|
20
|
+
args.delete days
|
21
|
+
options[:time_period] = Date.today - days.to_i.abs
|
22
|
+
options[:action] ||= :edit
|
23
|
+
end
|
24
|
+
|
25
|
+
OptionParser.new do |opts|
|
26
|
+
opts.banner = "Usage: done_log [options]"
|
27
|
+
|
28
|
+
opts.on "-NUM", OptionParser::DecimalInteger, "Go back NUM days"
|
29
|
+
|
30
|
+
opts.on "-e", "--edit", "Edit log(s)" do
|
31
|
+
options[:action] = :edit
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on "-s", "--show", "Display log(s)" do
|
35
|
+
options[:action] = :show
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on "-d", "--date [DATE]", Date, "Select date" do |date|
|
39
|
+
options[:time_period] = date
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on "--sprint", "Last 14 days" do
|
43
|
+
options[:time_period] = (Date.today - 14)..Date.today
|
44
|
+
options[:action] ||= :show
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on "-c", "--color-date COLOR", Done::ANSIColors.colors, "Set color for dates" do |color|
|
48
|
+
options[:date_color] = color.to_sym
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on_tail "-h", "--help", "Show this message" do
|
52
|
+
puts opts
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
end.parse(args)
|
56
|
+
|
57
|
+
default_options.merge(options)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.default_options
|
61
|
+
{
|
62
|
+
action: :edit,
|
63
|
+
time_period: Date.today
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: done_log
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Justin Collins
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-04-01 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Manage very simple daily work logs in text files.
|
14
|
+
email:
|
15
|
+
executables:
|
16
|
+
- done_log
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- README.md
|
21
|
+
- bin/done_log
|
22
|
+
- lib/done_log.rb
|
23
|
+
- lib/done_log/ansi_colors.rb
|
24
|
+
- lib/done_log/git_repo.rb
|
25
|
+
- lib/done_log/options.rb
|
26
|
+
homepage: https://github.com/presidentbeef/done_log
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata:
|
30
|
+
source_code_uri: https://github.com/presidentbeef/done_log
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 2.3.0
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubygems_version: 3.1.2
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Extremely simple daily log.
|
50
|
+
test_files: []
|