plansheet 0.24.1 → 0.27.0

Sign up to get free protection for your applications and to get access to all the features.
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: