plansheet 0.7.1 → 0.12.3
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/.rubocop.yml +6 -2
- data/Gemfile +5 -0
- data/Gemfile.lock +42 -1
- data/Guardfile +9 -0
- data/exe/plansheet +8 -1
- data/lib/plansheet/project/stringify.rb +47 -0
- data/lib/plansheet/project/yaml.rb +148 -0
- data/lib/plansheet/project.rb +142 -145
- data/lib/plansheet/sheet.rb +2 -2
- data/lib/plansheet/version.rb +1 -1
- metadata +5 -3
- data/.rubocop_todo.yml +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 72d3799f5166153c1775c768f0734cf0e390adfd80893ef1ae1aa710b7888b0b
|
4
|
+
data.tar.gz: a18fd6eeb6a787438ddcdff1e6b0f25a17688f05a7bccff82495520517754b19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c97c83093f8e70948a96392b5d02329128c5f1031a0645b35b9c42bebeabb82192207c3ff71671081792ef3d61e9277c219984f0f558a7b54c1fac632fa68445
|
7
|
+
data.tar.gz: 6723401a02c2086976e1002e4f8d08855f25726d1610fd9f4e593ae3ceac707c47bb6a1114ec1b40f707295c1c627fd5b72cce5ebe45f84b78b24d8304107bcf
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
inherit_from: .rubocop_todo.yml
|
2
|
-
|
3
1
|
AllCops:
|
4
2
|
TargetRubyVersion: 2.6
|
5
3
|
NewCops: enable
|
@@ -15,3 +13,9 @@ Style/StringLiteralsInInterpolation:
|
|
15
13
|
|
16
14
|
Layout/LineLength:
|
17
15
|
Max: 120
|
16
|
+
|
17
|
+
Metrics:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Style/Documentation:
|
21
|
+
Enabled: false
|
data/Gemfile
CHANGED
@@ -6,7 +6,12 @@ source "https://rubygems.org"
|
|
6
6
|
gemspec
|
7
7
|
|
8
8
|
group :development, optional: true do
|
9
|
+
gem "guard", "~> 2.18"
|
10
|
+
gem "guard-minitest", "~> 2.4"
|
9
11
|
gem "minitest", "~> 5.0"
|
10
12
|
gem "rake", "~> 13.0"
|
13
|
+
|
11
14
|
gem "rubocop", "~> 1.21"
|
15
|
+
gem "rubocop-minitest"
|
16
|
+
gem "rubocop-rake"
|
12
17
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,20 +1,51 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
plansheet (0.
|
4
|
+
plansheet (0.12.3)
|
5
5
|
dc-kwalify (~> 1.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
10
|
ast (2.4.2)
|
11
|
+
coderay (1.1.3)
|
11
12
|
dc-kwalify (1.0.0)
|
13
|
+
ffi (1.15.5)
|
14
|
+
formatador (1.1.0)
|
15
|
+
guard (2.18.0)
|
16
|
+
formatador (>= 0.2.4)
|
17
|
+
listen (>= 2.7, < 4.0)
|
18
|
+
lumberjack (>= 1.0.12, < 2.0)
|
19
|
+
nenv (~> 0.1)
|
20
|
+
notiffany (~> 0.0)
|
21
|
+
pry (>= 0.13.0)
|
22
|
+
shellany (~> 0.0)
|
23
|
+
thor (>= 0.18.1)
|
24
|
+
guard-compat (1.2.1)
|
25
|
+
guard-minitest (2.4.6)
|
26
|
+
guard-compat (~> 1.2)
|
27
|
+
minitest (>= 3.0)
|
28
|
+
listen (3.7.1)
|
29
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
30
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
31
|
+
lumberjack (1.2.8)
|
32
|
+
method_source (1.0.0)
|
12
33
|
minitest (5.15.0)
|
34
|
+
nenv (0.3.0)
|
35
|
+
notiffany (0.1.3)
|
36
|
+
nenv (~> 0.1)
|
37
|
+
shellany (~> 0.0)
|
13
38
|
parallel (1.22.1)
|
14
39
|
parser (3.1.2.0)
|
15
40
|
ast (~> 2.4.1)
|
41
|
+
pry (0.14.1)
|
42
|
+
coderay (~> 1.1)
|
43
|
+
method_source (~> 1.0)
|
16
44
|
rainbow (3.1.1)
|
17
45
|
rake (13.0.6)
|
46
|
+
rb-fsevent (0.11.1)
|
47
|
+
rb-inotify (0.10.1)
|
48
|
+
ffi (~> 1.0)
|
18
49
|
regexp_parser (2.4.0)
|
19
50
|
rexml (3.2.5)
|
20
51
|
rubocop (1.29.1)
|
@@ -28,17 +59,27 @@ GEM
|
|
28
59
|
unicode-display_width (>= 1.4.0, < 3.0)
|
29
60
|
rubocop-ast (1.18.0)
|
30
61
|
parser (>= 3.1.1.0)
|
62
|
+
rubocop-minitest (0.20.0)
|
63
|
+
rubocop (>= 0.90, < 2.0)
|
64
|
+
rubocop-rake (0.6.0)
|
65
|
+
rubocop (~> 1.0)
|
31
66
|
ruby-progressbar (1.11.0)
|
67
|
+
shellany (0.0.1)
|
68
|
+
thor (1.2.1)
|
32
69
|
unicode-display_width (2.1.0)
|
33
70
|
|
34
71
|
PLATFORMS
|
35
72
|
x86_64-linux
|
36
73
|
|
37
74
|
DEPENDENCIES
|
75
|
+
guard (~> 2.18)
|
76
|
+
guard-minitest (~> 2.4)
|
38
77
|
minitest (~> 5.0)
|
39
78
|
plansheet!
|
40
79
|
rake (~> 13.0)
|
41
80
|
rubocop (~> 1.21)
|
81
|
+
rubocop-minitest
|
82
|
+
rubocop-rake
|
42
83
|
|
43
84
|
BUNDLED WITH
|
44
85
|
2.3.6
|
data/Guardfile
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
directories(%w[lib test].select { |d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist") })
|
4
|
+
|
5
|
+
guard :minitest do
|
6
|
+
watch(%r{^test/test_(.*)\.rb$}) { "test" }
|
7
|
+
watch(%r{^lib/plansheet/(.*)\.rb$}) { "test" }
|
8
|
+
watch(%r{^lib/plansheet\.rb$}) { "test" }
|
9
|
+
end
|
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
|
+
"--location_filter LOCATION",
|
22
|
+
"location filter for CLI dump (WIP)"
|
23
|
+
)
|
20
24
|
options = {}
|
21
25
|
parser.parse!(into: options)
|
22
26
|
|
@@ -32,7 +36,10 @@ elsif options[:sort]
|
|
32
36
|
Plansheet.resort_projects_in_dir config["projects_dir"]
|
33
37
|
elsif options[:cli]
|
34
38
|
project_arr = Plansheet.load_projects_dir config["projects_dir"]
|
35
|
-
project_arr.sort
|
39
|
+
project_arr.sort!
|
40
|
+
project_arr.delete_if { |x| x.status == "dropped" || x.status == "done" }
|
41
|
+
project_arr.select! { |x| x.location == options[:location_filter] } if options[:location_filter]
|
42
|
+
project_arr.each do |proj|
|
36
43
|
puts proj
|
37
44
|
puts "\n"
|
38
45
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plansheet
|
4
|
+
class Project
|
5
|
+
def to_s
|
6
|
+
str = String.new
|
7
|
+
str << "# #{@name}\n"
|
8
|
+
STRING_PROPERTIES.each do |o|
|
9
|
+
str << stringify_string_property(o)
|
10
|
+
end
|
11
|
+
DATE_PROPERTIES.each do |o|
|
12
|
+
str << stringify_string_property(o)
|
13
|
+
end
|
14
|
+
ARRAY_PROPERTIES.each do |o|
|
15
|
+
str << stringify_array_property(o)
|
16
|
+
end
|
17
|
+
str
|
18
|
+
end
|
19
|
+
|
20
|
+
def stringify_string_property(prop)
|
21
|
+
if instance_variable_defined? "@#{prop}"
|
22
|
+
"#{prop}: #{instance_variable_get("@#{prop}")}\n"
|
23
|
+
else
|
24
|
+
""
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def stringify_date_property(prop)
|
29
|
+
if instance_variable_defined? "@#{prop}"
|
30
|
+
"#{prop}: #{instance_variable_get("@#{prop}")}\n"
|
31
|
+
else
|
32
|
+
""
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def stringify_array_property(prop)
|
37
|
+
str = String.new
|
38
|
+
if instance_variable_defined? "@#{prop}"
|
39
|
+
str << "#{prop}:\n"
|
40
|
+
instance_variable_get("@#{prop}").each do |t|
|
41
|
+
str << "- #{t}\n"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
str
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "date"
|
5
|
+
|
6
|
+
module Plansheet
|
7
|
+
# Once there's some stability in plansheet and dc-kwalify, will pre-load this
|
8
|
+
# to save the later YAML.load
|
9
|
+
PROJECT_YAML_SCHEMA = <<~YAML
|
10
|
+
desc: dc-tasks project schema
|
11
|
+
type: seq
|
12
|
+
sequence:
|
13
|
+
- type: map
|
14
|
+
mapping:
|
15
|
+
"project":
|
16
|
+
desc: Project name
|
17
|
+
type: str
|
18
|
+
required: yes
|
19
|
+
"priority":
|
20
|
+
desc: Project priority
|
21
|
+
type: str
|
22
|
+
enum:
|
23
|
+
- high
|
24
|
+
- medium
|
25
|
+
- low
|
26
|
+
"status":
|
27
|
+
desc: The current status of the project
|
28
|
+
type: str
|
29
|
+
enum:
|
30
|
+
- wip # project is a work-in-progress
|
31
|
+
- ready # project has tasks, ready to go
|
32
|
+
- waiting # project in waiting on some external person/event
|
33
|
+
- blocked # project is blocked by another project, but otherwise ready/wip
|
34
|
+
- planning # project in planning phase (set manually)
|
35
|
+
- idea # project is little more than an idea
|
36
|
+
- dropped # project has been explicitly dropped, but
|
37
|
+
# want to keep around for reference, etc
|
38
|
+
- done # project is finished, but want to keep around
|
39
|
+
# for reference, etc.
|
40
|
+
"location":
|
41
|
+
desc: Location
|
42
|
+
type: str
|
43
|
+
"notes":
|
44
|
+
desc: Free-form notes string
|
45
|
+
type: str
|
46
|
+
"time_estimate":
|
47
|
+
desc: The estimated amount of time before a project is completed
|
48
|
+
type: str
|
49
|
+
"frequency":
|
50
|
+
desc: The amount of time before a recurring project moves to ready status again from when it was last done (WIP)
|
51
|
+
type: str
|
52
|
+
pattern: /\\d+[dwDW]/
|
53
|
+
"lead_time":
|
54
|
+
desc: The amount of time before a recurring project is "due" moved to ready where the project (sort of a deferral mechanism) (WIP)
|
55
|
+
type: str
|
56
|
+
pattern: /\\d+[dwDW]/
|
57
|
+
"due":
|
58
|
+
desc: Due date of the task
|
59
|
+
type: date
|
60
|
+
"defer":
|
61
|
+
desc: Defer task until this day
|
62
|
+
type: date
|
63
|
+
"completed_on":
|
64
|
+
desc: When the (non-recurring) project was completed
|
65
|
+
type: date
|
66
|
+
"created_on":
|
67
|
+
desc: When the project was created
|
68
|
+
type: date
|
69
|
+
"starts_on":
|
70
|
+
desc: For ICS (WIP)
|
71
|
+
type: date
|
72
|
+
"last_reviewed":
|
73
|
+
desc: When the project was last reviewed (WIP)
|
74
|
+
type: date
|
75
|
+
"last_done":
|
76
|
+
desc: When the recurring project was last completed (WIP)
|
77
|
+
type: date
|
78
|
+
"dependencies":
|
79
|
+
desc: The names of projects that need to be completed before this project can be started/completed
|
80
|
+
type: seq
|
81
|
+
sequence:
|
82
|
+
- type: str
|
83
|
+
"externals":
|
84
|
+
desc: List of external commitments, ie who else cares about project completion?
|
85
|
+
type: seq
|
86
|
+
sequence:
|
87
|
+
- type: str
|
88
|
+
"urls":
|
89
|
+
desc: List of URLs that may be pertinent
|
90
|
+
type: seq
|
91
|
+
sequence:
|
92
|
+
- type: str
|
93
|
+
"tasks":
|
94
|
+
desc: List of tasks to do
|
95
|
+
type: seq
|
96
|
+
sequence:
|
97
|
+
- type: str
|
98
|
+
"done":
|
99
|
+
desc: List of tasks which have been completed
|
100
|
+
type: seq
|
101
|
+
sequence:
|
102
|
+
- type: str
|
103
|
+
"tags":
|
104
|
+
desc: List of tags (WIP)
|
105
|
+
type: seq
|
106
|
+
sequence:
|
107
|
+
- type: str
|
108
|
+
YAML
|
109
|
+
PROJECT_SCHEMA = YAML.safe_load(PROJECT_YAML_SCHEMA)
|
110
|
+
|
111
|
+
class ProjectYAMLFile
|
112
|
+
attr_reader :projects
|
113
|
+
|
114
|
+
def initialize(path)
|
115
|
+
@path = path
|
116
|
+
# TODO: this won't GC, inline validation instead?
|
117
|
+
|
118
|
+
# Handle pre-Ruby 3.1 psych versions (this is brittle)
|
119
|
+
@raw = if Psych::VERSION.split(".")[0].to_i >= 4
|
120
|
+
YAML.load_file(path, permitted_classes: [Date])
|
121
|
+
else
|
122
|
+
YAML.load_file(path)
|
123
|
+
end
|
124
|
+
|
125
|
+
validate_schema
|
126
|
+
@projects = @raw.map { |proj| Project.new proj }
|
127
|
+
end
|
128
|
+
|
129
|
+
def validate_schema
|
130
|
+
validator = Kwalify::Validator.new(Plansheet::PROJECT_SCHEMA)
|
131
|
+
errors = validator.validate(@raw)
|
132
|
+
# Check YAML validity
|
133
|
+
return unless errors && !errors.empty?
|
134
|
+
|
135
|
+
$stderr.write "Schema errors in #{@path}:\n"
|
136
|
+
errors.each { |err| puts "- [#{err.path}] #{err.message}" }
|
137
|
+
abort
|
138
|
+
end
|
139
|
+
|
140
|
+
def sort!
|
141
|
+
@projects.sort!
|
142
|
+
end
|
143
|
+
|
144
|
+
def yaml_dump
|
145
|
+
YAML.dump(@projects.map(&:to_h))
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/plansheet/project.rb
CHANGED
@@ -2,16 +2,19 @@
|
|
2
2
|
|
3
3
|
require "yaml"
|
4
4
|
require "date"
|
5
|
+
require_relative "project/yaml"
|
6
|
+
require_relative "project/stringify"
|
5
7
|
|
6
8
|
module Plansheet
|
7
9
|
PROJECT_STATUS_PRIORITY = {
|
8
10
|
"wip" => 1,
|
9
11
|
"ready" => 2,
|
10
12
|
"blocked" => 3,
|
11
|
-
"
|
12
|
-
"
|
13
|
-
"
|
14
|
-
"
|
13
|
+
"waiting" => 4,
|
14
|
+
"planning" => 5,
|
15
|
+
"idea" => 6,
|
16
|
+
"dropped" => 7,
|
17
|
+
"done" => 8
|
15
18
|
}.freeze
|
16
19
|
|
17
20
|
PROJECT_PRIORITY = {
|
@@ -20,72 +23,12 @@ module Plansheet
|
|
20
23
|
"low" => 3
|
21
24
|
}.freeze
|
22
25
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
- type: map
|
30
|
-
mapping:
|
31
|
-
"project":
|
32
|
-
desc: Project name
|
33
|
-
type: str
|
34
|
-
required: yes
|
35
|
-
"priority":
|
36
|
-
desc: Project priority
|
37
|
-
type: str
|
38
|
-
enum:
|
39
|
-
- high
|
40
|
-
- medium
|
41
|
-
- low
|
42
|
-
"status":
|
43
|
-
desc: The current status of the project
|
44
|
-
type: str
|
45
|
-
enum:
|
46
|
-
- wip # project is a work-in-progress
|
47
|
-
- ready # project is fully scoped, ready to go
|
48
|
-
- blocked # project is blocked, but otherwise ready/wip
|
49
|
-
- planning # project in planning phase
|
50
|
-
- idea # project is little more than an idea
|
51
|
-
- dropped # project has been explicitly dropped, but
|
52
|
-
# want to keep around for reference, etc
|
53
|
-
- done # project is finished, but want to keep around
|
54
|
-
# for reference, etc.
|
55
|
-
"location":
|
56
|
-
desc: Location
|
57
|
-
type: str
|
58
|
-
"notes":
|
59
|
-
desc: Free-form notes string
|
60
|
-
type: str
|
61
|
-
"due":
|
62
|
-
desc: Due date of the task (WIP)
|
63
|
-
type: date
|
64
|
-
"defer":
|
65
|
-
desc: Defer task until this day (WIP)
|
66
|
-
type: date
|
67
|
-
"externals":
|
68
|
-
desc: List of external commitments, ie who else cares about project completion?
|
69
|
-
type: seq
|
70
|
-
sequence:
|
71
|
-
- type: str
|
72
|
-
"urls":
|
73
|
-
desc: List of URLs that may be pertinent
|
74
|
-
type: seq
|
75
|
-
sequence:
|
76
|
-
- type: str
|
77
|
-
"tasks":
|
78
|
-
desc: List of tasks to do
|
79
|
-
type: seq
|
80
|
-
sequence:
|
81
|
-
- type: str
|
82
|
-
"done":
|
83
|
-
desc: List of tasks which have been completed
|
84
|
-
type: seq
|
85
|
-
sequence:
|
86
|
-
- type: str
|
87
|
-
YAML
|
88
|
-
PROJECT_SCHEMA = YAML.safe_load(PROJECT_YAML_SCHEMA)
|
26
|
+
def self.parse_date_duration(str)
|
27
|
+
return Regexp.last_match(1).to_i if str.strip.match(/(\d+)[dD]/)
|
28
|
+
return (Regexp.last_match(1).to_i * 7) if str.strip.match(/(\d+)[wW]/)
|
29
|
+
|
30
|
+
raise "Can't parse time duration string #{str}"
|
31
|
+
end
|
89
32
|
|
90
33
|
# The use of instance_variable_set/get probably seems a bit weird, but the
|
91
34
|
# intent is to avoid object allocation on non-existent project properties, as
|
@@ -96,10 +39,18 @@ module Plansheet
|
|
96
39
|
class Project
|
97
40
|
include Comparable
|
98
41
|
|
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
|
99
50
|
# NOTE: The order of these affects presentation!
|
100
|
-
STRING_PROPERTIES = %w[priority status location notes].freeze
|
101
|
-
DATE_PROPERTIES = %w[due defer].freeze
|
102
|
-
ARRAY_PROPERTIES = %w[externals urls tasks done].freeze
|
51
|
+
STRING_PROPERTIES = %w[priority status location notes time_estimate frequency lead_time].freeze
|
52
|
+
DATE_PROPERTIES = %w[due defer completed_on created_on starts_on last_done last_reviewed].freeze
|
53
|
+
ARRAY_PROPERTIES = %w[dependencies externals urls tasks done tags].freeze
|
103
54
|
|
104
55
|
ALL_PROPERTIES = STRING_PROPERTIES + DATE_PROPERTIES + ARRAY_PROPERTIES
|
105
56
|
|
@@ -126,109 +77,155 @@ module Plansheet
|
|
126
77
|
end
|
127
78
|
|
128
79
|
def <=>(other)
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
PROJECT_PRIORITY[@priority] <=> PROJECT_PRIORITY[other.priority]
|
80
|
+
ret_val = 0
|
81
|
+
DEFAULT_COMPARISON_ORDER.each do |method|
|
82
|
+
ret_val = send(method, other)
|
83
|
+
break if ret_val != 0
|
134
84
|
end
|
85
|
+
ret_val
|
135
86
|
end
|
136
87
|
|
137
|
-
def
|
138
|
-
|
88
|
+
def compare_priority(other)
|
89
|
+
PROJECT_PRIORITY[@priority] <=> PROJECT_PRIORITY[other.priority]
|
90
|
+
end
|
139
91
|
|
140
|
-
|
141
|
-
|
142
|
-
"wip"
|
143
|
-
else
|
144
|
-
"planning"
|
145
|
-
end
|
146
|
-
else
|
147
|
-
"idea"
|
148
|
-
end
|
92
|
+
def compare_status(other)
|
93
|
+
PROJECT_STATUS_PRIORITY[status] <=> PROJECT_STATUS_PRIORITY[other.status]
|
149
94
|
end
|
150
95
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
96
|
+
def compare_due(other)
|
97
|
+
# -1 is receiving object being older
|
98
|
+
|
99
|
+
# Handle nil
|
100
|
+
if @due.nil?
|
101
|
+
return 0 if other.due.nil?
|
102
|
+
|
103
|
+
return 1
|
104
|
+
elsif other.due.nil?
|
105
|
+
return -1
|
159
106
|
end
|
160
|
-
|
161
|
-
|
107
|
+
|
108
|
+
@due <=> other.due
|
109
|
+
end
|
110
|
+
|
111
|
+
def compare_defer(other)
|
112
|
+
receiver = @defer.nil? || @defer < Date.today ? Date.today : @defer
|
113
|
+
comparison = other.defer.nil? || other.defer < Date.today ? Date.today : other.defer
|
114
|
+
receiver <=> comparison
|
115
|
+
end
|
116
|
+
|
117
|
+
def compare_dependency(other)
|
118
|
+
return 0 if @dependencies.nil? && other.dependencies.nil?
|
119
|
+
|
120
|
+
if @dependencies.nil?
|
121
|
+
return -1 if other.dependencies.any? do |dep|
|
122
|
+
@name.downcase == dep.downcase
|
123
|
+
end
|
124
|
+
elsif @dependencies.any? do |dep|
|
125
|
+
other.name.downcase == dep.downcase
|
126
|
+
end
|
127
|
+
return 1
|
162
128
|
end
|
163
|
-
|
129
|
+
0
|
130
|
+
end
|
131
|
+
|
132
|
+
# Projects that are dropped or done are considered "complete", insofar as
|
133
|
+
# they are only kept around for later reference.
|
134
|
+
def compare_completeness(other)
|
135
|
+
return 0 if dropped_or_done? && other.dropped_or_done?
|
136
|
+
return 0 if !dropped_or_done? && !other.dropped_or_done?
|
137
|
+
|
138
|
+
dropped_or_done? ? 1 : -1
|
139
|
+
end
|
140
|
+
|
141
|
+
def status
|
142
|
+
return @status if @status
|
143
|
+
return recurring_status if recurring?
|
144
|
+
return task_based_status if @tasks || @done
|
145
|
+
return "done" if @completed_on && @tasks.nil?
|
146
|
+
|
147
|
+
"idea"
|
164
148
|
end
|
165
149
|
|
166
|
-
def
|
167
|
-
if
|
168
|
-
"
|
150
|
+
def task_based_status
|
151
|
+
if @tasks&.count&.positive? && @done&.count&.positive?
|
152
|
+
"wip"
|
153
|
+
elsif @tasks&.count&.positive?
|
154
|
+
"ready"
|
155
|
+
elsif @done&.count&.positive?
|
156
|
+
"done"
|
169
157
|
else
|
170
|
-
""
|
158
|
+
"idea"
|
171
159
|
end
|
172
160
|
end
|
173
161
|
|
174
|
-
def
|
175
|
-
|
176
|
-
|
162
|
+
def recurring_status
|
163
|
+
# add frequency to last_done
|
164
|
+
if @last_done
|
165
|
+
# This project has been done once before
|
166
|
+
subsequent_recurring_status
|
177
167
|
else
|
178
|
-
|
168
|
+
# This recurring project is being done for the first time
|
169
|
+
task_based_status
|
179
170
|
end
|
180
171
|
end
|
181
172
|
|
182
|
-
def
|
183
|
-
|
184
|
-
if
|
185
|
-
|
186
|
-
|
187
|
-
str << "- #{t}\n"
|
188
|
-
end
|
189
|
-
end
|
190
|
-
str
|
173
|
+
def subsequent_recurring_status
|
174
|
+
return "done" if @lead_time && defer > Date.today
|
175
|
+
return "done" if due > Date.today
|
176
|
+
|
177
|
+
task_based_status
|
191
178
|
end
|
192
179
|
|
193
|
-
def
|
194
|
-
|
195
|
-
|
196
|
-
|
180
|
+
def process_recurring
|
181
|
+
# TODO: Tasks will be moved from done->tasks if recurring project is
|
182
|
+
# starting again
|
183
|
+
end
|
184
|
+
|
185
|
+
# Due date either explicit or recurring
|
186
|
+
def due
|
187
|
+
return @due if @due
|
188
|
+
return recurring_due_date if recurring?
|
189
|
+
|
190
|
+
nil
|
191
|
+
end
|
192
|
+
|
193
|
+
def recurring_due_date
|
194
|
+
if @last_done
|
195
|
+
@last_done + Plansheet.parse_date_duration(@frequency)
|
196
|
+
else
|
197
|
+
Date.today
|
197
198
|
end
|
198
|
-
h.delete "priority" if h.key?("priority") && h["priority"] == "low"
|
199
|
-
h.delete "status" if h.key?("status") && h["status"] == "idea"
|
200
|
-
h
|
201
199
|
end
|
202
|
-
end
|
203
200
|
|
204
|
-
|
205
|
-
|
201
|
+
def defer
|
202
|
+
return @defer if @defer
|
203
|
+
return lead_time_deferral if @lead_time && due
|
206
204
|
|
207
|
-
|
208
|
-
@path = path
|
209
|
-
# TODO: this won't GC, inline validation instead?
|
210
|
-
@raw = YAML.load_file(path, permitted_classes: [Date])
|
211
|
-
validate_schema
|
212
|
-
@projects = @raw.map { |proj| Project.new proj }
|
205
|
+
nil
|
213
206
|
end
|
214
207
|
|
215
|
-
def
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
return unless errors && !errors.empty?
|
208
|
+
def lead_time_deferral
|
209
|
+
[(due - Plansheet.parse_date_duration(@lead_time)),
|
210
|
+
Date.today].max
|
211
|
+
end
|
220
212
|
|
221
|
-
|
222
|
-
|
223
|
-
abort
|
213
|
+
def recurring?
|
214
|
+
!@frequency.nil?
|
224
215
|
end
|
225
216
|
|
226
|
-
def
|
227
|
-
|
217
|
+
def dropped_or_done?
|
218
|
+
status == "dropped" || status == "done"
|
228
219
|
end
|
229
220
|
|
230
|
-
def
|
231
|
-
|
221
|
+
def to_h
|
222
|
+
h = { "project" => @name }
|
223
|
+
ALL_PROPERTIES.each do |prop|
|
224
|
+
h[prop] = instance_variable_get("@#{prop}") if instance_variable_defined?("@#{prop}")
|
225
|
+
end
|
226
|
+
h.delete "priority" if h.key?("priority") && h["priority"] == "low"
|
227
|
+
h.delete "status" if h.key?("status") && h["status"] == "idea"
|
228
|
+
h
|
232
229
|
end
|
233
230
|
end
|
234
231
|
end
|
data/lib/plansheet/sheet.rb
CHANGED
@@ -10,7 +10,7 @@ module Plansheet
|
|
10
10
|
projects_str = String.new
|
11
11
|
projects_str << sheet_header
|
12
12
|
|
13
|
-
sorted_arr.
|
13
|
+
sorted_arr.each do |p|
|
14
14
|
projects_str << project_minipage(p)
|
15
15
|
end
|
16
16
|
puts "Writing to #{output_file}"
|
@@ -32,7 +32,7 @@ module Plansheet
|
|
32
32
|
str = String.new
|
33
33
|
str << "\\begin{minipage}{4.5cm}\n"
|
34
34
|
str << project_header(proj)
|
35
|
-
proj
|
35
|
+
proj&.tasks&.each do |t|
|
36
36
|
str << "$\\square$ #{t} \\\\\n"
|
37
37
|
end
|
38
38
|
str << "\\end{minipage}\n"
|
data/lib/plansheet/version.rb
CHANGED
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.12.3
|
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-
|
11
|
+
date: 2022-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dc-kwalify
|
@@ -33,10 +33,10 @@ extensions: []
|
|
33
33
|
extra_rdoc_files: []
|
34
34
|
files:
|
35
35
|
- ".rubocop.yml"
|
36
|
-
- ".rubocop_todo.yml"
|
37
36
|
- CODE_OF_CONDUCT.md
|
38
37
|
- Gemfile
|
39
38
|
- Gemfile.lock
|
39
|
+
- Guardfile
|
40
40
|
- LICENSE.txt
|
41
41
|
- README.md
|
42
42
|
- Rakefile
|
@@ -44,6 +44,8 @@ files:
|
|
44
44
|
- exe/plansheet
|
45
45
|
- lib/plansheet.rb
|
46
46
|
- lib/plansheet/project.rb
|
47
|
+
- lib/plansheet/project/stringify.rb
|
48
|
+
- lib/plansheet/project/yaml.rb
|
47
49
|
- lib/plansheet/sheet.rb
|
48
50
|
- lib/plansheet/version.rb
|
49
51
|
homepage: https://dafyddcrosby.com
|
data/.rubocop_todo.yml
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# This configuration was generated by
|
2
|
-
# `rubocop --auto-gen-config`
|
3
|
-
# on 2022-06-02 13:59:36 UTC using RuboCop version 1.29.1.
|
4
|
-
# The point is for the user to remove these configuration records
|
5
|
-
# one by one as the offenses are removed from the code base.
|
6
|
-
# Note that changes in the inspected code, or installation of new
|
7
|
-
# versions of RuboCop, may require this file to be generated again.
|
8
|
-
|
9
|
-
# Offense count: 1
|
10
|
-
# Configuration parameters: IgnoredMethods.
|
11
|
-
Metrics/CyclomaticComplexity:
|
12
|
-
Max: 8
|
13
|
-
|
14
|
-
# Offense count: 1
|
15
|
-
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
|
16
|
-
Metrics/MethodLength:
|
17
|
-
Max: 12
|
18
|
-
|
19
|
-
# Offense count: 1
|
20
|
-
# Configuration parameters: IgnoredMethods.
|
21
|
-
Metrics/PerceivedComplexity:
|
22
|
-
Max: 10
|
23
|
-
|
24
|
-
# Offense count: 2
|
25
|
-
# Configuration parameters: AllowedConstants.
|
26
|
-
Style/Documentation:
|
27
|
-
Exclude:
|
28
|
-
- 'spec/**/*'
|
29
|
-
- 'test/**/*'
|
30
|
-
- 'lib/plansheet.rb'
|
31
|
-
- 'lib/plansheet/project.rb'
|