plansheet 0.24.1 → 0.27.0

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: fae1a985802ab6da236f5f3d54224b4468ae5cc5e6beba86510293e7a92fe0a8
4
- data.tar.gz: b2fe70fc1cb48dd794f561ff5037d2311366bb7e64c901cf4f32d33c0592e45b
3
+ metadata.gz: e590a914b9d074b7ce833d04aac6e5320684c15a398c1c88d5774ae884cad297
4
+ data.tar.gz: 7e7d5004b022e6286e0e7792b9392b95c1536c398c7915a2534ead4dbf364080
5
5
  SHA512:
6
- metadata.gz: f68b48246ce4e8b10c66dd805f783fd822886b168aa57bb7db71012934bd2f21f8a76c8f26dab6c8bf4f8156529b897a5d33c02cba2e67d39224579626ee00a0
7
- data.tar.gz: 751a5a7beffa1c35d7723521db61aa0ca26d1fafb6643017bfd2c3a3cb14e63dd5d4f775df34e2a9b644c24a8d582e0cd19d9cfd4a3b009673b9fb6d70faef55
6
+ metadata.gz: 4f03c2eaa802dd20a82180ab4a3ad2b23b2a3b69d6e7536f81a42305a3aab486175e18c767a9cb5b919a7c8d5d7e5a22032253e19b95d47d19c64a4583fd150f
7
+ data.tar.gz: 6a1923c54d08ba01d33dbfb8d0c76b813fb57b65edba8d79fd002db43b9c63299a301c763acb5084ab5d1e9a773af24be12b15d5f5c2d4fad3b009f820939cfa
data/.rubocop.yml CHANGED
@@ -11,6 +11,9 @@ Style/StringLiteralsInInterpolation:
11
11
  Enabled: true
12
12
  EnforcedStyle: double_quotes
13
13
 
14
+ Style/GuardClause:
15
+ Enabled: false
16
+
14
17
  Layout/LineLength:
15
18
  Max: 120
16
19
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- plansheet (0.24.1)
4
+ plansheet (0.27.0)
5
5
  dc-kwalify (~> 1.0)
6
6
  diffy (= 3.4.2)
7
7
  rgl (= 0.5.8)
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pathname"
3
4
  require "rgl/adjacency"
4
5
  require "rgl/topsort"
5
6
 
@@ -23,7 +24,6 @@ module Plansheet
23
24
  def initialize(config, debug: false)
24
25
  @projects_dir = config[:projects_dir]
25
26
  @sort_order = config[:sort_order]
26
- # @completed_projects_dir = config(:completed_projects_dir)
27
27
 
28
28
  # This bit of trickiness is because we don't know what the sort order is
29
29
  # until runtime. I'm sure this design decision definitely won't bite me
@@ -77,6 +77,25 @@ module Plansheet
77
77
  @projects.sort!
78
78
  end
79
79
 
80
+ def archive_projects
81
+ archive_dir = "#{@projects_dir}/archive/"
82
+ Dir.mkdir archive_dir unless Dir.exist? archive_dir
83
+
84
+ # NOTE: It would save writes if we sorted and did all month/namespaces
85
+ # writes only once, but as the normal case only sees a few projects
86
+ # archived at a time, I'll leave that for someone else to implement ;-)
87
+ projects_to_archive = @projects.select(&:archivable?)
88
+ projects_to_archive.each do |project|
89
+ path = Pathname.new "#{archive_dir}/#{project.completed_on_month}/#{project.namespace}.yml"
90
+ Dir.mkdir path.dirname unless path.dirname.exist?
91
+ pyf = ProjectYAMLFile.new path
92
+ pyf.append_project project
93
+ end
94
+
95
+ # Now that the projects have been archived, remove them from the pool
96
+ @projects.reject!(&:archivable?)
97
+ end
98
+
80
99
  def project_namespaces
81
100
  @projects.collect(&:namespace).uniq.sort
82
101
  end
@@ -86,12 +105,16 @@ module Plansheet
86
105
  end
87
106
 
88
107
  def write_projects
89
- # TODO: This leaves potential for duplicate projects where empty files
90
- # are involved once completed project directories are a thing - will need
91
- # to keep a list of project files to delete
92
- project_namespaces.each do |ns|
108
+ # Collect the namespaces *before* archiving is done, for the case where
109
+ # all active projects in a namespace have been completed and archived
110
+ namespaces_before_archiving = project_namespaces
111
+
112
+ archive_projects
113
+
114
+ namespaces_before_archiving.each do |ns|
115
+ projects = projects_in_namespace(ns)
93
116
  pyf = ProjectYAMLFile.new "#{@projects_dir}/#{ns}.yml"
94
- pyf.compare_and_write projects_in_namespace(ns)
117
+ pyf.compare_and_write projects
95
118
  end
96
119
  end
97
120
 
@@ -4,6 +4,7 @@ require "yaml"
4
4
  require "date"
5
5
  require "pathname"
6
6
 
7
+ require "diffy"
7
8
  require "kwalify"
8
9
 
9
10
  module Plansheet
@@ -152,7 +153,13 @@ module Plansheet
152
153
  # TODO: this won't GC, inline validation instead?
153
154
  end
154
155
 
156
+ def stub_file
157
+ File.write @path, YAML.dump([])
158
+ end
159
+
155
160
  def load_file
161
+ stub_file unless File.exist? @path
162
+
156
163
  # Handle pre-Ruby 3.1 psych versions (this is brittle)
157
164
  @raw = if Psych::VERSION.split(".")[0].to_i >= 4
158
165
  YAML.load_file(@path, permitted_classes: [Date])
@@ -189,16 +196,22 @@ module Plansheet
189
196
  @projects.sort!
190
197
  end
191
198
 
199
+ def append_project(project)
200
+ load_file
201
+ compare_and_write(@projects.append(project))
202
+ end
203
+
192
204
  def compare_and_write(projects)
205
+ load_file
206
+ orig_string = yaml_dump(@projects)
193
207
  updated_projects_string = yaml_dump(projects)
194
208
 
195
209
  # Compare the existing file to the newly generated one - we only want a
196
210
  # write if something has changed
197
- return if updated_projects_string == yaml_dump(load_file)
211
+ return if updated_projects_string == orig_string
198
212
 
199
213
  puts "#{@path} has changed, writing"
200
- require "diffy"
201
- puts Diffy::Diff.new(updated_projects_string, yaml_dump(load_file)).to_s(:color)
214
+ puts Diffy::Diff.new(orig_string, updated_projects_string).to_s(:color)
202
215
  File.write @path, updated_projects_string
203
216
  end
204
217
 
@@ -138,13 +138,25 @@ module Plansheet
138
138
  # Convert the field to minutes
139
139
  @time_estimate_minutes = Plansheet.parse_time_duration(@time_estimate)
140
140
  end
141
- if @time_estimate_minutes # rubocop:disable Style/GuardClause
141
+ if @time_estimate_minutes
142
142
  # Rewrite time_estimate field
143
143
  @time_estimate = Plansheet.build_time_duration(@time_estimate_minutes)
144
144
 
145
145
  yms = yearly_minutes_saved
146
146
  @time_roi_payoff = yms.to_f / @time_estimate_minutes if yms
147
147
  end
148
+
149
+ if done?
150
+ @completed_on ||= Date.today unless recurring?
151
+ remove_instance_variable("@status") if @status
152
+ remove_instance_variable("@time_estimate") if @time_estimate
153
+ remove_instance_variable("@time_estimate_minutes") if @time_estimate
154
+ remove_instance_variable("@time_roi_payoff") if @time_roi_payoff
155
+ end
156
+ end
157
+
158
+ def completed_on_month
159
+ @completed_on&.strftime("%Y-%m")
148
160
  end
149
161
 
150
162
  def yearly_minutes_saved
@@ -287,11 +299,6 @@ module Plansheet
287
299
  task_based_status
288
300
  end
289
301
 
290
- def process_recurring
291
- # TODO: Tasks will be moved from done->tasks if recurring project is
292
- # starting again
293
- end
294
-
295
302
  # Due date either explicit or recurring
296
303
  def due
297
304
  return @due if @due
@@ -326,6 +333,8 @@ module Plansheet
326
333
 
327
334
  def last_for_deferral
328
335
  return @last_done + Plansheet.parse_date_duration(@last_for) if @last_done
336
+
337
+ Date.today
329
338
  end
330
339
 
331
340
  def lead_time_deferral
@@ -338,13 +347,21 @@ module Plansheet
338
347
  end
339
348
 
340
349
  def recurring?
341
- !@frequency.nil? || !@day_of_week.nil? || !@last_done.nil?
350
+ !@frequency.nil? || !@day_of_week.nil? || !@last_done.nil? || !@last_for.nil?
342
351
  end
343
352
 
344
353
  def dropped_or_done?
345
354
  status == "dropped" || status == "done"
346
355
  end
347
356
 
357
+ def done?
358
+ status == "done"
359
+ end
360
+
361
+ def archivable?
362
+ !recurring? && @completed_on
363
+ end
364
+
348
365
  def self.task_time_estimate(str)
349
366
  Plansheet.parse_time_duration(Regexp.last_match(1)) if str.match(TIME_EST_REGEX)
350
367
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ module Plansheet
5
+ # The Sheet class constructs a Markdown/LaTeX file for use with pandoc
6
+ class Sheet
7
+ def initialize(output_file, project_arr)
8
+ projects_str = String.new
9
+ projects_str << sheet_header
10
+
11
+ project_arr.each do |p|
12
+ projects_str << project_minipage(p)
13
+ end
14
+ puts "Writing to #{output_file}"
15
+ File.write(output_file, projects_str)
16
+ end
17
+
18
+ def sheet_header
19
+ <<~FRONTMATTER
20
+ ---
21
+ geometry: margin=1.5cm
22
+ ---
23
+ \\thispagestyle{empty}
24
+
25
+ # Date: #{Date.today}
26
+ FRONTMATTER
27
+ end
28
+
29
+ def project_minipage(proj)
30
+ str = String.new
31
+ str << "\\begin{minipage}{6cm}\n"
32
+ str << project_header(proj)
33
+ proj&.tasks&.each do |t|
34
+ str << "$\\square$ #{sanitize_string(t)} \\\\\n"
35
+ end
36
+ str << "\\end{minipage}\n"
37
+ str
38
+ end
39
+
40
+ def sanitize_string(str)
41
+ str.gsub("_", '\_')
42
+ end
43
+
44
+ def project_header(proj)
45
+ str = String.new
46
+ str << "#{sanitize_string(proj.namespace)}: #{sanitize_string(proj.name)}\\\\\n"
47
+ str << proj.status.to_s
48
+ str << " - #{sanitize_string(proj.location)}" if proj.location
49
+ str << " due: #{proj.due}" if proj.due
50
+ str << " time: #{proj.time_estimate}" if proj.time_estimate
51
+ str << " \\\\\n"
52
+ str
53
+ end
54
+ end
55
+ end
@@ -1,55 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "date"
4
- module Plansheet
5
- # The Sheet class constructs a Markdown/LaTeX file for use with pandoc
6
- class Sheet
7
- def initialize(output_file, project_arr)
8
- projects_str = String.new
9
- projects_str << sheet_header
10
-
11
- project_arr.each do |p|
12
- projects_str << project_minipage(p)
13
- end
14
- puts "Writing to #{output_file}"
15
- File.write(output_file, projects_str)
16
- end
17
-
18
- def sheet_header
19
- <<~FRONTMATTER
20
- ---
21
- geometry: margin=1.5cm
22
- ---
23
- \\thispagestyle{empty}
24
-
25
- # Date: #{Date.today}
26
- FRONTMATTER
27
- end
28
-
29
- def project_minipage(proj)
30
- str = String.new
31
- str << "\\begin{minipage}{6cm}\n"
32
- str << project_header(proj)
33
- proj&.tasks&.each do |t|
34
- str << "$\\square$ #{sanitize_string(t)} \\\\\n"
35
- end
36
- str << "\\end{minipage}\n"
37
- str
38
- end
39
-
40
- def sanitize_string(str)
41
- str.gsub("_", '\_')
42
- end
43
-
44
- def project_header(proj)
45
- str = String.new
46
- str << "#{sanitize_string(proj.namespace)}: #{sanitize_string(proj.name)}\\\\\n"
47
- str << proj.status.to_s
48
- str << " - #{sanitize_string(proj.location)}" if proj.location
49
- str << " due: #{proj.due}" if proj.due
50
- str << " time: #{proj.time_estimate}" if proj.time_estimate
51
- str << " \\\\\n"
52
- str
53
- end
54
- end
55
- end
3
+ require_relative "sheet/latex"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Plansheet
4
- VERSION = "0.24.1"
4
+ VERSION = "0.27.0"
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.24.1
4
+ version: 0.27.0
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 00:00:00.000000000 Z
11
+ date: 2022-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dc-kwalify
@@ -76,6 +76,7 @@ files:
76
76
  - lib/plansheet/project/stringify.rb
77
77
  - lib/plansheet/project/yaml.rb
78
78
  - lib/plansheet/sheet.rb
79
+ - lib/plansheet/sheet/latex.rb
79
80
  - lib/plansheet/version.rb
80
81
  homepage: https://dafyddcrosby.com
81
82
  licenses: