Class which manages a 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.
THESE MUST STAY IN THE SAME ORDER! If you add new entries, you must
Saved reports specify the reporting frequency by reference to this constant and an index into its array of entries. If you produce a new TrackRecord version that changes the meaning of any array index rather than merely adding new entries, you will have to include a migration that maps old indices to new for existing saved report records.
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.
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.
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.
Sort fields for customers, projects and tasks; grouping options.
A row from the FREQUENCY constant and the current index into that array.
A row from the FREQUENCY constant and the current index into that array.
Sort fields for customers, projects and tasks; grouping options.
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 data for the ‘new’ view form. Custom attribute writer methods are used to call “rationalise_dates()” whenever a range value is altered.
Range data for the ‘new’ view form. Custom attribute writer methods are used to call “rationalise_dates()” whenever a range value is altered.
Array of ReportRows making up the report. The row objects contain arrays of cells, corresponding to columns of the report.
Array of ReportSection objects describing per-section totals of various kinds. See the ReportSection class and TrackRecordSections module for details.
Handle all (“all”), only billable (“billable”) or only non-billable (“non_billable”) tasks?
Sort fields for customers, projects and tasks; grouping options.
Read-only array of actual user and task objects based on the IDs. Not all users or tasks may be included, depending on security settings.
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 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 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).
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.
Read-only array of actual user and task objects based on the IDs. Not all users or tasks may be included, depending on security settings.
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.
# File lib/track_record_report.rb, line 357 def self.label( frequency ) return Report::FREQUENCY[ frequency ][ :label ] end
Class method which returns an array of labels for various report frequencies. The index into the array indicates the frequency index.
# File lib/track_record_report.rb, line 364 def self.labels return Report::FREQUENCY.map { | f | f[ :label ] } 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.
# File lib/track_record_report.rb, line 190 def initialize( current_user, params = nil ) @current_user = current_user @range_start = nil @range_end = nil @range_week_start = nil @range_week_end = nil @range_month_start = nil @range_month_end = nil @frequency = 0 @customer_sort_field = 'title' @project_sort_field = 'title' @task_sort_field = Task::DEFAULT_SORT_COLUMN @task_grouping = :default @task_filter = 'all' @include_totals = true @include_committed = false @include_not_committed = false @exclude_zero_rows = false @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 @tasks = [] @filtered_tasks = [] @task_ids = [] @active_task_ids = [] @inactive_task_ids = [] @users = [] @filtered_users = [] @reportable_user_ids = [] unless ( params.nil? ) # Adapted from ActiveRecord::Base "attributes=", Rails 2.1.0 # on 29-Jun-2008. attributes = params.dup attributes.stringify_keys! attributes.each do | key, value | if ( key.include?( '(' ) ) raise( "Multi-parameter attributes are not supported." ) else begin send( key + "=", value ) rescue # Ignore errors end end end end end
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.
# File lib/track_record_report.rb, line 276 def active_task_ids=( ids ) @provided_active_task_ids = map_raw_ids( ids ) assign_actual_tasks_from_provided_ids() 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).
# File lib/track_record_report.rb, line 248 def active_tasks @tasks.select { | task | task.active } end
Helper method which returns a user-displayable column heading appropriate for the report type. Pass the column index.
# File lib/track_record_report.rb, line 379 def column_heading( col_index ) col_range = @column_ranges[ col_index ] return send( @frequency_data[ :column ], col_range ) 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.
# File lib/track_record_report.rb, line 372 def column_title return @frequency_data[ :title ] end
Compile the report.
# File lib/track_record_report.rb, line 327 def compile rationalise_dates() apply_filters() sort_and_group() return if ( @filtered_tasks.empty? ) add_rows() add_columns() calculate!() end
Helper method which returns a user-displayable range describing the total date range for this report.
# File lib/track_record_report.rb, line 349 def display_range return heading_total( @range ) end
Set the ‘#frequency_data’ field when ‘frequency’ is updated externally.
# File lib/track_record_report.rb, line 310 def frequency=( freq ) @frequency = freq.to_i @frequency_data = FREQUENCY[ @frequency ] 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.
# File lib/track_record_report.rb, line 284 def inactive_task_ids=( ids ) @provided_inactive_task_ids = map_raw_ids( ids ) assign_actual_tasks_from_provided_ids() end
As above, but for inactive tasks.
# File lib/track_record_report.rb, line 254 def inactive_tasks @tasks.select { | task | ! task.active } end
Helper method which returns a user-displayable label describing this report type. There’s a class method equivalent below.
# File lib/track_record_report.rb, line 342 def label return @frequency_data[ :label ] 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’.
# File lib/track_record_report.rb, line 388 def partial_column?( col_index ) # [TODO] Doesn't work, because col_range accurately reflects the column range # rather than the quantised range. Getting at the latter is tricky, so # leaving this for later. At present the method is only used for display # purposes in the column headings. col_range = @column_ranges[ col_index ] return ( col_range.min < @range.min or col_range.max > @range.max ) end
# File lib/track_record_report.rb, line 319 def range_end=( value ); @range_end = value; rationalise_dates(); end
# File lib/track_record_report.rb, line 323 def range_month_end=( value ); @range_month_end = value; rationalise_dates(); end
# File lib/track_record_report.rb, line 322 def range_month_start=( value ); @range_month_start = value; rationalise_dates(); end
Rationalise overall date ranges whenever a related field is updated externally.
# File lib/track_record_report.rb, line 318 def range_start=( value ); @range_start = value; rationalise_dates(); end
# File lib/track_record_report.rb, line 321 def range_week_end=( value ); @range_week_end = value; rationalise_dates(); end
# File lib/track_record_report.rb, line 320 def range_week_start=( value ); @range_week_start = value; rationalise_dates(); end
Build the ‘user’ array if ‘#reportable_user_ids’ is updated externally.
# File lib/track_record_report.rb, line 291 def reportable_user_ids=( ids ) ids ||= [] ids = ids.values if ( ids.is_a?( Hash ) or ids.is_a?( HashWithIndifferentAccess ) ) @reportable_user_ids = ids.map { | str | str.to_i } # Security - if the current user is restricted they might try and hack # the form to view other user details. if ( @current_user.restricted? ) @reportable_user_ids = [ @current_user.id ] unless( @reportable_user_ids.empty? ) end # Turn the list of (now numeric) user IDs into user objects. @users = User.where( :id => @reportable_user_ids ) end
Build the ‘tasks’ array if ‘#task_ids’ is updated externally.
# File lib/track_record_report.rb, line 268 def task_ids=( ids ) @provided_task_ids = map_raw_ids( ids ) assign_actual_tasks_from_provided_ids() end
Set a task array directly (will always be filtered according to security settings for the current user).
# File lib/track_record_report.rb, line 261 def tasks=( array ) @tasks = array update_internal_task_lists() end