hbtrack 0.0.7 → 0.0.8
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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +9 -1
- data/README.md +15 -68
- data/bin/irb +7 -0
- data/exe/hbtrack +3 -1
- data/hbtrack.gemspec +3 -1
- data/lib/hbtrack.rb +4 -44
- data/lib/hbtrack/cli/cli.rb +53 -0
- data/lib/hbtrack/cli/view.rb +87 -0
- data/lib/hbtrack/command.rb +8 -3
- data/lib/hbtrack/command/add_command.rb +25 -19
- data/lib/hbtrack/command/import_command.rb +43 -0
- data/lib/hbtrack/command/list_command.rb +30 -36
- data/lib/hbtrack/command/remove_command.rb +10 -13
- data/lib/hbtrack/command/show_command.rb +40 -0
- data/lib/hbtrack/command/update_command.rb +40 -47
- data/lib/hbtrack/config.rb +3 -0
- data/lib/hbtrack/database/model.rb +9 -0
- data/lib/hbtrack/database/sequel_store.rb +146 -0
- data/lib/hbtrack/importer/abstract_importer.rb +34 -0
- data/lib/hbtrack/importer/hbtrack_importer.rb +76 -0
- data/lib/hbtrack/importer/streaks_importer.rb +56 -0
- data/lib/hbtrack/stat_formatter.rb +1 -1
- data/lib/hbtrack/util.rb +5 -12
- data/lib/hbtrack/version.rb +1 -1
- metadata +55 -7
- data/lib/hbtrack/habit.rb +0 -182
- data/lib/hbtrack/habit_printer.rb +0 -53
- data/lib/hbtrack/habit_tracker.rb +0 -91
- data/lib/hbtrack/store.rb +0 -18
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hbtrack
|
4
|
+
module Importer
|
5
|
+
class AbstractImporter
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@habits = {}
|
9
|
+
@entries = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Store in database
|
13
|
+
def store_in(store)
|
14
|
+
ids = {}
|
15
|
+
@habits.each do |id, habit|
|
16
|
+
ids[id] = store.add_habit(habit)
|
17
|
+
end
|
18
|
+
|
19
|
+
@entries.each do |key, entries|
|
20
|
+
id = ids[key]
|
21
|
+
entries.each do |entry|
|
22
|
+
store.add_entry_of(id, entry)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Import and parse the CSV from Streaks
|
28
|
+
def import_from(file)
|
29
|
+
raise 'Not implemented'
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'hbtrack/importer/abstract_importer'
|
4
|
+
require 'hbtrack/database/model'
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
module Hbtrack
|
8
|
+
module Importer
|
9
|
+
class HbtrackImporter < AbstractImporter
|
10
|
+
Habit = Hbtrack::Database::Habit
|
11
|
+
Entry = Hbtrack::Database::Entry
|
12
|
+
|
13
|
+
ENTRY_TYPE = {
|
14
|
+
'0' => 'missed',
|
15
|
+
'1' => 'completed',
|
16
|
+
' ' => 'skip'
|
17
|
+
}
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
super
|
21
|
+
@index = 1
|
22
|
+
end
|
23
|
+
|
24
|
+
# Import and parse the CSV from Streaks
|
25
|
+
def import_from(file)
|
26
|
+
raise 'File not found' unless File.exist?(file)
|
27
|
+
input = File.read(file).split(/\n\n/)
|
28
|
+
input.each_with_index do |collection, index|
|
29
|
+
extract_from(index, collection)
|
30
|
+
end
|
31
|
+
|
32
|
+
[@habits, @entries]
|
33
|
+
end
|
34
|
+
|
35
|
+
def extract_from(id, collection)
|
36
|
+
arr = collection.split("\n")
|
37
|
+
|
38
|
+
# Get habit name
|
39
|
+
habit_name = arr.shift
|
40
|
+
@habits[id] = create_habit(habit_name)
|
41
|
+
|
42
|
+
create_entries_of(id, arr)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Create a Habit
|
46
|
+
def create_habit(habit)
|
47
|
+
habit = Habit.new(habit, @index)
|
48
|
+
@index += 1
|
49
|
+
habit
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_entries_of(id, entries)
|
53
|
+
@entries[id] = entries.flat_map do |entry|
|
54
|
+
month, values = entry.split(': ')
|
55
|
+
|
56
|
+
values.split("").map.with_index(1) do |value, index|
|
57
|
+
create_entry(month, index, value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_entry(month, day, value)
|
63
|
+
timestamp = create_timestamp_for(month, day)
|
64
|
+
type = ENTRY_TYPE[value]
|
65
|
+
Entry.new(timestamp, type)
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_timestamp_for(month, day)
|
69
|
+
year, month = month.split(',').map(&:to_i)
|
70
|
+
time_zone = Time.new.zone
|
71
|
+
DateTime.new(year, month, day, 0, 0, 0, "#{time_zone}:00").to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'hbtrack/importer/abstract_importer'
|
4
|
+
require 'hbtrack/database/model'
|
5
|
+
require 'csv'
|
6
|
+
|
7
|
+
module Hbtrack
|
8
|
+
module Importer
|
9
|
+
class StreaksImporter < AbstractImporter
|
10
|
+
Habit = Hbtrack::Database::Habit
|
11
|
+
Entry = Hbtrack::Database::Entry
|
12
|
+
|
13
|
+
# Import and parse the CSV from Streaks
|
14
|
+
def import_from(file)
|
15
|
+
raise 'File not found' unless File.exist?(file)
|
16
|
+
CSV.foreach(file, headers: true) do |row|
|
17
|
+
extract_streaks_data(row)
|
18
|
+
end
|
19
|
+
# Handle the parsed data
|
20
|
+
[@habits, @entries]
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
# Extract Streaks data from each line
|
25
|
+
def extract_streaks_data(line)
|
26
|
+
task_id = line.fetch('task_id')
|
27
|
+
find_or_create_habit(task_id, line)
|
28
|
+
create_entry(task_id, line)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Find or create habit
|
32
|
+
def find_or_create_habit(id, line)
|
33
|
+
unless @habits.has_key? id
|
34
|
+
title = line.fetch('title').strip
|
35
|
+
page = line.fetch('page').to_i
|
36
|
+
display_order = line.fetch('display_order').to_i
|
37
|
+
display_order = page * display_order
|
38
|
+
@habits[id] = Habit.new(title, display_order)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Create entry
|
43
|
+
def create_entry(task_id, line)
|
44
|
+
date = line.fetch('entry_date') # Get Date of entry
|
45
|
+
timestamp = line.fetch('entry_timestamp').split("T") # Get Time of entry
|
46
|
+
# Create timestamp
|
47
|
+
timestamp = DateTime.parse(date + "T" + timestamp[1]).to_s
|
48
|
+
type = line.fetch('entry_type')
|
49
|
+
@entries[task_id] = [] unless @entries[task_id]
|
50
|
+
@entries[task_id] << Entry.new(timestamp, type)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -36,7 +36,7 @@ module Hbtrack
|
|
36
36
|
# @return [String] formatted result
|
37
37
|
def format(hash)
|
38
38
|
percentage = to_percentage(hash)[:done]
|
39
|
-
sprintf(
|
39
|
+
sprintf("Completion rate: %.2f%%", percentage)
|
40
40
|
end
|
41
41
|
|
42
42
|
# Convert the value in the hash into percentage
|
data/lib/hbtrack/util.rb
CHANGED
@@ -4,7 +4,7 @@ module Hbtrack
|
|
4
4
|
# This class contains the methods that
|
5
5
|
# are used to format the progress of a Habit
|
6
6
|
# into string
|
7
|
-
|
7
|
+
module Util
|
8
8
|
FONT_COLOR = {
|
9
9
|
green: "\e[32m",
|
10
10
|
red: "\e[31m",
|
@@ -19,7 +19,7 @@ module Hbtrack
|
|
19
19
|
value + string + "\e[0m"
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
# Format the string with title style.
|
24
24
|
#
|
25
25
|
# @param string [String] the string to be styled as title
|
@@ -54,16 +54,9 @@ module Hbtrack
|
|
54
54
|
" #{year}" + ' : '
|
55
55
|
end
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
#
|
61
|
-
# == Example
|
62
|
-
#
|
63
|
-
# Util.current_month
|
64
|
-
# #=> "August 2017"
|
65
|
-
def current_month
|
66
|
-
Date.today.strftime('%B %Y')
|
57
|
+
def get_date_from(key:)
|
58
|
+
date_component = key.to_s.split(',').map(&:to_i)
|
59
|
+
Date.new(date_component[0], date_component[1], 1)
|
67
60
|
end
|
68
61
|
|
69
62
|
# Get the month in string form from given key
|
data/lib/hbtrack/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hbtrack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kw7oe
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '3.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: simplecov
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rake
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +66,34 @@ dependencies:
|
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sequel
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '5.4'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5.4'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sqlite3
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
55
97
|
description: Habit Tracker CLI
|
56
98
|
email:
|
57
99
|
- choongkwern@hotmail.com
|
@@ -68,21 +110,27 @@ files:
|
|
68
110
|
- LICENSE.txt
|
69
111
|
- README.md
|
70
112
|
- Rakefile
|
113
|
+
- bin/irb
|
71
114
|
- exe/hbtrack
|
72
115
|
- hbtrack.gemspec
|
73
116
|
- lib/hbtrack.rb
|
117
|
+
- lib/hbtrack/cli/cli.rb
|
118
|
+
- lib/hbtrack/cli/view.rb
|
74
119
|
- lib/hbtrack/command.rb
|
75
120
|
- lib/hbtrack/command/add_command.rb
|
121
|
+
- lib/hbtrack/command/import_command.rb
|
76
122
|
- lib/hbtrack/command/list_command.rb
|
77
123
|
- lib/hbtrack/command/remove_command.rb
|
124
|
+
- lib/hbtrack/command/show_command.rb
|
78
125
|
- lib/hbtrack/command/update_command.rb
|
79
126
|
- lib/hbtrack/config.rb
|
127
|
+
- lib/hbtrack/database/model.rb
|
128
|
+
- lib/hbtrack/database/sequel_store.rb
|
80
129
|
- lib/hbtrack/error_handler.rb
|
81
|
-
- lib/hbtrack/
|
82
|
-
- lib/hbtrack/
|
83
|
-
- lib/hbtrack/
|
130
|
+
- lib/hbtrack/importer/abstract_importer.rb
|
131
|
+
- lib/hbtrack/importer/hbtrack_importer.rb
|
132
|
+
- lib/hbtrack/importer/streaks_importer.rb
|
84
133
|
- lib/hbtrack/stat_formatter.rb
|
85
|
-
- lib/hbtrack/store.rb
|
86
134
|
- lib/hbtrack/util.rb
|
87
135
|
- lib/hbtrack/version.rb
|
88
136
|
homepage: https://github.com/kw7oe/hbtrack
|
@@ -105,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
153
|
version: '0'
|
106
154
|
requirements: []
|
107
155
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.
|
156
|
+
rubygems_version: 2.7.3
|
109
157
|
signing_key:
|
110
158
|
specification_version: 4
|
111
159
|
summary: A CLI to track your habits.
|
data/lib/hbtrack/habit.rb
DELETED
@@ -1,182 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'date'
|
4
|
-
require 'hbtrack/util'
|
5
|
-
|
6
|
-
module Hbtrack
|
7
|
-
# Habit class
|
8
|
-
class Habit
|
9
|
-
attr_accessor :name, :progress
|
10
|
-
# Class Methods
|
11
|
-
|
12
|
-
class << self
|
13
|
-
# Generate hash key for progress based
|
14
|
-
# on date.
|
15
|
-
#
|
16
|
-
# Example
|
17
|
-
#
|
18
|
-
# Habit.get_progress_key_from(Date.new(2017, 7, 18))
|
19
|
-
# # => :"2017,7"
|
20
|
-
def get_progress_key_from(date)
|
21
|
-
date.strftime('%Y,%-m').to_sym
|
22
|
-
end
|
23
|
-
|
24
|
-
# Initialize Habit object from string.
|
25
|
-
#
|
26
|
-
# @param string [String] The string to be parse.
|
27
|
-
# @return [Habit] a habit object
|
28
|
-
#
|
29
|
-
# == Example
|
30
|
-
#
|
31
|
-
# Habit.initialize_from_string("workout\n2017,6:1001")
|
32
|
-
# # => #<Habit:0x007f9be6041b70 @name="workout",
|
33
|
-
# # @progress={:"2017,6"=>"1001"}>
|
34
|
-
#
|
35
|
-
# Habit.initialize_from_string("")
|
36
|
-
# # => nil
|
37
|
-
def initialize_from_string(string)
|
38
|
-
return nil if string.empty?
|
39
|
-
arr = string.split("\n")
|
40
|
-
habit_name = arr.shift
|
41
|
-
hash = {}
|
42
|
-
arr.each do |s|
|
43
|
-
a = s.split(': ')
|
44
|
-
value = a[1] ? a[1] : ''
|
45
|
-
hash[a[0].to_sym] = value
|
46
|
-
end
|
47
|
-
Habit.new(habit_name, hash)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def initialize(name,
|
52
|
-
progress = {
|
53
|
-
Habit.get_progress_key_from(Date.today) => ''
|
54
|
-
})
|
55
|
-
@name = name
|
56
|
-
@progress = progress
|
57
|
-
end
|
58
|
-
|
59
|
-
# The length of the habit name.
|
60
|
-
#
|
61
|
-
# @return [Numeric] length of the habit name
|
62
|
-
def name_length
|
63
|
-
name.length
|
64
|
-
end
|
65
|
-
|
66
|
-
# Get the latest progress key.
|
67
|
-
#
|
68
|
-
# @return [Symbol] latest progress key
|
69
|
-
def latest_key
|
70
|
-
key = Habit.get_progress_key_from(Date.today)
|
71
|
-
initialize_progress_hash_from(key) if progress[key].nil?
|
72
|
-
key
|
73
|
-
end
|
74
|
-
|
75
|
-
# Get the latest progress.
|
76
|
-
#
|
77
|
-
# @return [String] value of the progress
|
78
|
-
def latest_progress
|
79
|
-
progress[latest_key]
|
80
|
-
end
|
81
|
-
|
82
|
-
# Get the stat for the latest progress.
|
83
|
-
#
|
84
|
-
# @return [Hash] stat for the progress.
|
85
|
-
def latest_stat
|
86
|
-
stat_for_progress(latest_key)
|
87
|
-
end
|
88
|
-
|
89
|
-
# Find the month in progress with
|
90
|
-
# the longest name
|
91
|
-
#
|
92
|
-
# @return [String] month
|
93
|
-
def longest_month
|
94
|
-
key = progress.keys.max_by do |x|
|
95
|
-
Util.get_month_from(x).length
|
96
|
-
end
|
97
|
-
Util.get_month_from(key)
|
98
|
-
end
|
99
|
-
|
100
|
-
# Update the status of the progress
|
101
|
-
#
|
102
|
-
# @param done [true, false] If true, it is marked as done.
|
103
|
-
# Else, marked as undone.
|
104
|
-
# @param date [Date] The date of the progress
|
105
|
-
# @return [void]
|
106
|
-
def done(done = true, date = Date.today)
|
107
|
-
key = Habit.get_progress_key_from(date)
|
108
|
-
initialize_progress_hash_from(key)
|
109
|
-
update_progress_for(key, date.day, done)
|
110
|
-
end
|
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
|
-
|
120
|
-
# Get the stat of the progress.
|
121
|
-
#
|
122
|
-
# @param key [Symbol] key for the progress
|
123
|
-
# @return [Hash] stat of the progress in the form of
|
124
|
-
# == Example:
|
125
|
-
#
|
126
|
-
# habit.stat_for_progress("2017,5".to_sym)
|
127
|
-
# # => { done: 5, undone: 2 }
|
128
|
-
def stat_for_progress(key)
|
129
|
-
undone = @progress[key].split('').count { |x| x == '0' }
|
130
|
-
done = @progress[key].split('').count { |x| x == '1' }
|
131
|
-
{ done: done, undone: undone }
|
132
|
-
end
|
133
|
-
|
134
|
-
# Get all of the progress of the habit in string form
|
135
|
-
#
|
136
|
-
# == Example:
|
137
|
-
#
|
138
|
-
# habit.progress_output
|
139
|
-
# # => "2017,5: 0010001010\n2017,6: 000010010\n"
|
140
|
-
#
|
141
|
-
def progress_output
|
142
|
-
arr = @progress.map do |key, value|
|
143
|
-
"#{key}: #{value}\n"
|
144
|
-
end
|
145
|
-
arr.join('')
|
146
|
-
end
|
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
|
-
|
164
|
-
def to_s
|
165
|
-
"#{name}\n" + progress_output + "\n"
|
166
|
-
end
|
167
|
-
|
168
|
-
private
|
169
|
-
|
170
|
-
def initialize_progress_hash_from(key)
|
171
|
-
@progress[key] = '' unless @progress.key? key
|
172
|
-
end
|
173
|
-
|
174
|
-
def update_progress_for(key, day, done)
|
175
|
-
i = day - @progress[key].length - 1
|
176
|
-
result = @progress[key].split('')
|
177
|
-
i.times { result << ' ' }
|
178
|
-
result[day - 1] = done ? '1' : '0'
|
179
|
-
@progress[key] = result.join('')
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|