plansheet 0.15.0 → 0.17.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: f121593269dc11059fe547d8b72d58c2ad1a6685ae4826ac39daaff8b33f7c9a
4
- data.tar.gz: 4020dab046e837f02b64cfc975408240d472b04a26bc6f40e7e552d915d77336
3
+ metadata.gz: 4c094d7236b6603dcab810d785eab766754e0cd9f161a2cb2daf280962b41026
4
+ data.tar.gz: 2686468ff7aa21915ff73ff27c49990cd05c2dc4e75c139837a69c1580a49dd5
5
5
  SHA512:
6
- metadata.gz: ae82c5c2df93d55e520262754cc1c6e8b1b80a9a2fe230c09d00bd7ed4a38790c71b0dfcde5ac178e38112a26284219f21a8d99e48212601bbbcfae98607c0d1
7
- data.tar.gz: 6ca757244abd515dcd86bbedb5a31590a53fbc8720e35167549bfe326c1f96673acef49b0dd1044a0e92f43253ef1442d126872859cebfa0f1185bb06612da59
6
+ metadata.gz: 646ede4ae64522cfc33912eaefae96083de293800647700ec04b2569fe305d40bf7855a0780c63262db5fdaa5adf8472dba4790bd5f0b60228acc7a921b84417
7
+ data.tar.gz: 0c56e57fa79c225d9c3edf055e5c033e9bb9c5b63a49d2d1e7ae1bc17e3c1298e086af4c06d88d270fe9a6e83f3d6f67bba6c4f65f09afbcaf80ed46570f4a3f
data/Gemfile CHANGED
@@ -10,6 +10,7 @@ group :development, optional: true do
10
10
  gem "guard-minitest", "~> 2.4"
11
11
  gem "minitest", "~> 5.0"
12
12
  gem "rake", "~> 13.0"
13
+ gem "rdoc"
13
14
 
14
15
  gem "rubocop", "~> 1.21"
15
16
  gem "rubocop-minitest"
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- plansheet (0.15.0)
4
+ plansheet (0.17.1)
5
5
  dc-kwalify (~> 1.0)
6
+ rgl (= 0.5.8)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
@@ -12,6 +13,7 @@ GEM
12
13
  dc-kwalify (1.0.0)
13
14
  ffi (1.15.5)
14
15
  formatador (1.1.0)
16
+ generator (0.0.1)
15
17
  guard (2.18.0)
16
18
  formatador (>= 0.2.4)
17
19
  listen (>= 2.7, < 4.0)
@@ -25,6 +27,7 @@ GEM
25
27
  guard-minitest (2.4.6)
26
28
  guard-compat (~> 1.2)
27
29
  minitest (>= 3.0)
30
+ lazy_priority_queue (0.1.1)
28
31
  listen (3.7.1)
29
32
  rb-fsevent (~> 0.10, >= 0.10.3)
30
33
  rb-inotify (~> 0.9, >= 0.9.10)
@@ -41,13 +44,21 @@ GEM
41
44
  pry (0.14.1)
42
45
  coderay (~> 1.1)
43
46
  method_source (~> 1.0)
47
+ psych (4.0.4)
48
+ stringio
44
49
  rainbow (3.1.1)
45
50
  rake (13.0.6)
46
51
  rb-fsevent (0.11.1)
47
52
  rb-inotify (0.10.1)
48
53
  ffi (~> 1.0)
54
+ rdoc (6.4.0)
55
+ psych (>= 4.0.0)
49
56
  regexp_parser (2.4.0)
50
57
  rexml (3.2.5)
58
+ rgl (0.5.8)
59
+ lazy_priority_queue (~> 0.1.0)
60
+ rexml (~> 3.2, >= 3.2.4)
61
+ stream (~> 0.5.3)
51
62
  rubocop (1.29.1)
52
63
  parallel (~> 1.10)
53
64
  parser (>= 3.1.0.0)
@@ -65,6 +76,9 @@ GEM
65
76
  rubocop (~> 1.0)
66
77
  ruby-progressbar (1.11.0)
67
78
  shellany (0.0.1)
79
+ stream (0.5.4)
80
+ generator
81
+ stringio (3.0.2)
68
82
  thor (1.2.1)
69
83
  unicode-display_width (2.1.0)
70
84
 
@@ -77,6 +91,7 @@ DEPENDENCIES
77
91
  minitest (~> 5.0)
78
92
  plansheet!
79
93
  rake (~> 13.0)
94
+ rdoc
80
95
  rubocop (~> 1.21)
81
96
  rubocop-minitest
82
97
  rubocop-rake
data/exe/plansheet CHANGED
@@ -17,6 +17,10 @@ parser.on(
17
17
  "--cli",
18
18
  "CLI dump of projects (WIP)"
19
19
  )
20
+ parser.on(
21
+ "--calendar",
22
+ "List of projects ordered by due date"
23
+ )
20
24
  parser.on(
21
25
  "--location_filter LOCATION",
22
26
  "location filter for CLI dump (WIP)"
@@ -25,14 +29,26 @@ options = {}
25
29
  parser.parse!(into: options)
26
30
 
27
31
  config = Plansheet.load_config
28
- pool = Plansheet::Pool.new({ projects_dir: config["projects_dir"] })
32
+ pool = Plansheet::Pool.new({ projects_dir: config["projects_dir"],
33
+ sort_order: config["sort_order"] })
29
34
 
30
35
  if options[:sheet] || options.empty?
36
+ require "plansheet/sheet"
31
37
  Dir.mkdir config["output_dir"] unless Dir.exist? config["output_dir"]
32
38
  Plansheet::Sheet.new("#{config["output_dir"]}/projects.md", pool.projects)
33
39
  elsif options[:sort]
34
40
  # Pool sorts projects, this now just matches old behaviour
35
41
  pool.write_projects
42
+ elsif options[:calendar]
43
+ # TODO: add a project filter method
44
+ project_arr = pool.projects
45
+ project_arr.delete_if { |x| x.status == "dropped" || x.status == "done" }
46
+ project_arr.delete_if { |x| x.due.nil? }
47
+ project_arr.sort_by!(&:due)
48
+ project_arr.each do |proj|
49
+ puts proj
50
+ puts "\n"
51
+ end
36
52
  elsif options[:cli]
37
53
  # TODO: add a project filter method
38
54
  project_arr = pool.projects
@@ -1,21 +1,75 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rgl/adjacency"
4
+ require "rgl/topsort"
5
+
3
6
  module Plansheet
4
7
  # The "pool" is the aggregated collection of projects, calendar events, etc.
5
8
  class Pool
6
9
  attr_accessor :projects
7
10
 
11
+ DEFAULT_COMPARISON_ORDER = %w[
12
+ completeness
13
+ dependency
14
+ priority
15
+ defer
16
+ due
17
+ status
18
+ ].freeze
19
+
8
20
  def initialize(config)
9
21
  @projects_dir = config[:projects_dir]
22
+ @sort_order = config[:sort_order]
10
23
  # @completed_projects_dir = config(:completed_projects_dir)
11
24
 
25
+ # This bit of trickiness is because we don't know what the sort order is
26
+ # until runtime. I'm sure this design decision definitely won't bite me
27
+ # in the future ;-) Fortunately, it's also not a problem that can't be
28
+ # walked back from.
29
+ if config[:sort_order]
30
+ self.class.const_set("POOL_COMPARISON_ORDER", config[:sort_order])
31
+ else
32
+ self.class.const_set("POOL_COMPARISON_ORDER", Plansheet::Pool::DEFAULT_COMPARISON_ORDER)
33
+ end
34
+ require_relative "project"
12
35
  load_projects_dir(@projects_dir)
13
- # TODO: Slurp all files
14
36
  sort_projects
15
37
  end
16
38
 
17
39
  def sort_projects
18
40
  @projects.sort!
41
+ # lookup_hash returns the index of a project
42
+ lookup_hash = Hash.new nil
43
+
44
+ # initialize the lookups
45
+ @projects.each_index do |i|
46
+ lookup_hash[@projects[i].name] = i
47
+ end
48
+
49
+ pg = RGL::DirectedAdjacencyGraph.new
50
+ pg.add_vertices @projects
51
+ @projects.each_index do |proj_index|
52
+ next if @projects[proj_index].dropped_or_done?
53
+
54
+ @projects[proj_index]&.dependencies&.each do |dep|
55
+ di = lookup_hash[dep]
56
+ if di
57
+ # Don't add edges for dropped/done projects, they'll be sorted out
58
+ # later
59
+ next if @projects[di].dropped_or_done?
60
+
61
+ pg.add_edge(@projects[di], @projects[proj_index])
62
+ end
63
+ end
64
+ end
65
+
66
+ # The topological sort of pg is the correct dependency order of the
67
+ # projects
68
+ @projects = pg.topsort_iterator.to_a.flatten.uniq
69
+
70
+ # TODO: second sort doesn't deal with problems where deferred task gets
71
+ # pushed below.
72
+ @projects.sort!
19
73
  end
20
74
 
21
75
  def project_namespaces
@@ -31,10 +85,7 @@ module Plansheet
31
85
  # are involved once completed project directories are a thing - will need
32
86
  # to keep a list of project files to delete
33
87
  project_namespaces.each do |ns|
34
- # TODO: move this to ProjectYAMLFile
35
- #
36
- f = "#{@projects_dir}/#{ns}.yml"
37
- pyf = ProjectYAMLFile.new f
88
+ pyf = ProjectYAMLFile.new "#{@projects_dir}/#{ns}.yml"
38
89
  pyf.projects = projects_in_namespace(ns)
39
90
  pyf.write
40
91
  end
@@ -4,6 +4,8 @@ require "yaml"
4
4
  require "date"
5
5
  require "pathname"
6
6
 
7
+ require "kwalify"
8
+
7
9
  module Plansheet
8
10
  # Once there's some stability in plansheet and dc-kwalify, will pre-load this
9
11
  # to save the later YAML.load
@@ -161,7 +163,7 @@ module Plansheet
161
163
  end
162
164
 
163
165
  def yaml_dump
164
- YAML.dump(@projects.map { |x| x.to_h.except("namespace") })
166
+ YAML.dump(@projects.map { |x| x.to_h.delete_if { |k, _| k == "namespace" } })
165
167
  end
166
168
  end
167
169
  end
@@ -17,12 +17,6 @@ module Plansheet
17
17
  "done" => 8
18
18
  }.freeze
19
19
 
20
- PROJECT_PRIORITY = {
21
- "high" => 1,
22
- "medium" => 2,
23
- "low" => 3
24
- }.freeze
25
-
26
20
  def self.parse_date_duration(str)
27
21
  return Regexp.last_match(1).to_i if str.strip.match(/(\d+)[dD]/)
28
22
  return (Regexp.last_match(1).to_i * 7) if str.strip.match(/(\d+)[wW]/)
@@ -39,14 +33,13 @@ module Plansheet
39
33
  class Project
40
34
  include Comparable
41
35
 
42
- DEFAULT_COMPARISON_ORDER = %w[
43
- completeness
44
- dependency
45
- priority
46
- defer
47
- due
48
- status
49
- ].map { |x| "compare_#{x}".to_sym }.freeze
36
+ PROJECT_PRIORITY = {
37
+ "high" => 1,
38
+ "medium" => 2,
39
+ "low" => 3
40
+ }.freeze
41
+
42
+ COMPARISON_ORDER_SYMS = Plansheet::Pool::POOL_COMPARISON_ORDER.map { |x| "compare_#{x}".to_sym }.freeze
50
43
  # NOTE: The order of these affects presentation!
51
44
  # namespace is derived from file name
52
45
  STRING_PROPERTIES = %w[priority status location notes time_estimate frequency lead_time].freeze
@@ -55,7 +48,7 @@ module Plansheet
55
48
 
56
49
  ALL_PROPERTIES = STRING_PROPERTIES + DATE_PROPERTIES + ARRAY_PROPERTIES
57
50
 
58
- attr_reader :name, *ALL_PROPERTIES
51
+ attr_reader :name, :priority_val, *ALL_PROPERTIES
59
52
  attr_accessor :namespace
60
53
 
61
54
  def initialize(options)
@@ -76,12 +69,16 @@ module Plansheet
76
69
  # date/external commits/penalties for project failure, etc
77
70
  #
78
71
  # Assume all projects are low priority unless stated otherwise.
79
- @priority ||= "low"
72
+ @priority_val = if @priority
73
+ PROJECT_PRIORITY[@priority]
74
+ else
75
+ PROJECT_PRIORITY["low"]
76
+ end
80
77
  end
81
78
 
82
79
  def <=>(other)
83
80
  ret_val = 0
84
- DEFAULT_COMPARISON_ORDER.each do |method|
81
+ COMPARISON_ORDER_SYMS.each do |method|
85
82
  ret_val = send(method, other)
86
83
  break if ret_val != 0
87
84
  end
@@ -89,7 +86,7 @@ module Plansheet
89
86
  end
90
87
 
91
88
  def compare_priority(other)
92
- PROJECT_PRIORITY[@priority] <=> PROJECT_PRIORITY[other.priority]
89
+ priority_val <=> other.priority_val
93
90
  end
94
91
 
95
92
  def compare_status(other)
@@ -117,19 +114,25 @@ module Plansheet
117
114
  receiver <=> comparison
118
115
  end
119
116
 
120
- def compare_dependency(other)
121
- return 0 if @dependencies.nil? && other.dependencies.nil?
122
-
123
- if @dependencies.nil?
124
- return -1 if other.dependencies.any? do |dep|
125
- @name.downcase == dep.downcase
126
- end
127
- elsif @dependencies.any? do |dep|
128
- other.name.downcase == dep.downcase
129
- end
130
- return 1
117
+ def dependency_of?(other)
118
+ other&.dependencies&.any? do |dep|
119
+ @name&.downcase == dep.downcase
131
120
  end
132
- 0
121
+ end
122
+
123
+ def dependent_on?(other)
124
+ @dependencies&.any? do |dep|
125
+ other&.name&.downcase == dep.downcase
126
+ end
127
+ end
128
+
129
+ def compare_dependency(other)
130
+ # This approach might seem odd,
131
+ # but it's to handle circular dependencies
132
+ retval = 0
133
+ retval -= 1 if dependency_of?(other)
134
+ retval += 1 if dependent_on?(other)
135
+ retval
133
136
  end
134
137
 
135
138
  # Projects that are dropped or done are considered "complete", insofar as
@@ -5,12 +5,10 @@ module Plansheet
5
5
  # The Sheet class constructs a Markdown/LaTeX file for use with pandoc
6
6
  class Sheet
7
7
  def initialize(output_file, project_arr)
8
- sorted_arr = project_arr.sort!
9
-
10
8
  projects_str = String.new
11
9
  projects_str << sheet_header
12
10
 
13
- sorted_arr.each do |p|
11
+ project_arr.each do |p|
14
12
  projects_str << project_minipage(p)
15
13
  end
16
14
  puts "Writing to #{output_file}"
@@ -41,8 +39,9 @@ module Plansheet
41
39
 
42
40
  def project_header(proj)
43
41
  str = String.new
44
- str << "#{proj.name} - #{proj.status}"
42
+ str << "#{proj.namespace}: #{proj.name} - #{proj.status}"
45
43
  str << " - #{proj.location}" if proj.location
44
+ str << " - due #{proj.due}" if proj.due
46
45
  str << " \\\\\n"
47
46
  str
48
47
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Plansheet
4
- VERSION = "0.15.0"
4
+ VERSION = "0.17.1"
5
5
  end
data/lib/plansheet.rb CHANGED
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "plansheet/version"
4
- require_relative "plansheet/project"
5
4
  require_relative "plansheet/pool"
6
- require_relative "plansheet/sheet"
7
5
  require "yaml"
8
- require "kwalify"
9
6
 
10
7
  module Plansheet
11
8
  class Error < StandardError; 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.15.0
4
+ version: 0.17.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-06-09 00:00:00.000000000 Z
11
+ date: 2022-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dc-kwalify
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rgl
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.5.8
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.5.8
27
41
  description: Convert YAML project files into a nice PDF
28
42
  email:
29
43
  - dave@dafyddcrosby.com