done_log 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|