Class: Plansheet::Project
- Inherits:
-
Object
- Object
- Plansheet::Project
show all
- Includes:
- Comparable, TimeUtils
- Defined in:
- lib/plansheet/project.rb,
lib/plansheet/project/stringify.rb
Overview
The use of instance_variable_set/get probably seems a bit weird, but the intent is to avoid object allocation on non-existent project properties, as well as avoiding a bunch of copy-paste boilerplate when adding a new property. I suspect I’m guilty of premature optimization here, but it’s easier to do this at the start than untangle that later (ie easier to unwrap the loops if it’s not needed.
Constant Summary
collapse
- TIME_EST_REGEX =
/\((\d+\.?\d*[mMhH])\)$/.freeze
- TIME_EST_REGEX_NO_CAPTURE =
/\(\d+\.?\d*[mMhH]\)$/.freeze
- PROJECT_PRIORITY =
{
"high" => 1,
"medium" => 2,
"low" => 3
}.freeze
- COMPARISON_ORDER_SYMS =
Plansheet::Pool::POOL_COMPARISON_ORDER.map { |x| "compare_#{x}".to_sym }.freeze
- STRING_PROPERTIES =
NOTE: The order of these affects presentation! namespace is derived from file name
%w[priority status location notes time_estimate daily_time_roi weekly_time_roi yearly_time_roi
day_of_week frequency last_for lead_time].freeze
- DATE_PROPERTIES =
%w[due defer paused_on dropped_on completed_on created_on starts_on last_done
last_reviewed].freeze
- ARRAY_PROPERTIES =
%w[dependencies externals urls tasks setup_tasks cleanup_tasks done tags].freeze
- ALL_PROPERTIES =
STRING_PROPERTIES + DATE_PROPERTIES + ARRAY_PROPERTIES
Instance Attribute Summary collapse
Instance Method Summary
collapse
Methods included from TimeUtils
#build_time_duration, #parse_date_duration, #parse_time_duration
Constructor Details
#initialize(options) ⇒ Project
Returns a new instance of Project.
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
# File 'lib/plansheet/project.rb', line 71
def initialize(options)
@name = options["project"]
@namespace = options["namespace"]
ALL_PROPERTIES.each do |o|
instance_variable_set("@#{o}", options[o]) if options[o]
end
@priority_val = if @priority
PROJECT_PRIORITY[@priority]
else
PROJECT_PRIORITY["low"]
end
remove_instance_variable("@defer") if @defer && (@defer < Date.today)
instance_variable_set("@created_on", Date.today) unless @created_on
if @tasks
@tasks.compact!
remove_instance_variable("@tasks") if @tasks.empty?
end
if @tasks
@time_estimate_minutes = @tasks&.select do |t|
t.match? TIME_EST_REGEX_NO_CAPTURE
end&.nil_if_empty&.map { |t| task_time_estimate(t) }&.sum
elsif @time_estimate
@time_estimate_minutes = parse_time_duration(@time_estimate)
end
if @time_estimate_minutes
@time_estimate = build_time_duration(@time_estimate_minutes)
yms = yearly_minutes_saved
@time_roi_payoff = yms.to_f / @time_estimate_minutes if yms
end
if done?
remove_instance_variable("@status") if @status
unless recurring?
@completed_on ||= Date.today
remove_instance_variable("@time_estimate") if @time_estimate
remove_instance_variable("@time_estimate_minutes") if @time_estimate
remove_instance_variable("@time_roi_payoff") if @time_roi_payoff
end
elsif paused?
@paused_on ||= Date.today
remove_instance_variable("@status") if @status
elsif dropped?
@dropped_on ||= Date.today
remove_instance_variable("@status") if @status
end
end
|
Instance Attribute Details
#namespace ⇒ Object
Returns the value of attribute namespace.
69
70
71
|
# File 'lib/plansheet/project.rb', line 69
def namespace
@namespace
end
|
Instance Method Details
#<=>(other) ⇒ Object
157
158
159
160
161
162
163
164
|
# File 'lib/plansheet/project.rb', line 157
def <=>(other)
ret_val = 0
COMPARISON_ORDER_SYMS.each do |method|
ret_val = send(method, other)
break if ret_val != 0
end
ret_val
end
|
#archivable? ⇒ Boolean
350
351
352
|
# File 'lib/plansheet/project.rb', line 350
def archivable?
(!recurring? && @completed_on) || dropped?
end
|
#archive_month ⇒ Object
143
144
145
|
# File 'lib/plansheet/project.rb', line 143
def archive_month
@completed_on&.strftime("%Y-%m") || Date.today.strftime("%Y-%m")
end
|
#compare_completed_on(other) ⇒ Object
188
189
190
191
192
193
194
|
# File 'lib/plansheet/project.rb', line 188
def compare_completed_on(other)
retval = 0
retval += 1 if @completed_on
retval -= 1 if other.completed_on
retval = (other.completed_on <=> @completed_on) if retval.zero?
retval
end
|
#compare_completeness(other) ⇒ Object
Projects that are dropped or done are considered “complete”, insofar as they are only kept around for later reference.
240
241
242
243
244
245
|
# File 'lib/plansheet/project.rb', line 240
def compare_completeness(other)
retval = 0
retval += 1 if dropped_or_done?
retval -= 1 if other.dropped_or_done?
retval
end
|
#compare_defer(other) ⇒ Object
211
212
213
214
215
|
# File 'lib/plansheet/project.rb', line 211
def compare_defer(other)
receiver = @defer.nil? || @defer < Date.today ? Date.today : @defer
comparison = other.defer.nil? || other.defer < Date.today ? Date.today : other.defer
receiver <=> comparison
end
|
#compare_dependency(other) ⇒ Object
229
230
231
232
233
234
235
236
|
# File 'lib/plansheet/project.rb', line 229
def compare_dependency(other)
retval = 0
retval -= 1 if dependency_of?(other)
retval += 1 if dependent_on?(other)
retval
end
|
#compare_due(other) ⇒ Object
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
# File 'lib/plansheet/project.rb', line 196
def compare_due(other)
if @due.nil?
return 0 if other.due.nil?
return 1
elsif other.due.nil?
return -1
end
@due <=> other.due
end
|
#compare_name(other) ⇒ Object
This seems silly at first glance, but it’s to keep projects from flipping around on each sort when they are equal in all other respects
184
185
186
|
# File 'lib/plansheet/project.rb', line 184
def compare_name(other)
@name <=> other.name
end
|
#compare_priority(other) ⇒ Object
166
167
168
|
# File 'lib/plansheet/project.rb', line 166
def compare_priority(other)
priority_val <=> other.priority_val
end
|
#compare_status(other) ⇒ Object
#compare_time_roi(other) ⇒ Object
174
175
176
|
# File 'lib/plansheet/project.rb', line 174
def compare_time_roi(other)
other.time_roi_payoff <=> time_roi_payoff
end
|
#defer ⇒ Object
313
314
315
316
317
318
319
|
# File 'lib/plansheet/project.rb', line 313
def defer
return @defer if @defer
return lead_time_deferral if @lead_time && due
return last_for_deferral if @last_for
nil
end
|
#dependency_of?(other) ⇒ Boolean
217
218
219
220
221
|
# File 'lib/plansheet/project.rb', line 217
def dependency_of?(other)
other&.dependencies&.any? do |dep|
@name&.downcase == dep.downcase
end
end
|
#dependent_on?(other) ⇒ Boolean
223
224
225
226
227
|
# File 'lib/plansheet/project.rb', line 223
def dependent_on?(other)
@dependencies&.any? do |dep|
other&.name&.downcase == dep.downcase
end
end
|
#dropped_or_done? ⇒ Boolean
346
347
348
|
# File 'lib/plansheet/project.rb', line 346
def dropped_or_done?
dropped? || done?
end
|
#due ⇒ Object
Due date either explicit or recurring
290
291
292
293
294
295
|
# File 'lib/plansheet/project.rb', line 290
def due
return @due if @due
return recurring_due_date if recurring_due?
nil
end
|
#last_for_deferral ⇒ Object
321
322
323
324
325
|
# File 'lib/plansheet/project.rb', line 321
def last_for_deferral
return @last_done + parse_date_duration(@last_for) if @last_done
Date.today
end
|
#lead_time_deferral ⇒ Object
327
328
329
330
|
# File 'lib/plansheet/project.rb', line 327
def lead_time_deferral
[(due - parse_date_duration(@lead_time)),
Date.today].max
end
|
#recurring? ⇒ Boolean
336
337
338
|
# File 'lib/plansheet/project.rb', line 336
def recurring?
!@frequency.nil? || !@day_of_week.nil? || !@last_done.nil? || !@last_for.nil?
end
|
#recurring_due? ⇒ Boolean
332
333
334
|
# File 'lib/plansheet/project.rb', line 332
def recurring_due?
!@frequency.nil? || !@day_of_week.nil?
end
|
#recurring_due_date ⇒ Object
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
|
# File 'lib/plansheet/project.rb', line 297
def recurring_due_date
if @last_done
return @last_done + parse_date_duration(@frequency) if @frequency
if @day_of_week
return Date.today + 7 if @last_done == Date.today
return @last_done + 7 if @last_done < Date.today - 7
return NEXT_DOW[@day_of_week]
end
end
Date.today
end
|
#recurring_status ⇒ Object
270
271
272
273
274
275
276
277
278
279
|
# File 'lib/plansheet/project.rb', line 270
def recurring_status
if @last_done
subsequent_recurring_status
else
task_based_status
end
end
|
#status ⇒ Object
247
248
249
250
251
252
253
254
255
256
|
# File 'lib/plansheet/project.rb', line 247
def status
return @status if @status
return "dropped" if @dropped_on
return "paused" if @paused_on
return recurring_status if recurring?
return task_based_status if @tasks || @done
return "done" if @completed_on && @tasks.nil?
"idea"
end
|
#stringify_array_property(prop) ⇒ Object
38
39
40
41
42
43
44
45
46
47
|
# File 'lib/plansheet/project/stringify.rb', line 38
def stringify_array_property(prop)
str = String.new
if instance_variable_defined? "@#{prop}"
str << "#{prop}:\n"
instance_variable_get("@#{prop}").each do |t|
str << "- #{t}\n"
end
end
str
end
|
#stringify_date_property(prop) ⇒ Object
30
31
32
33
34
35
36
|
# File 'lib/plansheet/project/stringify.rb', line 30
def stringify_date_property(prop)
if instance_variable_defined? "@#{prop}"
"#{prop}: #{instance_variable_get("@#{prop}")}\n"
else
""
end
end
|
#stringify_string_property(prop) ⇒ Object
22
23
24
25
26
27
28
|
# File 'lib/plansheet/project/stringify.rb', line 22
def stringify_string_property(prop)
if instance_variable_defined? "@#{prop}"
"#{prop}: #{instance_variable_get("@#{prop}")}\n"
else
""
end
end
|
#subsequent_recurring_status ⇒ Object
281
282
283
284
285
286
287
|
# File 'lib/plansheet/project.rb', line 281
def subsequent_recurring_status
return "done" if @lead_time && defer > Date.today
return "done" if @last_for && defer > Date.today
return "done" if due && due > Date.today
task_based_status
end
|
#task_based_status ⇒ Object
258
259
260
261
262
263
264
265
266
267
268
|
# File 'lib/plansheet/project.rb', line 258
def task_based_status
if @tasks&.count&.positive? && @done&.count&.positive?
"wip"
elsif @tasks&.count&.positive?
"ready"
elsif @done&.count&.positive?
"done"
else
"idea"
end
end
|
#task_time_estimate(str) ⇒ Object
354
355
356
|
# File 'lib/plansheet/project.rb', line 354
def task_time_estimate(str)
parse_time_duration(Regexp.last_match(1)) if str.match(TIME_EST_REGEX)
end
|
#time_roi_payoff ⇒ Object
170
171
172
|
# File 'lib/plansheet/project.rb', line 170
def time_roi_payoff
@time_roi_payoff || 0
end
|
#to_h ⇒ Object
358
359
360
361
362
363
364
|
# File 'lib/plansheet/project.rb', line 358
def to_h
h = { "project" => @name, "namespace" => @namespace }
ALL_PROPERTIES.each do |prop|
h[prop] = instance_variable_get("@#{prop}") if instance_variable_defined?("@#{prop}")
end
h
end
|
#to_s ⇒ Object
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# File 'lib/plansheet/project/stringify.rb', line 5
def to_s
str = String.new
str << "# "
str << "#{@namespace} - " if @namespace
str << "#{@name}\n"
STRING_PROPERTIES.each do |o|
str << stringify_string_property(o)
end
DATE_PROPERTIES.each do |o|
str << stringify_string_property(o)
end
ARRAY_PROPERTIES.each do |o|
str << stringify_array_property(o)
end
str
end
|
#yearly_minutes_saved ⇒ Object
147
148
149
150
151
152
153
154
155
|
# File 'lib/plansheet/project.rb', line 147
def yearly_minutes_saved
if @daily_time_roi
parse_time_duration(@daily_time_roi) * 365
elsif @weekly_time_roi
parse_time_duration(@weekly_time_roi) * 52
elsif @yearly_time_roi
parse_time_duration(@yearly_time_roi)
end
end
|