jeudi 25 juin 2015

Exporting to CSV and Summing hours in Ruby on Rails

I have a Rails 3.2.21 app where I'm building time clock functionality. I'm currently writing a to_csv method that should do the following:

Create a header row with column names Iterate through a block of input (records) and display the employee username, clock_in, clock_out, station, and comment objects, then finally on the last line of the block display the total hours.

In between each user I want to display a sum of their total hours. As you can see in the to_csv method I'm able to get this to work "hackish" by shoveling an array of csv << [TimeFormatter.format_time(ce.user.clock_events.sum(&:total_hours))] into the CSV. The end result is it does give me the proper total hours for each employee's clock_events, but it repeats it after every entry because I'm obviously iterating over a block.

I'd like to figure out a way to abstract this outside of the block and figure out how to shovel in another array that calculates total_hours for all clock events by user without duplicate entries.

Below is my model, so if something is not clear, please let me know. Also if my question is confusing or doesn't make sense let me know and I'll be happy to clarify.

class ClockEvent < ActiveRecord::Base
  attr_accessible :clock_in, :clock_out, :user_id, :station_id, :comment
  belongs_to :user
  belongs_to :station
  scope :incomplete, -> { where(clock_out: nil) }
  scope :complete, -> { where("clock_out IS NOT NULL") }
  scope :current_week, -> {where("clock_in BETWEEN ? AND ?", Time.zone.now.beginning_of_week - 1.day, Time.zone.now.end_of_week - 1.day)}
  scope :search_between, lambda { |start_date, end_date| where("clock_in BETWEEN ? AND ?", start_date.beginning_of_day, end_date.end_of_day)}
  scope :search_by_start_date,  lambda { |start_date| where('clock_in BETWEEN ? AND ?', start_date.beginning_of_day, start_date.end_of_day) }
  scope :search_by_end_date, lambda { |end_date| where('clock_in BETWEEN ? AND ?', end_date.beginning_of_day, end_date.end_of_day) }

  def punch_in(station_id)
    self.clock_in = Time.zone.now
    self.station_id = station_id
  end

  def punch_out
    self.clock_out = Time.zone.now
  end

  def completed?
    clock_in.present? && clock_out.present?
  end

  def total_hours
    self.clock_out.to_i - self.clock_in.to_i
  end    

  def formatted_clock_in
    clock_in.try(:strftime, "%m/%d/%y-%H:%M")
  end

  def formatted_clock_out
    clock_out.try(:strftime, "%m/%d/%y-%H:%M")
  end

  def self.search(search)
    search ||= { type: "all" }

    results = scoped

    # If searching with BOTH a start and end date
    if search[:start_date].present? && search[:end_date].present?
      results = results.search_between(Date.parse(search[:start_date]), Date.parse(search[:end_date]))

      # If search with any other date parameters (including none)
    else
      results = results.search_by_start_date(Date.parse(search[:start_date])) if search[:start_date].present?
      results = results.search_by_end_date(Date.parse(search[:end_date])) if search[:end_date].present?
    end
    results
  end

  def self.to_csv(records = [], options = {})
    CSV.generate(options) do |csv|
      csv << ["Employee", "Clock-In", "Clock-Out", "Station", "Comment", "Total Shift Hours"]
      records.each do |ce|
        csv << [ce.user.try(:username), ce.formatted_clock_in, ce.formatted_clock_out, ce.station.try(:station_name), ce.comment, TimeFormatter.format_time(ce.total_hours)] 
        csv << [TimeFormatter.format_time(ce.user.clock_events.sum(&:total_hours))]      
      end
      csv << [TimeFormatter.format_time(records.sum(&:total_hours))]
    end
  end

end

Aucun commentaire:

Enregistrer un commentaire