aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stif/reflex_synchronization.rb
blob: 7570e4c49e1a9819dd818f4a5f4a028182a3f538 (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
187
188
189
module Stif
  module ReflexSynchronization
    class << self
      attr_accessor :imported_count, :updated_count, :deleted_count, :processed

      def reset_counts
        self.imported_count = 0
        self.updated_count  = 0
        self.deleted_count  = 0
        self.processed      = []
      end

      def processed_counts
        {
          imported: self.imported_count,
          updated: self.updated_count,
          deleted: self.deleted_count
        }
      end

      def log_processing_time message, time
        Rails.logger.info "Reflex:sync - #{message} done in #{time} seconds"
      end

      def increment_counts prop_name, value
        self.send("#{prop_name}=", self.send(prop_name) + value)
      end

      def defaut_referential
        StopAreaReferential.find_by(name: "Reflex")
      end

      def find_by_object_id objectid
        Chouette::StopArea.find_by(objectid: objectid)
      end

      def save_if_valid object
        if object.valid?
          object.save
        else
          Rails.logger.error "Reflex:sync - #{object.class.model_name} with objectid #{object.objectid} can't be saved - errors : #{object.errors.messages}"
        end
      end

      def synchronize
        reset_counts
        ['getOR', 'getOP'].each do |method|
          start   = Time.now
          results = Reflex::API.new().process(method)
          log_processing_time("Process #{method}", Time.now - start)
          stop_areas = results[:Quay] | results[:StopPlace]

          time = Benchmark.measure do
            stop_areas.each do |entry|
              next unless is_valid_type_of_place_ref?(method, entry)
              entry['TypeOfPlaceRef'] = self.stop_area_area_type entry, method
              self.create_or_update_stop_area entry
              self.processed << entry['id']
            end
          end
          log_processing_time("Create or update StopArea", time.real)

          time = Benchmark.measure do
            stop_areas.map{|entry| self.stop_area_set_parent(entry)}
          end
          log_processing_time("StopArea set parent", time.real)
        end

        # Set deleted_at for item not returned by api since last sync
        time = Benchmark.measure { self.set_deleted_stop_area }
        log_processing_time("StopArea #{self.deleted_count} deleted", time.real)
        self.processed_counts
      end

      def is_valid_type_of_place_ref? method, entry
        return true if entry["TypeOfPlaceRef"].nil?
        return true if method == 'getOR' && ['ZDL', 'LDA', 'ZDE'].include?(entry["TypeOfPlaceRef"])
        return true if method == 'getOP' && ['ZDE', 'ZDL'].include?(entry["TypeOfPlaceRef"])
      end

      def stop_area_area_type entry, method
        from = method.last
        from = 'r' if entry['OBJECT_STATUS'] == 'REFERENCE_OBJECT'
        from = 'p' if entry['OBJECT_STATUS'] == 'LOCAL_OBJECT'
        type = entry['TypeOfPlaceRef']

        if entry['type'] == 'Quay'
          type = "zde#{from}"
        else
          type = "zdl#{from}" if type != 'LDA'
        end
        type.downcase
      end

      def set_deleted_stop_area
        deleted = Chouette::StopArea.where(deleted_at: nil).pluck(:objectid).uniq - self.processed.uniq
        deleted.each_slice(50) do |object_ids|
          Chouette::StopArea.where(objectid: object_ids).update_all(deleted_at: Time.now)
        end
        increment_counts :deleted_count, deleted.size
      end

      def stop_area_set_parent entry
        return false unless entry['parent'] || entry['quays']
        stop = self.find_by_object_id entry['id']
        return false unless stop

        if entry['parent']
          stop.parent = self.find_by_object_id entry['parent']
          save_if_valid(stop) if stop.changed?
        end

        if entry['quays']
          entry['quays'].each do |id|
            children = self.find_by_object_id id
            next unless children
            children.parent = stop
            save_if_valid(children) if children.changed?
          end
        end
      end

      def access_point_access_type entry
        if entry['IsEntry'] ==  'true' && entry['IsExit'] == 'true'
          'in_out'
        elsif entry['IsEntry'] == 'true'
          'in'
        elsif entry['IsExit'] == 'true'
          'out'
        end
      end

      def create_or_update_access_point entry, stop_area
        access = Chouette::AccessPoint.find_or_create_by(objectid: entry['id'])
        # Hack, on save object_version will be incremented by 1
        entry['version']   = entry['version'].to_i + 1  if access.persisted?
        access.access_type = self.access_point_access_type(entry)
        access.stop_area = stop_area
        {
          :name           => 'Name',
          :object_version => 'version',
          :zip_code       => 'PostalRegion',
          :city_name      => 'Town'
        }.each do |k, v| access[k] = entry[v] end
        if entry['gml:pos']
          access['longitude'] = entry['gml:pos'][:lng]
          access['latitude']  = entry['gml:pos'][:lat]
        end
        save_if_valid(access) if access.changed?
      end

      def create_or_update_stop_area entry
        stop = Chouette::StopArea.find_or_create_by(objectid: entry['id'], stop_area_referential: self.defaut_referential )
        stop.kind = :commercial
        stop.deleted_at            = nil
        {
          :comment        => 'Description',
          :name           => 'Name',
          :area_type      => 'TypeOfPlaceRef',
          :object_version => 'version',
          :zip_code       => 'PostalRegion',
          :city_name      => 'Town',
          :stif_type      => 'OBJECT_STATUS'
        }.each do |k, v| stop[k] = entry[v] end
        # TODO: use stop.update_attributes instead of the above

        if entry['gml:pos']
          stop['longitude'] = entry['gml:pos'][:lng]
          stop['latitude']  = entry['gml:pos'][:lat]
        end

        if stop.changed?
          stop.created_at = entry[:created]
          stop.import_xml = entry[:xml]
          prop = stop.new_record? ? :imported_count : :updated_count
          increment_counts prop, 1
          save_if_valid(stop)
        end
        # Create AccessPoint from StopPlaceEntrance
        if entry[:stop_place_entrances]
          entry[:stop_place_entrances].each do |entrance|
            self.create_or_update_access_point entrance, stop
          end
        end
        stop
      end
    end
  end
end