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.
68
69
70
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
|
# File 'lib/plansheet/project.rb', line 68
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?
@completed_on ||= Date.today unless recurring?
remove_instance_variable("@status") if @status
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
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.
66
67
68
|
# File 'lib/plansheet/project.rb', line 66
def namespace
@namespace
end
|
Instance Method Details
#<=>(other) ⇒ Object
152
153
154
155
156
157
158
159
|
# File 'lib/plansheet/project.rb', line 152
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
351
352
353
|
# File 'lib/plansheet/project.rb', line 351
def archivable?
(!recurring? && @completed_on) || dropped?
end
|
#archive_month ⇒ Object
138
139
140
|
# File 'lib/plansheet/project.rb', line 138
def archive_month
@completed_on&.strftime("%Y-%m") || Date.today.strftime("%Y-%m")
end
|
#compare_completed_on(other) ⇒ Object
183
184
185
186
187
188
189
|
# File 'lib/plansheet/project.rb', line 183
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.
235
236
237
238
239
240
|
# File 'lib/plansheet/project.rb', line 235
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
206
207
208
209
210
|
# File 'lib/plansheet/project.rb', line 206
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
224
225
226
227
228
229
230
231
|
# File 'lib/plansheet/project.rb', line 224
def compare_dependency(other)
retval = 0
retval -= 1 if dependency_of?(other)
retval += 1 if dependent_on?(other)
retval
end
|
#compare_due(other) ⇒ Object
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
# File 'lib/plansheet/project.rb', line 191
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
179
180
181
|
# File 'lib/plansheet/project.rb', line 179
def compare_name(other)
@name <=> other.name
end
|
#compare_priority(other) ⇒ Object
161
162
163
|
# File 'lib/plansheet/project.rb', line 161
def compare_priority(other)
priority_val <=> other.priority_val
end
|
#compare_status(other) ⇒ Object
173
174
175
|
# File 'lib/plansheet/project.rb', line 173
def compare_status(other)
PROJECT_STATUS_PRIORITY[status] <=> PROJECT_STATUS_PRIORITY[other.status]
end
|
#compare_time_roi(other) ⇒ Object
169
170
171
|
# File 'lib/plansheet/project.rb', line 169
def compare_time_roi(other)
other.time_roi_payoff <=> time_roi_payoff
end
|
#defer ⇒ Object
308
309
310
311
312
313
314
|
# File 'lib/plansheet/project.rb', line 308
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
212
213
214
215
216
|
# File 'lib/plansheet/project.rb', line 212
def dependency_of?(other)
other&.dependencies&.any? do |dep|
@name&.downcase == dep.downcase
end
end
|
#dependent_on?(other) ⇒ Boolean
218
219
220
221
222
|
# File 'lib/plansheet/project.rb', line 218
def dependent_on?(other)
@dependencies&.any? do |dep|
other&.name&.downcase == dep.downcase
end
end
|
#done? ⇒ Boolean
343
344
345
|
# File 'lib/plansheet/project.rb', line 343
def done?
status == "done"
end
|
#dropped? ⇒ Boolean
339
340
341
|
# File 'lib/plansheet/project.rb', line 339
def dropped?
status == "dropped"
end
|
#dropped_or_done? ⇒ Boolean
347
348
349
|
# File 'lib/plansheet/project.rb', line 347
def dropped_or_done?
dropped? || done?
end
|
#due ⇒ Object
Due date either explicit or recurring
285
286
287
288
289
290
|
# File 'lib/plansheet/project.rb', line 285
def due
return @due if @due
return recurring_due_date if recurring_due?
nil
end
|
#last_for_deferral ⇒ Object
316
317
318
319
320
|
# File 'lib/plansheet/project.rb', line 316
def last_for_deferral
return @last_done + parse_date_duration(@last_for) if @last_done
Date.today
end
|
#lead_time_deferral ⇒ Object
322
323
324
325
|
# File 'lib/plansheet/project.rb', line 322
def lead_time_deferral
[(due - parse_date_duration(@lead_time)),
Date.today].max
end
|
#paused? ⇒ Boolean
335
336
337
|
# File 'lib/plansheet/project.rb', line 335
def paused?
status == "paused"
end
|
#recurring? ⇒ Boolean
331
332
333
|
# File 'lib/plansheet/project.rb', line 331
def recurring?
!@frequency.nil? || !@day_of_week.nil? || !@last_done.nil? || !@last_for.nil?
end
|
#recurring_due? ⇒ Boolean
327
328
329
|
# File 'lib/plansheet/project.rb', line 327
def recurring_due?
!@frequency.nil? || !@day_of_week.nil?
end
|
#recurring_due_date ⇒ Object
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
|
# File 'lib/plansheet/project.rb', line 292
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
265
266
267
268
269
270
271
272
273
274
|
# File 'lib/plansheet/project.rb', line 265
def recurring_status
if @last_done
subsequent_recurring_status
else
task_based_status
end
end
|
#status ⇒ Object
242
243
244
245
246
247
248
249
250
251
|
# File 'lib/plansheet/project.rb', line 242
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
276
277
278
279
280
281
282
|
# File 'lib/plansheet/project.rb', line 276
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
253
254
255
256
257
258
259
260
261
262
263
|
# File 'lib/plansheet/project.rb', line 253
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
355
356
357
|
# File 'lib/plansheet/project.rb', line 355
def task_time_estimate(str)
parse_time_duration(Regexp.last_match(1)) if str.match(TIME_EST_REGEX)
end
|
#time_roi_payoff ⇒ Object
165
166
167
|
# File 'lib/plansheet/project.rb', line 165
def time_roi_payoff
@time_roi_payoff || 0
end
|
#to_h ⇒ Object
359
360
361
362
363
364
365
|
# File 'lib/plansheet/project.rb', line 359
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
142
143
144
145
146
147
148
149
150
|
# File 'lib/plansheet/project.rb', line 142
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
|