work_md 0.1.0 → 0.2.3

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