在 Rails Ruby 中导出到 CSV 并汇总小时数

Exporting to CSV and Summing hours in Ruby on Rails

我有一个 Rails 3.2.21 应用程序,我正在其中构建时钟功能。我目前正在编写一个应该执行以下操作的 to_csv 方法:

创建一个包含列名的 header 行 遍历输入(记录)块并显示员工用户名、clock_in、clock_out、站点和评论 objects,最后在块的最后一行显示总小时数.

我想在每个用户之间显示他们的总小时数。正如您在 to_csv 方法中看到的那样,我可以通过将 csv << [TimeFormatter.format_time(ce.user.clock_events.sum(&:total_hours))] 的数组铲入 CSV 来使其工作 "hackish"。最终结果是它确实为每个员工 clock_events 提供了正确的总小时数,但它会在每次输入后重复它,因为我显然是在迭代一个块。

我想找出一种方法将其抽象到块外,并弄清楚如何铲入另一个数组,该数组计算 total_hours 用户的所有时钟事件而没有重复条目。

以下是我的模型,所以如果有什么不清楚的地方,请告诉我。另外,如果我的问题令人困惑或没有意义,请告诉我,我很乐意澄清。

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

在朋友的帮助和研究 APIdocs 的帮助下,我能够重构该方法:

def self.to_csv(records = [], options = {})
      CSV.generate(options) do |csv|
        csv << ["Employee", "Clock-In", "Clock-Out", "Station", "Comment", "Total Shift Hours"]
        # records.group_by{ |r| r.user }.each do |user, records|
          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(records.select{ |r| r.user == ce.user }.sum(&:total_hours))]
        end
       records.map(&:user).uniq.each do |user|
         csv << ["Total Hours for: #{user.username}"]
         csv << [TimeFormatter.format_time(records.select{ |r| r.user == user}.sum(&:total_hours))]
       end

        csv << ["Total Payroll Hours"]
        csv << [TimeFormatter.format_time(records.sum(&:total_hours))]
      end
    end