Class | TrackRecordReport::Report |
In: |
lib/track_record_report.rb
|
Parent: | ReportElementaryCalculator |
Class which manages a report.
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. |
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. |
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 331 331: def self.label( frequency ) 332: return Report::FREQUENCY[ frequency ][ :label ] 333: 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 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
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 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).
# 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.
# 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.
# File lib/track_record_report.rb, line 346 346: def column_title 347: return @frequency_data[ :title ] 348: end
Compile the report.
# 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.
# 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.
# 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.
# 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.
# File lib/track_record_report.rb, line 238 238: def inactive_tasks 239: @tasks.select { | task | ! task.active } 240: 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 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.
# 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).
# 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.
# 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