plansheet 0.3.3 → 0.6.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/exe/plansheet +33 -75
- data/lib/plansheet/project.rb +119 -1
- data/lib/plansheet/sheet.rb +50 -0
- data/lib/plansheet/version.rb +1 -1
- data/lib/plansheet.rb +28 -1
- metadata +3 -4
- data/exe/plansheet-sorter +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36b1a62d85f75f6fe04c0ad37b6d344dc9d9192a02d093283979986f09532935
|
4
|
+
data.tar.gz: 13a281430dfc803f940d05efa83c68758db7a4452912cfcfd6b5fe9a7247d69c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc3b217bf90a0333dc9f09b011c1f09caba8977a0f19bdd0a3695b76c519da54a5f2144897af49ee6e4f726a8abddc35532c51895c1c37a30422ddd39118676b
|
7
|
+
data.tar.gz: a553efe99e604078e55ae0ea9cfdfa625a026a242ac92025e6600ebc5e04935c528a4d1807ecbe3a2a469ec6c37d3923cf22bad9c6f292d3ab79d1eca868c58b
|
data/Gemfile.lock
CHANGED
data/exe/plansheet
CHANGED
@@ -1,81 +1,39 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "kwalify"
|
5
|
-
require "yaml"
|
6
4
|
require "plansheet"
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
else
|
41
|
-
"idea"
|
42
|
-
end
|
5
|
+
require "optparse"
|
6
|
+
|
7
|
+
parser = OptionParser.new
|
8
|
+
parser.on(
|
9
|
+
"--sheet",
|
10
|
+
"Generates MD/LaTeX project PDF"
|
11
|
+
)
|
12
|
+
parser.on(
|
13
|
+
"--sort",
|
14
|
+
"Sort project files"
|
15
|
+
)
|
16
|
+
parser.on(
|
17
|
+
"--cli",
|
18
|
+
"CLI dump of projects (WIP)"
|
19
|
+
)
|
20
|
+
options = {}
|
21
|
+
parser.parse!(into: options)
|
22
|
+
|
23
|
+
config = Plansheet.load_config
|
24
|
+
|
25
|
+
if options[:sheet] || options.empty?
|
26
|
+
project_arr = Plansheet.load_projects_dir config["projects_dir"]
|
27
|
+
|
28
|
+
Dir.mkdir config["output_dir"] unless Dir.exist? config["output_dir"]
|
29
|
+
|
30
|
+
Plansheet::Sheet.new("#{config["output_dir"]}/projects.md", project_arr)
|
31
|
+
elsif options[:sort]
|
32
|
+
Plansheet.resort_projects_in_dir config["projects_dir"]
|
33
|
+
elsif options[:cli]
|
34
|
+
project_arr = Plansheet.load_projects_dir config["projects_dir"]
|
35
|
+
project_arr.sort.each do |proj|
|
36
|
+
puts proj
|
37
|
+
puts "\n"
|
43
38
|
end
|
44
39
|
end
|
45
|
-
|
46
|
-
sorted_hash = project_hash.sort_by do |_, v|
|
47
|
-
Plansheet::PROJECT_STATUS_PRIORITY[v[:status]]
|
48
|
-
end
|
49
|
-
|
50
|
-
def project_minipage(proj)
|
51
|
-
str = String.new
|
52
|
-
str << "\\begin{minipage}{5cm}\n"
|
53
|
-
str << "#{proj[:name]} - #{proj[:status]} \\\\\n"
|
54
|
-
proj[:tasks]&.each do |t|
|
55
|
-
str << "$\\square$ #{t} \\\\\n"
|
56
|
-
end
|
57
|
-
str << "\\end{minipage}\n"
|
58
|
-
str
|
59
|
-
end
|
60
|
-
|
61
|
-
require "date"
|
62
|
-
projects_str = String.new
|
63
|
-
projects_str << <<~FRONTMATTER
|
64
|
-
---
|
65
|
-
geometry: margin=3cm
|
66
|
-
---
|
67
|
-
\\thispagestyle{empty}
|
68
|
-
|
69
|
-
# Date: #{Date.today}
|
70
|
-
FRONTMATTER
|
71
|
-
|
72
|
-
sorted_hash.first(60).each do |_, p|
|
73
|
-
projects_str << project_minipage(p)
|
74
|
-
end
|
75
|
-
|
76
|
-
output_file = "#{config["output_dir"]}/projects.md"
|
77
|
-
puts "Writing to #{output_file}"
|
78
|
-
Dir.mkdir config["output_dir"] unless Dir.exist? config["output_dir"]
|
79
|
-
f = File.open(output_file, "w")
|
80
|
-
f.write(projects_str)
|
81
|
-
f.close
|
data/lib/plansheet/project.rb
CHANGED
@@ -13,6 +13,17 @@ module Plansheet
|
|
13
13
|
"done" => 7
|
14
14
|
}.freeze
|
15
15
|
|
16
|
+
PROJECT_PRIORITY = {
|
17
|
+
"high" => 1,
|
18
|
+
"medium" => 2,
|
19
|
+
"low" => 3
|
20
|
+
}.freeze
|
21
|
+
PROJECT_PRIORITY_REV = {
|
22
|
+
1 => "high",
|
23
|
+
2 => "medium",
|
24
|
+
3 => "low"
|
25
|
+
}.freeze
|
26
|
+
|
16
27
|
# Once there's some stability in plansheet and dc-kwalify, will pre-load this
|
17
28
|
# to save the later YAML.load
|
18
29
|
PROJECT_YAML_SCHEMA = <<~YAML
|
@@ -39,11 +50,14 @@ module Plansheet
|
|
39
50
|
- done # project is finished, but want to keep around
|
40
51
|
# for reference, etc.
|
41
52
|
"priority":
|
42
|
-
desc: Project priority
|
53
|
+
desc: Project priority
|
43
54
|
type: str
|
44
55
|
enum:
|
45
56
|
- high
|
46
57
|
- low
|
58
|
+
"location":
|
59
|
+
desc: Location
|
60
|
+
type: str
|
47
61
|
"tasks":
|
48
62
|
desc: List of tasks to do
|
49
63
|
type: seq
|
@@ -59,4 +73,108 @@ module Plansheet
|
|
59
73
|
type: str
|
60
74
|
YAML
|
61
75
|
PROJECT_SCHEMA = YAML.safe_load(PROJECT_YAML_SCHEMA)
|
76
|
+
class Project
|
77
|
+
include Comparable
|
78
|
+
attr_reader :name, :tasks, :done, :notes, :location, :priority
|
79
|
+
|
80
|
+
def initialize(options)
|
81
|
+
@name = options["project"]
|
82
|
+
|
83
|
+
@tasks = options["tasks"] || []
|
84
|
+
@done = options["done"] || []
|
85
|
+
|
86
|
+
@notes = options["notes"] if options["notes"]
|
87
|
+
@priority = PROJECT_PRIORITY[options["priority"] || "medium"]
|
88
|
+
@location = options["location"] if options["location"]
|
89
|
+
@status = options["status"] if options["status"]
|
90
|
+
end
|
91
|
+
|
92
|
+
def <=>(other)
|
93
|
+
if @priority == other.priority
|
94
|
+
# TODO: if planning status, then sort based on tasks? category? alphabetically?
|
95
|
+
PROJECT_STATUS_PRIORITY[status] <=> PROJECT_STATUS_PRIORITY[other.status]
|
96
|
+
else
|
97
|
+
@priority <=> other.priority
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# TODO: clean up priority handling
|
102
|
+
def priority_string
|
103
|
+
PROJECT_PRIORITY_REV[@priority]
|
104
|
+
end
|
105
|
+
|
106
|
+
def status
|
107
|
+
return @status if @status
|
108
|
+
|
109
|
+
if @tasks.count.positive?
|
110
|
+
if @done.count.positive?
|
111
|
+
"wip"
|
112
|
+
else
|
113
|
+
"planning"
|
114
|
+
end
|
115
|
+
else
|
116
|
+
"idea"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_s
|
121
|
+
str = String.new
|
122
|
+
str << "# #{@name}\n"
|
123
|
+
str << "priority: #{priority_string}\n"
|
124
|
+
str << "status: #{status}\n"
|
125
|
+
str << "notes: #{notes}\n" unless @notes.nil?
|
126
|
+
str << "location: #{location}\n" unless @location.nil?
|
127
|
+
str << "tasks:\n" unless @tasks.empty?
|
128
|
+
@tasks.each do |t|
|
129
|
+
str << "- #{t}\n"
|
130
|
+
end
|
131
|
+
str << "done:\n" unless @done.empty?
|
132
|
+
@done.each do |d|
|
133
|
+
str << "- #{d}\n"
|
134
|
+
end
|
135
|
+
str
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_h
|
139
|
+
h = { "project" => @name }
|
140
|
+
h["priority"] = priority_string unless priority_string == "medium"
|
141
|
+
h["status"] = status unless status == "idea"
|
142
|
+
h["notes"] = @notes unless @notes.nil?
|
143
|
+
h["location"] = @location unless @location.nil?
|
144
|
+
h["tasks"] = @tasks unless @tasks.empty?
|
145
|
+
h["done"] = @done unless @done.empty?
|
146
|
+
h
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class ProjectYAMLFile
|
151
|
+
attr_reader :projects
|
152
|
+
|
153
|
+
def initialize(path)
|
154
|
+
@path = path
|
155
|
+
# TODO: this won't GC, inline validation instead?
|
156
|
+
@raw = YAML.load_file(path)
|
157
|
+
validate_schema
|
158
|
+
@projects = @raw.map { |proj| Project.new proj }
|
159
|
+
end
|
160
|
+
|
161
|
+
def validate_schema
|
162
|
+
validator = Kwalify::Validator.new(Plansheet::PROJECT_SCHEMA)
|
163
|
+
errors = validator.validate(@raw)
|
164
|
+
# Check YAML validity
|
165
|
+
return unless errors && !errors.empty?
|
166
|
+
|
167
|
+
$stderr.write "Schema errors in #{@path}:\n"
|
168
|
+
errors.each { |err| puts "- [#{err.path}] #{err.message}" }
|
169
|
+
abort
|
170
|
+
end
|
171
|
+
|
172
|
+
def sort!
|
173
|
+
@projects.sort!
|
174
|
+
end
|
175
|
+
|
176
|
+
def yaml_dump
|
177
|
+
YAML.dump(@projects.map(&:to_h))
|
178
|
+
end
|
179
|
+
end
|
62
180
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
module Plansheet
|
5
|
+
# The Sheet class constructs a Markdown/LaTeX file for use with pandoc
|
6
|
+
class Sheet
|
7
|
+
def initialize(output_file, project_arr)
|
8
|
+
sorted_arr = project_arr.sort!
|
9
|
+
|
10
|
+
projects_str = String.new
|
11
|
+
projects_str << sheet_header
|
12
|
+
|
13
|
+
sorted_arr.first(60).each do |p|
|
14
|
+
projects_str << project_minipage(p)
|
15
|
+
end
|
16
|
+
puts "Writing to #{output_file}"
|
17
|
+
File.write(output_file, projects_str)
|
18
|
+
end
|
19
|
+
|
20
|
+
def sheet_header
|
21
|
+
<<~FRONTMATTER
|
22
|
+
---
|
23
|
+
geometry: margin=1.5cm
|
24
|
+
---
|
25
|
+
\\thispagestyle{empty}
|
26
|
+
|
27
|
+
# Date: #{Date.today}
|
28
|
+
FRONTMATTER
|
29
|
+
end
|
30
|
+
|
31
|
+
def project_minipage(proj)
|
32
|
+
str = String.new
|
33
|
+
str << "\\begin{minipage}{4.5cm}\n"
|
34
|
+
str << project_header(proj)
|
35
|
+
proj.tasks.each do |t|
|
36
|
+
str << "$\\square$ #{t} \\\\\n"
|
37
|
+
end
|
38
|
+
str << "\\end{minipage}\n"
|
39
|
+
str
|
40
|
+
end
|
41
|
+
|
42
|
+
def project_header(proj)
|
43
|
+
str = String.new
|
44
|
+
str << "#{proj.name} - #{proj.status}"
|
45
|
+
str << " - #{proj.location}" if proj.location
|
46
|
+
str << " \\\\\n"
|
47
|
+
str
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/plansheet/version.rb
CHANGED
data/lib/plansheet.rb
CHANGED
@@ -2,8 +2,35 @@
|
|
2
2
|
|
3
3
|
require_relative "plansheet/version"
|
4
4
|
require_relative "plansheet/project"
|
5
|
+
require_relative "plansheet/sheet"
|
6
|
+
require "yaml"
|
7
|
+
require "kwalify"
|
5
8
|
|
6
9
|
module Plansheet
|
7
10
|
class Error < StandardError; end
|
8
|
-
|
11
|
+
|
12
|
+
def self.load_config
|
13
|
+
YAML.load_file "#{Dir.home}/.plansheet.yml"
|
14
|
+
rescue StandardError
|
15
|
+
abort "unable to load plansheet config file"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.resort_projects_in_dir(dir)
|
19
|
+
project_files = Dir.glob("#{dir}/*yml")
|
20
|
+
project_files.each do |f|
|
21
|
+
pyf = ProjectYAMLFile.new(f)
|
22
|
+
pyf.sort!
|
23
|
+
File.write(f, pyf.yaml_dump)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.load_projects_dir(dir)
|
28
|
+
project_arr = []
|
29
|
+
projects = Dir.glob("*yml", base: dir)
|
30
|
+
projects.each do |l|
|
31
|
+
project_arr << ProjectYAMLFile.new(File.join(dir, l)).projects
|
32
|
+
end
|
33
|
+
|
34
|
+
project_arr.flatten!
|
35
|
+
end
|
9
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plansheet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Crosby
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-05-
|
11
|
+
date: 2022-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dc-kwalify
|
@@ -29,7 +29,6 @@ email:
|
|
29
29
|
- dave@dafyddcrosby.com
|
30
30
|
executables:
|
31
31
|
- plansheet
|
32
|
-
- plansheet-sorter
|
33
32
|
extensions: []
|
34
33
|
extra_rdoc_files: []
|
35
34
|
files:
|
@@ -42,9 +41,9 @@ files:
|
|
42
41
|
- Rakefile
|
43
42
|
- examples/backpack.yml
|
44
43
|
- exe/plansheet
|
45
|
-
- exe/plansheet-sorter
|
46
44
|
- lib/plansheet.rb
|
47
45
|
- lib/plansheet/project.rb
|
46
|
+
- lib/plansheet/sheet.rb
|
48
47
|
- lib/plansheet/version.rb
|
49
48
|
homepage: https://dafyddcrosby.com
|
50
49
|
licenses:
|
data/exe/plansheet-sorter
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# An ugly script to sort projects by status (and eventually priority)
|
5
|
-
|
6
|
-
require "yaml"
|
7
|
-
require "plansheet"
|
8
|
-
|
9
|
-
def project_priority(project)
|
10
|
-
if project["status"]
|
11
|
-
project["status"]
|
12
|
-
elsif project["tasks"]
|
13
|
-
"planning"
|
14
|
-
else
|
15
|
-
"idea"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
filename = ARGV.first
|
20
|
-
contents = YAML.load_file(filename)
|
21
|
-
sorted_hash = contents.sort_by do |project|
|
22
|
-
Plansheet::PROJECT_STATUS_PRIORITY[project_priority project]
|
23
|
-
end
|
24
|
-
|
25
|
-
File.write(filename, YAML.dump(sorted_hash))
|