plansheet 0.15.0 → 0.17.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 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