af83/chouette-core

View on GitHub
app/models/concerns/timetable_support.rb

Summary

Maintainability
C
1 day
Test Coverage
module TimetableSupport
  extend ActiveSupport::Concern

  def presenter
    @presenter ||= ::TimeTablePresenter.new( self)
  end

  def periods_max_date
    return nil if self.periods.empty?

    min_start = self.periods.map(&:period_start).compact.min
    max_end = self.periods.map(&:period_end).compact.max
    result = nil

    if max_end && min_start
      max_end.downto( min_start) do |date|
        if self.valid_days.include?(date.cwday) && !self.excluded_date?(date)
            result = date
            break
        end
      end
    end
    result
  end

  def periods_min_date
    return nil if self.periods.empty?

    min_start = self.periods.map(&:period_start).compact.min
    max_end = self.periods.map(&:period_end).compact.max
    result = nil

    if max_end && min_start
      min_start.upto(max_end) do |date|
        if self.valid_days.include?(date.cwday) && !self.excluded_date?(date)
            result = date
            break
        end
      end
    end
    result
  end

  def bounding_dates
    bounding_min = self.all_dates.select{|d| d.in_out}.map(&:date).compact.min
    bounding_max = self.all_dates.select{|d| d.in_out}.map(&:date).compact.max

    unless self.periods.empty?
      bounding_min = periods_min_date if periods_min_date &&
          (bounding_min.nil? || (periods_min_date < bounding_min))

      bounding_max = periods_max_date if periods_max_date &&
          (bounding_max.nil? || (bounding_max < periods_max_date))
    end

    [bounding_min, bounding_max].compact
  end

  def month_inspect(date)
    (date.beginning_of_month..date.end_of_month).map do |d|
      {
        day: I18n.l(d, format: '%A'),
        date: d.to_s,
        wday: d.wday,
        wnumber: d.strftime("%W").to_s,
        mday: d.mday,
        include_date: include_in_dates?(d),
        excluded_date: excluded_date?(d)
      }
    end
  end

  def include_day?(day)
    include_in_dates?(day) || include_in_periods?(day)
  end
  alias include? include_day?

  def include_in_periods?(day)
    return false if excluded_date?(day)
    return false unless valid_days.include?(day.cwday)

    self.periods.any? do |period|
      period.period_start <= day && day <= period.period_end
    end
  end

  def include_in_dates?(day)
    self.dates.any?{ |d| d.date === day && d.in_out == true }
  end

  def excluded_date?(day)
    self.dates.any?{ |d| d.date === day && d.in_out == false }
  end

  # Returns a Period on boundings dates
  def period
    from, to = bounding_dates
    Period.new(from: from, to: to)
  end

  def state_update state
    update_attributes(self.class.state_permited_attributes(state))
    self.calendar_id = nil if self.respond_to?(:calendar_id) && !state['calendar']

    days = state['day_types'].split(',')
    Date::DAYNAMES.map(&:underscore).each do |name|
      prefix = human_attribute_name(name).first(2)
      send("#{name}=", days.include?(prefix))
    end

    # Delete dates to avoid overlap and build or update dates in memory
    deleted_dates = []
    state['current_month'].each do |d|
      date    = Date.parse(d['date'])
      checked = d['include_date'] || d['excluded_date']
      in_out  = d['include_date'] ? true : false

      date_id = saved_dates.key(date)
      time_table_date = self.find_date_by_id(date_id) if date_id

      if checked && time_table_date.present?
        self.update_in_out time_table_date, in_out # Update date
      elsif checked && time_table_date.blank?
        self.build_date in_out, date # Build date
      elsif !checked && time_table_date.present?
        deleted_dates << time_table_date # Delete date
      end
    end
    deleted_dates.each do |deleted_date|
      dates.delete deleted_date
    end

    # Delete periods to avoid overlap and build or update periods in memory
    deleted_periods = []
    state_periods = state['time_table_periods'].delete_if do |item|
      if item['deleted']
        period = self.find_period_by_id(item['id'])
        deleted_periods << period
        true
      else
        false
      end
    end
    self.delete_periods(deleted_periods)

    state_periods.each do |item|
      period = self.find_period_by_id(item['id']) if item['id']
      period ||= self.build_period

      period.period_start = Date.parse(item['period_start'])
      period.period_end   = Date.parse(item['period_end'])
    end

    self.save
  end

end