aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stif/codif_line_synchronization.rb
blob: 9da0e30cc880596ce632be32a491d0226c3ac35d (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
module Stif
  module CodifLineSynchronization
    class << self
      # Don't check last synchronizations if force_sync
      def synchronize force_sync = false
        # Check last synchronization and synchronization interval
        date = DateTime.now.to_date - LineReferential.first.sync_interval.days
        last_sync = LineReferential.first.line_referential_sync.line_sync_operations.where(status: :ok).last.try(:created_at)
        return if last_sync.present? && last_sync.to_date > date && !force_sync

        start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
        # TODO Check exceptions and status messages
        begin
          # Fetch Codifline data
          client = Codifligne::API.new
          operators       = client.operators
          lines           = client.lines
          networks        = client.networks
          groups_of_lines = client.groups_of_lines

          Rails.logger.info "Codifligne:sync - Codifligne request processed in #{elapsed_time_since start_time} seconds"

          # Create or update Companies
          stime = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
          operators.map       { |o| create_or_update_company(o) }
          log_create_or_update "Companies", operators.count, stime

          # Create or update Lines
          stime = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
          lines.map           { |l| create_or_update_line(l) }
          log_create_or_update "Lines", lines.count, stime

          # Create or update Networks
          stime = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
          networks.map        { |n| create_or_update_network(n) }
          log_create_or_update "Networks", networks.count, stime

          # Create or update Group of lines
          stime = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
          groups_of_lines.map { |g| create_or_update_group_of_lines(g) }
          log_create_or_update "Group of lines", groups_of_lines.count, stime

          # Delete deprecated Group of lines
          deleted_gr = delete_deprecated(groups_of_lines, Chouette::GroupOfLine)
          log_deleted "Group of lines", deleted_gr unless deleted_gr == 0

          # Delete deprecated Networks
          deleted_ne = delete_deprecated(networks, Chouette::Network)
          log_deleted "Networks", deleted_ne unless deleted_ne == 0

          # Delete deprecated Lines
          deleted_li = delete_deprecated_lines(lines)
          log_deleted "Lines", deleted_li unless deleted_li == 0

          # Delete deprecated Operators
          deleted_op = delete_deprecated(operators, Chouette::Company)
          log_deleted "Operators", deleted_op unless deleted_op == 0

          # Building log message
          total_codifligne_elements = operators.count + lines.count + networks.count + groups_of_lines.count
          total_deleted = deleted_op + deleted_li + deleted_ne + deleted_gr
          total_time = elapsed_time_since start_time

          LineReferential.first.line_referential_sync.record_status :ok, I18n.t('synchronization.codifligne.message.success', time: total_time, imported: total_codifligne_elements, deleted: total_deleted)
        rescue Exception => e
          total_time = elapsed_time_since start_time

          Rails.logger.error "Codifligne:sync - Error: #{e}, ended after #{total_time} seconds"
          LineReferential.first.line_referential_sync.record_status :ko, I18n.t('synchronization.codifligne.message.failure', time: total_time)
        end
      end

      def create_or_update_company(api_operator)
        params = {
          name: api_operator.name,
          objectid: api_operator.stif_id,
          import_xml: api_operator.xml
        }
        save_or_update(params, Chouette::Company)
      end

      def create_or_update_line(api_line)
        params = {
          name: api_line.name,
          objectid: api_line.stif_id,
          number: api_line.short_name,
          deactivated: (api_line.status == "inactive" ? true : false),
          import_xml: api_line.xml
        }

        # Find Company
        # TODO Check behavior when operator_codes count is 0 or > 1
        if api_line.operator_codes.any?
          company_id = "STIF:CODIFLIGNE:Operator:" + api_line.operator_codes.first
          params[:company] = Chouette::Company.find_by(objectid: company_id)
        end

        save_or_update(params, Chouette::Line)
      end

      def create_or_update_network(api_network)
        params = {
          name: api_network.name,
          objectid: api_network.stif_id,
          import_xml: api_network.xml
        }

        # Find Lines
        params[:lines] = []
        api_network.line_codes.each do |line|
          line_id = "STIF:CODIFLIGNE:Line:" + line
          params[:lines] << Chouette::Line.find_by(objectid: line_id)
        end

        save_or_update(params, Chouette::Network)
      end

      def create_or_update_group_of_lines(api_group_of_lines)
        params = {
          name: api_group_of_lines.name,
          objectid: api_group_of_lines.stif_id,
          import_xml: api_group_of_lines.xml
        }

        # Find Lines
        params[:lines] = []
        api_group_of_lines.line_codes.each do |line|
          line_id = "STIF:CODIFLIGNE:Line:" + line
          # TODO : handle when lines doesn't exist
          chouette_line = Chouette::Line.find_by(objectid: line_id)
          params[:lines] << chouette_line if chouette_line.present?
        end

        save_or_update(params, Chouette::GroupOfLine)
      end

      def delete_deprecated(objects, klass)
        ids = objects.map{ |o| o.stif_id }.to_a
        deprecated = klass.where.not(objectid: ids)
        deprecated.destroy_all.length
      end

      def delete_deprecated_lines(lines)
        ids = lines.map{ |l| l.stif_id }.to_a
        deprecated = Chouette::Line.where.not(objectid: ids).where(deactivated: false)
        deprecated.update_all deactivated: true
      end

      def save_or_update(params, klass)
        params[:line_referential] = LineReferential.first
        object = klass.where(objectid: params[:objectid]).first
        if object
          object.assign_attributes(params)
          object.save if object.changed?
        else
          object = klass.new(params)
          object.save if object.valid?
        end
        object
      end

      def elapsed_time_since start_time = 0
        Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) - start_time
      end

      def log_create_or_update name, count, start_time
        time = elapsed_time_since start_time
        Rails.logger.info "Codifligne:sync - #{count} #{name} retrieved"
        Rails.logger.info "Codifligne:sync - Create or update #{name} done in #{time} seconds"
      end

      def log_deleted name, count
        Rails.logger.info "Codifligne:sync - #{count} #{name} deleted"
      end
    end
  end
end