Class TrackRecordReport::Report
In: lib/track_record_report.rb
Parent: ReportElementaryCalculator

Class which manages a report.

Methods

Included Modules

TrackRecordSections

Constants

FREQUENCY = [ { :label => 'Totals only', :title => '', :column => :heading_total, :generator => :totals_report }, { :label => 'UK tax year', :title => 'UK tax year:', :column => :heading_tax_year, :generator => :periodic_report, :generator_arg => :end_of_uk_tax_year }, { :label => 'Calendar year', :title => 'Year:', :column => :heading_calendar_year, :generator => :periodic_report, :generator_arg => :end_of_year }, { :label => 'Calendar quarter', :title => 'Quarter starting:', :column => :heading_quarter_and_month, :generator => :periodic_report, :generator_arg => :end_of_quarter }, { :label => 'Monthly', :title => 'Month:', :column => :heading_quarter_and_month, :generator => :periodic_report, :generator_arg => :end_of_month }, { :label => 'Weekly', :title => 'Week starting:', :column => :heading_weekly, :generator => :periodic_report, :generator_arg => :end_of_week }, # Daily reports are harmful since they can cause EXTREMELY large reports # to be generated and this can take longer than the web browser will wait # before timing out. In the mean time, Rails keeps building the report... # # { :label => 'Daily', :title => 'Date:', :column => :heading_daily, :generator => :daily_report }, ]   Configure the handlers and human-readable labels for the ways in which reports get broken up, in terms of frequency. View code which presents a choice of report frequency should obtain the labels for the list with the ‘label’ method. Use the ‘column_title’ method for a column "title", shown alongside or above column headings. Use the ‘column_heading’ method for per-column headings.

Attributes

active_task_ids  [R] 
column_count  [R]  Number of columns after all calculations are complete; this is the same as the size of the ‘column_ranges’ or ‘column_totals’ arrays below, but using this explicit property is likely to make code more legible.
column_ranges  [R]  Array of ranges, one per column, giving the range for each of the columns held within the rows. The indices into this array match indices into the rows’ cell arrays.
column_totals  [R]  Array of ReportColumnTotal objects, one per column, giving the total hours for that column. The indices into this array match indices into the rows’ cell arrays.
customer_sort_field  [RW]  Sort fields for customers, projects and tasks; grouping options.
filtered_tasks  [RW] 
filtered_users  [RW] 
frequency  [R]  A row from the FREQUENCY constant and the current index into that array, as a stringify_keys.
frequency_data  [R]  A row from the FREQUENCY constant and the current index into that array, as a stringify_keys.
inactive_task_ids  [R] 
project_sort_field  [RW]  Sort fields for customers, projects and tasks; grouping options.
range  [RW]  Complete date range for the whole report; array of user IDs used for per-user breakdowns; array of task IDs the report will represent.
range_end  [RW]  Range data for the ‘new’ view form.
range_month_end  [RW] 
range_month_start  [RW] 
range_start  [RW]  Range data for the ‘new’ view form.
range_week_end  [RW] 
range_week_start  [RW] 
rows  [R]  Array of ReportRows making up the report. The row objects contain arrays of cells, corresponding to columns of the report.
sections  [R]  Array of ReportSection objects describing per-section totals of various kinds. See the ReportSection class and TrackRecordSections module for details.
task_filter  [RW]  Handle all ("all"), only billable ("billable") or only non-billable ("non_billable") tasks?
task_grouping  [RW] 
task_ids  [R] 
task_sort_field  [RW]  Sort fields for customers, projects and tasks; grouping options.
tasks  [R]  Read-only array of actual user and task objects based on the IDs; call "build_task_and_user_arrays" to build it. Not all users or tasks may be included, depending on security settings.
total_actual_remaining  [R]  Total duration of all tasks in all rows; number of hours remaining (may be negative for overrun) after all hours worked in tasks with non-zero duration. If ‘nil’, all tasks had zero duration. The ‘actual’ value only accounts for committed hours, while the ‘potential’ value includes both committed and not-committed hours (thus, subject to change).
total_duration  [R]  Total duration of all tasks in all rows; number of hours remaining (may be negative for overrun) after all hours worked in tasks with non-zero duration. If ‘nil’, all tasks had zero duration. The ‘actual’ value only accounts for committed hours, while the ‘potential’ value includes both committed and not-committed hours (thus, subject to change).
total_potential_remaining  [R]  Total duration of all tasks in all rows; number of hours remaining (may be negative for overrun) after all hours worked in tasks with non-zero duration. If ‘nil’, all tasks had zero duration. The ‘actual’ value only accounts for committed hours, while the ‘potential’ value includes both committed and not-committed hours (thus, subject to change).
user_column_totals  [R]  Array of ReportUserColumnTotal objects, each index corresponding to a user the "users" array at the same index. These give the total work done by that user across all rows.
user_ids  [R] 
users  [R]  Read-only array of actual user and task objects based on the IDs; call "build_task_and_user_arrays" to build it. Not all users or tasks may be included, depending on security settings.

Public Class methods

Class method equivalent of "label" above. Returns the label for the given frequency, which must be a valid index into Report::FREQUENCY. See also "labels" below.

[Source]

     # File lib/track_record_report.rb, line 331
331:     def self.label( frequency )
332:       return Report::FREQUENCY[ frequency ][ :label ]
333:     end

Class method which returns an array of labels for various report frequencies. The index into the array indicates the frequency index.

[Source]

     # File lib/track_record_report.rb, line 338
338:     def self.labels
339:       return Report::FREQUENCY.map { | f | f[ :label ] }
340:     end

Create a new Report. In the first parameter, pass the current TrackRecord user. In the next parameter pass nothing to use default values for a ‘new report’ view form, or pass "params[ :report ]" (or similar) to create using a params hash from a ‘new report’ form submission.

[Source]

     # File lib/track_record_report.rb, line 178
178:     def initialize( current_user, params = nil )
179:       @current_user = current_user
180: 
181:       @range_start           = nil
182:       @range_end             = nil
183:       @range_week_start      = nil
184:       @range_week_end        = nil
185:       @range_month_start     = nil
186:       @range_month_end       = nil
187:       @frequency             = 0
188: 
189:       @customer_sort_field   = 'title'
190:       @project_sort_field    = 'title'
191:       @task_sort_field       = Task::DEFAULT_SORT_COLUMN
192:       @task_grouping         = :default
193:       @task_filter           = 'all'
194: 
195:       @include_totals        = true
196:       @include_committed     = false
197:       @include_not_committed = false
198:       @exclude_zero_rows     = false
199:       @exclude_zero_cols     = false # Totals only - ignores zero com/non-com columns in CSV exports for total/com/non-com column groups with non-zero totals
200: 
201:       @tasks                 = []
202:       @filtered_tasks        = []
203:       @task_ids              = []
204:       @active_task_ids       = []
205:       @inactive_task_ids     = []
206: 
207:       @users                 = []
208:       @filtered_users        = []
209:       @user_ids              = []
210: 
211:       unless ( params.nil? )
212: 
213:         # Adapted from ActiveRecord::Base "attributes=", Rails 2.1.0
214:         # on 29-Jun-2008.
215: 
216:         attributes = params.dup
217:         attributes.stringify_keys!
218:         attributes.each do | key, value |
219:           if ( key.include?( '(' ) )
220:             raise( "Multi-parameter attributes are not supported." )
221:           else
222:             send( key + "=", value )
223:           end
224:         end
225:       end
226:     end

Public Instance methods

Build the ‘tasks’ array if ‘active_task_ids’ is updated externally. The result will be the sum of existing inactive and updated active task IDs.

[Source]

     # File lib/track_record_report.rb, line 260
260:     def active_task_ids=( ids )
261:       @active_task_ids = map_raw_ids( ids )
262:       assign_tasks_from_ids( @active_task_ids, @inactive_task_ids )
263:     end

Virtual accessor for the active task list, which just filters the master task list and returns the result (less RAM than keeping dual lists and not speed-critical as not used that often in practice).

[Source]

     # File lib/track_record_report.rb, line 232
232:     def active_tasks
233:       @tasks.select { | task | task.active }
234:     end

Helper method which returns a user-displayable column heading appropriate for the report type. Pass the column index.

[Source]

     # File lib/track_record_report.rb, line 353
353:     def column_heading( col_index )
354:       col_range = @column_ranges[ col_index ]
355:       return send( @frequency_data[ :column ], col_range )
356:     end

Helper method which returns a user-displayable column title to be shown once, next to or near per-column headings (see "column_heading"), appropriate for the report type.

[Source]

     # File lib/track_record_report.rb, line 346
346:     def column_title
347:       return @frequency_data[ :title ]
348:     end

Compile the report.

[Source]

     # File lib/track_record_report.rb, line 301
301:     def compile
302:       rationalise_dates()
303:       apply_filters()
304:       sort_and_group()
305: 
306:       return if ( @filtered_tasks.empty? )
307: 
308:       add_rows()
309:       add_columns()
310:       calculate!()
311:     end

Helper method which returns a user-displayable range describing the total date range for this report.

[Source]

     # File lib/track_record_report.rb, line 323
323:     def display_range
324:       return heading_total( @range )
325:     end

Set the ‘frequency_data’ field when ‘frequency’ is updated externally.

[Source]

     # File lib/track_record_report.rb, line 294
294:     def frequency=( freq )
295:       @frequency      = freq.to_i
296:       @frequency_data = FREQUENCY[ @frequency ]
297:     end

Build the ‘tasks’ array if ‘inactive_task_ids’ is updated externally. The result will be the sum of existing active and updated inactive task IDs.

[Source]

     # File lib/track_record_report.rb, line 268
268:     def inactive_task_ids=( ids )
269:       @inactive_task_ids = map_raw_ids( ids )
270:       assign_tasks_from_ids( @active_task_ids, @inactive_task_ids )
271:     end

As above, but for inactive tasks.

[Source]

     # File lib/track_record_report.rb, line 238
238:     def inactive_tasks
239:       @tasks.select { | task | ! task.active }
240:     end

Helper method which returns a user-displayable label describing this report type. There‘s a class method equivalent below.

[Source]

     # File lib/track_record_report.rb, line 316
316:     def label
317:       return @frequency_data[ :label ]
318:     end

Does the column at the given index only contain partial results, because it is the first or last column in the overall range and that range starts or ends somewhere in the middle? Returns ‘true’ if so, else ‘false’.

[Source]

     # File lib/track_record_report.rb, line 362
362:     def partial_column?( col_index )
363: 
364: # [TODO] Doesn't work, because col_range accurately reflects the column range
365: #        rather than the quantised range. Getting at the latter is tricky, so
366: #        leaving this for later. At present the method is only used for display
367: #        purposes in the column headings.
368: 
369:       col_range = @column_ranges[ col_index ]
370:       return ( col_range.first < @range.first or col_range.last > @range.last )
371:     end

Build the ‘tasks’ array if ‘task_ids’ is updated externally.

[Source]

     # File lib/track_record_report.rb, line 252
252:     def task_ids=( ids )
253:       @task_ids = map_raw_ids( ids )
254:       assign_tasks_from_ids( @task_ids )
255:     end

Set a task array directly (will always be filtered according to security settings for the current user).

[Source]

     # File lib/track_record_report.rb, line 245
245:     def tasks=( array )
246:       @tasks = array
247:       update_internal_task_lists()
248:     end

Build the ‘user’ array if ‘user_ids’ is updated externally.

[Source]

     # File lib/track_record_report.rb, line 275
275:     def user_ids=( ids )
276:       ids ||= []
277:       ids = ids.values if ( ids.is_a?( Hash ) or ids.is_a?( HashWithIndifferentAccess ) )
278:       @user_ids = ids.map { | str | str.to_i }
279: 
280:       # Security - if the current user is restricted they might try and hack
281:       # the form to view other user details.
282: 
283:       if ( @current_user.restricted? )
284:         @user_ids = [ @current_user.id ] unless( @user_ids.empty? )
285:       end
286: 
287:       # Turn the list of (now numeric) user IDs into user objects.
288: 
289:       @users = User.active.find( @user_ids )
290:     end

[Validate]