tcelfer 1.0.1

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.
@@ -0,0 +1,58 @@
1
+ # Tcelfer
2
+
3
+ ### [t]cel-fer:
4
+ - A fun way to keep track of simple summaries of your months.
5
+
6
+ ## Requirements
7
+ * Ruby 2.5.0+ (Tested with 2.6.0 mostly)
8
+ * Sqlite3
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'tcelfer', '~> 1.0'
16
+ ```
17
+
18
+ And then execute:
19
+ ```bash
20
+ $ bundle
21
+ ```
22
+ Or install it yourself as:
23
+ ```bash
24
+ $ gem install tcelfer
25
+ ```
26
+
27
+ ## Setup
28
+ ```bash
29
+ # Configure with one of the following:
30
+ # Just change the db location
31
+ export TCELFER_SQLITE_PATH=path/to/some_file.db
32
+ # Or copy `config/tcelfer.example.yml` and use:
33
+ export TCELFER_CONF=~/.config/tcelfer/my_conf.yml
34
+ # Initialize the database
35
+ $ bin/db_init
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ```bash
41
+ # TODO: Find a better way to do this
42
+ # export TCELFER_SQLITE_PATH TCELFER_CONF from above
43
+ # Record your day
44
+ $ tcelfer day [-d 2018-12-31]
45
+
46
+ # Reflect on your month
47
+ $ tcelfer report [-y|--year=)YEAR] -m|--month=MON [--legend]
48
+ ```
49
+
50
+ ## Development
51
+
52
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
53
+
54
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, and push git commits and tags
55
+
56
+ ## Contributing
57
+
58
+ Bug reports and pull requests are welcome on GitHub at https://github.com/agargiulo/tcelfer.
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2018 Anthony Gargiulo <anthony@agargiulo.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ require 'bundler/gem_tasks'
18
+ require 'rspec/core/rake_task'
19
+ require 'rubocop/rake_task'
20
+
21
+ RSpec::Core::RakeTask.new(:spec)
22
+ RuboCop::RakeTask.new(:rubocop)
23
+
24
+ task default: %i[spec rubocop]
25
+
26
+ task :clean do
27
+ FileUtils.rm_rf(Dir['tmp/*'], verbose: true)
28
+ end
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright (C) 2018 Anthony Gargiulo <anthony@agargiulo.com>
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ require 'tcelfer/cli'
19
+
20
+ Tcelfer::CLI::Base.start
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2018 Anthony Gargiulo <anthony@agargiulo.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ require 'tcelfer/config'
18
+ require 'tcelfer/errors'
19
+ require 'tcelfer/storage'
20
+ require 'tcelfer/version'
21
+
22
+ # Main man Tcelfer himself
23
+ module Tcelfer
24
+ # Found these on Reddit
25
+ DAY_RATINGS = [
26
+ 'Amazing, Fantastic Day',
27
+ 'Really Good, Happy Day',
28
+ 'Normal, Average Day',
29
+ 'Exhausted, Tired Day',
30
+ 'Stressed-Out, Frantic Day',
31
+ 'Frustrated, Angry Day',
32
+ 'Depressed, Sad Day'
33
+ ].freeze
34
+
35
+ # Separate because reasons that used to make sense and fixing is not worth at the moment
36
+ RATING_TO_COLOR_MAP = DAY_RATINGS.zip(
37
+ # ordering taking from a mix of examples on reddit
38
+ # and my own preferences.
39
+ # '#4B0082' is indigo and :green != 'green' :(
40
+ ['maroon', :green, 'peru', '#4B0082', :red, 'orange', :blue]
41
+ # based on ColorPicker generic RGB on macOS if you want those
42
+ # %w[#ca4674 #6ba089 #cca04d #4b0082 #bd2d26 #fb9f09 #5a86ac]
43
+ ).to_h.freeze
44
+
45
+ def self.config
46
+ @config ||= Config.new
47
+ @config.validate!
48
+ end
49
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2018 Anthony Gargiulo <anthony@agargiulo.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ require 'date'
18
+ require 'paint'
19
+ require 'thor'
20
+ require 'tty-prompt'
21
+
22
+ require 'tcelfer'
23
+ require 'tcelfer/cli/report'
24
+
25
+ module Tcelfer
26
+ module CLI
27
+ # Thor CLI for Tcelfer
28
+ class Base < Thor
29
+ def initialize(*_args)
30
+ # Quit on Ctrl-C please
31
+ @prompt = TTY::Prompt.new(interrupt: :exit)
32
+
33
+ super
34
+ end
35
+
36
+ # Useful return codes are nice, of course
37
+ def self.exit_on_failure?
38
+ true
39
+ end
40
+
41
+ # Set up debug things I guess? This could be messy
42
+ class_option(:verbose, aliases: %w[-v], type: :boolean, default: false)
43
+
44
+ desc 'version', 'Prints the current version'
45
+ def version
46
+ puts Tcelfer::VERSION
47
+ end
48
+
49
+ method_option(:date, aliases: %w[-d], desc: 'Any format valid for ruby Date e.g. 2019-10-31', required: false)
50
+ desc 'day', 'record info for a day'
51
+ def day
52
+ Tcelfer.config.debug = options[:debug]
53
+ store = Tcelfer::Storage.new
54
+ tc_day = rec_day! store
55
+ @prompt.say("Recorded [#{tc_day.date}]: #{Paint[tc_day.rating, :bold]}")
56
+ rescue Tcelfer::Error => err
57
+ @prompt.error("[#{err.class}]", err)
58
+ end
59
+
60
+ method_option(:month, aliases: %w[-m], type: :numeric)
61
+ method_option(:legend, aliases: %w[-k -l], type: :boolean, default: false)
62
+ method_option(:year, aliases: %w[-y], type: :numeric, default: Date.today.year)
63
+ desc 'report', 'generate a report'
64
+ def report
65
+ Tcelfer.config.debug = options[:debug]
66
+ rep = Report.new
67
+ @prompt.say(gen_report(rep, options['month'], options['year'], options['legend']))
68
+ rescue Tcelfer::Error => err
69
+ @prompt.error("[#{err.class}]", err)
70
+ end
71
+
72
+ private
73
+
74
+ # Heavy lifting for `tcelfer day ...`
75
+ # returns an instance of the Day model representing the users choices.
76
+ # @return [Tcelfer::Models::Day]
77
+ def rec_day!(store)
78
+ user_date = options.key?('date') ? Date.parse(options['date']) : Date.today
79
+
80
+ rate_prompt_settings = { required: true, filter: true, per_page: DAY_RATINGS.length }
81
+ rating = @prompt.select('How was your day?', DAY_RATINGS, **rate_prompt_settings)
82
+ notes = @prompt.ask('Any additional notes?')
83
+ store.rec_day(rating, notes, user_date)
84
+ end
85
+
86
+ # Heavy lifting for `tcelfer report ...`
87
+ # Returns a string report to print to the console
88
+ # @param [Tcelfer::CLI::Report] rep Report object
89
+ # @param [Integer] month
90
+ # @param [Integer] year
91
+ # @param [Boolean] legend do we want to include the legend in the report?
92
+ # @return [String]
93
+ def gen_report(rep, month, year, legend)
94
+ if month && legend
95
+ rep.month_with_legend(month, year)
96
+ elsif month
97
+ rep.generate_month_report(month, year)
98
+ elsif legend
99
+ Report.legend
100
+ else
101
+ raise Tcelfer::ReportError, 'Please provide either -m, -k/-l, or both'
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2018 Anthony Gargiulo <anthony@agargiulo.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ require 'terminal-table'
18
+
19
+ require 'tcelfer'
20
+
21
+ module Tcelfer
22
+ module CLI
23
+ # Generate reports with day/week/month/year granularity.
24
+ class Report
25
+ def initialize
26
+ @store = Tcelfer::Storage.new
27
+ end
28
+
29
+ # Takes a month and year and returns a pretty formatted Terminal::Table
30
+ # with days colored according to Tcelfer::RATING_TO_COLOR_MAP
31
+ # @param [Integer] month
32
+ # @param [Integer] year
33
+ # @return [Terminal::Table]
34
+ def generate_month_report(month, year)
35
+ mon_by_wday = @store.by_month(month, year).group_by { |day| day.date.wday }
36
+ if mon_by_wday.length < 7
37
+ raise ReportError, "Unable to process #{Date::MONTHNAMES[month]}, please have at least 7 days of data first"
38
+ end
39
+
40
+ pad_month_start!(mon_by_wday)
41
+ weeks = color_month(mon_by_wday)
42
+ Terminal::Table.new(rows: weeks, style: { all_separators: true }, headings: Date::ABBR_DAYNAMES)
43
+ end
44
+
45
+ # Same as generate_month_report except it includes a color legend
46
+ # @param [Integer] month
47
+ # @param [Integer] year
48
+ # @return [Terminal::Table]
49
+ def month_with_legend(month, year)
50
+ tab = generate_month_report month, year
51
+ Terminal::Table.new(rows: [[tab, Report.legend]]).tap do |combined|
52
+ combined.style = { border_x: '', border_y: '', border_i: '', width: `tput cols`.to_i }
53
+ combined.headings = ["== #{Date::MONTHNAMES[month]} - #{year} ==", '== Legend ==']
54
+ end
55
+ end
56
+
57
+ # Formats Tcelfer::RATING_TO_COLOR_MAP into a pretty legend Terminal::Table
58
+ # @return [Terminal::Table]
59
+ def self.legend
60
+ leg = Tcelfer::RATING_TO_COLOR_MAP.map do |rating, color|
61
+ [Paint[' ', :inverse, color], rating]
62
+ end
63
+ Terminal::Table.new(rows: leg, headings: %w[Rating Color].map { |head| Paint[head, :bold] })
64
+ end
65
+
66
+ private
67
+
68
+ # Takes a hash with keys from 0 to 6 (ostensibly)
69
+ # and adds `nil` so the first of the month is the first
70
+ # colored day in that week row
71
+ # @param [Hash] mon_by_wday
72
+ # @return [Nil]
73
+ def pad_month_start!(mon_by_wday)
74
+ mon_by_wday.keys.sort.each do |wd|
75
+ dates = mon_by_wday[wd]
76
+ break unless dates.first.date.day != 1
77
+
78
+ dates.unshift(nil)
79
+ end
80
+ end
81
+
82
+ # convert a Tcelfer::Day object
83
+ # to an ANSI colored space
84
+ # @param [Tcelfer::Day] day
85
+ # @return [String]
86
+ def color_day(day)
87
+ return ' ' unless day
88
+
89
+ color = Tcelfer::RATING_TO_COLOR_MAP[day.rating]
90
+ Paint[' ', :inverse, color]
91
+ end
92
+
93
+ # Returns an array of arrays of colored "dates" based on #color_day
94
+ # @param [Hash] mon_by_wday
95
+ # @return [Array]
96
+ def color_month(mon_by_wday)
97
+ mon_by_wday[0].zip(*mon_by_wday.values_at(*(1..6))).map { |week| week.map(&method(:color_day)) }
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2018 Anthony Gargiulo <anthony@agargiulo.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ require 'anyway'
18
+
19
+ module Tcelfer
20
+ # Configuration for tcelfer, thanks to anyway_config
21
+ class Config < Anyway::Config
22
+ config_name :tcelfer
23
+ attr_config(
24
+ :sqlite_path,
25
+ debug: false,
26
+ update_existing: false
27
+ )
28
+
29
+ def validate!
30
+ raise Tcelfer::StorageError, 'TCELFER_SQLITE_PATH not defined, cannot continue' if sqlite_path.nil?
31
+
32
+ self
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2018 Anthony Gargiulo <anthony@agargiulo.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module Tcelfer
19
+ # Base error class, extend this one please
20
+ class Error < StandardError; end
21
+
22
+ # Issues with Storage
23
+ class StorageError < Error; end
24
+
25
+ # When we have a dupe and weren't asked to overwrite
26
+ class DuplicateDayError < StorageError
27
+ # The return value here is used by Sinatra's error handling
28
+ # It leads into the `error 4XX {...}` blocks
29
+ def http_status
30
+ 409
31
+ end
32
+ end
33
+
34
+ # Issues with reports
35
+ class ReportError < Error; end
36
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2018 Anthony Gargiulo <anthony@agargiulo.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ require 'sequel'
18
+
19
+ module Tcelfer
20
+ module Models
21
+ # Day model
22
+ class Day < Sequel::Model(:days)
23
+ # Pretty format for a Day model
24
+ # 2019-01-17: Normal, Average Day
25
+ # @return [String]
26
+ def to_s
27
+ str = "#{date}: #{rating}"
28
+ str += " || Notes: #{notes}" unless notes.nil?
29
+ str
30
+ end
31
+ end
32
+ end
33
+ end