work-md 0.3.5
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/bin/console +15 -0
- data/bin/setup +8 -0
- data/bin/work-md +7 -0
- data/lib/work/md/cli.rb +72 -0
- data/lib/work/md/commands/annotations.rb +24 -0
- data/lib/work/md/commands/config.rb +37 -0
- data/lib/work/md/commands/parse.rb +112 -0
- data/lib/work/md/commands/today.rb +15 -0
- data/lib/work/md/commands/tyesterday.rb +20 -0
- data/lib/work/md/commands/yesterday.rb +15 -0
- data/lib/work/md/config.rb +73 -0
- data/lib/work/md/file.rb +59 -0
- data/lib/work/md/parser/engine.rb +198 -0
- data/lib/work/md/version.rb +7 -0
- data/lib/work/md.rb +23 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9d880e4894863c9e62e915bb397194711aee0ec9d144dd0e53acffb4af268904
|
4
|
+
data.tar.gz: f4082880a4a1d0feca7e6a4c6fda1c57b865b7c7a692a4e22a68c30e5adc6b3b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f262f4192079f5c19b51b07e7575d2efa8419d5f29a1479a04d36cf9795c68c9f0cb06563050ed643ffa95427695b710318b8db97293a0e64daf4d3396c2742c
|
7
|
+
data.tar.gz: ca947bf521e40588ce9c7db992bd23954b7c3b4fe4dcf2345ce888196b1b1c5ac524fc5068aa3716ce625a2ab80e2e3ad915e845928135e35796848ecfc01564
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'work_md'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/bin/work-md
ADDED
data/lib/work/md/cli.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Work
|
4
|
+
module Md
|
5
|
+
module Cli
|
6
|
+
class CommandMissing < RuntimeError; end
|
7
|
+
|
8
|
+
ALIAS_COMMANDS =
|
9
|
+
{
|
10
|
+
't' => 'today',
|
11
|
+
'ty' => 'tyesterday',
|
12
|
+
'y' => 'yesterday',
|
13
|
+
'c' => 'config',
|
14
|
+
'p' => 'parse',
|
15
|
+
'a' => 'annotations'
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
def self.execute(argv)
|
19
|
+
first_argv_argument = argv.shift
|
20
|
+
|
21
|
+
raise CommandMissing if first_argv_argument.nil?
|
22
|
+
|
23
|
+
command =
|
24
|
+
(ALIAS_COMMANDS[first_argv_argument] ||
|
25
|
+
first_argv_argument).capitalize
|
26
|
+
|
27
|
+
Object
|
28
|
+
.const_get("Work::Md::Commands::#{command}")
|
29
|
+
.send(:execute, argv)
|
30
|
+
rescue NameError
|
31
|
+
puts help(
|
32
|
+
::TTY::Box.frame(
|
33
|
+
"Command '#{first_argv_argument}' not found!",
|
34
|
+
**error_frame_style
|
35
|
+
)
|
36
|
+
)
|
37
|
+
rescue CommandMissing
|
38
|
+
help('Welcome! =)')
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.help(message = '')
|
42
|
+
# rubocop:disable Layout/LineLength
|
43
|
+
puts ::TTY::Box.frame(
|
44
|
+
message,
|
45
|
+
'Track your work activities, write annotations, recap what you did for a week, month or specific days... and much more!',
|
46
|
+
'',
|
47
|
+
'commands available:',
|
48
|
+
'',
|
49
|
+
'- work-md',
|
50
|
+
'- work-md today',
|
51
|
+
'- work-md yesterday',
|
52
|
+
'- work-md tyesterday',
|
53
|
+
'- work-md parse',
|
54
|
+
'- work-md annotations',
|
55
|
+
'- work-md config',
|
56
|
+
'',
|
57
|
+
'more information in github.com/work-md',
|
58
|
+
padding: 1,
|
59
|
+
title: { top_left: '(work-md)', bottom_right: "(v#{Work::Md::VERSION})" }
|
60
|
+
)
|
61
|
+
# rubocop:enable Layout/LineLength
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.error_frame_style
|
65
|
+
{
|
66
|
+
padding: 1,
|
67
|
+
title: { top_left: '(error)' }
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Work
|
4
|
+
module Md
|
5
|
+
module Commands
|
6
|
+
class Annotations
|
7
|
+
class << self
|
8
|
+
def execute(_argv = [])
|
9
|
+
file_name = 'annotations.md'
|
10
|
+
work_dir = Work::Md::Config.work_dir
|
11
|
+
|
12
|
+
::FileUtils.mkdir_p(work_dir.to_s)
|
13
|
+
|
14
|
+
unless ::File.exist?("#{work_dir}/#{file_name}")
|
15
|
+
::File.open("#{work_dir}/#{file_name}", 'w+') { |f| f.puts("# \n\n") }
|
16
|
+
end
|
17
|
+
|
18
|
+
Work::Md::File.open_in_editor([file_name])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Work
|
4
|
+
module Md
|
5
|
+
module Commands
|
6
|
+
class Config
|
7
|
+
class << self
|
8
|
+
def execute(_argv = [])
|
9
|
+
file_name = 'config.yml'
|
10
|
+
work_dir = ::Work::Md::Config::DEFAULT_WORK_DIR
|
11
|
+
|
12
|
+
unless ::File.exist?("#{work_dir}/#{file_name}")
|
13
|
+
::FileUtils.mkdir_p(work_dir)
|
14
|
+
|
15
|
+
::File.open("#{work_dir}/#{file_name}", 'w+') do |f|
|
16
|
+
f.puts('# Example configuration:')
|
17
|
+
f.puts('#')
|
18
|
+
f.puts('# title: Your Name')
|
19
|
+
f.puts('# editor: vim')
|
20
|
+
f.puts('# lang: en')
|
21
|
+
f.puts('#')
|
22
|
+
f.puts('title: # Title your work-md files')
|
23
|
+
f.puts('editor: # Your default editor')
|
24
|
+
f.puts("lang: # Only 'pt', 'en' and 'es' avaiable")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
::Work::Md::File.open_in_editor(
|
29
|
+
[file_name],
|
30
|
+
dir: work_dir
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Work
|
4
|
+
module Md
|
5
|
+
module Commands
|
6
|
+
class Parse
|
7
|
+
class << self
|
8
|
+
def execute(argv = [])
|
9
|
+
parsed_file_path = Work::Md::Config.work_dir + '/parsed.md'
|
10
|
+
args = Hash[argv.join(' ').scan(/-?([^=\s]+)(?:=(\S+))?/)]
|
11
|
+
parser = Work::Md::Parser::Engine.new
|
12
|
+
t = Work::Md::Config.translations
|
13
|
+
|
14
|
+
year = args['y'] || Time.new.year
|
15
|
+
month = args['m'] || Time.new.month
|
16
|
+
|
17
|
+
month = "0#{month.to_i}" if month.to_i < 10
|
18
|
+
|
19
|
+
add_file_to_parser = lambda do |day|
|
20
|
+
day = "0#{day.to_i}" if day.to_i < 10
|
21
|
+
|
22
|
+
file_name = Work::Md::Config.work_dir + "/#{year}/#{month}/#{day}.md"
|
23
|
+
|
24
|
+
parser.add_file(file_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
if args['d'].include?('..')
|
28
|
+
range = args['d'].split('..')
|
29
|
+
|
30
|
+
(range[0].to_i..range[1].to_i).each { |day| add_file_to_parser.call(day) }
|
31
|
+
else
|
32
|
+
args['d'].split(',').each { |day| add_file_to_parser.call(day) }
|
33
|
+
end
|
34
|
+
|
35
|
+
parser.freeze
|
36
|
+
|
37
|
+
::File.delete(parsed_file_path) if ::File.exist? parsed_file_path
|
38
|
+
|
39
|
+
::File.open(parsed_file_path, 'w+') do |f|
|
40
|
+
f.puts("# #{Work::Md::Config.title}\n\n")
|
41
|
+
f.puts("### #{t[:tasks]} (#{parser.tasks.size}):\n\n")
|
42
|
+
parser.tasks.each do |task|
|
43
|
+
f.puts("- [#{task}\n\n") if task != ' ]'
|
44
|
+
end
|
45
|
+
f.puts("---\n\n")
|
46
|
+
f.puts("### #{t[:meetings]} (#{parser.meetings.size}):\n\n")
|
47
|
+
parser.meetings.each do |meeting|
|
48
|
+
f.puts("- [#{meeting}\n\n") if meeting != ' ]'
|
49
|
+
end
|
50
|
+
f.puts("---\n\n")
|
51
|
+
f.puts("### #{t[:interruptions]} (#{parser.interruptions.size}):\n\n")
|
52
|
+
parser.interruptions.each do |interruption|
|
53
|
+
f.puts("- #{interruption}\n")
|
54
|
+
end
|
55
|
+
f.puts("---\n\n")
|
56
|
+
f.puts("### #{t[:difficulties]} (#{parser.difficulties.size}):\n\n")
|
57
|
+
parser.difficulties.each do |difficulty|
|
58
|
+
f.puts("- #{difficulty}\n")
|
59
|
+
end
|
60
|
+
f.puts("---\n\n")
|
61
|
+
f.puts("### #{t[:observations]} (#{parser.observations.size}):\n\n")
|
62
|
+
parser.observations.each do |observation|
|
63
|
+
f.puts("- #{observation}\n")
|
64
|
+
end
|
65
|
+
f.puts("---\n\n")
|
66
|
+
f.puts("### #{t[:pomodoros]} (#{parser.average_pomodoros} #{t[:per_day]}):\n\n")
|
67
|
+
f.puts("**#{t[:total]}: #{parser.pomodoros_sum}**")
|
68
|
+
f.puts("\n\n")
|
69
|
+
parser.pomodoros_bars.each do |pomodoro_bar|
|
70
|
+
f.puts(pomodoro_bar)
|
71
|
+
f.puts("\n\n")
|
72
|
+
end
|
73
|
+
f.puts("---\n\n")
|
74
|
+
f.puts("### #{t[:days_bars]}:\n\n")
|
75
|
+
f.puts("**#{t[:pomodoros]}: ⬛ | #{t[:meetings]}: 📅 | #{t[:interruptions]}: ⚠️ | #{t[:difficulties]}: 😓 | #{t[:observations]}: 📝 | #{t[:tasks]}: ✔️**")
|
76
|
+
|
77
|
+
f.puts("\n\n")
|
78
|
+
parser.days_bars.each do |day_bar|
|
79
|
+
f.puts(day_bar)
|
80
|
+
f.puts("\n\n")
|
81
|
+
end
|
82
|
+
|
83
|
+
f.puts("\n\n")
|
84
|
+
end
|
85
|
+
|
86
|
+
editor = Work::Md::Config.editor
|
87
|
+
|
88
|
+
if editor.nil?
|
89
|
+
::TTY::Editor.open(parsed_file_path)
|
90
|
+
else
|
91
|
+
::TTY::Editor.open(parsed_file_path, command: editor)
|
92
|
+
end
|
93
|
+
rescue StandardError => e
|
94
|
+
Work::Md::Cli.help(
|
95
|
+
::TTY::Box.frame(
|
96
|
+
"message: #{e.message}",
|
97
|
+
'',
|
98
|
+
'Usage examples:',
|
99
|
+
'',
|
100
|
+
'work-md parse -d=1 -m=5 -y=2000 | get day 1 from month 5 and year 2000',
|
101
|
+
'work-md parse -d=1,2,3 | get day 1, 2 and 3 from the current month and year',
|
102
|
+
'work-md parse -d=1,2 -m=4 | get day 1 and 2 from month 4 and current year',
|
103
|
+
'work-md parse -d=1..10 -m=4 | get day 1 to 10 from month 4 and current year',
|
104
|
+
**Work::Md::Cli.error_frame_style
|
105
|
+
)
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Work
|
4
|
+
module Md
|
5
|
+
module Commands
|
6
|
+
class Tyesterday
|
7
|
+
class << self
|
8
|
+
def execute(_argv = [])
|
9
|
+
file_names =
|
10
|
+
[DateTime.now, Date.today.prev_day].map do |date|
|
11
|
+
Work::Md::File.create_if_not_exist(date)
|
12
|
+
end
|
13
|
+
|
14
|
+
Work::Md::File.open_in_editor(file_names)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Work
|
6
|
+
module Md
|
7
|
+
module Config
|
8
|
+
DEFAULT_WORK_DIR = "#{Dir.home}/work-md"
|
9
|
+
TRANSLATIONS = {
|
10
|
+
'pt' =>
|
11
|
+
{
|
12
|
+
tasks: 'Atividades',
|
13
|
+
meetings: 'Reuniões',
|
14
|
+
interruptions: 'Interrupções',
|
15
|
+
difficulties: 'Dificuldades',
|
16
|
+
observations: 'Observações',
|
17
|
+
pomodoros: 'Pomodoros / Ciclos',
|
18
|
+
per_day: 'por dia',
|
19
|
+
total: 'total',
|
20
|
+
days_bars: 'Resumo'
|
21
|
+
},
|
22
|
+
'en' =>
|
23
|
+
{
|
24
|
+
tasks: 'Tasks',
|
25
|
+
meetings: 'Meetings',
|
26
|
+
interruptions: 'Interruptions',
|
27
|
+
difficulties: 'Difficulties',
|
28
|
+
observations: 'Observations',
|
29
|
+
pomodoros: 'Pomodoros / Cycles',
|
30
|
+
per_day: 'per day',
|
31
|
+
total: 'all',
|
32
|
+
days_bars: 'Summary'
|
33
|
+
},
|
34
|
+
'es' =>
|
35
|
+
{
|
36
|
+
tasks: 'Tareas',
|
37
|
+
meetings: 'Reuniones',
|
38
|
+
interruptions: 'Interrupciones',
|
39
|
+
difficulties: 'Dificultades',
|
40
|
+
observations: 'Observaciones',
|
41
|
+
pomodoros: 'Pomodoros / Ciclos',
|
42
|
+
per_day: 'por día',
|
43
|
+
total: 'total',
|
44
|
+
days_bars: 'Abstracto'
|
45
|
+
}
|
46
|
+
}.freeze
|
47
|
+
|
48
|
+
def self.title
|
49
|
+
yaml_file['title'] || ''
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.editor
|
53
|
+
ENV['EDITOR'] || ENV['VISUAL'] || yaml_file['editor'] || nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.work_dir
|
57
|
+
ENV['WORK_MD_DIR'] || DEFAULT_WORK_DIR
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.translations
|
61
|
+
TRANSLATIONS[ENV['WORK_MD_LANG']] ||
|
62
|
+
TRANSLATIONS[yaml_file['lang']] ||
|
63
|
+
TRANSLATIONS['en']
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.yaml_file
|
67
|
+
YAML.load_file("#{DEFAULT_WORK_DIR}/config.yml")
|
68
|
+
rescue StandardError
|
69
|
+
{}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/work/md/file.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Work
|
4
|
+
module Md
|
5
|
+
class File
|
6
|
+
def self.open_or_create(some_date, dir: nil)
|
7
|
+
open_in_editor([create_if_not_exist(some_date, dir: dir)], dir: dir)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.create_if_not_exist(some_date, dir: nil)
|
11
|
+
t = Work::Md::Config.translations
|
12
|
+
work_dir = dir || Work::Md::Config.work_dir
|
13
|
+
|
14
|
+
file_name = "#{some_date.strftime('%Y/%m/%d')}.md"
|
15
|
+
|
16
|
+
return file_name if ::File.exist?("#{work_dir}/#{file_name}")
|
17
|
+
|
18
|
+
::FileUtils
|
19
|
+
.mkdir_p("#{work_dir}/#{some_date.strftime('%Y/%m')}")
|
20
|
+
|
21
|
+
::File.open(
|
22
|
+
"#{work_dir}/#{file_name}",
|
23
|
+
'w+'
|
24
|
+
) do |f|
|
25
|
+
f.puts("# #{some_date.strftime('%d/%m/%Y')} - #{Work::Md::Config.title} \n\n")
|
26
|
+
f.puts("### #{t[:tasks]}:\n\n")
|
27
|
+
f.puts("- [ ]\n\n")
|
28
|
+
f.puts("---\n\n")
|
29
|
+
f.puts("### #{t[:meetings]}:\n\n")
|
30
|
+
f.puts("- [ ]\n\n")
|
31
|
+
f.puts("---\n\n")
|
32
|
+
f.puts("### #{t[:interruptions]}:\n\n")
|
33
|
+
f.puts("---\n\n")
|
34
|
+
f.puts("### #{t[:difficulties]}:\n\n")
|
35
|
+
f.puts("---\n\n")
|
36
|
+
f.puts("### #{t[:observations]}:\n\n")
|
37
|
+
f.puts("---\n\n")
|
38
|
+
f.puts("### #{t[:pomodoros]}:\n\n")
|
39
|
+
f.puts("0\n\n")
|
40
|
+
end
|
41
|
+
|
42
|
+
file_name
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.open_in_editor(file_names = [], dir: nil)
|
46
|
+
editor = Work::Md::Config.editor
|
47
|
+
work_dir = dir || Work::Md::Config.work_dir
|
48
|
+
|
49
|
+
::FileUtils.cd(work_dir) do
|
50
|
+
ENV['EDITOR'] = editor unless editor.nil?
|
51
|
+
|
52
|
+
return ::TTY::Editor.open(file_names[0]) if file_names[1].nil?
|
53
|
+
|
54
|
+
::TTY::Editor.open(file_names[0], file_names[1])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Work
|
4
|
+
module Md
|
5
|
+
module Parser
|
6
|
+
# rubocop:disable Metrics/ClassLength
|
7
|
+
class Engine
|
8
|
+
IS_FROZEN_ERROR_MESSAGE = 'Work::Md::Parser::Engine is frozen'
|
9
|
+
IS_NOT_FROZEN_ERROR_MESSAGE = 'Work::Md::Parser::Engine is not frozen'
|
10
|
+
|
11
|
+
class ParsedFile
|
12
|
+
attr_accessor :tasks,
|
13
|
+
:meetings,
|
14
|
+
:interruptions,
|
15
|
+
:difficulties,
|
16
|
+
:observations,
|
17
|
+
:date,
|
18
|
+
:pomodoros
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@t = Work::Md::Config.translations
|
23
|
+
@parsed_files = []
|
24
|
+
@frozen = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_file(file)
|
28
|
+
raise IS_FROZEN_ERROR_MESSAGE if @frozen
|
29
|
+
|
30
|
+
begin
|
31
|
+
file_content = ::File.read(file)
|
32
|
+
rescue Errno::ENOENT
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
return unless file_content.start_with?('# ')
|
37
|
+
|
38
|
+
@parsed_files.push(parse_file_content(file_content))
|
39
|
+
end
|
40
|
+
|
41
|
+
def done_tasks
|
42
|
+
raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
|
43
|
+
|
44
|
+
@done_tasks ||=
|
45
|
+
tasks.select { |t| t.start_with?('x]') || t.start_with?('X]') }
|
46
|
+
end
|
47
|
+
|
48
|
+
def tasks
|
49
|
+
raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
|
50
|
+
|
51
|
+
@tasks ||= @parsed_files.map(&:tasks).flatten
|
52
|
+
end
|
53
|
+
|
54
|
+
def meetings
|
55
|
+
raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
|
56
|
+
|
57
|
+
@meetings ||= @parsed_files.map(&:meetings).flatten
|
58
|
+
end
|
59
|
+
|
60
|
+
def interruptions
|
61
|
+
raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
|
62
|
+
|
63
|
+
@interruptions ||= @parsed_files.map(&:interruptions).flatten
|
64
|
+
end
|
65
|
+
|
66
|
+
def difficulties
|
67
|
+
raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
|
68
|
+
|
69
|
+
@difficulties ||= @parsed_files.map(&:difficulties).flatten
|
70
|
+
end
|
71
|
+
|
72
|
+
def observations
|
73
|
+
raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
|
74
|
+
|
75
|
+
@observations ||= @parsed_files.map(&:observations).flatten
|
76
|
+
end
|
77
|
+
|
78
|
+
def average_pomodoros
|
79
|
+
if @parsed_files.size.positive? && pomodoros_sum.positive?
|
80
|
+
return (pomodoros_sum.to_f / @parsed_files.size).round(1)
|
81
|
+
end
|
82
|
+
|
83
|
+
0
|
84
|
+
end
|
85
|
+
|
86
|
+
def pomodoros_sum
|
87
|
+
raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
|
88
|
+
|
89
|
+
@pomodoros_sum ||=
|
90
|
+
@parsed_files.reduce(0) { |sum, f| sum + f.pomodoros || 0 }
|
91
|
+
end
|
92
|
+
|
93
|
+
def pomodoros_bars(_file = nil)
|
94
|
+
raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
|
95
|
+
|
96
|
+
@pomodoros_bars ||=
|
97
|
+
@parsed_files.map do |f|
|
98
|
+
"(#{f.date}) #{(1..f.pomodoros).map { '⬛' }.join}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
103
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
104
|
+
def days_bars
|
105
|
+
raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
|
106
|
+
|
107
|
+
return @days_bars if @days_bars
|
108
|
+
|
109
|
+
@days_bars ||=
|
110
|
+
@parsed_files.map do |f|
|
111
|
+
pom = (1..f.pomodoros).map { '⬛' }.join
|
112
|
+
mee = f.meetings.map { '📅' }.join
|
113
|
+
int = f.interruptions.map { '⚠️' }.join
|
114
|
+
dif = f.difficulties.map { '😓' }.join
|
115
|
+
obs = f.observations.map { '📝' }.join
|
116
|
+
tas = f.tasks.map { '✔️' }.join
|
117
|
+
|
118
|
+
"(#{f.date}) #{pom}#{mee}#{int}#{dif}#{obs}#{tas}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
122
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
123
|
+
|
124
|
+
def freeze
|
125
|
+
@frozen = true
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def parse_file_content(file_content)
|
131
|
+
parsed_file = ParsedFile.new
|
132
|
+
|
133
|
+
file_content
|
134
|
+
.split('### ')
|
135
|
+
.each { |content| parse_content(parsed_file, content) }
|
136
|
+
|
137
|
+
parsed_file
|
138
|
+
end
|
139
|
+
|
140
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
141
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
142
|
+
def parse_content(parsed_file, content)
|
143
|
+
if content.start_with?('# ')
|
144
|
+
parsed_file.date =
|
145
|
+
content.split(' - ')[0].gsub('# ', '').gsub("\n\n", '')
|
146
|
+
elsif content.start_with?(@t[:tasks])
|
147
|
+
parsed_file.tasks = parse_check_list(content)
|
148
|
+
elsif content.start_with?(@t[:meetings])
|
149
|
+
parsed_file.meetings = parse_check_list(content)
|
150
|
+
elsif content.start_with?(@t[:interruptions])
|
151
|
+
parsed_file.interruptions =
|
152
|
+
parse_list(content).map do |interruption|
|
153
|
+
"(#{parsed_file.date}) #{interruption}"
|
154
|
+
end
|
155
|
+
elsif content.start_with?(@t[:difficulties])
|
156
|
+
parsed_file.difficulties = parse_list(content).map do |difficulty|
|
157
|
+
"(#{parsed_file.date}) #{difficulty}"
|
158
|
+
end
|
159
|
+
elsif content.start_with?(@t[:observations])
|
160
|
+
parsed_file.observations = parse_list(content).map do |observations|
|
161
|
+
"(#{parsed_file.date}) #{observations}"
|
162
|
+
end
|
163
|
+
elsif content.start_with?(@t[:pomodoros])
|
164
|
+
parsed_file.pomodoros = parse_pomodoro(content)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
168
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
169
|
+
|
170
|
+
def parse_check_list(content)
|
171
|
+
clear_list(basic_parse(content).split('- ['))
|
172
|
+
end
|
173
|
+
|
174
|
+
def parse_list(content)
|
175
|
+
clear_list(basic_parse(content).split('- '))
|
176
|
+
end
|
177
|
+
|
178
|
+
def parse_pomodoro(content)
|
179
|
+
basic_parse(content).scan(/\d+/).first.to_i
|
180
|
+
end
|
181
|
+
|
182
|
+
def basic_parse(content)
|
183
|
+
content.split(":\n\n")[1]
|
184
|
+
end
|
185
|
+
|
186
|
+
def clear_list(list)
|
187
|
+
return list unless list.is_a?(Array)
|
188
|
+
|
189
|
+
list
|
190
|
+
.map { |s| s.gsub('---', '') unless s.nil? }
|
191
|
+
.select { |s| (s != '') && (s != "\n\n") }
|
192
|
+
.map(&:strip)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
# rubocop:enable Metrics/ClassLength
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
data/lib/work/md.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'md/version'
|
4
|
+
require_relative 'md/config'
|
5
|
+
require_relative 'md/file'
|
6
|
+
require_relative 'md/commands/today'
|
7
|
+
require_relative 'md/commands/config'
|
8
|
+
require_relative 'md/commands/yesterday'
|
9
|
+
require_relative 'md/commands/tyesterday'
|
10
|
+
require_relative 'md/commands/annotations'
|
11
|
+
require_relative 'md/parser/engine'
|
12
|
+
require_relative 'md/commands/parse'
|
13
|
+
require_relative 'md/cli'
|
14
|
+
require 'date'
|
15
|
+
require 'fileutils'
|
16
|
+
require 'tty-box'
|
17
|
+
require 'tty-editor'
|
18
|
+
|
19
|
+
# work-md == Work::Md (rubygems standards)
|
20
|
+
module Work
|
21
|
+
module Md
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: work-md
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Henrique Fernandez Teixeira
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-09-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tty-box
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.7.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.7.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tty-editor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.7.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.7.0
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- hriqueft@gmail.com
|
44
|
+
executables:
|
45
|
+
- work-md
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- bin/console
|
50
|
+
- bin/setup
|
51
|
+
- bin/work-md
|
52
|
+
- lib/work/md.rb
|
53
|
+
- lib/work/md/cli.rb
|
54
|
+
- lib/work/md/commands/annotations.rb
|
55
|
+
- lib/work/md/commands/config.rb
|
56
|
+
- lib/work/md/commands/parse.rb
|
57
|
+
- lib/work/md/commands/today.rb
|
58
|
+
- lib/work/md/commands/tyesterday.rb
|
59
|
+
- lib/work/md/commands/yesterday.rb
|
60
|
+
- lib/work/md/config.rb
|
61
|
+
- lib/work/md/file.rb
|
62
|
+
- lib/work/md/parser/engine.rb
|
63
|
+
- lib/work/md/version.rb
|
64
|
+
homepage:
|
65
|
+
licenses:
|
66
|
+
- MIT
|
67
|
+
metadata:
|
68
|
+
source_code_uri: https://github.com/work-md/work-md
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.3.0
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubygems_version: 3.2.15
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: Track your work activities, write annotations, recap what you did for a week,
|
88
|
+
month or specific days... and much more!
|
89
|
+
test_files: []
|