首页 > 解决方案 > 在我的 Rails 应用程序中计算加班的最佳方法是什么?

问题描述

嗨,我正在为自己构建一个小型 Rails 会议应用程序。我有一张名为 Meetings 的表格,有 5 列 name:string start_date:datime end_date:datetime duration:integer overtime:integer

我的问题是是否有比我正在做的更好的计算加班时间的方法?

持续时间是每天 start_date 和 end_date 之间的总时间,每天可以有多个会议。

加班时间是每天的总持续时间减去 7 小时 25 分钟(26700 秒),每天超过 7.25 的任何总会议持续时间都是加班时间。我得到了我想要的结果(见下图),但我想知道是否有更好的方法?我在 index.html.erb 中使用了 if 语句。得到我想要的结果,因为保存到数据库的加班数据不是我需要的实际加班数据。

从下图中,我通常只希望将“22 分钟和 40 分钟”保存为整数。我还想展示每周的总加班时间,这就是为什么我认为我的方式行不通的原因。他们是更好的方法吗?

提前感谢任何输入。

我的 Meeting.rb 模型文件中的两个计算列持续时间和加班时间

class Meeting < ApplicationRecord

#callbacks save to db

 before_save :set_duration
  before_save :daily_total_ot

#calculate duration

 def set_duration
      self.duration = (end_date - start_date).to_i
    end

#Calculate total duration for each day 

def daily_total_duration
     
      Meeting.where("start_date >= ? AND end_date < ?", self.start_date.beginning_of_day, self.start_date.end_of_day).sum(:duration) 
      
 end

#calculate total overtime for each day 

  def daily_total_ot
     self.overtime = Meeting.where("start_date >= ? AND end_date < ?", self.start_date.beginning_of_day, self.start_date.end_of_day).sum(:duration) - 26700.to_i
    
      end

end 

在我看来,我有会议 .index.html.erb

<h1>Meetings</h1><%= Time.zone.now.strftime('%d-%b-%Y')  %>
<table class="table">
  <thead class="thead-dark">
    <tr>
      <th>Start date</th>
      <th>End date</th>
      <th>Duration</th>
      <th>Duration Time</th>
      <th>Daily Total <br>Duration</th>
      <th>overtime saved</th>
      <th>Real OT</th>
      <th>Show</th>
      <th>Edit</th>
      <th>Destroy</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @meetings.each do |meeting| %>
      <tr>
        <td><%= meeting.name %></td>
        <td><%= meeting.start_date.strftime("%d-%b-%Y %I:%M %p") %></td> 
        <td><%= meeting.end_date.strftime("%d-%b-%Y %I:%M %p") %></td>

        <td><%= meeting.duration %></td>
        
        <td><%= Time.at(meeting.daily_total_duration).utc.strftime("%H hours and %M minutes ") %></td>
       <td><%= meeting.daily_total_ot %></td>

       <% if  (meeting.daily_total_duration) > 26700 %>
        <td><%= Time.at(meeting.daily_total_ot).utc.strftime("%H hours and %M minutes ") %></td>
         <% else  %>
        <td>No Overtime</td>
        <% end %>
       
        <td><%= link_to 'Show', meeting %></td>
        <td><%= link_to 'Edit', edit_meeting_path(meeting) %></td>
        <td><%= link_to 'Destroy', meeting, method: :delete, data: { confirm: 'Are you sure?' } %></td>
        
      </tr>
    <% end %>
  </tbody>
</table>

<br><br>

在此处输入图像描述

标签: ruby-on-railsrubydatetime

解决方案


当前方法的问题是,您无法再次“保存”任何会议,否则会计算错误。

加班的“正确”查询是

def previous_meetings_for_same_day
  # meeting should start today ...
  start = start_date.beginning_of_day
  # ...and finish before this one
  limit = end_date - 1.second
  
  # notice the use of open ranges instead of raw SQL
  Meeting.where(start_date: start.., end_date: ..limit)
end

def daily_total_duration
  previous_meetings_for_same_day.sum(:duration).to_i + duration
end

def computed_overtime
  # clamp "forces" a value to fit in the given range
  # in this case if the receiver is negative, it returns 0
  (daily_total_duration - 26700).clamp(0..)
end

def daily_total_ot
  self.overtime = computed_overtime
end

这样,较早的会议将被考虑到以后的会议,而不是相反。

通过限制差异,您还可以避免保存负值。


更新:

如果您使用 PostgreSQL,则可以使用映射到ActiveSupport::Durationruby​​ 中的“interval”类型。

这样,这些方法会发生一些变化,因为它们不再是整数了:

MAX_DAILY_DURATION = 7.hours + 25.minutes

def set_duration
  self.duration = end_date - start_date
end

def daily_total_duration
  previous_meetings_for_same_day.sum(:duration) + duration
end

def computed_overtime
  # clamp "forces" a value to fit in the given range
  # in this case if the receiver is negative, it returns 0
  (daily_total_duration - MAX_DAILY_DURATION).clamp(0.seconds..)
end

def daily_total_ot
  self.overtime = computed_overtime
end

推荐阅读