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.
@@ -1,26 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'hbtrack/util'
4
+
3
5
  module Hbtrack
4
6
  # This class contains the methods to
5
7
  # handle the operation of mutliple habits
6
8
  class HabitTracker
7
- attr_reader :habits, :hp
8
-
9
- def self.help # Refactoring needed
10
- puts 'usage: hbtrack list [-p] [ habit_name ]'
11
- puts ' hbtrack add habit_name'
12
- puts ' hbtrack done [-y] habit_name'
13
- puts ' hbtrack undone [-y] habit_name'
14
- puts ' hbtrack remove habit_name'
15
- end
9
+ attr_accessor :habits
10
+ attr_reader :hp, :output_file_name
16
11
 
17
12
  def initialize(file = FILE_NAME,
18
13
  output = FILE_NAME)
19
14
  @habits = []
20
15
  @file_name = file
21
16
  @output_file_name = output
22
- @sf = CompleteSF.new
23
- @hp = HabitPrinter.new(@sf)
24
17
  initialize_habits_from_file
25
18
  end
26
19
 
@@ -30,12 +23,6 @@ module Hbtrack
30
23
  input.each { |string| @habits << Habit.initialize_from_string(string) }
31
24
  end
32
25
 
33
- def parse_arguments(args)
34
- head = args.shift
35
- tail = args
36
- send(head, tail)
37
- end
38
-
39
26
  # This methods find a habit based on the name given.
40
27
  # Blocks are executed when habit is not found.
41
28
  def find(habit_name)
@@ -46,102 +33,40 @@ module Hbtrack
46
33
  habit
47
34
  end
48
35
 
49
- def longest_name
50
- @habits.max_by(&:name_length).name
51
- end
52
-
53
- def method_missing(*args)
54
- HabitTracker.help
55
- end
56
-
57
- def total_habits_stat
58
- @habits.reduce({done: 0, undone: 0}) do |stat, habit|
59
- stat[:done] += habit.latest_stat[:done]
60
- stat[:undone] += habit.latest_stat[:undone]
61
- stat
62
- end
63
- end
64
-
65
- def overall_stat_description
66
- Util.title("Total") +
67
- @sf.format(total_habits_stat)
68
- end
69
-
70
- private
71
- def list(args)
72
- habit_name, options = parse_options(args)
73
- set_sf_based_on(options)
36
+ def find_or_create(habit_name)
74
37
  habit = find(habit_name) do
75
- if habit_name.nil?
76
- list_all_habits
77
- else
78
- raise_habit_not_found(habit_name)
79
- end
80
- return
81
- end
82
- puts Util.title habit.name
83
- puts @hp.print_all_progress(habit)
84
- end
85
-
86
- def set_sf_based_on(options)
87
- return if options.empty?
88
- if options[0] == '-p' || options[0] == '--percentage'
89
- @sf = CompletionRateSF.new
90
- @hp = HabitPrinter.new(@sf)
91
- end
92
- end
93
-
94
- def list_all_habits
95
- puts Util.title Util.current_month
96
- @habits.each_with_index do |h, index|
97
- space = longest_name.length - h.name_length
98
- puts "#{index + 1}. " +
99
- "#{@hp.print_latest_progress(h, space)}"
38
+ habit = create(habit_name)
39
+ return habit
100
40
  end
101
- puts "\n" + overall_stat_description
102
41
  end
103
42
 
104
- def add(args)
105
- habit_name, _options = parse_options(args)
106
- find(habit_name) do
107
- habit = create(habit_name)
108
- save_to_file(habit, 'Add') unless habit.nil?
109
- return
110
- end
111
- puts CLI.blue("#{habit_name} already existed!")
43
+ def longest_name
44
+ @habits.max_by(&:name_length).name
112
45
  end
113
46
 
114
- def done(args)
115
- habit_name, options = parse_options(args)
116
- day = get_day_based_on(options)
117
- habit = find_or_create(habit_name)
118
- save_to_file(habit, 'Done') do
119
- habit.done(true, day)
47
+ def total_habits_stat
48
+ @habits.each_with_object(done: 0, undone: 0) do |habit, stat|
49
+ stat[:done] += habit.latest_stat[:done]
50
+ stat[:undone] += habit.latest_stat[:undone]
120
51
  end
121
52
  end
122
53
 
123
- def undone(args)
124
- habit_name, options = parse_options(args)
125
- day = get_day_based_on(options)
126
- habit = find(habit_name) { raise_if_habit_error(habit_name) }
127
- save_to_file(habit, 'Undone', 'blue') do
128
- habit.done(false, day)
54
+ def done_count_for(date:)
55
+ habits.reduce(0) do |a, habit|
56
+ val = habit.done_for(date: date) == '1' ? 1 : 0
57
+ a + val
129
58
  end
130
59
  end
131
60
 
132
- def remove(args)
133
- habit_name, _options = parse_options(args)
134
- habit = find(habit_name) { raise_if_habit_error(habit_name) }
135
- save_to_file(habit, 'Remove', 'blue') do
136
- @habits.delete(habit)
61
+ def undone_count_for(date:)
62
+ habits.reduce(0) do |a, habit|
63
+ val = habit.done_for(date: date) == '0' ? 1 : 0
64
+ a + val
137
65
  end
138
66
  end
139
67
 
140
- def find_or_create(habit_name)
141
- habit = find(habit_name) do
142
- habit = create(habit_name)
143
- return habit
144
- end
68
+ def overall_stat_description(formatter)
69
+ Util.title('Total') + formatter.format(total_habits_stat)
145
70
  end
146
71
 
147
72
  def create(habit_name)
@@ -151,67 +76,14 @@ module Hbtrack
151
76
  return habit
152
77
  end
153
78
 
154
- if habit_name && habit_name.length > 11
155
- raise_habit_name_too_long
156
- else
157
- raise_invalid_arguments
158
- end
159
- end
160
-
161
- def parse_options(args)
162
- options = args.select { |x| x =~ /\A-/ }
163
- args -= options
164
- [args[0], options]
165
- end
166
-
167
- def get_day_based_on(options)
168
- yesterday = options[0] == '-y' || options[0] == '--yesterday'
169
- return Date.today unless yesterday
170
- Date.today - 1
171
- end
172
-
173
- def raise_error_msg(msg)
174
- puts CLI.red msg
175
- return
176
- end
177
-
178
- def raise_if_habit_error(habit_name)
179
- return raise_invalid_arguments if habit_name.nil?
180
- raise_habit_not_found(habit_name)
181
- end
182
-
183
- def raise_habit_not_found(habit_name)
184
- raise_error_msg "Invalid habit: #{habit_name} not found."
185
- end
186
-
187
- def raise_invalid_arguments
188
- raise_error_msg "Invalid argument: habit_name is expected."
189
- end
79
+ return ErrorHandler.raise_habit_name_too_long if habit_name && habit_name.length > 11
190
80
 
191
- def raise_habit_name_too_long
192
- raise_error_msg "habit_name too long."
81
+ ErrorHandler.raise_invalid_arguments
193
82
  end
194
83
 
195
84
  def invalid_habit_name?(habit_name)
196
- habit_name.nil? || habit_name =~ /\s+/ ||
197
- habit_name.length > 11
198
- end
199
-
200
- def save
201
- File.open(@output_file_name, 'w') do |f|
202
- @habits.each do |habit|
203
- f.puts habit
204
- end
205
- end
206
- end
207
-
208
- def save_to_file(habit, action, color = 'green')
209
- unless habit.nil?
210
- yield if block_given?
211
- save
212
- output = "#{action} #{habit.name}!"
213
- puts CLI.public_send(color, output)
214
- end
85
+ habit_name.nil? || habit_name =~ /\s+/ ||
86
+ habit_name.length > 11
215
87
  end
216
88
  end
217
89
  end
@@ -0,0 +1,84 @@
1
+ module Hbtrack
2
+ class Calendar
3
+
4
+ WEEKDAYS = [
5
+ "Mon",
6
+ "Tue",
7
+ "Wed",
8
+ "Thu",
9
+ "Fri",
10
+ "Sat",
11
+ "Sun"
12
+ ]
13
+
14
+ # Generate weeks of the current month,
15
+ # which contains 7 days.
16
+ def self.weeks(date)
17
+ range = date.beginning_of_month..date.end_of_month
18
+ result = range.chunk_while { |date| date.cwday < 7 }.to_a
19
+
20
+ if result.first.length < 7
21
+ (7 - result.first.length).times do
22
+ result.first.unshift(NullDay.new)
23
+ end
24
+ end
25
+
26
+ if result.last.length < 7
27
+ (7 - result.last.length).times do
28
+ result.last.push(NullDay.new)
29
+ end
30
+ end
31
+
32
+ result
33
+ end
34
+
35
+ # Generate <td> elements for days
36
+ def self.td_for(date, progress)
37
+ progress = progress.split("")
38
+
39
+ className = if date.day.nil?
40
+ "null_day"
41
+ elsif progress[date.day - 1] == "1"
42
+ "done"
43
+ elsif progress[date.day - 1] == "0"
44
+ "undone"
45
+ end
46
+
47
+ string = <<~EOF
48
+ <td>
49
+ <p class="#{className}">
50
+ #{add_padding_to(date.day)}
51
+ </p>
52
+ </td>
53
+ EOF
54
+ end
55
+
56
+ def self.add_padding_to(day)
57
+ return nil if day.nil?
58
+ return "0#{day}" if day < 10
59
+ day.to_s
60
+ end
61
+
62
+ end
63
+
64
+ # Null Object Pattern
65
+ class NullDay
66
+ attr_reader :day
67
+ def initialize
68
+ @day = nil
69
+ end
70
+ end
71
+
72
+
73
+ end
74
+
75
+ # Extend Date Class
76
+ class Date
77
+ def beginning_of_month
78
+ Date.new(self.year, self.month, 1)
79
+ end
80
+
81
+ def end_of_month
82
+ Date.new(self.year, self.month, -1)
83
+ end
84
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'hbtrack/report/calendar'
5
+
6
+ module Hbtrack
7
+ class Generator
8
+ def initialize(hbt)
9
+ @hbt = hbt
10
+ initialize_stat
11
+ @week_days = Hbtrack::Calendar::WEEKDAYS
12
+ @weeks = Hbtrack::Calendar.weeks(Date.today)
13
+ end
14
+
15
+ def initialize_stat
16
+ @stat = @hbt.total_habits_stat
17
+ @stat[:total] = @stat[:done] + @stat[:undone]
18
+ @stat[:percentage] = (@stat[:done] / @stat[:total].to_f).round(2) * 100
19
+ end
20
+
21
+ def td_for(date)
22
+ Hbtrack::Calendar.td_for(date, @hbt.habits.last.latest_progress)
23
+ end
24
+
25
+ def generate
26
+ file_path = File.expand_path("../report.html.erb", __FILE__)
27
+ renderer = ERB.new(File.read(file_path))
28
+ File.write('report.html', renderer.result(binding))
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,89 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title></title>
5
+ <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.5.2/css/bulma.min.css">
6
+
7
+ <style type="text/css">
8
+ .text-center { margin-top: 10%; text-align: center; }
9
+
10
+ .calender { margin: 0 auto; width: 400px; height: 374.8571428571px; padding: 16px 18px; border: 1px solid #B3B3B3; overflow: hidden; }
11
+ .calender table { border-collapse: collapse; }
12
+ .calender .weekdays th { text-align: center; vertical-align: middle; text-transform: uppercase; height: 57.1428571429px; width: 57.1428571429px; }
13
+ .calender .days { height: 57.1428571429px; width: 57.1428571429px; }
14
+ .calender .days td { text-align: center; cursor: pointer; }
15
+ .calender .days td p { border-radius: 50%; width: 44px; height: 44px; margin: 0; display: inline-block; text-align: center; padding: 10px; }
16
+ .calender .days td p.null_day { display: none; }
17
+ .calender .days td p.undone { color: white; background-color: #FF1654; }
18
+ .calender .days td p.done { color: white; background-color: #70C1B3; }
19
+ .calender .days td:hover p { background-color: #E6E6E6; }
20
+ .calender .days td:hover p.undone { background-color: #CC1243; }
21
+ .calender .days td:hover p.done { background-color: #549187; }
22
+ </style>
23
+ </head>
24
+
25
+ <body>
26
+ <!-- Title -->
27
+ <section class="section">
28
+ <div class="container">
29
+ <div class="columns is-centered">
30
+ <h1 class="title">Hbtrack</h1>
31
+ </div>
32
+ </div>
33
+ </section>
34
+
35
+ <!-- Calendar -->
36
+ <section class="section>">
37
+ <div class = "calender">
38
+ <table>
39
+ <!-- Generate Week Days -->
40
+ <tr class = "weekdays">
41
+ <% @week_days.each do |day| %>
42
+ <th><%= day %></th>
43
+ <% end %>
44
+ </tr>
45
+
46
+ <!-- Generate Days -->
47
+ <% @weeks.each do |week| %>
48
+ <tr class = "days">
49
+ <% week.each do |day| %>
50
+ <%= td_for(day) %>
51
+ <% end %>
52
+ </tr>
53
+ <% end %>
54
+ </table>
55
+ </div>
56
+ </section>
57
+
58
+ <!-- Stat -->
59
+ <section class="section">
60
+ <nav class="level">
61
+ <div class="level-item has-text-centered">
62
+ <div>
63
+ <p class="heading">Total</p>
64
+ <p class="title"><%= @stat[:total] %></p>
65
+ </div>
66
+ </div>
67
+ <div class="level-item has-text-centered">
68
+ <div>
69
+ <p class="heading">Done</p>
70
+ <p class="title"><%= @stat[:done] %></p>
71
+ </div>
72
+ </div>
73
+ <div class="level-item has-text-centered">
74
+ <div>
75
+ <p class="heading">Undone</p>
76
+ <p class="title"><%= @stat[:undone] %></p>
77
+ </div>
78
+ </div>
79
+ <div class="level-item has-text-centered">
80
+ <div>
81
+ <p class="heading">Completion Rate</p>
82
+ <p class="title"><%= @stat[:percentage] %>%</p>
83
+ </div>
84
+ </div>
85
+ </nav>
86
+ </section>
87
+ <!-- End of Stat -->
88
+ </body>
89
+ </html>
@@ -1,31 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Hbtrack
4
- # This is an abstract class for classes that
5
- # are used to format the progress of a Habit
6
- # into string
7
- class StatFormatter
8
- def initialize
9
- end
3
+ require 'hbtrack/util'
10
4
 
11
- def format
12
- raise "Not Implemented"
13
- end
14
- end
15
-
16
- class DoneUndoneSF < StatFormatter
5
+ module Hbtrack
6
+ class DoneUndoneSF
17
7
  # Format in terms of the count of done and undone.
18
8
  # @param hash [Hash]
19
9
  # @option hash [String] :done total of done
20
10
  # @option hash [String] :undone total of undone
21
11
  # @return [String] formatted result
22
12
  def format(hash)
23
- CLI.green("Done: #{hash[:done]}") + "\n" +
24
- CLI.red("Undone: #{hash[:undone]}")
13
+ Util.green("Done: #{hash[:done]}") + "\n" +
14
+ Util.red("Undone: #{hash[:undone]}")
25
15
  end
26
16
  end
27
17
 
28
- class CompleteSF < StatFormatter
18
+ class CompleteSF
29
19
  # Format in terms of the total and the count of
30
20
  # done and undone.
31
21
  # @param hash [Hash]
@@ -38,7 +28,7 @@ module Hbtrack
38
28
  end
39
29
  end
40
30
 
41
- class CompletionRateSF < StatFormatter
31
+ class CompletionRateSF
42
32
  # Format in terms of the completion rate of the habit.
43
33
  # @param hash [Hash]
44
34
  # @option hash [String] :done total of done
@@ -46,7 +36,7 @@ module Hbtrack
46
36
  # @return [String] formatted result
47
37
  def format(hash)
48
38
  percentage = to_percentage(hash)[:done]
49
- "Completion rate: #{'%.2f' % percentage}%"
39
+ sprintf('Completion rate: %.2f%', percentage)
50
40
  end
51
41
 
52
42
  # Convert the value in the hash into percentage
@@ -58,7 +48,7 @@ module Hbtrack
58
48
  total = hash[:done] + hash[:undone]
59
49
  done_p = 0
60
50
  undone_p = 0
61
- unless total == 0
51
+ unless total.zero?
62
52
  done_p = hash[:done] / total.to_f * 100
63
53
  undone_p = hash[:undone] / total.to_f * 100
64
54
  end
@@ -66,4 +56,3 @@ module Hbtrack
66
56
  end
67
57
  end
68
58
  end
69
-
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hbtrack
4
+ class Store
5
+ def initialize(habits, file_name)
6
+ @habits = habits
7
+ @file_name = file_name
8
+ end
9
+
10
+ def save
11
+ File.open(@file_name, 'w') do |f|
12
+ @habits.each do |habit|
13
+ f.puts habit
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/hbtrack/util.rb CHANGED
@@ -1,57 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hbtrack
4
- # This class contains the methods that
4
+ # This class contains the methods that
5
5
  # are used to format the progress of a Habit
6
6
  # into string
7
7
  class Util
8
+ FONT_COLOR = {
9
+ green: "\e[32m",
10
+ red: "\e[31m",
11
+ blue: "\e[34m"
12
+ }.freeze
8
13
 
9
14
  class << self
10
-
15
+ # Define Util.green, Util.red, Util.blue
16
+ # which colorize string output in terminal
17
+ FONT_COLOR.each do |key, value|
18
+ define_method(key) do |string|
19
+ value + string + "\e[0m"
20
+ end
21
+ end
11
22
  # Format the string with title style.
12
23
  #
13
24
  # @param string [String] the string to be styled as title
14
25
  # @return [Nil]
15
- #
26
+ #
16
27
  # == Example
17
28
  #
18
29
  # puts Util.title("Title")
19
30
  # # Title
20
31
  # # -----
21
- # #=> nil
22
- def title(string)
23
- string + "\n" +
24
- "-" * string.length + "\n"
32
+ # #=> nil
33
+ def title(string)
34
+ string + "\n" +
35
+ '-' * string.length + "\n"
25
36
  end
26
37
 
27
38
  # Convert key into date in string form.
28
- #
29
- # @param key [Symbol] The key of the progress in the
39
+ #
40
+ # @param key [Symbol] The key of the progress in the
30
41
  # form of :'year, month'. Example: :"2017,7"
31
- # @param no_of_space [Numeric] number of space to be
42
+ # @param no_of_space [Numeric] number of space to be
32
43
  # added in front
33
44
  # @return [String] a string in date form.
34
- #
45
+ #
35
46
  # == Example
36
47
  #
37
48
  # Util.convert_key_to_date(:"2017,7", 0)
38
49
  # #=> "July 2016 : "
39
50
  def convert_key_to_date(key, no_of_space)
40
- year = key.to_s.split(",")[0]
41
- ' ' * no_of_space + get_month_from(key) +
42
- " #{year}" + " : "
51
+ year = key.to_s.split(',')[0]
52
+ ' ' * no_of_space + get_month_from(key) +
53
+ " #{year}" + ' : '
43
54
  end
44
55
 
45
56
  # Format the current month and year into string
46
- #
57
+ #
47
58
  # @return [String] Month and Year in String.
48
- #
59
+ #
49
60
  # == Example
50
61
  #
51
62
  # Util.current_month
52
63
  # #=> "August 2017"
53
64
  def current_month
54
- Date.today.strftime("%B %Y")
65
+ Date.today.strftime('%B %Y')
55
66
  end
56
67
 
57
68
  # Get the month in string form from given key
@@ -60,7 +71,7 @@ module Hbtrack
60
71
  # @return [String] month
61
72
  #
62
73
  # == Example
63
- #
74
+ #
64
75
  # Util.get_month_from(:"2017,7")
65
76
  # #=> "July"
66
77
  def get_month_from(key)
@@ -70,4 +81,3 @@ module Hbtrack
70
81
  end
71
82
  end
72
83
  end
73
-
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hbtrack
4
- VERSION = '0.0.5'
4
+ VERSION = '0.0.6'
5
5
  end