versed 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 59b8a64d63d661969146d5667b97061c8f9950dd
4
+ data.tar.gz: f4cb1bf662a0724638ccb34c4cbd38ec6260807a
5
+ SHA512:
6
+ metadata.gz: a854a6542ab4e8ac84675091b70f9f49b126c3d4cbd1303b5ef840b16abd97cda7e891c4c4ecf6b7aa17d454418c248f2be027c457087376b8118fe1d7bea4b4
7
+ data.tar.gz: cb427eac5343b99ee7510221d239dc4541a6e88ac582e3402c1bb49e7753258cf3c3e55846031cfe8f1527b0fd3c158dd0b341f0c93b5405b62526d5692967d8
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Chris Knadler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,82 @@
1
+ # Versed
2
+
3
+ Versed turns monthly activity logs into a clean visualization of your progress
4
+ against your monthly goals.
5
+
6
+ ## Install
7
+
8
+ ```
9
+ $ gem install versed
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```
15
+ $ versed -h
16
+ usage: versed [-h] [--version]
17
+ versed <schedule> <log> [output_path]
18
+
19
+ -h, --help Print usage information
20
+ --version Print version
21
+
22
+ Versed takes in a schedule and weekly log (as YAML files) and outputs a
23
+ visualization of time spent this week (in an HTML page).
24
+ ```
25
+
26
+ ## Input
27
+
28
+ ```
29
+ # schedule.yaml
30
+
31
+ Sunday:
32
+ Task 1: 60 # time in minutes
33
+
34
+ Monday:
35
+ Task 1: 60
36
+ Task 2: 120
37
+
38
+ Tuesday:
39
+ Task 2: 60
40
+ Task 3: 30
41
+
42
+ Wednesday:
43
+ Task 1: 30
44
+ Task 3: 30
45
+
46
+ Friday:
47
+ Task 4: 60
48
+
49
+ ```
50
+
51
+ ```
52
+ # log.yaml
53
+ #
54
+ # Note: A log is expected to only contain entries from a single month.
55
+
56
+ 2016.11.01:
57
+ Task 1: 45
58
+
59
+ 2016.11.02:
60
+ Task 4: 75
61
+
62
+ 2016.11.03:
63
+ Task 1: 15
64
+
65
+ 2016.11.04:
66
+ Task 1: 15
67
+ Task 3: 60
68
+
69
+ ...
70
+ ```
71
+
72
+ ## Output
73
+
74
+ Versed outputs an HTML page that visualizes the conformance of your logged
75
+ activities to your schedule.
76
+
77
+ ![screen one](assets/screen_one.png)
78
+ ![screen two](assets/screen_two.png)
79
+
80
+ ## License
81
+
82
+ MIT.
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+ require "versed/version"
5
+ require "versed/generator"
6
+
7
+ ###
8
+ # help text
9
+ ###
10
+
11
+ BANNER = <<END
12
+ usage: versed [-h] [--version]
13
+ versed <schedule> <log> [output_path]
14
+
15
+ END
16
+
17
+ DESC = <<END
18
+
19
+ Versed takes in a schedule and weekly log (as YAML files) and outputs a
20
+ visualization of time spent this week (in an HTML page).
21
+
22
+ See https://github.com/cknadler/versed for more details.
23
+
24
+ END
25
+
26
+ ###
27
+ # option parsing
28
+ ###
29
+
30
+ opts = OptionParser.new do |o|
31
+ o.banner = BANNER
32
+
33
+ # other
34
+ o.on("-h", "--help", "Print usage information") do
35
+ puts o
36
+ exit
37
+ end
38
+ o.on("--version", "Print version") do
39
+ puts Versed::VERSION
40
+ exit
41
+ end
42
+
43
+ o.separator DESC
44
+ end
45
+
46
+ begin
47
+ opts.parse!
48
+ rescue OptionParser::InvalidOption => e
49
+ puts e
50
+ puts opts
51
+ exit 1
52
+ end
53
+
54
+ if ARGV.size < 2
55
+ puts opts
56
+ puts "Too few arguments." unless ARGV.empty?
57
+ exit 1
58
+ end
59
+
60
+ ###
61
+ # run
62
+ ###
63
+
64
+ Versed::Generator.run(ARGV[0], ARGV[1], ARGV[2])
@@ -0,0 +1,44 @@
1
+ require "versed/task"
2
+
3
+ module Versed
4
+ class Category
5
+ attr_reader :id, :tasks
6
+
7
+ def initialize(id, date_range)
8
+ @id = id
9
+ @tasks = []
10
+ date_range.each { |date| @tasks << Task.new(id, date) }
11
+ end
12
+
13
+ def incomplete?
14
+ total_min_incomplete > 0
15
+ end
16
+
17
+ def total_min_scheduled
18
+ scheduled = 0
19
+ @tasks.each do |task|
20
+ next unless task.time_scheduled
21
+ scheduled += task.time_scheduled
22
+ end
23
+ scheduled
24
+ end
25
+
26
+ def total_min_logged
27
+ logged = 0
28
+ @tasks.each do |task|
29
+ next unless task.time_spent
30
+ logged += task.time_spent
31
+ end
32
+ logged
33
+ end
34
+
35
+ def total_min_incomplete
36
+ incomplete = total_min_scheduled - total_min_logged
37
+ incomplete >= 0 ? incomplete : 0
38
+ end
39
+
40
+ def percent_incomplete
41
+ ((total_min_incomplete / total_min_scheduled.to_f) * 100).round(1)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ require "versed/task"
2
+
3
+ module Versed
4
+ class Day
5
+ attr_reader :date, :tasks
6
+
7
+ def initialize(date)
8
+ @date = date
9
+ @tasks = []
10
+ end
11
+
12
+ def active?
13
+ @tasks.each { |t| return true if t.time_spent? }
14
+ false
15
+ end
16
+
17
+ def time_on_schedule
18
+ time = 0
19
+ @tasks.each do |task|
20
+ next unless task.time_spent? && task.time_scheduled?
21
+ if task.time_scheduled < task.time_spent
22
+ time += task.time_scheduled
23
+ else
24
+ time += task.time_spent
25
+ end
26
+ end
27
+ time
28
+ end
29
+
30
+ def time_off_schedule
31
+ time = 0
32
+ @tasks.each do |task|
33
+ next unless task.time_spent
34
+ if !task.time_scheduled
35
+ time += task.time_spent
36
+ elsif task.time_scheduled < task.time_spent
37
+ time += task.time_spent - task.time_scheduled
38
+ end
39
+ end
40
+ time
41
+ end
42
+
43
+ def time_scheduled
44
+ @tasks.collect { |t| t.time_scheduled? ? t.time_scheduled : 0 }.reduce(0, :+)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,62 @@
1
+ require "mustache"
2
+ require "versed/reader"
3
+ require "versed/schedule"
4
+ require "versed/schedule_view"
5
+
6
+ module Versed
7
+ module Generator
8
+
9
+ # The CLI entry point for the Versed program. Parses the input files, parses
10
+ # the content into task objects, generates the visualization HTML and
11
+ # converts the HTML to a PDF.
12
+ def self.run(schedule_path, log_path, output_path)
13
+ # read in input
14
+ raw_schedule = Versed::Reader.read(schedule_path)
15
+ raw_log = Versed::Reader.read(log_path)
16
+
17
+ # determine date range
18
+ origin = Date.parse(raw_log.keys[0])
19
+ start_date = Date.new(origin.year, origin.month, 1)
20
+ end_date = Date.new(origin.year, origin.month, -1)
21
+ date_range = start_date..end_date
22
+ validate_log(raw_log, date_range)
23
+
24
+ # map model and view model
25
+ schedule = Versed::Schedule.new(raw_schedule, raw_log, date_range)
26
+ schedule_view = Versed::ScheduleView.new(schedule)
27
+
28
+ # make HTML page
29
+ templates_path = File.expand_path(File.join(__FILE__, "../../../templates"))
30
+ Mustache.template_path = templates_path
31
+ main_template_path = File.join(templates_path, "page.mustache")
32
+ html = Mustache.render(IO.read(main_template_path), schedule_view.to_hash)
33
+
34
+ # determine output path
35
+ output_path = Dir.pwd unless output_path
36
+ output_path = File.expand_path(output_path)
37
+ if File.directory?(output_path)
38
+ file_name = schedule.days[0].date.strftime("%Y-%m.html")
39
+ output_path = File.join(output_path, file_name)
40
+ end
41
+
42
+ # output
43
+ file = File.open(output_path, "w")
44
+ file << html
45
+ file.close
46
+ end
47
+
48
+ private
49
+
50
+ def self.validate_log(raw_log, date_range)
51
+ raw_log.keys.each do |raw_day|
52
+ day = Date.parse(raw_day)
53
+ unless date_range.include?(day)
54
+ puts "Days from multiple months present."
55
+ puts "#{day} not present in #{date_range}"
56
+ puts "Ensure log only contains days from one calendar month."
57
+ exit 1
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,22 @@
1
+ require 'yaml'
2
+
3
+ module Versed
4
+ module Reader
5
+
6
+ # Reads YAML from a file
7
+ # @param path [String] The path to the file
8
+ # @return [Hash] The parsed YAML file
9
+ def self.read(path)
10
+ begin
11
+ return YAML.load(IO.read(path))
12
+ rescue YAML::Error => e
13
+ puts "Encountered an error reading YAML from #{path}"
14
+ puts e.message
15
+ exit 1
16
+ rescue StandardError => e
17
+ puts e.message
18
+ exit 1
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,98 @@
1
+ require "versed/category"
2
+ require "versed/day"
3
+
4
+ module Versed
5
+ class Schedule
6
+ attr_reader :days, :categories
7
+
8
+ def initialize(raw_schedule, raw_log, date_range)
9
+ @date_range = date_range
10
+ map_categories(raw_schedule, raw_log)
11
+ map_days
12
+ map_time_scheduled(raw_schedule)
13
+ map_time_spent(raw_log)
14
+ end
15
+
16
+ def categories
17
+ @categories.values
18
+ end
19
+
20
+ # Returns an array of incomplete tasks. This array is sorted first by
21
+ # percentage incomplete, then by total number of minutes incomplete.
22
+ def incomplete_tasks
23
+ # TODO: refactor with reject
24
+ incomplete = []
25
+ categories.each { |c| incomplete << c if c.incomplete? }
26
+ incomplete.sort_by { |c| [-c.percent_incomplete, -c.total_min_incomplete] }
27
+ end
28
+
29
+ private
30
+
31
+ def map_categories(raw_schedule, raw_log)
32
+ @categories = {}
33
+ (category_ids(raw_schedule) + category_ids(raw_log)).uniq.sort.each do |id|
34
+ @categories[id] = Versed::Category.new(id, @date_range)
35
+ end
36
+ end
37
+
38
+ def map_days
39
+ @days = []
40
+ @date_range.each { |d| @days << Day.new(d) }
41
+
42
+ # map category tasks to days
43
+ categories.each do |category|
44
+ category.tasks.each_with_index do |task, index|
45
+ @days[index].tasks << task
46
+ end
47
+ end
48
+ end
49
+
50
+ def map_time_scheduled(raw_schedule)
51
+ @days.each_with_index do |day, day_id|
52
+ schedule_day = raw_schedule[Date::DAYNAMES[day.date.wday]]
53
+ next unless schedule_day
54
+
55
+ schedule_day.each do |scheduled_task_name, time_scheduled|
56
+ category = lookup_category(scheduled_task_name)
57
+ category.tasks[day_id].time_scheduled = time_scheduled
58
+ end
59
+ end
60
+ end
61
+
62
+ def map_time_spent(raw_log)
63
+ raw_log.each do |day, tasks|
64
+ day_id = Date.parse(day).mday - 1
65
+
66
+ tasks.each do |log_task_name, time_spent|
67
+ category = lookup_category(log_task_name)
68
+ assert(category, "Any category here should have been in the log or schedule.")
69
+ category.tasks[day_id].time_spent = time_spent
70
+ end
71
+ end
72
+ end
73
+
74
+ # Finds the category object for the given category id
75
+ # @param id [String] A category id
76
+ # @return [Category] The category object matching the id
77
+ def lookup_category(id)
78
+ category = @categories[id]
79
+ unless category
80
+ puts "Unrecognized category id: #{id}"
81
+ exit 1
82
+ end
83
+ category
84
+ end
85
+
86
+
87
+ # Finds all unique category ids in a log or a schedule
88
+ # @param entries [Hash] A parsed log or schedule
89
+ # @return [Array, String] Unique category ids
90
+ def category_ids(entries)
91
+ category_ids = []
92
+ entries.each do |day, tasks|
93
+ category_ids += tasks.keys
94
+ end
95
+ category_ids.uniq
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,181 @@
1
+ require "versed/schedule"
2
+
3
+ module Versed
4
+ class ScheduleView
5
+
6
+ DAYS_PER_ROW = 8
7
+
8
+ def initialize(schedule)
9
+ @schedule = schedule
10
+ end
11
+
12
+ def to_hash
13
+ hash = {
14
+ "sections" => [],
15
+ "metadata" => metadata,
16
+ "incomplete_tasks" => incomplete_tasks
17
+ }
18
+
19
+ # fill in days
20
+ section = nil
21
+ @schedule.days.each_with_index do |day, day_id|
22
+ if day_id % DAYS_PER_ROW == 0
23
+ section = {
24
+ "days" => [],
25
+ "categories" => []
26
+ }
27
+ hash["sections"] << section
28
+ end
29
+
30
+ section["days"] << day.date.strftime("%m.%d")
31
+ end
32
+
33
+ # determine row date ranges
34
+ day_ranges = []
35
+ day_max = @schedule.days.size - 1
36
+ start_date = 0
37
+ while start_date <= day_max
38
+ end_date = [start_date + DAYS_PER_ROW - 1, day_max].min
39
+ day_ranges << (start_date..end_date)
40
+ start_date = end_date + 1
41
+ end
42
+
43
+ # fill in categories and tasks
44
+ @schedule.categories.each do |category|
45
+ day_ranges.each_with_index do |range, section_index|
46
+ hash["sections"][section_index]["categories"] << category_hash(category, range)
47
+ end
48
+ end
49
+
50
+ # create header
51
+ origin = @schedule.days.first.date
52
+ hash["header"] = "#{Date::MONTHNAMES[origin.month]} #{origin.year}"
53
+ hash["sub_header"] = "Generated on #{Date.today}"
54
+
55
+ hash
56
+ end
57
+
58
+ private
59
+
60
+ ###
61
+ # model hashes
62
+ ###
63
+
64
+ def category_hash(category, day_range)
65
+ hash = {
66
+ "id" => category.id,
67
+ "tasks" => []
68
+ }
69
+
70
+ category.tasks[day_range].each do |task|
71
+ hash["tasks"] << task.to_hash
72
+ end
73
+
74
+ hash
75
+ end
76
+
77
+ ###
78
+ # metadata
79
+ ###
80
+
81
+ def metadata
82
+ [
83
+ {
84
+ "id" => "Days Active",
85
+ "value" => "#{days_active} (#{days_active_percent}%)"
86
+ },
87
+ {
88
+ "id" => "Time Logged",
89
+ "value" => "#{total_min_logged} min (#{total_hr_logged} hr)"
90
+ },
91
+ {
92
+ "id" => "Time Logged Per Day",
93
+ "value" => "#{min_logged_per_day} min (#{hr_logged_per_day} hr)"
94
+ },
95
+ {
96
+ "id" => "Completed",
97
+ "value" => "#{total_min_logged_on_schedule} / #{total_min_scheduled} (#{completed_percent}%)"
98
+ },
99
+ {
100
+ "id" => "Off Schedule",
101
+ "value" => "#{total_min_logged_off_schedule} / #{total_min_logged} (#{off_schedule_percent}%)"
102
+ }
103
+ ]
104
+ end
105
+
106
+ def days_past
107
+ @days_past ||= @schedule.days.reject { |day| day.date >= Date.today }
108
+ end
109
+
110
+ def days_active
111
+ days_past.count { |d| d.active? }
112
+ end
113
+
114
+ def days_active_percent
115
+ percent(days_active, days_past.size)
116
+ end
117
+
118
+ def total_min_logged
119
+ total_min_logged_on_schedule + total_min_logged_off_schedule
120
+ end
121
+
122
+ def total_min_logged_on_schedule
123
+ days_past.collect { |d| d.time_on_schedule }.reduce(0, :+)
124
+ end
125
+
126
+ def total_min_logged_off_schedule
127
+ days_past.collect { |d| d.time_off_schedule }.reduce(0, :+)
128
+ end
129
+
130
+ def total_hr_logged
131
+ divide(total_min_logged, 60)
132
+ end
133
+
134
+ def min_logged_per_day
135
+ divide(total_min_logged, days_past.size)
136
+ end
137
+
138
+ def hr_logged_per_day
139
+ divide(min_logged_per_day, 60)
140
+ end
141
+
142
+ def total_min_scheduled
143
+ days_past.collect { |d| d.time_scheduled }.reduce(0, :+)
144
+ end
145
+
146
+ def completed_percent
147
+ percent(total_min_logged_on_schedule, total_min_scheduled)
148
+ end
149
+
150
+ def off_schedule_percent
151
+ percent(total_min_logged_off_schedule, total_min_logged)
152
+ end
153
+
154
+ ###
155
+ # Incompmlete Tasks
156
+ ###
157
+
158
+ def incomplete_tasks
159
+ top_tasks = []
160
+ @schedule.incomplete_tasks.each do |category|
161
+ hash = {}
162
+ hash["id"] = category.id
163
+ hash["value"] = "#{category.total_min_logged} / #{category.total_min_scheduled} (-#{category.percent_incomplete}%)"
164
+ top_tasks << hash
165
+ end
166
+ top_tasks
167
+ end
168
+
169
+ ###
170
+ # General
171
+ ###
172
+
173
+ def percent(a, b)
174
+ ((a / b.to_f) * 100).round(1)
175
+ end
176
+
177
+ def divide(a, b)
178
+ (a / b.to_f).round(1)
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,55 @@
1
+ module Versed
2
+ class Task
3
+ attr_accessor :category_id, :time_spent, :time_scheduled, :date
4
+
5
+ def initialize(category_id, date)
6
+ @category_id = category_id
7
+ @date = date
8
+ end
9
+
10
+ def to_hash
11
+ {
12
+ "time_spent" => self.time_spent.to_s,
13
+ "time_scheduled" => self.time_scheduled.to_s,
14
+ "style" => style
15
+ }
16
+ end
17
+
18
+ def time_spent
19
+ Date.today > self.date ? @time_spent : nil
20
+ end
21
+
22
+ def time_scheduled
23
+ Date.today > self.date ? @time_scheduled : nil
24
+ end
25
+
26
+ def time_spent?
27
+ self.time_spent && self.time_spent > 0
28
+ end
29
+
30
+ def time_scheduled?
31
+ self.time_scheduled && self.time_scheduled > 0
32
+ end
33
+
34
+ private
35
+
36
+ DANGER_STYLE = "danger"
37
+ WARN_STYLE = "warning"
38
+ SUCCESS_STYLE = "success"
39
+ ACTIVE_STYLE = "active"
40
+
41
+ def style
42
+ return ACTIVE_STYLE if self.date >= Date.today
43
+
44
+ return unless self.time_scheduled && self.time_scheduled > 0
45
+
46
+ if !self.time_spent || self.time_spent <= 0
47
+ return DANGER_STYLE
48
+ elsif self.time_spent < self.time_scheduled
49
+ return WARN_STYLE
50
+ else
51
+ return SUCCESS_STYLE
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ module Versed
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,10 @@
1
+ <h4>Incomplete Tasks</h4>
2
+
3
+ <table class="table table-bordered table-condensed">
4
+ {{#incomplete_tasks}}
5
+ <tr>
6
+ <th scope="row" class="active">{{id}}</th>
7
+ <td>{{value}}</td>
8
+ </tr>
9
+ {{/incomplete_tasks}}
10
+ </table>
@@ -0,0 +1,14 @@
1
+ <table class="table table-bordered table-condensed">
2
+ <thead>
3
+ <tr class="active">
4
+ {{#metadata}}
5
+ <th>{{id}}</th>
6
+ {{/metadata}}
7
+ </tr>
8
+ <thead/>
9
+ <tr>
10
+ {{#metadata}}
11
+ <td>{{value}}</td>
12
+ {{/metadata}}
13
+ </tr>
14
+ </table>
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1">
8
+ <!-- bootstrap cdn ref -->
9
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
10
+ </head>
11
+
12
+ <h1>{{header}}</h1>
13
+
14
+ <p><em>{{sub_header}}</em></p>
15
+
16
+ {{> week_table}}
17
+
18
+ {{> meta_table}}
19
+
20
+ {{> incomplete_table}}
21
+
22
+ </html>
@@ -0,0 +1,21 @@
1
+ {{#sections}}
2
+ <table class="table table-bordered table-condensed">
3
+ <thead>
4
+ <tr class="active">
5
+ <th></th>
6
+ {{#days}}
7
+ <th>{{.}}</th>
8
+ {{/days}}
9
+ </tr>
10
+ </thead>
11
+
12
+ {{#categories}}
13
+ <tr>
14
+ <th scope="row" class="active">{{id}}</th>
15
+ {{#tasks}}
16
+ <td class="{{style}}">{{time_spent}}</td>
17
+ {{/tasks}}
18
+ </tr>
19
+ {{/categories}}
20
+ </table>
21
+ {{/sections}}
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: versed
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Knadler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mustache
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ description: Versed helps you visualize your progress against your scheduled weekly
28
+ routine.
29
+ email: takeshi91k@gmail.com
30
+ executables:
31
+ - versed
32
+ extensions: []
33
+ extra_rdoc_files:
34
+ - README.md
35
+ - LICENSE
36
+ files:
37
+ - LICENSE
38
+ - README.md
39
+ - bin/versed
40
+ - lib/versed/category.rb
41
+ - lib/versed/day.rb
42
+ - lib/versed/generator.rb
43
+ - lib/versed/reader.rb
44
+ - lib/versed/schedule.rb
45
+ - lib/versed/schedule_view.rb
46
+ - lib/versed/task.rb
47
+ - lib/versed/version.rb
48
+ - templates/incomplete_table.mustache
49
+ - templates/meta_table.mustache
50
+ - templates/page.mustache
51
+ - templates/week_table.mustache
52
+ homepage: https://github.com/cknadler/versed
53
+ licenses:
54
+ - MIT
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.0.0
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 2.4.5
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: Visualize routine adherence and track progress
76
+ test_files: []