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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e590a914b9d074b7ce833d04aac6e5320684c15a398c1c88d5774ae884cad297
4
- data.tar.gz: 7e7d5004b022e6286e0e7792b9392b95c1536c398c7915a2534ead4dbf364080
3
+ metadata.gz: a918dce2e7ab41c30070205e2dcb75679696047b231e0a0999ed0a940fa3aea8
4
+ data.tar.gz: 5cec12572afb320929ff5efd951bf9f31e69aeff53d35e4886ea17d6891ad30c
5
5
  SHA512:
6
- metadata.gz: 4f03c2eaa802dd20a82180ab4a3ad2b23b2a3b69d6e7536f81a42305a3aab486175e18c767a9cb5b919a7c8d5d7e5a22032253e19b95d47d19c64a4583fd150f
7
- data.tar.gz: 6a1923c54d08ba01d33dbfb8d0c76b813fb57b65edba8d79fd002db43b9c63299a301c763acb5084ab5d1e9a773af24be12b15d5f5c2d4fad3b009f820939cfa
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 "guard", "~> 2.18"
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.27.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.15.0)
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.4.0)
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.29.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.17.0, < 2.0)
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.18.0)
85
+ rubocop-ast (1.19.1)
73
86
  parser (>= 3.1.1.0)
74
- rubocop-minitest (0.20.0)
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.1.0)
97
+ unicode-display_width (2.2.0)
85
98
 
86
99
  PLATFORMS
87
100
  x86_64-linux
88
101
 
89
102
  DEPENDENCIES
90
- guard (~> 2.18)
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
- structured printout (a la [pandoc](https://pandoc.org)) from a
5
- pile of formatted [YAML](https://yaml.org) files. Still very much a work-in-progress
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
- Dir.mkdir config["output_dir"] unless Dir.exist? config["output_dir"]
50
- Plansheet::Sheet.new("#{config["output_dir"]}/projects.md", pool.projects)
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
- puts "combined time estimate: #{pool.projects.sum { |x| x.time_estimate_minutes || 0 }} minutes"
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
@@ -79,14 +79,14 @@ module Plansheet
79
79
 
80
80
  def archive_projects
81
81
  archive_dir = "#{@projects_dir}/archive/"
82
- Dir.mkdir archive_dir unless Dir.exist? archive_dir
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.completed_on_month}/#{project.namespace}.yml"
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
@@ -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
- "dropped" => 7,
24
- "done" => 8
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 last_reviewed].freeze
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| Plansheet::Project.task_time_estimate(t) }&.sum
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 = Plansheet.parse_time_duration(@time_estimate)
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 = Plansheet.build_time_duration(@time_estimate_minutes)
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 completed_on_month
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
- Plansheet.parse_time_duration(@daily_time_roi) * 365
144
+ parse_time_duration(@daily_time_roi) * 365
165
145
  elsif @weekly_time_roi
166
- Plansheet.parse_time_duration(@weekly_time_roi) * 52
146
+ parse_time_duration(@weekly_time_roi) * 52
167
147
  elsif @yearly_time_roi
168
- Plansheet.parse_time_duration(@yearly_time_roi)
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
- return 0 if dropped_or_done? && other.dropped_or_done?
257
- return 0 if !dropped_or_done? && !other.dropped_or_done?
258
-
259
- dropped_or_done? ? 1 : -1
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 + Plansheet.parse_date_duration(@frequency) if @frequency
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 + Plansheet.parse_date_duration(@last_for) if @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 - Plansheet.parse_date_duration(@lead_time)),
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 dropped_or_done?
354
- status == "dropped" || status == "done"
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 self.task_time_estimate(str)
366
- Plansheet.parse_time_duration(Regexp.last_match(1)) if str.match(TIME_EST_REGEX)
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 Sheet
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
- geometry: margin=1.5cm
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
- # Date: #{Date.today}
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Plansheet
4
- VERSION = "0.27.0"
4
+ VERSION = "0.30.2"
5
5
  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.27.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-15 00:00:00.000000000 Z
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: