aoc_cli 0.1.3 → 0.2.0
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 +4 -4
- data/.gitignore +2 -0
- data/CHANGELOG.md +18 -2
- data/Gemfile +0 -8
- data/README.md +138 -53
- data/aoc_cli.gemspec +1 -1
- data/bin/setup +1 -2
- data/lib/aoc_cli.rb +1 -1
- data/lib/aoc_cli/commands.rb +86 -69
- data/lib/aoc_cli/database.rb +75 -14
- data/lib/aoc_cli/day.rb +58 -87
- data/lib/aoc_cli/errors.rb +18 -3
- data/lib/aoc_cli/files.rb +125 -56
- data/lib/aoc_cli/help.rb +30 -9
- data/lib/aoc_cli/interface.rb +45 -28
- data/lib/aoc_cli/paths.rb +33 -30
- data/lib/aoc_cli/solve.rb +27 -35
- data/lib/aoc_cli/tables.rb +79 -79
- data/lib/aoc_cli/tools.rb +30 -6
- data/lib/aoc_cli/version.rb +1 -3
- data/lib/aoc_cli/year.rb +49 -32
- data/sample/aoc.rc +21 -0
- metadata +4 -4
- data/pkg/aoc_cli-0.1.2.gem +0 -0
data/lib/aoc_cli/help.rb
CHANGED
@@ -8,8 +8,9 @@ def flag(short, full)
|
|
8
8
|
str += "\t\t"
|
9
9
|
str
|
10
10
|
end
|
11
|
-
|
12
|
-
|
11
|
+
|
12
|
+
help = <<~help
|
13
|
+
Advent of Code - cli, version #{AocCli::VERSION}
|
13
14
|
#{"C. Welham Feb 2021".italic}
|
14
15
|
|
15
16
|
#{title("Usage")}
|
@@ -17,12 +18,16 @@ Advent of Code - cli, version 0.1.0
|
|
17
18
|
|
18
19
|
#{title("Setup")}
|
19
20
|
- Store session cookie keys to access AoC
|
20
|
-
|
21
|
-
#{flag("-
|
21
|
+
|
22
|
+
#{flag("-k","--key")}Store a session cookie to use the cli.
|
23
|
+
#{flag("-u","--user")}Set alias for key (default: "main")
|
24
|
+
#{flag("-U","--default")}Get/set default alias
|
25
|
+
#{flag("-h","--help")}Print this screen
|
22
26
|
|
23
27
|
#{title("Year Directory")}
|
24
28
|
- Initialise a year directory to use aoc-cli.
|
25
29
|
- If no alias is passed, the default key is used
|
30
|
+
|
26
31
|
#{flag("-y","--init-year")}Initialise directory and fetch calendar
|
27
32
|
#{flag("-u","--user")}Specify alias for initialisation
|
28
33
|
#{flag("-d","--init-day")}Create day subdirectory, fetch puzzle and input
|
@@ -30,16 +35,21 @@ Advent of Code - cli, version 0.1.0
|
|
30
35
|
|
31
36
|
#{title("Day Subdirectory")}
|
32
37
|
- These commands can be run from the day subdirectory
|
38
|
+
|
33
39
|
#{flag("-s","--solve")}Attempt puzzle
|
34
|
-
#{flag("-r","--refresh")}Refresh puzzle
|
35
|
-
#{flag("-a","--attempts")}Prints previous attempt table
|
36
40
|
#{flag("-p","--part")}Specify part (attempts)
|
41
|
+
#{flag("-r","--refresh")}Refresh puzzle
|
42
|
+
|
43
|
+
#{title("Reddit")}
|
44
|
+
- Defaults to a Reddit CLI if one is installed
|
45
|
+
|
37
46
|
#{flag("-R","--reddit")}Open Reddit solution megathread
|
38
|
-
#{flag("-
|
47
|
+
#{flag("-B","--browser")}Open Reddit thread in browser
|
39
48
|
|
40
49
|
#{title("Manual Usage")}
|
41
50
|
- AocCli uses metadata so that these flags do not need to be entered.
|
42
51
|
- Command flags can be entered manually, but this is not recommended
|
52
|
+
|
43
53
|
#{flag("-u","--user")}Specify user
|
44
54
|
#{flag("-Y","--year")}Specify year
|
45
55
|
#{flag("-D","--day")}Specify day
|
@@ -48,6 +58,17 @@ Advent of Code - cli, version 0.1.0
|
|
48
58
|
#{title("Configuration")}
|
49
59
|
- Pass with a value to update setting
|
50
60
|
- Pass without an argument to display current setting.
|
51
|
-
|
52
|
-
#{flag("-U","--default")}Default key alias to use
|
61
|
+
|
62
|
+
#{flag("-U","--default")}Default key alias to use
|
63
|
+
#{flag("-G","--gen-config")}Creates an example config file
|
64
|
+
|
65
|
+
#{title("Tables")}
|
66
|
+
- Print stats in a terminal-friendly table
|
67
|
+
|
68
|
+
#{flag("-a","--attempts")}Prints previous attempts for puzzle (part needed)
|
69
|
+
#{flag("-c","--simple-cal")}Prints your progress
|
70
|
+
#{flag("-C","--fancy-cal")}Prints your calendar file
|
71
|
+
#{flag("-S","--stats")}Prints your stats (time taken, number of attempts)
|
72
|
+
|
53
73
|
help
|
74
|
+
system("echo '#{help}' | less -r")
|
data/lib/aoc_cli/interface.rb
CHANGED
@@ -3,17 +3,16 @@ module AocCli
|
|
3
3
|
class Query
|
4
4
|
def initialize
|
5
5
|
ARGV.size > 0 ?
|
6
|
-
run(opts:Opts.new.parse_args) :
|
7
|
-
puts(Help.print)
|
6
|
+
run(opts:Opts.new.parse_args) : Help.print
|
8
7
|
rescue StandardError => e
|
9
8
|
abort e.message
|
10
9
|
end
|
11
10
|
def run(opts:)
|
12
|
-
cmd =
|
13
|
-
.const_get("AocCli::Commands::#{opts.cmd}")
|
11
|
+
cmd = Commands.const_get(Validate.cmd(opts.cmd))
|
14
12
|
.new(opts.args)
|
15
13
|
.exec
|
16
|
-
cmd.respond if cmd.class
|
14
|
+
cmd.respond if cmd.class
|
15
|
+
.instance_methods.include?(:respond)
|
17
16
|
end
|
18
17
|
end
|
19
18
|
class Help
|
@@ -31,60 +30,65 @@ module AocCli
|
|
31
30
|
case ARGV.shift
|
32
31
|
when "-a", "--attempts"
|
33
32
|
@cmd = :AttemptsTable
|
34
|
-
when "-
|
33
|
+
when "-B", "--browser"
|
34
|
+
@cmd = :OpenReddit
|
35
35
|
args[:browser] = true
|
36
|
+
when "-c", "--simple-cal"
|
37
|
+
@cmd = :CalendarTable
|
38
|
+
when "-C", "--fancy-cal"
|
39
|
+
@cmd = :PrintCal
|
36
40
|
when "-d", "--init-day"
|
37
41
|
@cmd = :DayInit
|
38
42
|
args[:day] = Validate.day(ARGV.shift.to_i)
|
43
|
+
when "-D", "--day"
|
44
|
+
args[:day] = ARGV.shift.to_i
|
45
|
+
when "-G", "--gen-config"
|
46
|
+
@cmd = :GenerateConfig
|
39
47
|
when "-h", "--help"
|
40
48
|
exit Help.print
|
41
49
|
when "-k", "--key"
|
42
50
|
@cmd = :KeyStore
|
43
51
|
args[:key] = Validate.set_key(ARGV.shift)
|
44
52
|
when "-p", "--part"
|
45
|
-
args[:part] =
|
53
|
+
args[:part] = ARGV.shift.to_i
|
46
54
|
when "-r", "--refresh"
|
47
55
|
@cmd = :Refresh
|
56
|
+
when "-R", "--reddit"
|
57
|
+
@cmd = :OpenReddit
|
48
58
|
when "-s", "--solve"
|
49
59
|
@cmd = :DaySolve
|
50
60
|
args[:ans] = Validate.ans(ARGV.shift)
|
61
|
+
when "-S", "--stats"
|
62
|
+
@cmd = :StatsTable
|
51
63
|
when "-u", "--user"
|
52
64
|
args[:user] = ARGV.shift
|
65
|
+
when "-U", "--default"
|
66
|
+
@cmd = :DefaultAlias
|
67
|
+
args[:user] = ARGV.shift
|
53
68
|
when "-y", "--init-year"
|
54
69
|
@cmd = :YearInit
|
55
70
|
args[:year] = Validate.year(ARGV.shift.to_i)
|
56
|
-
when "-B", "--browser"
|
57
|
-
@cmd = :DefaultReddit
|
58
|
-
args[:value] = ARGV.shift
|
59
|
-
when "-D", "--day"
|
60
|
-
args[:day] = Validate.day(ARGV.shift.to_i)
|
61
|
-
when "-R", "--reddit"
|
62
|
-
@cmd = :OpenReddit
|
63
|
-
when "-S", "--stats"
|
64
|
-
@cmd = :StatsTable
|
65
|
-
when "-U", "--default-user"
|
66
|
-
@cmd = :DefaultUser
|
67
|
-
args[:user] = ARGV.shift
|
68
71
|
when "-Y", "--year"
|
69
|
-
args[:year] =
|
72
|
+
args[:year] = ARGV.shift.to_i
|
70
73
|
else raise E::FlagInv
|
71
74
|
end
|
72
75
|
end
|
73
|
-
raise E::NoCmd if cmd.nil?
|
74
76
|
self
|
75
77
|
end
|
76
78
|
end
|
77
79
|
class Validate
|
80
|
+
def self.cmd(cmd)
|
81
|
+
raise E::CmdNil if cmd.nil?
|
82
|
+
cmd
|
83
|
+
end
|
78
84
|
def self.user(user)
|
79
85
|
raise E::UserNil if user.nil?
|
80
|
-
raise E::UserInv.new(user) unless
|
81
|
-
.new.is_set?(key:"cookie=>#{user}")
|
86
|
+
raise E::UserInv.new(user) unless user_in_config?(user)
|
82
87
|
user
|
83
88
|
end
|
84
89
|
def self.set_user(user)
|
85
90
|
raise E::UserNil if user.nil?
|
86
|
-
raise E::UserDup if
|
87
|
-
.is_set?(key:"cookie=>#{user}")
|
91
|
+
raise E::UserDup.new(user) if user_in_config?(user)
|
88
92
|
user
|
89
93
|
end
|
90
94
|
def self.year(year)
|
@@ -107,12 +111,14 @@ module AocCli
|
|
107
111
|
end
|
108
112
|
def self.set_key(key)
|
109
113
|
raise E::KeyNil if key.nil?
|
110
|
-
raise E::KeyDup if Files::Config
|
111
|
-
.
|
114
|
+
raise E::KeyDup.new(key) if Files::Config::Tools
|
115
|
+
.is_set?(val:"#{key}(\b|$)")
|
116
|
+
raise E::KeyInv unless valid_key?(key)
|
112
117
|
key
|
113
118
|
end
|
114
|
-
def self.
|
119
|
+
def self.key(key)
|
115
120
|
raise E::KeyNil if key.nil?
|
121
|
+
raise E::KeyInv unless valid_key?(key)
|
116
122
|
key
|
117
123
|
end
|
118
124
|
def self.ans(ans)
|
@@ -132,6 +138,17 @@ module AocCli
|
|
132
138
|
Metafile.get(:year) != year.to_s
|
133
139
|
dir
|
134
140
|
end
|
141
|
+
def self.no_config
|
142
|
+
raise E::ConfigExist if File.exist?(Paths::Config.path)
|
143
|
+
Paths::Config.path
|
144
|
+
end
|
145
|
+
private
|
146
|
+
def self.valid_key?(key)
|
147
|
+
/session=(?:[a-f0-9]){96}/.match?(key)
|
148
|
+
end
|
149
|
+
def self.user_in_config?(user)
|
150
|
+
Files::Config::Tools.is_set?(key:"cookie=>#{user}")
|
151
|
+
end
|
135
152
|
end
|
136
153
|
end
|
137
154
|
end
|
data/lib/aoc_cli/paths.rb
CHANGED
@@ -1,16 +1,29 @@
|
|
1
1
|
module AocCli
|
2
2
|
module Paths
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
require 'fileutils'
|
4
|
+
class Year
|
5
|
+
attr_reader :user, :year
|
6
|
+
def initialize(u:Metafile.get(:user),
|
7
|
+
y:Metafile.get(:year))
|
8
|
+
@user = Validate.user(u)
|
9
|
+
@year = Validate.year(y)
|
8
10
|
end
|
9
|
-
def
|
10
|
-
|
11
|
+
def in_year?
|
12
|
+
File.exist?("./.meta") ?
|
13
|
+
Metafile.type == :ROOT : true
|
11
14
|
end
|
12
|
-
def
|
13
|
-
"
|
15
|
+
def year_dir
|
16
|
+
in_year? ? "." : ".."
|
17
|
+
end
|
18
|
+
def local(f:)
|
19
|
+
"#{Validate.not_init(dir:year_dir,
|
20
|
+
year:year)}/#{filename(f:f)}"
|
21
|
+
end
|
22
|
+
def filename(f:)
|
23
|
+
case f.to_sym
|
24
|
+
when :Stars then "#{year}.md"
|
25
|
+
when :meta then ".meta"
|
26
|
+
end
|
14
27
|
end
|
15
28
|
end
|
16
29
|
class Day
|
@@ -21,6 +34,9 @@ module AocCli
|
|
21
34
|
@year = Validate.year(y)
|
22
35
|
@day = Validate.day(d)
|
23
36
|
end
|
37
|
+
def create_cache
|
38
|
+
FileUtils.mkdir_p(cache_dir) unless Dir.exist?(cache_dir)
|
39
|
+
end
|
24
40
|
def filename(f:)
|
25
41
|
case f.to_sym
|
26
42
|
when :Input then "input"
|
@@ -51,29 +67,16 @@ module AocCli
|
|
51
67
|
[cache_path(f:f), local(f:f)]
|
52
68
|
end
|
53
69
|
end
|
54
|
-
class
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@user = Validate.user(u)
|
59
|
-
@year = Validate.year(y)
|
60
|
-
end
|
61
|
-
def in_year?
|
62
|
-
File.exist?("./.meta") ?
|
63
|
-
Metafile.type == :ROOT : true
|
64
|
-
end
|
65
|
-
def year_dir
|
66
|
-
in_year? ? "." : ".."
|
70
|
+
class Config
|
71
|
+
def self.create
|
72
|
+
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
|
73
|
+
File.write(path, "", mode:"a") unless File.exist?(path)
|
67
74
|
end
|
68
|
-
def
|
69
|
-
"#{
|
70
|
-
year:year)}/#{filename(f:f)}"
|
75
|
+
def self.dir
|
76
|
+
"#{Dir.home}/.config/aoc-cli"
|
71
77
|
end
|
72
|
-
def
|
73
|
-
|
74
|
-
when :Stars then "#{year}.md"
|
75
|
-
when :meta then ".meta"
|
76
|
-
end
|
78
|
+
def self.path
|
79
|
+
"#{dir}/aoc.rc"
|
77
80
|
end
|
78
81
|
end
|
79
82
|
class Database < Config
|
data/lib/aoc_cli/solve.rb
CHANGED
@@ -16,10 +16,8 @@ module AocCli
|
|
16
16
|
@answer = Validate.ans(a)
|
17
17
|
end
|
18
18
|
def raw
|
19
|
-
@raw ||= Tools::Post
|
20
|
-
|
21
|
-
data:{level:part, answer:answer})
|
22
|
-
.plain
|
19
|
+
@raw ||= Tools::Post.new(u:user, y:year, d:day,
|
20
|
+
data:{level:part, answer:answer}).plain
|
23
21
|
end
|
24
22
|
def check
|
25
23
|
case raw
|
@@ -29,8 +27,7 @@ module AocCli
|
|
29
27
|
end
|
30
28
|
end
|
31
29
|
def respond
|
32
|
-
|
33
|
-
.const_get("AocCli::Solve::Respond::#{check}")
|
30
|
+
Respond.const_get(check)
|
34
31
|
.new(attempt:self)
|
35
32
|
.respond
|
36
33
|
.react
|
@@ -45,44 +42,35 @@ module AocCli
|
|
45
42
|
end
|
46
43
|
class Correct < Response
|
47
44
|
def react
|
48
|
-
|
49
|
-
.new(attempt:attempt)
|
50
|
-
.correct
|
51
|
-
Database::Stats::Complete
|
52
|
-
.new(n:log.count_attempts)
|
53
|
-
.update
|
45
|
+
Database.correct(attempt:attempt)
|
54
46
|
Year.refresh
|
55
|
-
Day.refresh
|
56
|
-
Database::Stats::Init.new.init
|
47
|
+
Day.refresh(files:[:Puzzle])
|
57
48
|
end
|
58
49
|
def respond
|
59
|
-
response
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
50
|
+
puts <<~response
|
51
|
+
#{"Correct!".bold.green} #{
|
52
|
+
case attempt.part
|
53
|
+
when "1" then "Downloading part two..."
|
54
|
+
when "2" then "This day is now complete!".green
|
55
|
+
end }
|
56
|
+
response
|
64
57
|
self
|
65
58
|
end
|
66
|
-
private
|
67
|
-
def next_part
|
68
|
-
"Downloading part two.."
|
69
|
-
end
|
70
|
-
def complete
|
71
|
-
"This day is now complete!".green
|
72
|
-
end
|
73
59
|
end
|
74
60
|
class Incorrect < Response
|
75
61
|
def react
|
76
|
-
Database::
|
62
|
+
Database::Attempt
|
77
63
|
.new(attempt:attempt)
|
78
64
|
.incorrect(high:high, low:low)
|
79
65
|
end
|
80
66
|
def respond
|
81
|
-
response
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
67
|
+
puts <<~response
|
68
|
+
#{"Incorrect".red.bold}: You guessed - #{attempt
|
69
|
+
.answer.to_s.red} #{
|
70
|
+
high ? "(too high)" :
|
71
|
+
low ? "(too low)" : ""}
|
72
|
+
#{"Please wait".yellow} #{wait_time} before answering again
|
73
|
+
response
|
86
74
|
self
|
87
75
|
end
|
88
76
|
def high
|
@@ -91,12 +79,16 @@ module AocCli
|
|
91
79
|
def low
|
92
80
|
/too low/.match?(attempt.raw)
|
93
81
|
end
|
82
|
+
def wait_time
|
83
|
+
attempt.raw.scan(/(?:(one minute|\d+ minutes))/)
|
84
|
+
.first.first.to_s
|
85
|
+
end
|
94
86
|
end
|
95
87
|
class Wait < Response
|
96
88
|
def respond
|
97
|
-
response
|
98
|
-
|
99
|
-
|
89
|
+
puts <<~response
|
90
|
+
#{"Please wait".yellow.bold}: #{time.to_s}
|
91
|
+
response
|
100
92
|
self
|
101
93
|
end
|
102
94
|
def time
|
data/lib/aoc_cli/tables.rb
CHANGED
@@ -1,34 +1,47 @@
|
|
1
1
|
module AocCli
|
2
2
|
module Tables
|
3
|
-
class
|
3
|
+
class Table
|
4
4
|
require 'terminal-table'
|
5
|
-
attr_reader :user, :year, :
|
6
|
-
def initialize(u:Metafile.get(:user),
|
7
|
-
y:Metafile.get(:year),
|
8
|
-
d:Metafile.get(:day),
|
9
|
-
p:Metafile.get(:part))
|
5
|
+
attr_reader :user, :year, :table, :cols, :where
|
6
|
+
def initialize(u:Metafile.get(:user), y:Metafile.get(:year))
|
10
7
|
@user = Validate.user(u)
|
11
8
|
@year = Validate.year(y)
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
end
|
10
|
+
def border
|
11
|
+
Prefs.bool(key:"unicode_tables") ? :unicode : :ascii
|
12
|
+
end
|
13
|
+
def data
|
14
|
+
Database::Query
|
15
15
|
.new(path:Paths::Database.cfg(user))
|
16
|
+
.select(t:table, cols:cols, where:where)
|
16
17
|
end
|
17
|
-
def
|
18
|
-
puts rows.count > 0 ?
|
19
|
-
"You have not attempted this puzzle yet!"
|
18
|
+
def print
|
19
|
+
puts rows.count > 0 ? make : nil_message
|
20
20
|
end
|
21
|
-
|
22
|
-
def table
|
21
|
+
def make
|
23
22
|
tab = Terminal::Table.new(
|
24
23
|
:headings => headings,
|
25
24
|
:rows => rows,
|
26
25
|
:title => title)
|
27
26
|
tab.style = {
|
28
|
-
:border =>
|
27
|
+
:border => border,
|
29
28
|
:alignment => :center}
|
30
29
|
tab
|
31
30
|
end
|
31
|
+
end
|
32
|
+
class Attempts < Table
|
33
|
+
attr_reader :day, :part
|
34
|
+
def initialize(u:Metafile.get(:user),
|
35
|
+
y:Metafile.get(:year),
|
36
|
+
d:Metafile.get(:day),
|
37
|
+
p:Metafile.get(:part))
|
38
|
+
super(u:u, y:y)
|
39
|
+
@day = Validate.day(d)
|
40
|
+
@part = Validate.part(p)
|
41
|
+
@table = :attempts
|
42
|
+
@cols = "time, answer, high, low, correct"
|
43
|
+
@where = {year:year, day:day, part:part}
|
44
|
+
end
|
32
45
|
def title
|
33
46
|
"#{year} - Day #{day}:#{part}".bold
|
34
47
|
end
|
@@ -36,56 +49,33 @@ module AocCli
|
|
36
49
|
["Answer", "Time", "Hint"]
|
37
50
|
end
|
38
51
|
def rows
|
39
|
-
@rows ||=
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
def parse_ans(attempt)
|
49
|
-
attempt[4] == 1 ?
|
50
|
-
attempt[1].to_s.green :
|
51
|
-
attempt[1].to_s.red
|
52
|
-
end
|
53
|
-
def parse_time(attempt)
|
54
|
-
DateTime
|
55
|
-
.strptime(attempt[0], "%Y-%m-%d %H:%M:%S %Z")
|
52
|
+
@rows ||= data.map do |d|
|
53
|
+
[parse_ans(d), parse_time(d), parse_hint(d)]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
def parse_ans(row)
|
57
|
+
row[4] == 1 ? row[1].to_s.green : row[1].to_s.red
|
58
|
+
end
|
59
|
+
def parse_time(row)
|
60
|
+
DateTime.strptime(row[0], "%Y-%m-%d %H:%M:%S %Z")
|
56
61
|
.strftime("%H:%M - %d/%m/%y")
|
57
62
|
end
|
58
|
-
def parse_hint(
|
59
|
-
|
60
|
-
|
63
|
+
def parse_hint(row)
|
64
|
+
row[3] == 1 ? "low" :
|
65
|
+
row[2] == 1 ? "high" : "-"
|
66
|
+
end
|
67
|
+
def nil_message
|
68
|
+
"You have not attempted part #{part} yet!"
|
61
69
|
end
|
62
70
|
end
|
63
71
|
module Stats
|
64
|
-
class Year
|
65
|
-
attr_reader :user, :year, :db
|
72
|
+
class Year < Table
|
66
73
|
def initialize(u:Metafile.get(:user),
|
67
74
|
y:Metafile.get(:year))
|
68
|
-
|
69
|
-
@
|
70
|
-
@
|
71
|
-
|
72
|
-
end
|
73
|
-
def print
|
74
|
-
puts rows.count > 0 ? table :
|
75
|
-
"You have not completed any puzzles yet"
|
76
|
-
end
|
77
|
-
def rows
|
78
|
-
@rows ||= stats.map{|s| [s[0], s[1], s[2], s[3]]}
|
79
|
-
end
|
80
|
-
def table
|
81
|
-
tab = Terminal::Table.new(
|
82
|
-
:headings => headings,
|
83
|
-
:rows => rows,
|
84
|
-
:title => title)
|
85
|
-
tab.style = {
|
86
|
-
:border => :unicode,
|
87
|
-
:alignment => :center}
|
88
|
-
tab
|
75
|
+
super(u:u, y:y)
|
76
|
+
@table = :stats
|
77
|
+
@cols = "day, part, attempts, elapsed"
|
78
|
+
@where = {year:"'#{year}'", correct:"'1'"}
|
89
79
|
end
|
90
80
|
def title
|
91
81
|
"Year #{year}"
|
@@ -93,15 +83,11 @@ module AocCli
|
|
93
83
|
def headings
|
94
84
|
["Day", "Part", "Attempts", "Time (h:m:s)"]
|
95
85
|
end
|
96
|
-
def
|
97
|
-
@
|
98
|
-
t:"stats",
|
99
|
-
cols:"day, part, attempts, elapsed",
|
100
|
-
data:where)
|
86
|
+
def rows
|
87
|
+
@rows ||= data.map{|d| [d[0], d[1], d[2], d[3]]}
|
101
88
|
end
|
102
|
-
def
|
103
|
-
|
104
|
-
correct:"'1'"}
|
89
|
+
def nil_message
|
90
|
+
"You have not completed any puzzles yet"
|
105
91
|
end
|
106
92
|
end
|
107
93
|
class Day < Year
|
@@ -111,9 +97,10 @@ module AocCli
|
|
111
97
|
d:Metafile.get(:day))
|
112
98
|
super(u:u, y:y)
|
113
99
|
@day = Validate.day(d)
|
114
|
-
|
115
|
-
|
116
|
-
|
100
|
+
@cols = "part, attempts, elapsed"
|
101
|
+
@where = { year:"'#{year}'",
|
102
|
+
day:"'#{day}'",
|
103
|
+
correct:"'1'" }
|
117
104
|
end
|
118
105
|
def title
|
119
106
|
"Year #{year}: Day #{day}"
|
@@ -121,18 +108,31 @@ module AocCli
|
|
121
108
|
def headings
|
122
109
|
["Part", "Attempts", "Time (h:m:s)"]
|
123
110
|
end
|
124
|
-
def
|
125
|
-
@
|
126
|
-
t:"stats",
|
127
|
-
cols:"part, attempts, elapsed",
|
128
|
-
data:where)
|
129
|
-
end
|
130
|
-
def where
|
131
|
-
{year:"'#{year}'",
|
132
|
-
day:"'#{day}'",
|
133
|
-
correct:"'1'"}
|
111
|
+
def rows
|
112
|
+
@rows ||= data.map{|d| [d[0], d[1], d[2]]}
|
134
113
|
end
|
135
114
|
end
|
136
115
|
end
|
116
|
+
class Calendar < Table
|
117
|
+
def initialize(u:Metafile.get(:user),
|
118
|
+
y:Metafile.get(:year))
|
119
|
+
super(u:u, y:y)
|
120
|
+
@table = :calendar
|
121
|
+
@cols = "*"
|
122
|
+
@where = {year:year}
|
123
|
+
end
|
124
|
+
def title
|
125
|
+
"#{user}: #{year}"
|
126
|
+
end
|
127
|
+
def headings
|
128
|
+
["Day", "Stars"]
|
129
|
+
end
|
130
|
+
def rows
|
131
|
+
@rows ||= data.map{|d| [d[1], parse_stars(d[2])]}
|
132
|
+
end
|
133
|
+
def parse_stars(day)
|
134
|
+
day.to_i == 0 ? ".." : ("*" * day.to_i).ljust(2, ".")
|
135
|
+
end
|
136
|
+
end
|
137
137
|
end
|
138
138
|
end
|