plansheet 0.27.0 → 0.30.2
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 +3 -22
- data/Gemfile +1 -8
- data/Gemfile.lock +22 -15
- data/README.md +5 -3
- data/exe/plansheet +19 -3
- data/lib/plansheet/pool.rb +2 -2
- data/lib/plansheet/project/yaml.rb +8 -0
- data/lib/plansheet/project.rb +43 -53
- data/lib/plansheet/sheet/latex.rb +49 -5
- data/lib/plansheet/time.rb +35 -0
- data/lib/plansheet/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a918dce2e7ab41c30070205e2dcb75679696047b231e0a0999ed0a940fa3aea8
|
4
|
+
data.tar.gz: 5cec12572afb320929ff5efd951bf9f31e69aeff53d35e4886ea17d6891ad30c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30fdf0a0f98d5dd99de508849c9380210d4b8515d8549ccc4cfb60a8da2263fecd9d6b72fcbaf76fd539aeb826a1c69342930a403d6289942c637e1e5f70e54d
|
7
|
+
data.tar.gz: e0cf9b8ec2daa63f29b396ca58e1321d4303fc3ce2cbbccaf3a7bc282d33e2756ed55bce9747ff74223ee026130c1c8b69746ff97a38f189bc540e95f73dd49d
|
data/.rubocop.yml
CHANGED
@@ -1,24 +1,5 @@
|
|
1
|
+
inherit_gem:
|
2
|
+
dc-rubocop: default.yml
|
3
|
+
|
1
4
|
AllCops:
|
2
5
|
TargetRubyVersion: 2.6
|
3
|
-
NewCops: enable
|
4
|
-
SuggestExtensions: false
|
5
|
-
|
6
|
-
Style/StringLiterals:
|
7
|
-
Enabled: true
|
8
|
-
EnforcedStyle: double_quotes
|
9
|
-
|
10
|
-
Style/StringLiteralsInInterpolation:
|
11
|
-
Enabled: true
|
12
|
-
EnforcedStyle: double_quotes
|
13
|
-
|
14
|
-
Style/GuardClause:
|
15
|
-
Enabled: false
|
16
|
-
|
17
|
-
Layout/LineLength:
|
18
|
-
Max: 120
|
19
|
-
|
20
|
-
Metrics:
|
21
|
-
Enabled: false
|
22
|
-
|
23
|
-
Style/Documentation:
|
24
|
-
Enabled: false
|
data/Gemfile
CHANGED
@@ -6,13 +6,6 @@ source "https://rubygems.org"
|
|
6
6
|
gemspec
|
7
7
|
|
8
8
|
group :development, optional: true do
|
9
|
-
gem "
|
10
|
-
gem "guard-rake", "~> 1.0"
|
11
|
-
gem "minitest", "~> 5.0"
|
12
|
-
gem "rake", "~> 13.0"
|
9
|
+
gem "dc-devtools"
|
13
10
|
gem "rdoc"
|
14
|
-
|
15
|
-
gem "rubocop", "~> 1.21"
|
16
|
-
gem "rubocop-minitest"
|
17
|
-
gem "rubocop-rake"
|
18
11
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
plansheet (0.
|
4
|
+
plansheet (0.30.2)
|
5
5
|
dc-kwalify (~> 1.0)
|
6
6
|
diffy (= 3.4.2)
|
7
7
|
rgl (= 0.5.8)
|
@@ -11,7 +11,18 @@ GEM
|
|
11
11
|
specs:
|
12
12
|
ast (2.4.2)
|
13
13
|
coderay (1.1.3)
|
14
|
+
dc-devtools (0.0.202207232239)
|
15
|
+
dc-rubocop (>= 0.0.3)
|
16
|
+
guard (~> 2.18)
|
17
|
+
guard-rake (~> 1.0)
|
18
|
+
minitest
|
19
|
+
rake (~> 13.0)
|
20
|
+
rubocop (~> 1.30)
|
21
|
+
rubocop-minitest
|
22
|
+
rubocop-rake (~> 0.6)
|
14
23
|
dc-kwalify (1.0.0)
|
24
|
+
dc-rubocop (0.0.3)
|
25
|
+
rubocop
|
15
26
|
diffy (3.4.2)
|
16
27
|
ffi (1.15.5)
|
17
28
|
formatador (1.1.0)
|
@@ -28,13 +39,14 @@ GEM
|
|
28
39
|
guard-rake (1.0.0)
|
29
40
|
guard
|
30
41
|
rake
|
42
|
+
json (2.6.2)
|
31
43
|
lazy_priority_queue (0.1.1)
|
32
44
|
listen (3.7.1)
|
33
45
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
34
46
|
rb-inotify (~> 0.9, >= 0.9.10)
|
35
47
|
lumberjack (1.2.8)
|
36
48
|
method_source (1.0.0)
|
37
|
-
minitest (5.
|
49
|
+
minitest (5.16.2)
|
38
50
|
nenv (0.3.0)
|
39
51
|
notiffany (0.1.3)
|
40
52
|
nenv (~> 0.1)
|
@@ -54,24 +66,25 @@ GEM
|
|
54
66
|
ffi (~> 1.0)
|
55
67
|
rdoc (6.4.0)
|
56
68
|
psych (>= 4.0.0)
|
57
|
-
regexp_parser (2.
|
69
|
+
regexp_parser (2.5.0)
|
58
70
|
rexml (3.2.5)
|
59
71
|
rgl (0.5.8)
|
60
72
|
lazy_priority_queue (~> 0.1.0)
|
61
73
|
rexml (~> 3.2, >= 3.2.4)
|
62
74
|
stream (~> 0.5.3)
|
63
|
-
rubocop (1.
|
75
|
+
rubocop (1.32.0)
|
76
|
+
json (~> 2.3)
|
64
77
|
parallel (~> 1.10)
|
65
78
|
parser (>= 3.1.0.0)
|
66
79
|
rainbow (>= 2.2.2, < 4.0)
|
67
80
|
regexp_parser (>= 1.8, < 3.0)
|
68
81
|
rexml (>= 3.2.5, < 4.0)
|
69
|
-
rubocop-ast (>= 1.
|
82
|
+
rubocop-ast (>= 1.19.1, < 2.0)
|
70
83
|
ruby-progressbar (~> 1.7)
|
71
84
|
unicode-display_width (>= 1.4.0, < 3.0)
|
72
|
-
rubocop-ast (1.
|
85
|
+
rubocop-ast (1.19.1)
|
73
86
|
parser (>= 3.1.1.0)
|
74
|
-
rubocop-minitest (0.20.
|
87
|
+
rubocop-minitest (0.20.1)
|
75
88
|
rubocop (>= 0.90, < 2.0)
|
76
89
|
rubocop-rake (0.6.0)
|
77
90
|
rubocop (~> 1.0)
|
@@ -81,21 +94,15 @@ GEM
|
|
81
94
|
generator
|
82
95
|
stringio (3.0.2)
|
83
96
|
thor (1.2.1)
|
84
|
-
unicode-display_width (2.
|
97
|
+
unicode-display_width (2.2.0)
|
85
98
|
|
86
99
|
PLATFORMS
|
87
100
|
x86_64-linux
|
88
101
|
|
89
102
|
DEPENDENCIES
|
90
|
-
|
91
|
-
guard-rake (~> 1.0)
|
92
|
-
minitest (~> 5.0)
|
103
|
+
dc-devtools
|
93
104
|
plansheet!
|
94
|
-
rake (~> 13.0)
|
95
105
|
rdoc
|
96
|
-
rubocop (~> 1.21)
|
97
|
-
rubocop-minitest
|
98
|
-
rubocop-rake
|
99
106
|
|
100
107
|
BUNDLED WITH
|
101
108
|
2.3.6
|
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# Plansheet
|
2
2
|
|
3
|
-
Plansheet is a simple to-do list generator program that creates a
|
4
|
-
|
5
|
-
|
3
|
+
Plansheet is a simple to-do list generator program that creates a LaTeX
|
4
|
+
printout (that can be turned into a PDF by pdflatex) from a pile of formatted
|
5
|
+
[YAML](https://yaml.org) files.
|
6
|
+
|
7
|
+
Plansheet is still a work-in-progress, and while effort is made not to break over minor/patch versions, it's best effort. If you aren't comfortable with fixing Ruby using a lot of metaprogramming, check back in a year or two
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
data/exe/plansheet
CHANGED
@@ -29,6 +29,10 @@ parser.on(
|
|
29
29
|
"--time-roi",
|
30
30
|
"Show projects with a time return-on-investment"
|
31
31
|
)
|
32
|
+
parser.on(
|
33
|
+
"--needs-thinking",
|
34
|
+
"Show projects that need tasks, time estimates, etc (WIP)"
|
35
|
+
)
|
32
36
|
parser.on(
|
33
37
|
"--calendar",
|
34
38
|
"List of projects ordered by due date"
|
@@ -46,15 +50,18 @@ pool = Plansheet::Pool.new({ projects_dir: config["projects_dir"],
|
|
46
50
|
|
47
51
|
if options[:sheet] || options.empty?
|
48
52
|
require "plansheet/sheet"
|
49
|
-
|
50
|
-
Plansheet::
|
53
|
+
FileUtils.mkdir_p config["output_dir"]
|
54
|
+
Plansheet::LaTeXSheet.new("#{config["output_dir"]}/projects.tex", pool.projects)
|
51
55
|
elsif options[:irb]
|
52
56
|
binding.irb # rubocop:disable Lint/Debugger
|
53
57
|
elsif options[:stats]
|
58
|
+
require "plansheet/time"
|
59
|
+
include Plansheet::TimeUtils # rubocop:disable Style/MixinUsage
|
54
60
|
puts "# of projects: #{pool.projects.count}"
|
55
61
|
puts "# of tasks: #{pool.projects.sum { |x| x&.tasks&.count || 0 }}"
|
56
62
|
puts "# of locations: #{pool.projects.collect(&:location).flatten.delete_if(&:nil?).uniq.count}"
|
57
|
-
|
63
|
+
time = Plansheet::TimeUtils.build_time_duration(pool.projects.sum { |x| x.time_estimate_minutes || 0 })
|
64
|
+
puts "combined time estimate: #{time}"
|
58
65
|
elsif options[:sort]
|
59
66
|
# Pool sorts projects, this now just matches old behaviour
|
60
67
|
pool.write_projects
|
@@ -65,6 +72,15 @@ elsif options[:"time-roi"]
|
|
65
72
|
puts "time ROI payoff: #{proj.time_roi_payoff}"
|
66
73
|
puts "\n"
|
67
74
|
end
|
75
|
+
elsif options[:"needs-thinking"]
|
76
|
+
puts "Projects with no tasks"
|
77
|
+
project_arr = pool.projects.sort
|
78
|
+
project_arr.delete_if(&:dropped_or_done?)
|
79
|
+
project_arr.delete_if(&:tasks)
|
80
|
+
project_arr.each do |proj|
|
81
|
+
puts proj
|
82
|
+
puts "\n"
|
83
|
+
end
|
68
84
|
elsif options[:calendar]
|
69
85
|
# TODO: add a project filter method
|
70
86
|
project_arr = pool.projects
|
data/lib/plansheet/pool.rb
CHANGED
@@ -79,14 +79,14 @@ module Plansheet
|
|
79
79
|
|
80
80
|
def archive_projects
|
81
81
|
archive_dir = "#{@projects_dir}/archive/"
|
82
|
-
|
82
|
+
FileUtils.mkdir_p archive_dir
|
83
83
|
|
84
84
|
# NOTE: It would save writes if we sorted and did all month/namespaces
|
85
85
|
# writes only once, but as the normal case only sees a few projects
|
86
86
|
# archived at a time, I'll leave that for someone else to implement ;-)
|
87
87
|
projects_to_archive = @projects.select(&:archivable?)
|
88
88
|
projects_to_archive.each do |project|
|
89
|
-
path = Pathname.new "#{archive_dir}/#{project.
|
89
|
+
path = Pathname.new "#{archive_dir}/#{project.archive_month}/#{project.namespace}.yml"
|
90
90
|
Dir.mkdir path.dirname unless path.dirname.exist?
|
91
91
|
pyf = ProjectYAMLFile.new path
|
92
92
|
pyf.append_project project
|
@@ -42,6 +42,8 @@ module Plansheet
|
|
42
42
|
- blocked # project is blocked by another project, but otherwise ready/wip
|
43
43
|
- planning # project in planning phase (set manually)
|
44
44
|
- idea # project is little more than an idea
|
45
|
+
- paused # project shouldn't be archived, but should be excluded from
|
46
|
+
# generated output unless paused projects are requested
|
45
47
|
- dropped # project has been explicitly dropped, but
|
46
48
|
# want to keep around for reference, etc
|
47
49
|
- done # project is finished, but want to keep around
|
@@ -100,6 +102,12 @@ module Plansheet
|
|
100
102
|
"completed_on":
|
101
103
|
desc: When the (non-recurring) project was completed
|
102
104
|
type: date
|
105
|
+
"dropped_on":
|
106
|
+
desc: When the project was dropped
|
107
|
+
type: date
|
108
|
+
"paused_on":
|
109
|
+
desc: When the project was paused
|
110
|
+
type: date
|
103
111
|
"created_on":
|
104
112
|
desc: When the project was created
|
105
113
|
type: date
|
data/lib/plansheet/project.rb
CHANGED
@@ -4,6 +4,7 @@ require "yaml"
|
|
4
4
|
require "date"
|
5
5
|
require_relative "project/yaml"
|
6
6
|
require_relative "project/stringify"
|
7
|
+
require_relative "./time"
|
7
8
|
|
8
9
|
# Needed for Project#time_estimate, would be much happier *not* patching Array
|
9
10
|
class Array
|
@@ -20,8 +21,9 @@ module Plansheet
|
|
20
21
|
"waiting" => 4,
|
21
22
|
"planning" => 5,
|
22
23
|
"idea" => 6,
|
23
|
-
"
|
24
|
-
"
|
24
|
+
"paused" => 7,
|
25
|
+
"dropped" => 8,
|
26
|
+
"done" => 9
|
25
27
|
}.freeze
|
26
28
|
|
27
29
|
# Pre-compute the next days-of-week
|
@@ -30,36 +32,6 @@ module Plansheet
|
|
30
32
|
[d.strftime("%A"), d]
|
31
33
|
end.freeze
|
32
34
|
|
33
|
-
def self.parse_date_duration(str)
|
34
|
-
return Regexp.last_match(1).to_i if str.strip.match(/(\d+)[dD]/)
|
35
|
-
return (Regexp.last_match(1).to_i * 7) if str.strip.match(/(\d+)[wW]/)
|
36
|
-
|
37
|
-
raise "Can't parse time duration string #{str}"
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.parse_time_duration(str)
|
41
|
-
if str.match(/(\d+h) (\d+m)/)
|
42
|
-
return (parse_time_duration(Regexp.last_match(1)) + parse_time_duration(Regexp.last_match(2)))
|
43
|
-
end
|
44
|
-
|
45
|
-
return Regexp.last_match(1).to_i if str.strip.match(/(\d+)m/)
|
46
|
-
return (Regexp.last_match(1).to_f * 60).to_i if str.strip.match(/(\d+\.?\d*)h/)
|
47
|
-
|
48
|
-
raise "Can't parse time duration string #{str}"
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.build_time_duration(minutes)
|
52
|
-
if minutes > 59
|
53
|
-
if (minutes % 60).zero?
|
54
|
-
"#{minutes / 60}h"
|
55
|
-
else
|
56
|
-
"#{minutes / 60}h #{minutes % 60}m"
|
57
|
-
end
|
58
|
-
else
|
59
|
-
"#{minutes}m"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
35
|
# The use of instance_variable_set/get probably seems a bit weird, but the
|
64
36
|
# intent is to avoid object allocation on non-existent project properties, as
|
65
37
|
# well as avoiding a bunch of copy-paste boilerplate when adding a new
|
@@ -68,6 +40,7 @@ module Plansheet
|
|
68
40
|
# unwrap the loops if it's not needed.
|
69
41
|
class Project
|
70
42
|
include Comparable
|
43
|
+
include Plansheet::TimeUtils
|
71
44
|
|
72
45
|
TIME_EST_REGEX = /\((\d+\.?\d*[mMhH])\)$/.freeze
|
73
46
|
TIME_EST_REGEX_NO_CAPTURE = /\(\d+\.?\d*[mMhH]\)$/.freeze
|
@@ -83,7 +56,8 @@ module Plansheet
|
|
83
56
|
# namespace is derived from file name
|
84
57
|
STRING_PROPERTIES = %w[priority status location notes time_estimate daily_time_roi weekly_time_roi yearly_time_roi
|
85
58
|
day_of_week frequency last_for lead_time].freeze
|
86
|
-
DATE_PROPERTIES = %w[due defer completed_on created_on starts_on last_done
|
59
|
+
DATE_PROPERTIES = %w[due defer paused_on dropped_on completed_on created_on starts_on last_done
|
60
|
+
last_reviewed].freeze
|
87
61
|
ARRAY_PROPERTIES = %w[dependencies externals urls tasks done tags].freeze
|
88
62
|
|
89
63
|
ALL_PROPERTIES = STRING_PROPERTIES + DATE_PROPERTIES + ARRAY_PROPERTIES
|
@@ -132,15 +106,15 @@ module Plansheet
|
|
132
106
|
if @tasks
|
133
107
|
@time_estimate_minutes = @tasks&.select do |t|
|
134
108
|
t.match? TIME_EST_REGEX_NO_CAPTURE
|
135
|
-
end&.nil_if_empty&.map { |t|
|
109
|
+
end&.nil_if_empty&.map { |t| task_time_estimate(t) }&.sum
|
136
110
|
elsif @time_estimate
|
137
111
|
# No tasks with estimates, but there's an explicit time_estimate
|
138
112
|
# Convert the field to minutes
|
139
|
-
@time_estimate_minutes =
|
113
|
+
@time_estimate_minutes = parse_time_duration(@time_estimate)
|
140
114
|
end
|
141
115
|
if @time_estimate_minutes
|
142
116
|
# Rewrite time_estimate field
|
143
|
-
@time_estimate =
|
117
|
+
@time_estimate = build_time_duration(@time_estimate_minutes)
|
144
118
|
|
145
119
|
yms = yearly_minutes_saved
|
146
120
|
@time_roi_payoff = yms.to_f / @time_estimate_minutes if yms
|
@@ -152,20 +126,26 @@ module Plansheet
|
|
152
126
|
remove_instance_variable("@time_estimate") if @time_estimate
|
153
127
|
remove_instance_variable("@time_estimate_minutes") if @time_estimate
|
154
128
|
remove_instance_variable("@time_roi_payoff") if @time_roi_payoff
|
129
|
+
elsif paused?
|
130
|
+
@paused_on ||= Date.today
|
131
|
+
remove_instance_variable("@status") if @status
|
132
|
+
elsif dropped?
|
133
|
+
@dropped_on ||= Date.today
|
134
|
+
remove_instance_variable("@status") if @status
|
155
135
|
end
|
156
136
|
end
|
157
137
|
|
158
|
-
def
|
159
|
-
@completed_on&.strftime("%Y-%m")
|
138
|
+
def archive_month
|
139
|
+
@completed_on&.strftime("%Y-%m") || Date.today.strftime("%Y-%m")
|
160
140
|
end
|
161
141
|
|
162
142
|
def yearly_minutes_saved
|
163
143
|
if @daily_time_roi
|
164
|
-
|
144
|
+
parse_time_duration(@daily_time_roi) * 365
|
165
145
|
elsif @weekly_time_roi
|
166
|
-
|
146
|
+
parse_time_duration(@weekly_time_roi) * 52
|
167
147
|
elsif @yearly_time_roi
|
168
|
-
|
148
|
+
parse_time_duration(@yearly_time_roi)
|
169
149
|
end
|
170
150
|
end
|
171
151
|
|
@@ -253,14 +233,16 @@ module Plansheet
|
|
253
233
|
# Projects that are dropped or done are considered "complete", insofar as
|
254
234
|
# they are only kept around for later reference.
|
255
235
|
def compare_completeness(other)
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
236
|
+
retval = 0
|
237
|
+
retval += 1 if dropped_or_done?
|
238
|
+
retval -= 1 if other.dropped_or_done?
|
239
|
+
retval
|
260
240
|
end
|
261
241
|
|
262
242
|
def status
|
263
243
|
return @status if @status
|
244
|
+
return "dropped" if @dropped_on
|
245
|
+
return "paused" if @paused_on
|
264
246
|
return recurring_status if recurring?
|
265
247
|
return task_based_status if @tasks || @done
|
266
248
|
return "done" if @completed_on && @tasks.nil?
|
@@ -309,7 +291,7 @@ module Plansheet
|
|
309
291
|
|
310
292
|
def recurring_due_date
|
311
293
|
if @last_done
|
312
|
-
return @last_done +
|
294
|
+
return @last_done + parse_date_duration(@frequency) if @frequency
|
313
295
|
|
314
296
|
if @day_of_week
|
315
297
|
return Date.today + 7 if @last_done == Date.today
|
@@ -332,13 +314,13 @@ module Plansheet
|
|
332
314
|
end
|
333
315
|
|
334
316
|
def last_for_deferral
|
335
|
-
return @last_done +
|
317
|
+
return @last_done + parse_date_duration(@last_for) if @last_done
|
336
318
|
|
337
319
|
Date.today
|
338
320
|
end
|
339
321
|
|
340
322
|
def lead_time_deferral
|
341
|
-
[(due -
|
323
|
+
[(due - parse_date_duration(@lead_time)),
|
342
324
|
Date.today].max
|
343
325
|
end
|
344
326
|
|
@@ -350,20 +332,28 @@ module Plansheet
|
|
350
332
|
!@frequency.nil? || !@day_of_week.nil? || !@last_done.nil? || !@last_for.nil?
|
351
333
|
end
|
352
334
|
|
353
|
-
def
|
354
|
-
status == "
|
335
|
+
def paused?
|
336
|
+
status == "paused"
|
337
|
+
end
|
338
|
+
|
339
|
+
def dropped?
|
340
|
+
status == "dropped"
|
355
341
|
end
|
356
342
|
|
357
343
|
def done?
|
358
344
|
status == "done"
|
359
345
|
end
|
360
346
|
|
347
|
+
def dropped_or_done?
|
348
|
+
dropped? || done?
|
349
|
+
end
|
350
|
+
|
361
351
|
def archivable?
|
362
|
-
!recurring? && @completed_on
|
352
|
+
(!recurring? && @completed_on) || dropped?
|
363
353
|
end
|
364
354
|
|
365
|
-
def
|
366
|
-
|
355
|
+
def task_time_estimate(str)
|
356
|
+
parse_time_duration(Regexp.last_match(1)) if str.match(TIME_EST_REGEX)
|
367
357
|
end
|
368
358
|
|
369
359
|
def to_h
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "date"
|
4
4
|
module Plansheet
|
5
5
|
# The Sheet class constructs a Markdown/LaTeX file for use with pandoc
|
6
|
-
class
|
6
|
+
class LaTeXSheet
|
7
7
|
def initialize(output_file, project_arr)
|
8
8
|
projects_str = String.new
|
9
9
|
projects_str << sheet_header
|
@@ -12,20 +12,64 @@ module Plansheet
|
|
12
12
|
projects_str << project_minipage(p)
|
13
13
|
end
|
14
14
|
puts "Writing to #{output_file}"
|
15
|
+
projects_str << sheet_footer
|
15
16
|
File.write(output_file, projects_str)
|
16
17
|
end
|
17
18
|
|
18
19
|
def sheet_header
|
20
|
+
# LaTeX used to be generated by pandoc
|
19
21
|
<<~FRONTMATTER
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
\\PassOptionsToPackage{unicode}{hyperref}
|
23
|
+
\\PassOptionsToPackage{hyphens}{url}
|
24
|
+
\\documentclass[]{article}
|
25
|
+
\\author{}
|
26
|
+
\\date{#{Date.today}}
|
27
|
+
\\usepackage{amsmath,amssymb}
|
28
|
+
\\usepackage{lmodern}
|
29
|
+
\\usepackage{iftex}
|
30
|
+
\\usepackage[T1]{fontenc}
|
31
|
+
\\usepackage[utf8]{inputenc}
|
32
|
+
\\usepackage{textcomp}
|
33
|
+
\\IfFileExists{upquote.sty}{\\usepackage{upquote}}{}
|
34
|
+
\\IfFileExists{microtype.sty}{
|
35
|
+
\\usepackage[]{microtype}
|
36
|
+
\\UseMicrotypeSet[protrusion]{basicmath}
|
37
|
+
}{}
|
38
|
+
\\makeatletter
|
39
|
+
\\@ifundefined{KOMAClassName}{
|
40
|
+
\\IfFileExists{parskip.sty}{
|
41
|
+
\\usepackage{parskip}
|
42
|
+
}{
|
43
|
+
\\setlength{\\parindent}{0pt}
|
44
|
+
\\setlength{\\parskip}{6pt plus 2pt minus 1pt}}
|
45
|
+
}{
|
46
|
+
\\KOMAoptions{parskip=half}}
|
47
|
+
\\makeatother
|
48
|
+
\\usepackage{xcolor}
|
49
|
+
\\IfFileExists{xurl.sty}{\\usepackage{xurl}}{}
|
50
|
+
\\IfFileExists{bookmark.sty}{\\usepackage{bookmark}}{\\usepackage{hyperref}}
|
51
|
+
\\hypersetup{
|
52
|
+
hidelinks,
|
53
|
+
pdfcreator={LaTeX via plansheet}}
|
54
|
+
\\urlstyle{same}
|
55
|
+
\\usepackage[margin=1.5cm]{geometry}
|
56
|
+
\\setlength{\\emergencystretch}{3em}
|
57
|
+
\\providecommand{\\tightlist}{
|
58
|
+
\\setlength{\\itemsep}{0pt}\\setlength{\\parskip}{0pt}}
|
59
|
+
\\setcounter{secnumdepth}{-\\maxdimen}
|
60
|
+
|
61
|
+
\\begin{document}
|
62
|
+
|
23
63
|
\\thispagestyle{empty}
|
24
64
|
|
25
|
-
|
65
|
+
\\section{Date: #{Date.today}}
|
26
66
|
FRONTMATTER
|
27
67
|
end
|
28
68
|
|
69
|
+
def sheet_footer
|
70
|
+
'\end{document}'
|
71
|
+
end
|
72
|
+
|
29
73
|
def project_minipage(proj)
|
30
74
|
str = String.new
|
31
75
|
str << "\\begin{minipage}{6cm}\n"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plansheet
|
4
|
+
module TimeUtils
|
5
|
+
def parse_date_duration(str)
|
6
|
+
return Regexp.last_match(1).to_i if str.strip.match(/(\d+)[dD]/)
|
7
|
+
return (Regexp.last_match(1).to_i * 7) if str.strip.match(/(\d+)[wW]/)
|
8
|
+
|
9
|
+
raise "Can't parse time duration string #{str}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse_time_duration(str)
|
13
|
+
if str.match(/(\d+h) (\d+m)/)
|
14
|
+
return (parse_time_duration(Regexp.last_match(1)) + parse_time_duration(Regexp.last_match(2)))
|
15
|
+
end
|
16
|
+
|
17
|
+
return Regexp.last_match(1).to_i if str.strip.match(/(\d+)m/)
|
18
|
+
return (Regexp.last_match(1).to_f * 60).to_i if str.strip.match(/(\d+\.?\d*)h/)
|
19
|
+
|
20
|
+
raise "Can't parse time duration string #{str}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def build_time_duration(minutes)
|
24
|
+
if minutes > 59
|
25
|
+
if (minutes % 60).zero?
|
26
|
+
"#{minutes / 60}h"
|
27
|
+
else
|
28
|
+
"#{minutes / 60}h #{minutes % 60}m"
|
29
|
+
end
|
30
|
+
else
|
31
|
+
"#{minutes}m"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
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.30.2
|
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-07-
|
11
|
+
date: 2022-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dc-kwalify
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- lib/plansheet/project/yaml.rb
|
78
78
|
- lib/plansheet/sheet.rb
|
79
79
|
- lib/plansheet/sheet/latex.rb
|
80
|
+
- lib/plansheet/time.rb
|
80
81
|
- lib/plansheet/version.rb
|
81
82
|
homepage: https://dafyddcrosby.com
|
82
83
|
licenses:
|