class OpeningHoursConverter::OpeningHoursBuilder

Public Instance Methods

build(date_ranges) click to toggle source
# File lib/opening_hours_converter/opening_hours_builder.rb, line 6
def build(date_ranges)
  rules = []

  oh_rules = nil
  oh_rule_added = nil
  range_general = nil
  range_general_for = nil

  date_ranges.each_with_index do |date_range, date_range_index|
    next unless !date_range.nil?

    day_ph = false
    off_day_ph = false
    phs = []

    date_range.typical.intervals.each_with_index do |interval, interval_id|
      next unless interval&.day_start == -2 && interval&.day_start == interval&.day_end

      if interval.is_off
        off_day_ph = true
      else
        day_ph = true
      end

      phs << interval
      date_range.typical.remove_interval(interval_id)
    end

    range_general = nil
    range_general_for = nil
    range_general_id = date_range_index - 1

    while range_general_id >= 0 && range_general.nil?
      if !date_range.nil?
        general_for = date_ranges[range_general_id].is_general_for?(date_range)
        if date_ranges[range_general_id].has_same_typical?(date_range) && (date_ranges[range_general_id].wide_interval.equals(date_range.wide_interval) || general_for)
          range_general = range_general_id
        elsif general_for && date_ranges[range_general_id].defines_typical_week? && date_range.defines_typical_week?
          range_general_for = range_general_id
        end
      end
      range_general_id -= 1
    end

    next unless date_range_index == 0 || range_general.nil?
    if date_range.defines_typical_week?
      oh_rules = if !range_general_for.nil?
                   build_week_diff(date_range, date_ranges[range_general_for])
                 else
                   build_week(date_range)
                 end
    else
      oh_rules = build_day(date_range)
    end

    oh_rules.each_with_index do |_rule, i|
      oh_rules[i].add_comment(date_range.comment)
    end

    oh_rules.map do |oh_rule|
      oh_rule_added = false
      rule_index = 0

      while !oh_rule_added && rule_index < rules.length
        if rules[rule_index].same_time?(oh_rule) && !rules[rule_index].equals(oh_rule) && rules[rule_index].comment == oh_rule.comment
          begin
            for date_id in 0...oh_rule.date.length
              rules[rule_index].add_date(oh_rule.date[date_id])
            end
            oh_rule_added = true
          rescue Exception => e
            if oh_rule.date[0].wide_interval.type == 'holiday' && oh_rule.date[0].wide_interval.get_time_selector == 'PH'
              rules[rule_index].add_ph_weekday
              oh_rule_added = true
            elsif rules[rule_index].date[0].wide_interval.type == 'holiday' && rules[rule_index].date[0].wide_interval.get_time_selector == 'PH'
              oh_rule.add_ph_weekday
              rules[rule_index] = oh_rule
              oh_rule_added = true
            else
              rule_index += 1
            end
          end
        else
          rule_index += 1
        end
      end

      if off_day_ph || day_ph
        if date_range.typical.intervals.uniq == [nil]
          oh_rule.date.first.weekdays = [-2]
          if off_day_ph
            oh_rule.is_defined_off = off_day_ph
            phs = []
          else
            phs.reverse.each do |interval|
              oh_rule.add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, interval.end))
            end
          end
        else
          if times_are_compatible?(oh_rule, phs)
            holiday_intervals = get_compatible_intervals(oh_rule, phs)
            oh_rule.add_ph_weekday if holiday_intervals[:compatible].length > 0
            rules << oh_rule if !oh_rule_added
            oh_rule_added = true
            holiday_intervals[:incompatible].each do |interval|
              rules += build_off_holiday(date_range) if off_day_ph
              rules += build_holiday(date_range) if day_ph
            end
          else
            rules << oh_rule if !oh_rule_added
            oh_rule_added = true
            rules += build_off_holiday(date_range) if off_day_ph
            rules += build_holiday(date_range) if day_ph
          end
        end
      end

      rules << oh_rule if !oh_rule_added

      next unless oh_rule == oh_rules.last && oh_rule.has_overwritten_weekday?
      oh_rule_over = OpeningHoursConverter::OpeningHoursRule.new

      oh_rule.date.each do |date|
        oh_rule_over.add_date(OpeningHoursConverter::OpeningHoursDate.new(date.wide_interval, date.weekdays_over))
      end
      oh_rule_over.add_time(OpeningHoursConverter::OpeningHoursTime.new)
      oh_rules << oh_rule_over
    end
  end

  result = ''
  if rules.empty?
    date_ranges.each do |dr|
      result += "#{dr.wide_interval.get_time_selector} off"
    end
  else
    result += rules.map(&:get).join('; ')
  end

  result.strip
end
build_day(date_range) click to toggle source
# File lib/opening_hours_converter/opening_hours_builder.rb, line 223
def build_day(date_range)
  intervals = date_range.typical.get_intervals(true)

  rule = OpeningHoursConverter::OpeningHoursRule.new
  date = OpeningHoursConverter::OpeningHoursDate.new(date_range.wide_interval, [-1])
  rule.add_date(date)

  intervals.each do |interval|
    if !interval.nil?
      rule.add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, interval.end))
      rule.is_defined_off = rule.is_defined_off ? true : interval.is_off
    end
  end

  [rule]
end
build_holiday(date_range) click to toggle source
# File lib/opening_hours_converter/opening_hours_builder.rb, line 190
def build_holiday(date_range)
  start_year = date_range.wide_interval.start&.key?(:year) ? date_range.wide_interval.start[:year] : date_range.wide_interval.start
  end_year = date_range.wide_interval.end&.key?(:year) ? date_range.wide_interval.end[:year] : date_range.wide_interval.end
  intervals = date_range.typical.get_intervals(true)

  if date_range.wide_interval.type == 'week'
    intervals.each do |interval|
      date_range.typical.add_interval(OpeningHoursConverter::Interval.new(-2, interval.start, -2, interval.end, interval.is_off))
    end
  else
    date_range = OpeningHoursConverter::DateRange.new(OpeningHoursConverter::WideInterval.new.holiday('PH', start_year, end_year))
  end

  for i in 0..6
    intervals.each do |interval|
      if !interval.nil?
        date_range.typical.add_interval(OpeningHoursConverter::Interval.new(i, interval.start, i, interval.end, interval.is_off))
      end
    end
  end
  rule = OpeningHoursConverter::OpeningHoursRule.new
  date = OpeningHoursConverter::OpeningHoursDate.new(date_range.wide_interval, [-1])
  rule.add_date(date)

  date_range.typical.intervals.each do |interval|
    if !interval.nil?
      rule.add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, interval.end))
      rule.is_defined_off = rule.is_defined_off || interval.is_off
    end
  end
  [rule]
end
build_off_holiday(date_range) click to toggle source
# File lib/opening_hours_converter/opening_hours_builder.rb, line 176
def build_off_holiday(date_range)
  start_year = date_range.wide_interval.start&.key?(:year) ? date_range.wide_interval.start[:year] : date_range.wide_interval.start
  end_year = date_range.wide_interval.end&.key?(:year) ? date_range.wide_interval.end[:year] : date_range.wide_interval.end

  date_range = OpeningHoursConverter::DateRange.new(OpeningHoursConverter::WideInterval.new.holiday('PH', start_year, end_year))

  rule = OpeningHoursConverter::OpeningHoursRule.new
  date = OpeningHoursConverter::OpeningHoursDate.new(date_range.wide_interval, [-1])
  rule.add_date(date)
  rule.is_defined_off = true

  [rule]
end
build_week(date_range) click to toggle source
# File lib/opening_hours_converter/opening_hours_builder.rb, line 240
def build_week(date_range)
  result = []

  intervals = date_range.typical.get_intervals(true)
  days = create_time_intervals(date_range.wide_interval, intervals)

  days_status = Array.new(OSM_DAYS.length, 0)

  days.each_with_index do |day, index|
    if day.is_off? && days_status[index] == 0
      days_status[index] = 8
    elsif day.is_off? && days_status[index] < 0 && days_status[index] > -8
      days_status[index] = -8
      merged = false
      md_off = 0
      while !merged && md_off < index
        if days[md_off].is_off?
          days[md_off].add_weekday(index)
          merged = true
        else
          md_off += 1
        end
        result << days[index] if !merged
      end
    elsif days_status[index] <= 0 && days_status[index] > -8
      days_status[index] = index + 1
      last_same_day = index
      same_day_count = 1

      for j in (index + 1)...days.length do
        next unless day.same_time?(days[j])
        days_status[j] = index + 1
        day.add_weekday(j)
        last_same_day = j
        same_day_count += 1
      end
      if same_day_count == 1
        result << day
      elsif same_day_count == 2
        day.add_weekday(last_same_day)
        result << day
      elsif same_day_count > 2
        day.add_weekday(last_same_day)
        result << day
      end
    end
  end
  result = days if result == [] && days_status == [8, 8, 8, 8, 8, 8, 8]

  result = merge_days(result)

  result
end
build_week_diff(date_range, general_date_range) click to toggle source
# File lib/opening_hours_converter/opening_hours_builder.rb, line 294
def build_week_diff(date_range, general_date_range)
  intervals = date_range.typical.get_intervals_diff(general_date_range.typical)
  days = create_time_intervals(
    date_range.wide_interval,
    intervals[:open]
  )

  intervals[:closed].each do |interval|
    for i in interval.day_start..interval.day_end do
      days[i].add_time(OpeningHoursConverter::OpeningHoursTime.new)
    end
  end

  days_status = Array.new(OSM_DAYS.length, 0)
  result = []

  days.each_with_index do |day, index|
    if day.is_off? && day.time.length == 1
      days_status[index] = -8
      merged = false
      md_off = 0

      while !merged && md_off < index
        if days[md_off].is_off? && days[md_off].time.length == 1
          days[md_off].add_weekday(index)
          merged = true
        else
          md_off += 1
        end
      end

      result << day if !merged
    elsif day.is_off? && day.time.empty?
      days_status[index] = 8
    elsif days_status[index] <= 0 && days_status[index] > -8
      days_status[index] = index + 1
      same_day_count = 1
      last_same_day = 1
      result << day

      for j in (index + 1)...days.length do
        next unless day.same_time?(days[j])
        days_status[j] = index + 1
        day.add_weekday(j)
        last_same_day = j
        same_day_count += 1
      end

      if same_day_count == 1
        result << day
      elsif same_day_count == 2
        day.add_weekday(last_same_day)
        result << day
      elsif same_day_count > 2
        for j in (index + 1)...last_same_day do
          next unless days_status[j] == 0
          days_status[j] = -index - 1
          day.add_overwritten_weekday(j) if !days[j].time.empty?
        end
        day.add_weekday(last_same_day)
        result << day
      end

    end
  end
  result = merge_days(result)
  result
end
create_time_intervals(wide_interval, intervals) click to toggle source
# File lib/opening_hours_converter/opening_hours_builder.rb, line 388
def create_time_intervals(wide_interval, intervals)
  days = []

  for i in 0...7
    days << OpeningHoursConverter::OpeningHoursRule.new
    days[i].add_date(OpeningHoursConverter::OpeningHoursDate.new(wide_interval, [i]))
  end

  intervals.each do |interval|
    next if interval.nil?

    begin
      if interval.day_start == interval.day_end
        days[interval.day_start].add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, interval.end))
        days[interval.day_start].is_defined_off = days[interval.day_start].is_defined_off ? true : interval.is_off
      elsif interval.day_end - interval.day_start == 1
        days[interval.day_start].add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, MINUTES_MAX))
        days[interval.day_start].is_defined_off = days[interval.day_start].is_defined_off ? true : interval.is_off
        days[interval.day_end].add_time(OpeningHoursConverter::OpeningHoursTime.new(0, interval.end))
        days[interval.day_end].is_defined_off = days[interval.day_end].is_defined_off ? true : interval.is_off
      else
        for j in interval.day_start..interval.day_end
          days[j].is_defined_off = days[j].is_defined_off ? true : interval.is_off
          if j == interval.day_start
            days[j].add_time(OpeningHoursConverter::OpeningHoursTime.new(interval.start, MINUTES_MAX))
          elsif j == interval.day_end
            days[j].add_time(OpeningHoursConverter::OpeningHoursTime.new(0, interval.end))
          else
            days[j].add_time(OpeningHoursConverter::OpeningHoursTime.new(0, MINUTES_MAX))
          end
        end
      end
    rescue Exception => e
      puts e
    end
  end

  days
end
get_compatible_intervals(oh_rule, phs) click to toggle source
# File lib/opening_hours_converter/opening_hours_builder.rb, line 160
def get_compatible_intervals(oh_rule, phs)
  compatibility = {
    compatible: [],
    incompatible: []
  }

  compatibility[:compatible] = phs.select do |ph_interval|
    return false if ph_interval.is_off != oh_rule.is_defined_off

    oh_rule.time.any? { |rule_time| rule_time.start == ph_interval.start && rule_time.end == ph_interval.end }
  end
  compatibility[:incompatible] = phs - compatibility[:compatible]

  compatibility
end
merge_days(rules) click to toggle source
# File lib/opening_hours_converter/opening_hours_builder.rb, line 363
def merge_days(rules)
  return rules if rules.empty?
  result = []
  result << rules[0]
  dm = 0

  for d in 1...rules.length do
    date_merged = false
    dm = 0
    while !date_merged && dm < d
      if rules[dm].same_time?(rules[d])
        wds = rules[d].date[0].weekdays
        wds.each do |wd|
          rules[dm].add_weekday(wd)
        end
        date_merged = true
      end
      dm += 1
    end
    result << rules[d] if !date_merged
  end

  result
end
times_are_compatible?(oh_rule, phs) click to toggle source
# File lib/opening_hours_converter/opening_hours_builder.rb, line 148
def times_are_compatible?(oh_rule, phs)
  if oh_rule.is_defined_off
    phs.any?(&:is_off)
  else
    phs.any? do |ph_interval|
      oh_rule.time.any? do |rule_time|
        rule_time.start == ph_interval.start && rule_time.end == ph_interval.end
      end
    end
  end
end