versed 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []