hbtrack 0.0.5 → 0.0.6

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
  SHA1:
3
- metadata.gz: 202f4fe27cf01abbf78f19ca7a3fd075e151c9ad
4
- data.tar.gz: d0433a6543c279963e4876f372f0147d23b376fe
3
+ metadata.gz: 36d2ea881a273459943278a8ed55e05162544426
4
+ data.tar.gz: 05f7cb9199065ab8eca1f558a4d7bda5dc971341
5
5
  SHA512:
6
- metadata.gz: 33653f712de657b5fd7e40192a0d371f540a121740c265544c7dde950c221d7d8d58343e53d08105652de0e2e9f86af8344ec04e02cfa063aeb80e5a183d1565
7
- data.tar.gz: be1d05fc272fbcf4a31ec40c92c5a7b49f4aa6ca5519424459b8e70963d7f581866b1265302f9b5896e8efe4f512fb0ca19a8b240633b8dbbc20428b6f62a68d
6
+ metadata.gz: 17b23168d92bb400ff17d5b6685268fb5aafbf3ed2f27e9a8685dea89e56316e3af7a4af0ab23f93ce1b525aab0d9746b84ac76714df560eb33ea03cf22cb5ba
7
+ data.tar.gz: 73b292c9e40a08ed498879ebba2c1c88f82f17a5d902c84aa4074f404341b01e457bbdcfe96729bf32c8f2de87dedb80bc78e92ee3792a4c5a18d736633d68e4
data/.gitignore CHANGED
@@ -11,6 +11,6 @@ test/test_output
11
11
  /spec/reports/
12
12
  /tmp/
13
13
  *.gem
14
- *.gemspec
14
+ *.html
15
15
 
16
16
 
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.4.2
5
+ script: "bundle exec rspec"
6
+
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ## Hbtrack
1
+ # Hbtrack [![Gem Version](https://badge.fury.io/rb/hbtrack.svg)](https://badge.fury.io/rb/hbtrack) [![Build Status](https://travis-ci.org/kw7oe/hbtrack.svg?branch=master)](https://travis-ci.org/kw7oe/hbtrack)
2
2
 
3
3
  `hbtrack` is a simple command lines tool to keep track of your daily habits. The functionality of the current version is very limited.
4
4
 
@@ -10,42 +10,36 @@ gem install hbtrack
10
10
 
11
11
  ## Usage
12
12
 
13
- #### Add a new habit
14
13
  ```
15
- $ hbtrack add habit_name
16
- ```
17
- Duplicate habit name will be ignore.
14
+ Usage: hbtrack <command> [<habit_name>] [options]
18
15
 
19
- #### Mark habit as done/undone
20
- ```
21
- $ hbtrack done habit_name
22
- ```
23
- This will mark the current habit as done for the current day.
16
+ Commands:
17
+ add: Add habit(s)
18
+ remove: Remove habit(s)
19
+ list: List habit(s)
20
+ done: Mark habit(s) as done
21
+ undone: Mark habit(s) as undone
24
22
 
23
+ Options:
24
+ -h, --help Show help messages of the command
25
25
  ```
26
- $ hbtrack undone habit_name
27
- ```
28
- This will mark the current habit as undone for the current day.
29
26
 
30
- You can also mark your habit done/undone for the previous day by adding `-y` or `--yesterday` option:
31
- ```
32
- $ hbtrack done/undone -y habit_name
33
- ```
27
+ **Note:** For more details about available options for each command, type `hbtrack <command> -h`
34
28
 
35
- #### Remove a habit
36
- ```
37
- $ hbtrack remove habit_name
38
- ```
29
+ ## Features
30
+ - [x] List all habits
31
+ - with completion rate or total/done/undone count
32
+ - [x] Add single or multiple habits at once
33
+ - [x] Mark:
34
+ - single or mutiple habits as done/undone
35
+ - remaining habits of as done/undone
36
+ - habit(s) as done for specific day with `--day DAY`
37
+ - [x] Remove single or multiple habits at once
38
+ - [x] Generate report in HTML format. *(In Progress)*
39
39
 
40
- #### Listing Progress
40
+ ## Output
41
41
 
42
- You can list all your habits progress by:
43
- ```
44
- $ hbtrack list
45
- ```
46
- This will list all the habits you added and its progress for the current month.
47
-
48
- **Output:**
42
+ ### List all habits:
49
43
  ```
50
44
  August 2017
51
45
  -----------
@@ -62,12 +56,7 @@ All: 15, Done: 13, Undone: 2
62
56
 
63
57
  **Note:** The actual output is colorized where green color font indicate done and red color font indicate undone.
64
58
 
65
- You can also look at the progress of an individual habit by:
66
- ```
67
- $ hbtrack list habit_name
68
- ```
69
-
70
- **Output:**
59
+ ### List a habit:
71
60
  ```
72
61
  workout
73
62
  -------
@@ -75,13 +64,11 @@ workout
75
64
  August 2017 : *** All: 3, Done: 3, Undone: 0
76
65
  ```
77
66
 
67
+ ### List all habits with `-p`:
78
68
 
79
69
  Extra options such as `-p` or `--percentage` can be provided to list the stats of your habits in terms of completion rate.
80
- ```
81
- $ hbtrack list -p habit_name
82
- ```
83
70
 
84
- With the extra options `-p`, the output will be:
71
+
85
72
  ```
86
73
  August 2017
87
74
  -----------
@@ -118,6 +105,7 @@ The first line represent the name of the habit. The second rows onward represent
118
105
  * `2017,6` represent your progress during June 2017.
119
106
  * `1` is used to represent done.
120
107
  * `0` is used to represent undone.
108
+ * Black space is used to represent not recorded/dayoff.
121
109
  * Each habit is seperated by a newline. For example:
122
110
 
123
111
  ```
@@ -129,7 +117,7 @@ read
129
117
  ```
130
118
 
131
119
  ## Bugs/Features
132
- The project is still in development.
120
+ The project is still under development.
133
121
 
134
122
  If there are any bugs/features request, feel free to create a new issues.
135
123
 
data/hbtrack.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path('../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require 'hbtrack/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'hbtrack'
10
+ spec.version = Hbtrack::VERSION
11
+ spec.authors = ['kw7oe']
12
+ spec.email = ['choongkwern@hotmail.com']
13
+
14
+ spec.summary = 'A CLI to track your habits.'
15
+ spec.description = 'Habit Tracker CLI'
16
+ spec.homepage = 'https://github.com/kw7oe/hbtrack'
17
+ spec.license = 'MIT'
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
20
+ f.match(%r{^(test|spec|features)/})
21
+ end
22
+ spec.bindir = 'exe'
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ['lib']
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.14'
27
+ spec.add_development_dependency 'minitest', '~> 5.10'
28
+ spec.add_development_dependency 'rspec', '~> 3.6'
29
+ spec.add_development_dependency 'rake', '~> 10.0'
30
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require 'hbtrack/command'
5
+ require 'hbtrack/store'
6
+
7
+ module Hbtrack
8
+ class AddCommand < Command
9
+ def initialize(hbt, options)
10
+ super(hbt, options)
11
+ end
12
+
13
+ def execute
14
+ return add(@names) unless @names.empty?
15
+ super
16
+ end
17
+
18
+ def create_option_parser
19
+ OptionParser.new do |opts|
20
+ opts.banner = 'Usage: hbtrack add [<habit_name>]'
21
+ end
22
+ end
23
+
24
+ def add(names)
25
+ added = []
26
+ names.each do |name|
27
+ @hbt.find(name) do
28
+ @hbt.create(name)
29
+ added << name
30
+ end
31
+ end
32
+
33
+ Store.new(@hbt.habits, @hbt.output_file_name).save
34
+
35
+ output = [
36
+ Hbtrack::Util.green("Add #{added.join(',')}!")
37
+ ]
38
+
39
+ names -= added
40
+ unless names.empty?
41
+ output << "\n"
42
+ output << Hbtrack::Util.blue("#{names.join(',')} already existed!")
43
+ end
44
+
45
+ output.join
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require 'hbtrack/command'
5
+
6
+ module Hbtrack
7
+ class ListCommand < Command
8
+ attr_reader :printer, :formatter
9
+
10
+ def initialize(hbt, options)
11
+ @percentage = false
12
+
13
+ super(hbt, options)
14
+ @formatter = @percentage ? CompletionRateSF.new : CompleteSF.new
15
+ @printer = HabitPrinter.new(@formatter)
16
+ end
17
+
18
+ def execute
19
+ return list_all(@printer) if @all
20
+ return list(@names[0], @printer) unless @names.empty?
21
+ super
22
+ end
23
+
24
+ def create_option_parser
25
+ OptionParser.new do |opts|
26
+ opts.banner = 'Usage: hbtrack list [<habit_name>] [options]'
27
+ opts.separator ''
28
+ opts.separator 'Options:'
29
+
30
+ opts.on('-p', '--percentage', 'List habit(s) with completion rate') do
31
+ @percentage = true
32
+ end
33
+
34
+ opts.on('-a', '--all', 'List all habits') do
35
+ @all = true
36
+ end
37
+
38
+ opts.on_tail('-h', '--help', 'Prints this help') do
39
+ puts opts
40
+ exit
41
+ end
42
+ end
43
+ end
44
+
45
+ def list(name, printer)
46
+ habit = @hbt.find(name) do
47
+ return ErrorHandler.raise_habit_not_found(name)
48
+ end
49
+
50
+ title = Util.title habit.name
51
+ progress = printer.print_all_progress(habit)
52
+ footer = "\n" + habit.overall_stat_description(printer.formatter)
53
+
54
+ "#{title}#{progress}\n#{footer}"
55
+ end
56
+
57
+ def list_all(printer)
58
+ return Util.blue 'No habits added yet.' if @hbt.habits.empty?
59
+
60
+ title = Util.title Util.current_month
61
+ progress = @hbt.habits.each_with_index.map do |h, index|
62
+ space = @hbt.longest_name.length - h.name_length
63
+ "#{index + 1}. " \
64
+ "#{printer.print_latest_progress(h, space)}"
65
+ end.join("\n")
66
+ footer = "\n" + @hbt.overall_stat_description(@formatter)
67
+
68
+ "#{title}#{progress}\n#{footer}"
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require 'hbtrack/command'
5
+ require 'hbtrack/store'
6
+
7
+ module Hbtrack
8
+ class RemoveCommand < Command
9
+ def initialize(hbt, options)
10
+ super(hbt, options)
11
+ end
12
+
13
+ def execute
14
+ return remove(@names) if @names
15
+ super
16
+ end
17
+
18
+ def create_option_parser
19
+ OptionParser.new do |opts|
20
+ opts.banner = 'Usage: hbtrack remove [<habit_name>]'
21
+ end
22
+ end
23
+
24
+ def remove(names)
25
+ names.each do |name|
26
+ habit = @hbt.find(name) do
27
+ return ErrorHandler.raise_if_habit_error(name)
28
+ end
29
+
30
+ @hbt.habits.delete(habit)
31
+ end
32
+
33
+ Store.new(@hbt.habits, @hbt.output_file_name).save
34
+ Hbtrack::Util.blue("Remove #{names.join(',')}!")
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require 'hbtrack/command'
5
+ require 'hbtrack/store'
6
+
7
+ module Hbtrack
8
+ class UpdateCommand < Command
9
+ def initialize(hbt, options, is_done)
10
+ @day = Date.today
11
+ @is_done = is_done
12
+ @remaining = false
13
+ super(hbt, options)
14
+ end
15
+
16
+ def execute
17
+ return update_remaining(@day, @is_done) if @remaining
18
+ return update_all(@day, @is_done) if @all
19
+ return update(@names, @day, @is_done) if @names
20
+ super
21
+ end
22
+
23
+ def create_option_parser
24
+ action = @is_done ? 'Done' : 'Undone'
25
+ OptionParser.new do |opts|
26
+ opts.banner = "Usage: hbtrack #{action.downcase} [<habit_name>] [options]"
27
+ opts.separator ''
28
+ opts.separator 'Options:'
29
+ opts.on('-a', '--all', "#{action} all habits") do
30
+ @all = true
31
+ end
32
+
33
+ opts.on('-r', '--remaining', "#{action} remaining habits") do
34
+ @remaining = true
35
+ end
36
+
37
+ opts.on('-y', '--yesterday', "#{action} habit(s) for yesterday") do
38
+ @day = Date.today - 1
39
+ end
40
+
41
+ opts.on('--day DAY', OptionParser::OctalInteger, "#{action} habit(s) for specific day") do |day|
42
+ @day = Date.new(Date.today.year, Date.today.month, day.to_i)
43
+ end
44
+
45
+ opts.on('-h', '--help', 'Prints this help') do
46
+ puts opts
47
+ exit
48
+ end
49
+ end
50
+ end
51
+
52
+ def update(names, day, is_done)
53
+ names.each do |name|
54
+ habit = if is_done
55
+ @hbt.find_or_create(name)
56
+ else
57
+ @hbt.find(name) do
58
+ return ErrorHandler.raise_if_habit_error(name)
59
+ end
60
+ end
61
+
62
+ habit.done(is_done, day)
63
+ end
64
+
65
+ Store.new(@hbt.habits, @hbt.output_file_name).save
66
+
67
+ Hbtrack::Util.green("#{action(is_done)} #{names.join(',')}!")
68
+ end
69
+
70
+ def update_all(day, is_done)
71
+ @hbt.habits.each { |habit| habit.done(is_done, day) }
72
+
73
+ Store.new(@hbt.habits, @hbt.output_file_name).save
74
+
75
+ Hbtrack::Util.green("#{action(is_done)} all habits!")
76
+ end
77
+
78
+ def update_remaining(day, is_done)
79
+ @hbt.habits.each do |habit|
80
+ habit.done(is_done, day) if habit.done_for(date: day).nil?
81
+ end
82
+
83
+ Store.new(@hbt.habits, @hbt.output_file_name).save
84
+
85
+ Hbtrack::Util.green("#{action(is_done)} remaining habit(s)!")
86
+ end
87
+
88
+ private
89
+
90
+ def action(is_done)
91
+ is_done ? 'Done' : 'Undone'
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hbtrack
4
+ class Command
5
+ def initialize(hbt, options)
6
+ @hbt = hbt
7
+ @all = false
8
+ @option_parser = create_option_parser
9
+
10
+ unprocess_option = @option_parser.parse(options)
11
+ @names = unprocess_option
12
+ end
13
+
14
+ def execute
15
+ help
16
+ end
17
+
18
+ def create_option_parser
19
+ raise 'Not Implemented'
20
+ end
21
+
22
+ def help
23
+ @option_parser.help
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hbtrack
4
+ class ErrorHandler
5
+ class << self
6
+ def raise_error_msg(msg)
7
+ Util.red msg
8
+ end
9
+
10
+ def raise_if_habit_error(habit_name)
11
+ return raise_invalid_arguments if habit_name.nil?
12
+ raise_habit_not_found(habit_name)
13
+ end
14
+
15
+ def raise_habit_not_found(habit_name)
16
+ raise_error_msg "Invalid habit: #{habit_name} not found."
17
+ end
18
+
19
+ def raise_invalid_arguments
20
+ raise_error_msg 'Invalid argument: habit_name is expected.'
21
+ end
22
+
23
+ def raise_habit_name_too_long
24
+ raise_error_msg 'habit_name too long.'
25
+ end
26
+ end
27
+ end
28
+ end
data/lib/hbtrack/habit.rb CHANGED
@@ -1,14 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'date'
4
+ require 'hbtrack/util'
4
5
 
5
- module Hbtrack
6
-
6
+ module Hbtrack
7
7
  # Habit class
8
8
  class Habit
9
- attr_accessor :name
10
- attr_reader :progress
11
-
9
+ attr_accessor :name, :progress
12
10
  # Class Methods
13
11
 
14
12
  class << self
@@ -66,14 +64,12 @@ module Hbtrack
66
64
  end
67
65
 
68
66
  # Get the latest progress key.
69
- #
67
+ #
70
68
  # @return [Symbol] latest progress key
71
- def latest_key
69
+ def latest_key
72
70
  key = Habit.get_progress_key_from(Date.today)
73
- if progress[key].nil?
74
- initialize_progress_hash_from(key)
75
- end
76
- key
71
+ initialize_progress_hash_from(key) if progress[key].nil?
72
+ key
77
73
  end
78
74
 
79
75
  # Get the latest progress.
@@ -90,12 +86,12 @@ module Hbtrack
90
86
  stat_for_progress(latest_key)
91
87
  end
92
88
 
93
- # Find the month in progress with
89
+ # Find the month in progress with
94
90
  # the longest name
95
- #
91
+ #
96
92
  # @return [String] month
97
93
  def longest_month
98
- key = progress.keys.max_by do |x|
94
+ key = progress.keys.max_by do |x|
99
95
  Util.get_month_from(x).length
100
96
  end
101
97
  Util.get_month_from(key)
@@ -103,7 +99,7 @@ module Hbtrack
103
99
 
104
100
  # Update the status of the progress
105
101
  #
106
- # @param done [true, false] If true, it is marked as done.
102
+ # @param done [true, false] If true, it is marked as done.
107
103
  # Else, marked as undone.
108
104
  # @param date [Date] The date of the progress
109
105
  # @return [void]
@@ -113,17 +109,25 @@ module Hbtrack
113
109
  update_progress_for(key, date.day, done)
114
110
  end
115
111
 
112
+ # TODO: test needed
113
+ # Get the done status for specific date
114
+ #
115
+ # @return [Integer]
116
+ def done_for(date:) # TODO: Test needed
117
+ latest_progress.split('')[date.day - 1]
118
+ end
119
+
116
120
  # Get the stat of the progress.
117
121
  #
118
122
  # @param key [Symbol] key for the progress
119
- # @return [Hash] stat of the progress in the form of
123
+ # @return [Hash] stat of the progress in the form of
120
124
  # == Example:
121
125
  #
122
126
  # habit.stat_for_progress("2017,5".to_sym)
123
127
  # # => { done: 5, undone: 2 }
124
128
  def stat_for_progress(key)
125
129
  undone = @progress[key].split('').count { |x| x == '0' }
126
- done = @progress[key].length - undone
130
+ done = @progress[key].split('').count { |x| x == '1' }
127
131
  { done: done, undone: undone }
128
132
  end
129
133
 
@@ -141,11 +145,28 @@ module Hbtrack
141
145
  arr.join('')
142
146
  end
143
147
 
148
+ def overall_stat
149
+ done = 0
150
+ undone = 0
151
+ @progress.each do |key, _value|
152
+ stat = stat_for_progress(key)
153
+ done += stat[:done]
154
+ undone += stat[:undone]
155
+ end
156
+ { done: done, undone: undone }
157
+ end
158
+
159
+ def overall_stat_description(formatter)
160
+ Util.title('Total') +
161
+ formatter.format(overall_stat)
162
+ end
163
+
144
164
  def to_s
145
165
  "#{name}\n" + progress_output + "\n"
146
166
  end
147
167
 
148
168
  private
169
+
149
170
  def initialize_progress_hash_from(key)
150
171
  @progress[key] = '' unless @progress.key? key
151
172
  end
@@ -153,8 +174,8 @@ module Hbtrack
153
174
  def update_progress_for(key, day, done)
154
175
  i = day - @progress[key].length - 1
155
176
  result = @progress[key].split('')
156
- i.times { result << '0' }
157
- result[day-1] = done ? '1' : '0'
177
+ i.times { result << ' ' }
178
+ result[day - 1] = done ? '1' : '0'
158
179
  @progress[key] = result.join('')
159
180
  end
160
181
  end
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'hbtrack/util'
4
+
3
5
  module Hbtrack
4
- # This class contains the methods that
6
+ # This class contains the methods that
5
7
  # are used to format the progress of a Habit
6
8
  # into string
7
9
  class HabitPrinter
10
+ attr_accessor :formatter
8
11
 
9
- def initialize(stat_formatter)
10
- @stat_formatter = stat_formatter
12
+ def initialize(formatter = CompleteSF.new)
13
+ @formatter = formatter
11
14
  end
12
15
 
13
16
  def calculate_space_needed_for(habit, key)
@@ -16,30 +19,35 @@ module Hbtrack
16
19
 
17
20
  def print_latest_progress(habit, no_of_space = 0)
18
21
  habit.name.to_s + ' ' * no_of_space + ' : ' +
19
- print_progress(
20
- habit.latest_progress,
21
- habit.latest_stat
22
- )
22
+ print_progress(
23
+ habit.latest_progress,
24
+ habit.latest_stat
25
+ )
23
26
  end
24
27
 
25
28
  def print_all_progress(habit)
26
- habit.progress.map do |key, value|
29
+ habit.progress.map do |key, value|
27
30
  space = calculate_space_needed_for(habit, key)
28
31
  Util.convert_key_to_date(key, space) +
29
- print_progress(
30
- value,
31
- habit.stat_for_progress(key)
32
- )
32
+ print_progress(
33
+ value,
34
+ habit.stat_for_progress(key)
35
+ )
33
36
  end.join("\n")
34
37
  end
35
38
 
36
39
  def print_progress(progress, stat)
37
40
  output = progress.split('').map do |x|
38
- x == '0' ? CLI.red('*') : CLI.green('*')
41
+ if x == '0'
42
+ Util.red('*')
43
+ elsif x == '1'
44
+ Util.green('*')
45
+ else
46
+ ' '
47
+ end
39
48
  end.join('')
40
49
  output + ' ' * (32 - progress.size) +
41
- @stat_formatter.format(stat)
50
+ @formatter.format(stat)
42
51
  end
43
52
  end
44
53
  end
45
-