work_md 0.1.0 → 0.2.3

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: e1c858bf12a976912f85e9fd1a22b39e2bd5daf4e56ec46299c1297aa4e576cb
4
+ data.tar.gz: 8a542af4507b9765d9317f23ad7db72bc1f4cf8ffe7bc27342ea9c1e8f592e82
5
5
  SHA512:
6
- metadata.gz: 1dc33cce0525aa56a359619cfae40acd1809317844789ebf50adce5d038f410026a3232cf1bbbf15c63d9c9747c010fee84a339ba436d6cc431e7af6f40b8f84
7
- data.tar.gz: 732380d8fa80db98fff04ef5cbd662b789afecc0e68939700a77576fd75731b6339390e303d51b1e0f27a86a0dabf9f0dd6b04a3cb9afcae1376054874aee63c
6
+ metadata.gz: '02389407af0f604e5cf9fac532639b5cb5553179cd98d67fa18aecf56bc182cccbc2bc674cd1f8ca3f5b11ede62d82d8b85320613a55e0cf6d432f273618e06a'
7
+ data.tar.gz: 3983b233410dd07224abf01bf74998c31307780b52b43cdb83495d4745ed83938c72d0f0cb449e34dcdb7cded9e28fd5dec440fedacd823446f8ec0309152457
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,90 @@
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]} (#{parser.tasks.size}):\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]} (#{parser.meetings.size}):\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]} (#{parser.interruptions.size}):\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]} (#{parser.difficulties.size}):\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
+ editor = WorkMd::Config.editor
68
+
69
+ unless editor.nil?
70
+ ::TTY::Editor.open(PARSED_FILE_PATH, {command: editor})
71
+ else
72
+ ::TTY::Editor.open(PARSED_FILE_PATH)
73
+ end
74
+ rescue
75
+ WorkMd::Cli.info(
76
+ ::TTY::Box.frame(
77
+ "Usage examples:",
78
+ "",
79
+ "work_md parse -d=1 -m=5 -y=2000 | get day 1 from month 5 and year 2000",
80
+ "work_md parse -d=1,2,3 | get day 1, 2 and 3 from the current month and year",
81
+ "work_md parse -d=1,2 -m=4 | get day 1 and 2 from month 4 and current year",
82
+ **WorkMd::Cli.error_frame_style
83
+ )
84
+ )
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -4,44 +4,50 @@ 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
+ editor = WorkMd::Config.editor
41
+
42
+ ::FileUtils.cd(work_dir) do
43
+ unless editor.nil?
44
+ ::TTY::Editor.open(
45
+ "#{today.strftime('%Y/%m/%d')}.md",
46
+ {command: editor}
47
+ )
48
+ else
49
+ ::TTY::Editor.open("#{today.strftime('%Y/%m/%d')}.md")
50
+ end
45
51
  end
46
52
  end
47
53
  end
@@ -1,16 +1,55 @@
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
+ TRANSLATIONS = {
9
+ 'pt' =>
10
+ {
11
+ tasks: 'Atividades',
12
+ meetings: 'Reuniões',
13
+ annotations: 'Anotações',
14
+ meeting_annotations: 'Anotações de Reunião',
15
+ interruptions: 'Interrupções',
16
+ difficulties: 'Dificuldades',
17
+ pomodoros: 'Pomodoros'
18
+ },
19
+ 'en' =>
20
+ {
21
+ tasks: 'Tasks',
22
+ meetings: 'Meetings',
23
+ annotations: 'Annotations',
24
+ meeting_annotations: 'Meeting Annotations',
25
+ interruptions: 'Interruptions',
26
+ difficulties: 'Difficulties',
27
+ pomodoros: 'Pomodoros'
28
+ }
29
+ }.freeze
30
+
31
+ def self.title
32
+ yaml_file['title'] || ''
33
+ end
34
+
8
35
  def self.editor
9
- ENV['EDITOR']
36
+ ENV['EDITOR'] || ENV['VISUAL'] || yaml_file['editor'] || nil
10
37
  end
11
38
 
12
39
  def self.work_dir
13
- "#{Dir.home}/work_md/"
40
+ ENV['WORK_MD_DIR'] || DEFAULT_WORK_DIR
41
+ end
42
+
43
+ def self.translations
44
+ TRANSLATIONS[ENV['WORK_MD_LANG']] ||
45
+ TRANSLATIONS[yaml_file['lang']] ||
46
+ TRANSLATIONS['en']
47
+ end
48
+
49
+ def self.yaml_file
50
+ YAML.load_file(DEFAULT_WORK_DIR + '/config.yml')
51
+ rescue StandardError
52
+ {}
14
53
  end
15
54
  end
16
55
  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.3'
5
5
  end
metadata CHANGED
@@ -1,15 +1,43 @@
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.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henrique Fernandez Teixeira
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-06 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2021-07-13 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
13
41
  description:
14
42
  email:
15
43
  - hriqueft@gmail.com
@@ -23,8 +51,10 @@ 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
59
  homepage:
30
60
  licenses: