Class: Period

Inherits:
ActiveRecord::Base
  • Object
show all
Extended by:
UserSystem
Includes:
Localization, UserSystem
Defined in:
app/models/period.rb

Constant Summary

Constants included from Localization

Localization::LOCALIZED_STRINGS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Localization

#l, load_localized_strings, #valid_language?

Class Method Details

.find_active_or_future(check_tasks = false) ⇒ Object



16
17
18
19
20
21
22
23
# File 'app/models/period.rb', line 16

def self.find_active_or_future(check_tasks = false)
  periods = find(:all, :conditions => "end_on >= '#{Date.today.strftime '%Y-%m-%d'}'")
  if (check_tasks)
    ended_periods_with_open_tasks = Task.find(:all, :conditions => ['finished_at IS NULL AND period_id NOT IN (?)', periods.map{|p| p.id}]).map {|t| t.period}
    periods + ended_periods_with_open_tasks
  end
  periods.select {|p| p.party.includes?(current_user)}
end

Instance Method Details

#active?(check_tasks = false) ⇒ Boolean

Returns:

  • (Boolean)


51
52
53
# File 'app/models/period.rb', line 51

def active?(check_tasks = false)
  start_on <= Date.today && (end_on >= Date.today || (check_tasks && active_tasks?))
end

#active_or_future?(check_tasks = false) ⇒ Boolean

Returns:

  • (Boolean)


63
64
65
# File 'app/models/period.rb', line 63

def active_or_future?(check_tasks = false)
  end_on >= Date.today || (check_tasks && active_tasks?)
end

#active_tasks?Boolean

Returns:

  • (Boolean)


55
56
57
# File 'app/models/period.rb', line 55

def active_tasks?
  not tasks.select {|task| task.active?}.empty?
end

#backlogsObject



177
178
179
# File 'app/models/period.rb', line 177

def backlogs
  tasks.map {|task| task.backlog}.uniq
end

#burn_down_graph(size) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'app/models/period.rb', line 181

def burn_down_graph(size)
  begin
    require 'gruff'
    rescue MissingSourceFile => e
    return File.read("public/images/rmagick_#{size}.gif") if File.exists? "public/images/rmagick_#{size}.gif"
    return File.read("public/images/rmagick.gif")
  end
  g = Gruff::Line.new(size)
  g.theme_37signals
  g.title = l(:burn_down_chart) + " " + name
  g.font = '/usr/share/fonts/bitstream-vera/Vera.ttf'
  g.legend_font_size = 14
  g.hide_dots = true
  g.colors = %w{#0000ff #ff8800}
  if track_work?
    g.colors += %w{#00ff00}
  end
  if previous_period = higher_item
    g.colors += %w{#cccccc #cccccc}
  end
  g.colors += %w{#8888ff #d7a790}
  
  recorded_dates = self.recorded_dates
  
  observed_todo_data = get_todo_data(recorded_dates)
  g.data("#{l :todo} (#{l :obs})", observed_todo_data)
  g.data(l(:projection), projection_data(observed_todo_data))
  
  actual_todo_data = get_todo_data(recorded_dates, true)
  g.data("#{l :todo}", actual_todo_data)
  g.data(l(:projection), projection_data(actual_todo_data))
  
  g.data(l(:done), get_work_data(recorded_dates)) if track_work?
  
  if previous_period = higher_item
    g.data("#{l :previous_abr} #{l :obs}", previous_period.get_todo_data(previous_period.dates))
    g.data("#{l :previous_abr}", previous_period.get_todo_data(previous_period.dates, true))
  end
  
  g.minimum_value = 0
  
  all_dates = dates
  labels = {1 => all_dates[1].to_s, all_dates.length-1 => all_dates.last.to_s}
  labels.merge({all_dates.index(Date.today) => Date.today.to_s}) if all_dates.index(Date.today) && (all_dates.index(Date.today) / all_dates.length) > 0.10
  g.labels = labels
  
  # g.draw_vertical_legend
  
  g.maximum_value = (g.maximum_value.to_s[0..0].to_i + 1) * (10**Math::log10(g.maximum_value.to_i).to_i) if g.maximum_value > 0
  g.to_blob
end

#completed_tasksObject



32
33
34
# File 'app/models/period.rb', line 32

def completed_tasks
  tasks.select {|task| task.completed?}
end

#datesObject



109
110
111
112
113
# File 'app/models/period.rb', line 109

def dates
  all_dates = []
   (start_on-1).upto(end_on) {|date| all_dates << date}
  return all_dates
end

#enable_subtasks?Boolean

Returns:

  • (Boolean)


169
170
171
# File 'app/models/period.rb', line 169

def enable_subtasks?
  not backlogs.find {|backlog| backlog.enable_subtasks?}.nil?
end

#estimate_data(date, actual = false) ⇒ Object



71
72
73
74
75
76
77
# File 'app/models/period.rb', line 71

def estimate_data(date, actual=false)
  total = BigDecimal('0')
  tasks.each do |task|
    total += task.estimate_data(date, actual)
  end
  total
end

#estimates?Boolean

Returns:

  • (Boolean)


157
158
159
# File 'app/models/period.rb', line 157

def estimates?
  not backlogs.find {|backlog| backlog.track_todo?}.nil?
end

#future?Boolean

Returns:

  • (Boolean)


59
60
61
# File 'app/models/period.rb', line 59

def future?
  start_on >= Date.today
end

#get_todo_data(dates, actual = false) ⇒ Object



233
234
235
# File 'app/models/period.rb', line 233

def get_todo_data(dates, actual=false)
  dates.map { |date| estimate_data(date, actual) }
end

#get_work_data(dates) ⇒ Object



237
238
239
# File 'app/models/period.rb', line 237

def get_work_data(dates)
  dates.map { |date| work_data(date) }
end

#invoice?Boolean

Returns:

  • (Boolean)


173
174
175
# File 'app/models/period.rb', line 173

def invoice?
  not backlogs.find {|backlog| backlog.enable_invoicing?}.nil?
end

#most_frequent_backlogObject



40
41
42
43
44
45
46
47
48
49
# File 'app/models/period.rb', line 40

def most_frequent_backlog
  all_backlogs = tasks.map {|t| t.backlog}.compact
  return nil if all_backlogs.empty?
  freq = {}
  all_backlogs.each do |b|
    freq[b.id] ||= 0
    freq[b.id] += 1
  end
  freq.to_a.sort_by {|backlog, count| -count}.first[0]
end

#nameObject



105
106
107
# File 'app/models/period.rb', line 105

def name
  "#{party.name}##{position}"
end

#open_tasksObject



36
37
38
# File 'app/models/period.rb', line 36

def open_tasks
  tasks.select {|task| task.active?}
end

#passed?Boolean

Returns:

  • (Boolean)


67
68
69
# File 'app/models/period.rb', line 67

def passed?
  end_on < Date.today
end

#projection_data(observed_todo_data) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'app/models/period.rb', line 241

def projection_data(observed_todo_data)
  if observed_todo_data.length <= 1
    velocity = party.current_speed
  else
    velocity = (observed_todo_data[0] - observed_todo_data[-1]).to_f / (observed_todo_data.length-1)
  end
  projection_data = dates.map do |date|
    if date < Date.today 
      nil
    else
      value = observed_todo_data[0] - (date-start_on+1).to_f*velocity
      value >= 0 ? value : 0
    end
  end
end

#recorded_datesObject



115
116
117
118
119
120
121
122
123
124
125
# File 'app/models/period.rb', line 115

def recorded_dates
  dates = []
  if start_on > Date.today
    dates << (start_on-1)
  elsif end_on < Date.today
   (start_on-1).upto(end_on) {|date| dates << date}
  else
   (start_on-1).upto(Date.today) {|date| dates << date}
  end  
  return dates
end

#required_speedObject



87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'app/models/period.rb', line 87

def required_speed
  todo = estimate_data(Date.today)
  remaining_days = end_on - Date.today
  if todo == 0
    0
  elsif remaining_days > 0
    todo / remaining_days
  elsif remaining_days == 0
    todo
  else
    todo * (-remaining_days)
  end
end

#speedObject



25
26
27
28
29
30
# File 'app/models/period.rb', line 25

def speed
  start_todo = estimate_data(start_on, true)
  end_todo = estimate_data(end_on, true)
  days = end_on - start_on
  return (start_todo - end_todo).to_f / days
end

#to_sObject



101
102
103
# File 'app/models/period.rb', line 101

def to_s
  name
end

#track_times?Boolean

Returns:

  • (Boolean)


165
166
167
# File 'app/models/period.rb', line 165

def track_times?
  not backlogs.find {|backlog| backlog.track_times?}.nil?
end

#track_work?Boolean

Returns:

  • (Boolean)


161
162
163
# File 'app/models/period.rb', line 161

def track_work?
  not backlogs.find {|backlog| backlog.}.nil?
end

#validateObject



12
13
14
# File 'app/models/period.rb', line 12

def validate
  errors.add "A period cannot end before it starts" unless end_on >= start_on
end

#work_data(date) ⇒ Object



79
80
81
82
83
84
85
# File 'app/models/period.rb', line 79

def work_data(date)
  total = BigDecimal('0')
  tasks.each do |task|
    total += task.work_data(date)
  end
  total
end

#works_for_week(week_no, with_empty_works = false, user_id = nil) ⇒ Object

Return an array with a work object per day: [

[<work>, <work>, <work>, <work>, <work>, nil, nil],
[<work>, nil,    <work>, <work>, <work>, nil, nil]

]



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'app/models/period.rb', line 132

def works_for_week(week_no, with_empty_works = false, user_id = nil)
  first = Date.commercial(Date.today.year, week_no, 1)
  last = first + 6
  works = tasks.map {|t| t.works_with_children}.flatten
  works.reject! {|work| work.hours == 0} unless with_empty_works
  works.reject! {|work| work.user_id != user_id} if user_id && backlog.enable_users
  by_day = (0..6).map do |i|
    works.select {|w| w.completed_at && w.completed_at.to_date == first + i}.sort do |w1, w2|
      if w1.completed_at != w2.completed_at
        w1.completed_at <=> w2.completed_at
      elsif w1.started_at && w2.started_at
        w1.started_at <=> w2.started_at
      elsif w1.started_at
        1
      else
        -1
      end
    end
  end
  by_day.each {|d| d[works.size] = d[works.size]}
   = by_day.transpose
   = .select {|l| l.compact.size > 0}
  
end