aboutsummaryrefslogtreecommitdiffstats
path: root/app/models/calendar.rb
blob: bb38e74df0cec4489e382b8b9e3b1b2a7eeab3d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
require 'range_ext'
require_relative 'calendar/date_value'
require_relative 'calendar/period'

class Calendar < ActiveRecord::Base

  belongs_to :organisation
  has_many :time_tables

  validates_presence_of :name, :short_name, :organisation
  validates_uniqueness_of :short_name

  after_initialize :init_dates_and_date_ranges

  scope :contains_date, ->(date) { where('date ? = any (dates) OR date ? <@ any (date_ranges)', date, date) }

  def init_dates_and_date_ranges
    self.dates ||= []
    self.date_ranges ||= []
  end

  def self.ransackable_scopes(auth_object = nil)
    [:contains_date]
  end

  def convert_to_time_table
    Chouette::TimeTable.new.tap do |tt|
      self.dates.each do |d|
        tt.dates << Chouette::TimeTableDate.new(date: d, in_out: true)
      end
      self.periods.each do |p|
        tt.periods << Chouette::TimeTablePeriod.new(period_start: p.begin, period_end: p.end)
      end
      tt.int_day_types = 508
    end
  end


  ### Calendar::Period
  # Required by coocon
  def build_period
    Calendar::Period.new
  end

  def periods
    @periods ||= init_periods
  end

  def init_periods
    (date_ranges || [])
      .each_with_index
      .map( &Calendar::Period.method(:from_range) )
  end
  private :init_periods

  validate :validate_periods

  def validate_periods
    periods_are_valid = periods.all?(&:valid?)

    periods.each do |period|
      if period.intersect?(periods)
        period.errors.add(:base, I18n.t('calendars.errors.overlapped_periods'))
        periods_are_valid = false
      end
    end

    unless periods_are_valid
      errors.add(:periods, :invalid)
    end
  end

  def flatten_date_array attributes, key
    date_int = %w(1 2 3).map {|e| attributes["#{key}(#{e}i)"].to_i }
    Date.new(*date_int)
  end

  def periods_attributes=(attributes = {})
    @periods = []
    attributes.each do |index, period_attribute|
      # Convert date_select to date
      ['begin', 'end'].map do |attr|
        period_attribute[attr] = flatten_date_array(period_attribute, attr)
      end
      period = Calendar::Period.new(period_attribute.merge(id: index))
      @periods << period unless period.marked_for_destruction?
    end

    date_ranges_will_change!
  end

  before_validation :fill_date_ranges

  def fill_date_ranges
    if @periods
      self.date_ranges = @periods.map(&:range).compact.sort_by(&:begin)
    end
  end

  after_save :clear_periods

  def clear_periods
    @periods = nil
  end

  private :clear_periods

  ### Calendar::DateValue

  # Required by coocon
  def build_date_value
    Calendar::DateValue.new
  end

  def date_values
    @date_values ||= init_date_values
  end

  def init_date_values
    if dates
      dates.each_with_index.map { |d, index| Calendar::DateValue.from_date(index, d) }
    else
      []
    end
  end
  private :init_date_values

  validate :validate_date_values

  def validate_date_values
    date_values_are_valid = date_values.all?(&:valid?)

    date_values.each do |date_value|
      if date_values.count { |d| d.value == date_value.value } > 1
        date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_dates'))
        date_values_are_valid = false
      end
      date_ranges.each do |date_range|
        if date_range.cover? date_value.value
          date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges'))
          date_values_are_valid = false
        end
      end
    end

    unless date_values_are_valid
      errors.add(:date_values, :invalid)
    end
  end

  def date_values_attributes=(attributes = {})
    @date_values = []
    attributes.each do |index, date_value_attribute|
      date_value_attribute['value'] = flatten_date_array(date_value_attribute, 'value')
      date_value = Calendar::DateValue.new(date_value_attribute.merge(id: index))
      @date_values << date_value unless date_value.marked_for_destruction?
    end

    dates_will_change!
  end

  before_validation :fill_dates

  def fill_dates
    if @date_values
      self.dates = @date_values.map(&:value).compact.sort
    end
  end

  after_save :clear_date_values

  def clear_date_values
    @date_values = nil
  end

  private :clear_date_values

end