work_md 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b513f11cea5e174a366551222bcd7c3a9ae12494ecdbb7d5f6b326bfe259cd0
4
- data.tar.gz: 96951bcfd495236d08d41a912171caac121afd74413aaf2a920b2985dec7a392
3
+ metadata.gz: f2935dcad2a4d572722fa3c39017ca68b3446fc07bad13fa8c993c7d3a60529f
4
+ data.tar.gz: add270c4aa64a634f4a22ff0f37246940d1216f78fc20b378be7c1a1d374af0e
5
5
  SHA512:
6
- metadata.gz: 1dc33cce0525aa56a359619cfae40acd1809317844789ebf50adce5d038f410026a3232cf1bbbf15c63d9c9747c010fee84a339ba436d6cc431e7af6f40b8f84
7
- data.tar.gz: 732380d8fa80db98fff04ef5cbd662b789afecc0e68939700a77576fd75731b6339390e303d51b1e0f27a86a0dabf9f0dd6b04a3cb9afcae1376054874aee63c
6
+ metadata.gz: a7c21fa7695913b8161a6b85514b69a77bb15a86a6ddc14692f6b9db1178ac8088028c108e94536e29588d899e8b83d63c1d9b52cbdf3a93cdeab7402c5371b7
7
+ data.tar.gz: 3ebadf962e40746eaaf6005005e2608af36d968c6bbfb675cd0dbc37b8dfb409dbeb2aab0902df45f3a5f6bb70684248acaebe28039db166ef20d848af3ccf5f
data/lib/work_md.rb CHANGED
@@ -3,9 +3,13 @@
3
3
  require_relative 'work_md/version'
4
4
  require_relative 'work_md/config'
5
5
  require_relative 'work_md/commands/today'
6
+ require_relative 'work_md/parser/engine'
7
+ require_relative 'work_md/commands/parse'
6
8
  require_relative 'work_md/cli'
7
9
  require 'date'
8
10
  require 'fileutils'
11
+ require 'tty-box'
12
+ require 'tty-editor'
9
13
 
10
14
  module WorkMd
11
15
  end
data/lib/work_md/cli.rb CHANGED
@@ -6,7 +6,8 @@ module WorkMd
6
6
 
7
7
  ALIAS_COMMANDS =
8
8
  {
9
- 't' => 'today'
9
+ 't' => 'today',
10
+ 'p' => 'parse'
10
11
  }.freeze
11
12
 
12
13
  DEFAULT_COMMAND = WorkMd::Commands::Today
@@ -23,17 +24,40 @@ module WorkMd
23
24
  .const_get("WorkMd::Commands::#{command}")
24
25
  .send(:execute, argv)
25
26
  rescue NameError
26
- error("Command '#{first_argv_argument}' not found!")
27
+ puts info(
28
+ ::TTY::Box.frame(
29
+ "Command '#{first_argv_argument}' not found!",
30
+ **error_frame_style
31
+ )
32
+ )
27
33
  rescue CommandMissing
28
34
  DEFAULT_COMMAND.execute(argv)
29
35
  end
30
36
 
31
- def self.error(message)
32
- puts 'x - work_md error ------ x'
33
- puts ''
34
- puts message
35
- puts ''
36
- puts 'x ---------------------- x'
37
+ def self.info(message = '')
38
+ # rubocop:disable Layout/LineLength
39
+ puts ::TTY::Box.frame(
40
+ message,
41
+ 'Track your work activities, write annotations, recap what you did for a week, month or specific days... and much more!',
42
+ '',
43
+ 'commands available:',
44
+ '',
45
+ '- work_md',
46
+ '- work_md today',
47
+ '- work_md parse',
48
+ '',
49
+ 'read more in github.com/henriquefernandez/work_md',
50
+ padding: 1,
51
+ title: { top_left: '(work_md)', bottom_right: "(v#{WorkMd::VERSION})" }
52
+ )
53
+ # rubocop:enable Layout/LineLength
54
+ end
55
+
56
+ def self.error_frame_style
57
+ {
58
+ padding: 1,
59
+ title: { top_left: '(error)' }
60
+ }
37
61
  end
38
62
  end
39
63
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkMd
4
+ module Commands
5
+ class Parse
6
+ PARSED_FILE_PATH = WorkMd::Config.work_dir + '/parsed.md'
7
+
8
+ class << self
9
+ def execute(argv = [])
10
+ begin
11
+ args = Hash[argv.join(' ').scan(/-?([^=\s]+)(?:=(\S+))?/)]
12
+ parser = WorkMd::Parser::Engine.new
13
+ t = WorkMd::Config.translations
14
+
15
+ year = args['y'] || Time.new.year
16
+ month = args['m'] || Time.new.month
17
+
18
+ month = "0#{month.to_i}" if month.to_i < 10
19
+
20
+ args['d'].split(',').each do |day|
21
+ day = "0#{day.to_i}" if day.to_i < 10
22
+
23
+ file_name = WorkMd::Config.work_dir + "/#{year}/#{month}/#{day}.md"
24
+
25
+ parser.add_file(file_name)
26
+ end
27
+
28
+ parser.freeze
29
+
30
+ File.delete(PARSED_FILE_PATH) if File.exist? PARSED_FILE_PATH
31
+
32
+ File.open(PARSED_FILE_PATH, 'w+') do |f|
33
+ f.puts("# #{WorkMd::Config.title}\n\n")
34
+ f.puts("### #{t[:tasks]}:\n\n")
35
+ parser.tasks.each do |task|
36
+ f.puts("- [#{task}\n\n") if task != ' ]'
37
+ end
38
+ f.puts("---\n\n")
39
+ f.puts("### #{t[:meetings]}:\n\n")
40
+ parser.meetings.each do |meeting|
41
+ f.puts("- #{meeting}\n\n")
42
+ end
43
+ f.puts("---\n\n")
44
+ f.puts("### #{t[:annotations]}:\n\n")
45
+ parser.annotations.each do |annotation|
46
+ f.puts("- #{annotation.gsub('###', '')}") unless annotation.nil?
47
+ end
48
+ f.puts("###### #{t[:meeting_annotations]}:\n\n")
49
+ parser.meeting_annotations.each do |meeting_annotation|
50
+ f.puts("- #{meeting_annotation}\n\n")
51
+ end
52
+ f.puts("---\n\n")
53
+ f.puts("### #{t[:interruptions]}:\n\n")
54
+ parser.interruptions.each do |interruption|
55
+ f.puts("- #{interruption}\n\n")
56
+ end
57
+ f.puts("---\n\n")
58
+ f.puts("### #{t[:difficulties]}:\n\n")
59
+ parser.difficulties.each do |difficulty|
60
+ f.puts("- #{difficulty}\n\n")
61
+ end
62
+ f.puts("---\n\n")
63
+ f.puts("### #{t[:pomodoros]}:\n\n")
64
+ f.puts(parser.pomodoros)
65
+ end
66
+
67
+ ::TTY::Editor.open(PARSED_FILE_PATH)
68
+ rescue
69
+ WorkMd::Cli.info(
70
+ ::TTY::Box.frame(
71
+ "Usage examples:",
72
+ "",
73
+ "work_md parse -d=1 -m=5 -y=2000 | get day 1 from month 5 and year 2000",
74
+ "work_md parse -d=1,2,3 | get day 1, 2 and 3 from the current month and year",
75
+ "work_md parse -d=1,2 -m=4 | get day 1 and 2 from month 4 and current year",
76
+ **WorkMd::Cli.error_frame_style
77
+ )
78
+ )
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -4,44 +4,41 @@ module WorkMd
4
4
  module Commands
5
5
  class Today
6
6
  class << self
7
- def description; end
8
-
9
7
  def execute(_argv = [])
10
8
  today = DateTime.now
9
+ t = WorkMd::Config.translations
10
+ work_dir = WorkMd::Config.work_dir
11
11
 
12
12
  ::FileUtils
13
- .mkdir_p("#{WorkMd::Config.work_dir}/#{today.strftime('%Y/%m')}")
13
+ .mkdir_p("#{work_dir}/#{today.strftime('%Y/%m')}")
14
14
  unless ::File
15
15
  .exist?(
16
- "#{WorkMd::Config.work_dir}/#{today.strftime('%Y/%m/%d')}.md"
16
+ "#{work_dir}/#{today.strftime('%Y/%m/%d')}.md"
17
17
  )
18
18
  ::File.open(
19
- "#{WorkMd::Config.work_dir}/#{today.strftime('%Y/%m/%d')}.md",
19
+ "#{work_dir}/#{today.strftime('%Y/%m/%d')}.md",
20
20
  'w+'
21
21
  ) do |f|
22
- f.puts("# #{today.strftime('%d/%m/%Y')} \n\n")
23
- f.puts("### Atividades:\n\n")
22
+ f.puts("# #{today.strftime('%d/%m/%Y')} - #{WorkMd::Config.title} \n\n")
23
+ f.puts("### #{t[:tasks]}:\n\n")
24
24
  f.puts("- [ ]\n\n")
25
25
  f.puts("---\n\n")
26
- f.puts("### Reuniões:\n\n")
26
+ f.puts("### #{t[:meetings]}:\n\n")
27
27
  f.puts("---\n\n")
28
- f.puts("### Anotações:\n\n")
29
- f.puts("###### Anotações de Reunião:\n\n")
28
+ f.puts("### #{t[:annotations]}:\n\n")
29
+ f.puts("###### #{t[:meeting_annotations]}:\n\n")
30
30
  f.puts("---\n\n")
31
- f.puts("### Interrupções:\n\n")
31
+ f.puts("### #{t[:interruptions]}:\n\n")
32
32
  f.puts("---\n\n")
33
- f.puts("### Dificuldades:\n\n")
33
+ f.puts("### #{t[:difficulties]}:\n\n")
34
34
  f.puts("---\n\n")
35
- f.puts("### Pomodoros:\n\n")
35
+ f.puts("### #{t[:pomodoros]}:\n\n")
36
36
  f.puts("0\n\n")
37
- f.puts("---\n\n")
38
- f.puts("### Ponto:\n\n")
39
- f.puts("- \n\n")
40
37
  end
41
38
  end
42
39
 
43
- ::FileUtils.cd(WorkMd::Config.work_dir) do
44
- system("#{WorkMd::Config.editor} #{today.strftime('%Y/%m/%d')}.md")
40
+ ::FileUtils.cd(work_dir) do
41
+ ::TTY::Editor.open("#{today.strftime('%Y/%m/%d')}.md")
45
42
  end
46
43
  end
47
44
  end
@@ -1,16 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # TODO: Make this a class with receive an yaml file directory,
4
- # this is easier to mock in test
5
- # TODO: Set language (i18n) as a config
3
+ require 'yaml'
4
+
6
5
  module WorkMd
7
6
  module Config
7
+ DEFAULT_WORK_DIR = Dir.home + '/work_md'
8
+ YAML_FILE = YAML.load_file(DEFAULT_WORK_DIR + '/config.yml')
9
+ DEFAULT_EDITOR = 'vi'
10
+ TRANSLATIONS = {
11
+ 'pt' =>
12
+ {
13
+ tasks: 'Atividades',
14
+ meetings: 'Reuniões',
15
+ annotations: 'Anotações',
16
+ meeting_annotations: 'Anotações de Reunião',
17
+ interruptions: 'Interrupções',
18
+ difficulties: 'Dificuldades',
19
+ pomodoros: 'Pomodoros'
20
+ },
21
+ 'en' =>
22
+ {
23
+ tasks: 'Tasks',
24
+ meetings: 'Meetings',
25
+ annotations: 'Annotations',
26
+ meeting_annotations: 'Meeting Annotations',
27
+ interruptions: 'Interruptions',
28
+ difficulties: 'Difficulties',
29
+ pomodoros: 'Pomodoros'
30
+ }
31
+ }.freeze
32
+
33
+ def self.title
34
+ YAML_FILE['title'] || ''
35
+ end
36
+
8
37
  def self.editor
9
- ENV['EDITOR']
38
+ ENV['EDITOR'] || ENV['VISUAL'] || YAML_FILE['editor'] || DEFAULT_EDITOR
10
39
  end
11
40
 
12
41
  def self.work_dir
13
- "#{Dir.home}/work_md/"
42
+ ENV['WORK_MD_DIR'] || DEFAULT_WORK_DIR
43
+ end
44
+
45
+ def self.translations
46
+ TRANSLATIONS[ENV['WORK_MD_LANG']] ||
47
+ TRANSLATIONS[YAML_FILE['lang']] ||
48
+ TRANSLATIONS['en']
14
49
  end
15
50
  end
16
51
  end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkMd
4
+ module Parser
5
+ class Engine
6
+ IS_FROZEN_ERROR_MESSAGE = 'WorkMd::Parser::Engine is frozen'
7
+ IS_NOT_FROZEN_ERROR_MESSAGE = 'WorkMd::Parser::Engine is not frozen'
8
+
9
+ class ParsedFile
10
+ attr_accessor :tasks,
11
+ :annotations,
12
+ :meeting_annotations,
13
+ :meetings,
14
+ :interruptions,
15
+ :difficulties,
16
+ :pomodoros
17
+ end
18
+
19
+ def initialize
20
+ @t = WorkMd::Config.translations
21
+ @parsed_files = []
22
+ @frozen = false
23
+ end
24
+
25
+ def add_file(file)
26
+ raise IS_FROZEN_ERROR_MESSAGE if @frozen
27
+
28
+ begin
29
+ file_content = File.read(file)
30
+ rescue Errno::ENOENT
31
+ return
32
+ end
33
+
34
+ return unless file_content.start_with?('# ')
35
+
36
+ @parsed_files.push(parse_file_content(file_content))
37
+ end
38
+
39
+ def done_tasks
40
+ raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
41
+
42
+ @done_tasks ||=
43
+ tasks.filter { |t| t.start_with?('x]') || t.start_with?('X]') }
44
+ end
45
+
46
+ def tasks
47
+ raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
48
+
49
+ @tasks ||= @parsed_files.map(&:tasks).flatten
50
+ end
51
+
52
+ def annotations
53
+ raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
54
+
55
+ @annotations ||= @parsed_files.map(&:annotations).flatten
56
+ end
57
+
58
+ def meeting_annotations
59
+ raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
60
+
61
+ @meeting_annotations ||=
62
+ @parsed_files.map(&:meeting_annotations).flatten
63
+ end
64
+
65
+ def meetings
66
+ raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
67
+
68
+ @meetings ||= @parsed_files.map(&:meetings).flatten
69
+ end
70
+
71
+ def interruptions
72
+ raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
73
+
74
+ @interruptions ||= @parsed_files.map(&:interruptions).flatten
75
+ end
76
+
77
+ def difficulties
78
+ raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
79
+
80
+ @difficulties ||= @parsed_files.map(&:difficulties).flatten
81
+ end
82
+
83
+ def pomodoros
84
+ raise IS_NOT_FROZEN_ERROR_MESSAGE unless @frozen
85
+
86
+ @pomodoros ||=
87
+ @parsed_files.reduce(0) { |sum, f| sum + f.pomodoros || 0 }
88
+ end
89
+
90
+ def freeze
91
+ @frozen = true
92
+ end
93
+
94
+ private
95
+
96
+ def parse_file_content(file_content)
97
+ parsed_file = ParsedFile.new
98
+
99
+ file_content
100
+ .split('### ')
101
+ .each { |content| parse_content(parsed_file, content) }
102
+
103
+ parsed_file
104
+ end
105
+
106
+ # rubocop:disable Metrics/CyclomaticComplexity
107
+ # rubocop:disable Metrics/PerceivedComplexity
108
+ def parse_content(parsed_file, content)
109
+ if content.start_with?(@t[:tasks])
110
+ parsed_file.tasks = parse_task_list(content)
111
+ elsif content.start_with?(@t[:meetings])
112
+ parsed_file.meetings = parse_list(content)
113
+ elsif content.start_with?(@t[:meeting_annotations])
114
+ parsed_file.meeting_annotations = basic_parse(content)
115
+ elsif content.start_with?(@t[:annotations])
116
+ parsed_file.annotations = basic_parse(content)
117
+ elsif content.start_with?(@t[:interruptions])
118
+ parsed_file.interruptions = parse_list(content)
119
+ elsif content.start_with?(@t[:difficulties])
120
+ parsed_file.difficulties = parse_list(content)
121
+ elsif content.start_with?(@t[:pomodoros])
122
+ parsed_file.pomodoros = parse_pomodoro(content)
123
+ end
124
+ end
125
+ # rubocop:enable Metrics/CyclomaticComplexity
126
+ # rubocop:enable Metrics/PerceivedComplexity
127
+
128
+ def parse_task_list(content)
129
+ clear_list(basic_parse(content).split('- ['))
130
+ end
131
+
132
+ def parse_list(content)
133
+ clear_list(basic_parse(content).split('- '))
134
+ end
135
+
136
+ def parse_pomodoro(content)
137
+ basic_parse(content).scan(/\d+/).first.to_i
138
+ end
139
+
140
+ def basic_parse(content)
141
+ content.split(":\n\n")[1]
142
+ end
143
+
144
+ def clear_list(list)
145
+ return list unless list.is_a?(Array)
146
+
147
+ list
148
+ .map { |s| s.gsub('---', '') unless s.nil? }
149
+ .filter { |s| (s != '') && (s != "\n\n") }
150
+ .map(&:strip)
151
+ end
152
+ end
153
+ end
154
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WorkMd
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,16 +1,44 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: work_md
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henrique Fernandez Teixeira
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-06 00:00:00.000000000 Z
12
- dependencies: []
13
- description:
11
+ date: 2021-07-11 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:
14
42
  email:
15
43
  - hriqueft@gmail.com
16
44
  executables:
@@ -23,15 +51,17 @@ files:
23
51
  - bin/work_md
24
52
  - lib/work_md.rb
25
53
  - lib/work_md/cli.rb
54
+ - lib/work_md/commands/parse.rb
26
55
  - lib/work_md/commands/today.rb
27
56
  - lib/work_md/config.rb
57
+ - lib/work_md/parser/engine.rb
28
58
  - lib/work_md/version.rb
29
- homepage:
59
+ homepage:
30
60
  licenses:
31
61
  - MIT
32
62
  metadata:
33
63
  source_code_uri: https://github.com/henriquefernandez/work_md
34
- post_install_message:
64
+ post_install_message:
35
65
  rdoc_options: []
36
66
  require_paths:
37
67
  - lib
@@ -46,8 +76,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
46
76
  - !ruby/object:Gem::Version
47
77
  version: '0'
48
78
  requirements: []
49
- rubygems_version: 3.2.15
50
- signing_key:
79
+ rubygems_version: 3.1.6
80
+ signing_key:
51
81
  specification_version: 4
52
82
  summary: Track your work activities, write annotations, recap what you did for a week,
53
83
  month or specific days... and much more!