aboutsummaryrefslogtreecommitdiffstats
path: root/app/models/chouette/journey_pattern.rb
blob: 0c1905286d8c29d441f5fd5a97f57d0c1cc9d343 (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
179
180
181
182
183
184
185
186
module Chouette
  class JourneyPattern < Chouette::TridentActiveRecord
    include ChecksumSupport
    include JourneyPatternRestrictions
    include ObjectidSupport
    # FIXME http://jira.codehaus.org/browse/JRUBY-6358
    self.primary_key = "id"

    belongs_to :route
    has_many :vehicle_journeys, :dependent => :destroy
    has_many :vehicle_journey_at_stops, :through => :vehicle_journeys
    has_and_belongs_to_many :stop_points, -> { order("stop_points.position") }, :before_add => :vjas_add, :before_remove => :vjas_remove, :after_add => :shortcuts_update_for_add, :after_remove => :shortcuts_update_for_remove
    has_many :stop_areas, through: :stop_points
    has_many :journey_pattern_sections
    has_many :route_sections, through: :journey_pattern_sections, dependent: :destroy

    validates_presence_of :route
    validates_presence_of :name

    #validates :stop_points, length: { minimum: 2, too_short: :minimum }, on: :update
    enum section_status: { todo: 0, completed: 1, control: 2 }

    attr_accessor  :control_checked
    after_update :control_route_sections, :unless => "control_checked"


    def local_id
      "IBOO-#{self.referential.id}-#{self.route.line.get_objectid.local_id}-#{self.id}"
    end

    def checksum_attributes
      values = self.slice(*['name', 'published_name', 'registration_number']).values
      values << self.stop_points.map(&:stop_area).map(&:user_objectid)
      values.flatten
    end

    def self.state_update route, state
      transaction do
        state.each do |item|
          item.delete('errors')
          jp = find_by(objectid: item['object_id']) || state_create_instance(route, item)
          next if item['deletable'] && jp.persisted? && jp.destroy
          # Update attributes and stop_points associations
          jp.update_attributes(state_permited_attributes(item)) unless item['new_record']
          jp.state_stop_points_update(item) if !jp.errors.any? && jp.persisted?
          item['errors'] = jp.errors if jp.errors.any?
        end

        if state.any? {|item| item['errors']}
          state.map {|item| item.delete('object_id') if item['new_record']}
          raise ::ActiveRecord::Rollback
        end
      end

      state.map {|item| item.delete('new_record')}
      state.delete_if {|item| item['deletable']}
    end

    def self.state_permited_attributes item
      {
        name: item['name'],
        published_name: item['published_name'],
        registration_number: item['registration_number']
      }
    end

    def self.state_create_instance route, item
      # Flag new record, so we can unset object_id if transaction rollback
      jp = route.journey_patterns.create(state_permited_attributes(item))

      # FIXME
      # DefaultAttributesSupport will trigger some weird validation on after save
      # wich will call to valid?, wich will populate errors
      # In this case, we mark jp to be valid if persisted? return true
      jp.errors.clear if jp.persisted?

      item['object_id']  = jp.objectid
      item['new_record'] = true
      jp
    end

    def state_stop_points_update item
      item['stop_points'].each do |sp|
        exist = stop_area_ids.include?(sp['id'])
        next if exist && sp['checked']

        stop_point = route.stop_points.find_by(stop_area_id: sp['id'])
        if !exist && sp['checked']
          stop_points << stop_point
        end
        if exist && !sp['checked']
          stop_points.delete(stop_point)
        end
      end
    end

    # TODO: this a workarround
    # otherwise, we loose the first stop_point
    # when creating a new journey_pattern
    def special_update
      bck_sp = self.stop_points.map {|s| s}
      self.update_attributes :stop_points => []
      self.update_attributes :stop_points => bck_sp
    end

    def departure_stop_point
      return unless departure_stop_point_id
      Chouette::StopPoint.find( departure_stop_point_id)
    end

    def arrival_stop_point
      return unless arrival_stop_point_id
      Chouette::StopPoint.find( arrival_stop_point_id)
    end

    def shortcuts_update_for_add( stop_point)
      stop_points << stop_point unless stop_points.include?( stop_point)

      ordered_stop_points = stop_points
      ordered_stop_points = ordered_stop_points.sort { |a,b| a.position <=> b.position} unless ordered_stop_points.empty?

      self.update_attributes( :departure_stop_point_id => (ordered_stop_points.first && ordered_stop_points.first.id),
                              :arrival_stop_point_id => (ordered_stop_points.last && ordered_stop_points.last.id))
    end

    def shortcuts_update_for_remove( stop_point)
      stop_points.delete( stop_point) if stop_points.include?( stop_point)

      ordered_stop_points = stop_points
      ordered_stop_points = ordered_stop_points.sort { |a,b| a.position <=> b.position} unless ordered_stop_points.empty?

      self.update_attributes( :departure_stop_point_id => (ordered_stop_points.first && ordered_stop_points.first.id),
                              :arrival_stop_point_id => (ordered_stop_points.last && ordered_stop_points.last.id))
    end

    def vjas_add( stop_point)
      return if new_record?

      vehicle_journeys.each do |vj|
        vjas = vj.vehicle_journey_at_stops.create :stop_point_id => stop_point.id
      end
    end

    def vjas_remove( stop_point)
      return if new_record?

      vehicle_journey_at_stops.where( :stop_point_id => stop_point.id).each do |vjas|
        vjas.destroy
      end
    end

    def control_route_sections
      stop_area_ids = self.stop_points.map(&:stop_area_id)
      control_route_sections_by_stop_areas(stop_area_ids)
    end

    def control_route_sections_by_stop_areas(stop_area_ids)
      journey_pattern_section_all
      i = 0
      to_control = false
      stop_area_ids.each_cons(2) do |a|
        jps = @route_sections_orders[i]
        i += 1
        unless jps
          to_control = true
          next
        end
        unless [jps.route_section.departure.id, jps.route_section.arrival.id] == a
          jps.destroy
          to_control = true
        end
      end
      self.control_checked = true
      to_control ? self.control! : self.completed!
    end

    protected

    def journey_pattern_section_all
      @route_sections_orders = {}
      self.journey_pattern_sections.all.map do |journey_pattern_section|
        @route_sections_orders[journey_pattern_section.rank] = journey_pattern_section
      end
    end
  end
end