YaleSTC/shifts

View on GitHub
app/models/location.rb

Summary

Maintainability
B
6 hrs
Test Coverage
class Location < ActiveRecord::Base
  validates_presence_of :loc_group
  validates_presence_of :name
  validates_presence_of :description
  validates_presence_of :short_name
  validates_presence_of :min_staff
  validates_presence_of :category
  validates_numericality_of :max_staff
  validates_numericality_of :min_staff
  validates_numericality_of :priority

  validates_uniqueness_of :name, scope: :loc_group_id
  validates_uniqueness_of :short_name, scope: :loc_group_id
  validate :max_staff_greater_than_min_staff

  scope :active, conditions: {active: true}
  scope :in_group,
    lambda {|loc_group,*order| {
      conditions: {loc_group_id: loc_group.id},
      order: order.flatten.first || 'priority ASC'
  }}

  belongs_to :loc_group
  belongs_to :category
  has_many :time_slots
  has_many :template_time_slots
  has_many :shifts
  has_many :tasks
  has_and_belongs_to_many :data_objects
  has_and_belongs_to_many :requested_shifts
  # These connect a location with the superclass notice and its subclasses
  has_and_belongs_to_many :notices
  has_and_belongs_to_many :announcements, join_table: :locations_notices, association_foreign_key: :notice_id
  has_and_belongs_to_many :links,         join_table: :locations_notices, association_foreign_key: :notice_id
  has_and_belongs_to_many :stickies,      join_table: :locations_notices, association_foreign_key: :notice_id
  has_and_belongs_to_many :restrictions

  delegate :department, to: :loc_group

  def admin_permission
    self.loc_group.admin_permission
  end

  def locations
    [self]
  end

  # Announcements and Stickies, not Links
  def current_notices
    ((self.notices & Notice.active_notices) + Notice.active.global).uniq
  end

  def restrictions #TODO: this could probalbly be optimized
    Restriction.current.select{|r| r.locations.include?(self)}
  end

  def deactivate
    self.active = false
    self.save!
    #Location activation must be set prior to individual shift activation; Shift class before_save
    shifts.after_date(Time.now.utc).update_all active: false
  end

  def activate
    self.active = true
    self.save!
    #Location activation must be set prior to individual shift activation; Shift class before_save
    @shifts = shifts.after_date(Time.now.utc)
    @shifts.each do |shift|
      if shift.user.is_active?(shift.department) && shift.calendar.active
        shift.active = true
      end
      shift.save
    end
  end

  def count_people_for(shift_list, min_block)
    people_count = {}
    people_count.default = 0
    unless shift_list.nil?
      shift_list.each do |shift|
        t = shift.start
        while (t<shift.end)
          people_count[t.to_s(:am_pm)] += 1
          t += min_block
        end
      end
    end
    people_count
  end

  #necessary for the public cluster view
  def is_staffed_in_list?(shift_list, time)
    time = time.in_time_zone
    remaining_shifts = shift_list.select{|s| s.start <= time && (s.submitted? ? s.report.departed : s.end) >= time && !s.missed? && (s.start + s.department.department_config.grace_period.minutes <= Time.now ? (s.signed_in? || s.submitted?) : true)}
    return remaining_shifts == [] ? false : true
  end

  #necessary for tasks
  #this really should be on the shift model I think
  def shifts_between(start_time, end_time)
    start_time = start_time.to_time
    end_time = end_time.to_time
    shifts = Shift.where("start >= ? AND end <= ? AND location_id = ? AND active = ?", start_time, end_time, self.id, true)
  end

  def summary_stats(start_date, end_date)
    shifts_set = shifts.on_days(start_date, end_date).active
    summary_stats = {}

    summary_stats[:start_date] = start_date
    summary_stats[:end_date] = end_date
    summary_stats[:total] = shifts_set.size
    summary_stats[:late] = shifts_set.select{|s| s.late == true}.size
    summary_stats[:missed] = shifts_set.select{|s| s.missed == true}.size
    summary_stats[:left_early] = shifts_set.select{|s| s.left_early == true}.size

    return summary_stats
  end

  def detailed_stats(start_date, end_date)
    shifts_set = shifts.on_days(start_date, end_date).active
    detailed_stats = {}

    shifts_set.each do |s|
       stat_entry = {}
       stat_entry[:id] = s.id
       stat_entry[:shift] = s.short_display
       stat_entry[:in] = s.created_at
       stat_entry[:out] = s.updated_at

       if s.missed
         stat_entry[:notes] = "Missed"
       elsif s.late && s.left_early
         stat_entry[:notes] = "Late and left early"
       elsif s.late
         stat_entry[:notes] = "Late"
       elsif s.left_early
         stat_entry[:notes] = "Left early"
       else
         stat_entry[:notes]
       end
       stat_entry[:missed] = s.missed
       stat_entry[:late] = s.late
       stat_entry[:left_early] = s.left_early
       stat_entry[:updates_hour] = s.updates_hour
       detailed_stats[s.id] = stat_entry
    end

    return detailed_stats
  end

  protected

  def max_staff_greater_than_min_staff
    errors.add("The minimum number of staff cannot be larger than the maximum.", "") if (self.min_staff and self.max_staff and self.min_staff > self.max_staff)
  end

end