plansheet 0.2.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ccfb13735d8626143db08903deac727870c66e6bd6fec21648fd70bf4050727
4
- data.tar.gz: 847f0a0f78cdb0035ee03829f2761e3780fb6296738ac89bcca55635079b9e3a
3
+ metadata.gz: 3546f42fdd342ea128be90bff2e19b7bcb5c0910990e2132b89b87cbf6a1f776
4
+ data.tar.gz: 2ea5d505262832e19e77a63e7aa7c59c9fff0b98ddc3ee8218c68b010f4768eb
5
5
  SHA512:
6
- metadata.gz: 34835dc03602c977afb76a4b5202f3ccf7322445086550d11d6506a4cce329f437d1f1a650b8500f46f6f81d72879f8a0ed506ed087411c68c1362b50523aeb8
7
- data.tar.gz: ff0832cc110fa2f8ab2350867aa9c3642cc4eeb6ad9bea9cb620e69dceb4afe5f8e139d9c23f4d9bfd5270000ebc4f5885d1a6cce5ea5db2270edd3caf277428
6
+ metadata.gz: 335cb1a3e74ab82a1441ec034099427dc0caabaad8a87c1f2620c7143d1fd4772023fe089a7305063fb12a96d169e44dd0b01f8f20437f45bdfa945b03aa2422
7
+ data.tar.gz: 5d618d61c6a07d8e32e46cfb4dba5df0264370f01d8b4ecef4c68f9fdcf6fcf600ca33bda0a8918e63992434e2a009e4d836f1f838851e0ee94de53128fe1ab2
data/.rubocop.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 2.6
3
3
  NewCops: enable
4
+ SuggestExtensions: false
4
5
 
5
6
  Style/StringLiterals:
6
7
  Enabled: true
data/Gemfile CHANGED
@@ -6,7 +6,7 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  group :development, optional: true do
9
- gem "rake", "~> 13.0"
10
9
  gem "minitest", "~> 5.0"
10
+ gem "rake", "~> 13.0"
11
11
  gem "rubocop", "~> 1.21"
12
12
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- plansheet (0.2.0)
4
+ plansheet (0.5.1)
5
5
  dc-kwalify (~> 1.0)
6
6
 
7
7
  GEM
@@ -0,0 +1,10 @@
1
+ - project: Add snazzy patches to backpack
2
+ status: planning
3
+ tasks:
4
+ - gather patches around the house in one spot
5
+ - determine which patches to put on
6
+ - find thread that matches patch colors
7
+ - plug in sewing machine
8
+ - put thread onto bobbin
9
+ - sew patches onto backpack
10
+
data/exe/plansheet CHANGED
@@ -1,77 +1,39 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "yaml"
5
-
6
- begin
7
- config = YAML.load_file "#{Dir.home}/.plansheet.yml"
8
- rescue StandardError
9
- abort "unable to load plansheet config file"
10
- end
11
-
12
- status_priority = {
13
- "wip" => 1,
14
- "ready" => 2,
15
- "blocked" => 3,
16
- "planning" => 4,
17
- "idea" => 5,
18
- "dropped" => 6,
19
- "done" => 7
20
- }
21
-
22
- project_hash = {}
23
- projects = Dir.glob("*yml", base: config["projects_dir"])
24
- projects.each do |l|
25
- contents = YAML.load_file(File.join(config["projects_dir"], l))
26
- key = l.gsub(".yml", "")
27
- contents.each do |project|
28
- key = project["project"]
29
- project_hash[key] = {
30
- name: project["project"]
31
- }
32
- %w[tasks desc done].each do |k|
33
- project_hash[key][k.to_sym] = project[k] if project[k]
34
- end
35
- project_hash[key][:status] = if project["status"]
36
- project["status"]
37
- elsif project_hash[key][:tasks]
38
- "planning"
39
- else
40
- "idea"
41
- end
4
+ require "plansheet"
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.each do |proj|
36
+ puts proj
37
+ puts "\n"
42
38
  end
43
39
  end
44
-
45
- sorted_hash = project_hash.sort_by { |_, v| status_priority[v[:status]] }
46
-
47
- def project_minipage(proj)
48
- p proj
49
- str = String.new
50
- str << "\\begin{minipage}{5cm}\n"
51
- str << "#{proj[:name]} - #{proj[:status]} \\\\\n"
52
- proj[:tasks]&.each do |t|
53
- str << "$\\square$ #{t} \\\\\n"
54
- end
55
- str << "\\end{minipage}\n"
56
- str
57
- end
58
-
59
- require "date"
60
- projects_str = String.new
61
- projects_str << <<~FRONTMATTER
62
- ---
63
- geometry: margin=3cm
64
- ---
65
- \\thispagestyle{empty}
66
-
67
- # Date: #{Date.today}
68
- FRONTMATTER
69
-
70
- sorted_hash.first(30).each do |_, p|
71
- projects_str << project_minipage(p)
72
- end
73
-
74
- Dir.mkdir config["output_dir"] unless Dir.exist? config["output_dir"]
75
- f = File.open("#{config["output_dir"]}/projects.md", "w")
76
- f.write(projects_str)
77
- f.close
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Plansheet
6
+ PROJECT_STATUS_PRIORITY = {
7
+ "wip" => 1,
8
+ "ready" => 2,
9
+ "blocked" => 3,
10
+ "planning" => 4,
11
+ "idea" => 5,
12
+ "dropped" => 6,
13
+ "done" => 7
14
+ }.freeze
15
+
16
+ # Once there's some stability in plansheet and dc-kwalify, will pre-load this
17
+ # to save the later YAML.load
18
+ PROJECT_YAML_SCHEMA = <<~YAML
19
+ desc: dc-tasks project schema
20
+ type: seq
21
+ sequence:
22
+ - type: map
23
+ mapping:
24
+ "project":
25
+ desc: Project name
26
+ type: str
27
+ required: yes
28
+ "status":
29
+ desc: The current status of the project
30
+ type: str
31
+ enum:
32
+ - wip # project is a work-in-progress
33
+ - ready # project is fully scoped, ready to go
34
+ - blocked # project is blocked, but otherwise ready/wip
35
+ - planning # project in planning phase
36
+ - idea # project is little more than an idea
37
+ - dropped # project has been explicitly dropped, but
38
+ # want to keep around for reference, etc
39
+ - done # project is finished, but want to keep around
40
+ # for reference, etc.
41
+ "priority":
42
+ desc: Project priority (not currently implemented)
43
+ type: str
44
+ enum:
45
+ - high
46
+ - low
47
+ "location":
48
+ desc: Location (not currently implemented)
49
+ type: str
50
+ "tasks":
51
+ desc: List of tasks to do
52
+ type: seq
53
+ sequence:
54
+ - type: str
55
+ "done":
56
+ desc: List of tasks which have been completed
57
+ type: seq
58
+ sequence:
59
+ - type: str
60
+ "notes":
61
+ desc: Free-form notes string
62
+ type: str
63
+ YAML
64
+ PROJECT_SCHEMA = YAML.safe_load(PROJECT_YAML_SCHEMA)
65
+ class Project
66
+ attr_reader :name, :tasks, :done, :notes, :location
67
+
68
+ def initialize(options)
69
+ @name = options["project"]
70
+
71
+ @tasks = options["tasks"] || []
72
+ @done = options["done"] || []
73
+
74
+ @notes = options["notes"] if options["notes"]
75
+ @location = options["location"] if options["location"]
76
+ @status = options["status"] if options["status"]
77
+ end
78
+
79
+ def status
80
+ return @status if @status
81
+
82
+ if @tasks.count.positive?
83
+ if @done.count.positive?
84
+ "wip"
85
+ else
86
+ "planning"
87
+ end
88
+ else
89
+ "idea"
90
+ end
91
+ end
92
+
93
+ def to_s
94
+ str = String.new
95
+ str << "# #{@name}\n"
96
+ str << "status: #{status}\n"
97
+ str << "notes: #{notes}\n" unless @notes.nil?
98
+ str << "location: #{location}\n" unless @location.nil?
99
+ str << "tasks:\n" unless @tasks.empty?
100
+ @tasks.each do |t|
101
+ str << "- #{t}\n"
102
+ end
103
+ str << "done:\n" unless @done.empty?
104
+ @done.each do |d|
105
+ str << "- #{d}\n"
106
+ end
107
+ str
108
+ end
109
+ def to_h
110
+ h = { "project" => @name }
111
+ h["status"] = status unless status == "idea"
112
+ h["notes"] = @notes unless @notes.nil?
113
+ h["location"] = @location unless @location.nil?
114
+ h["tasks"] = @tasks unless @tasks.empty?
115
+ h["done"] = @done unless @done.empty?
116
+ h
117
+ end
118
+ end
119
+
120
+ class ProjectYAMLFile
121
+ attr_reader :projects
122
+
123
+ def initialize(path)
124
+ @path = path
125
+ # TODO: this won't GC, inline validation instead?
126
+ @raw = YAML.load_file(path)
127
+ validate_schema
128
+ @projects = @raw.map { |proj| Project.new proj }
129
+ end
130
+
131
+ def validate_schema
132
+ validator = Kwalify::Validator.new(Plansheet::PROJECT_SCHEMA)
133
+ errors = validator.validate(@raw)
134
+ # Check YAML validity
135
+ if errors && !errors.empty?
136
+ $stderr.write "Schema errors in #{l}\n"
137
+ errors.each { |err| puts "- [#{err.path}] #{err.message}" }
138
+ abort
139
+ end
140
+ end
141
+
142
+ def sort!
143
+ @projects.sort_by! do |project|
144
+ Plansheet::PROJECT_STATUS_PRIORITY[project.status]
145
+ end
146
+ end
147
+
148
+ def yaml_dump
149
+ YAML.dump(@projects.map(&:to_h))
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,52 @@
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_by do |p|
9
+ Plansheet::PROJECT_STATUS_PRIORITY[p.status]
10
+ end
11
+
12
+ projects_str = String.new
13
+ projects_str << sheet_header
14
+
15
+ sorted_arr.first(60).each do |p|
16
+ projects_str << project_minipage(p)
17
+ end
18
+ puts "Writing to #{output_file}"
19
+ File.write(output_file, projects_str)
20
+ end
21
+
22
+ def sheet_header
23
+ <<~FRONTMATTER
24
+ ---
25
+ geometry: margin=1.5cm
26
+ ---
27
+ \\thispagestyle{empty}
28
+
29
+ # Date: #{Date.today}
30
+ FRONTMATTER
31
+ end
32
+
33
+ def project_minipage(proj)
34
+ str = String.new
35
+ str << "\\begin{minipage}{4.5cm}\n"
36
+ str << project_header(proj)
37
+ proj.tasks.each do |t|
38
+ str << "$\\square$ #{t} \\\\\n"
39
+ end
40
+ str << "\\end{minipage}\n"
41
+ str
42
+ end
43
+
44
+ def project_header(proj)
45
+ str = String.new
46
+ str << "#{proj.name} - #{proj.status}"
47
+ str << " - #{proj.location}" if proj.location
48
+ str << " \\\\\n"
49
+ str
50
+ end
51
+ end
52
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Plansheet
4
- VERSION = "0.2.0"
4
+ VERSION = "0.5.1"
5
5
  end
data/lib/plansheet.rb CHANGED
@@ -1,8 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "plansheet/version"
4
+ require_relative "plansheet/project"
5
+ require_relative "plansheet/sheet"
6
+ require "yaml"
7
+ require "kwalify"
4
8
 
5
9
  module Plansheet
6
10
  class Error < StandardError; end
7
- # Your code goes here...
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
8
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.2.0
4
+ version: 0.5.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-19 00:00:00.000000000 Z
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:
@@ -40,16 +39,18 @@ files:
40
39
  - LICENSE.txt
41
40
  - README.md
42
41
  - Rakefile
42
+ - examples/backpack.yml
43
43
  - exe/plansheet
44
- - exe/plansheet-sorter
45
44
  - lib/plansheet.rb
45
+ - lib/plansheet/project.rb
46
+ - lib/plansheet/sheet.rb
46
47
  - lib/plansheet/version.rb
47
- - project.schema.yaml
48
48
  homepage: https://dafyddcrosby.com
49
49
  licenses:
50
50
  - MIT
51
51
  metadata:
52
52
  homepage_uri: https://dafyddcrosby.com
53
+ rubygems_mfa_required: 'true'
53
54
  post_install_message:
54
55
  rdoc_options: []
55
56
  require_paths:
data/exe/plansheet-sorter DELETED
@@ -1,32 +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
-
8
- status_priority = {
9
- "wip" => 1,
10
- "ready" => 2,
11
- "blocked" => 3,
12
- "planning" => 4,
13
- "idea" => 5,
14
- "dropped" => 6,
15
- "done" => 7
16
- }
17
-
18
- def project_priority(project)
19
- if project["status"]
20
- project["status"]
21
- elsif project["tasks"]
22
- "planning"
23
- else
24
- "idea"
25
- end
26
- end
27
-
28
- filename = ARGV.first
29
- contents = YAML.load_file(filename)
30
- sorted_hash = contents.sort_by { |project| status_priority[project_priority project] }
31
-
32
- File.write(filename, YAML.dump(sorted_hash))
data/project.schema.yaml DELETED
@@ -1,41 +0,0 @@
1
- desc: dc-tasks project schema
2
- type: seq
3
- sequence:
4
- - type: map
5
- mapping:
6
- "project":
7
- desc: Project name
8
- type: str
9
- required: yes
10
- "status":
11
- desc: The current status of the project
12
- type: str
13
- enum:
14
- - wip # project is a work-in-progress
15
- - ready # project is fully scoped, ready to go
16
- - blocked # project is blocked, but otherwise ready/wip
17
- - planning # project in planning phase
18
- - idea # project is little more than an idea
19
- - dropped # project has been explicitly dropped, but
20
- # want to keep around for reference, etc
21
- - done # project is finished, but want to keep around
22
- # for reference, etc.
23
- "priority":
24
- desc: Project priority (not currently implemented)
25
- type: str
26
- enum:
27
- - high
28
- - low
29
- "tasks":
30
- desc: List of tasks to do
31
- type: seq
32
- sequence:
33
- - type: str
34
- "done":
35
- desc: List of tasks which have been completed
36
- type: seq
37
- sequence:
38
- - type: str
39
- "notes":
40
- desc: Free-form notes string
41
- type: str