diff options
Diffstat (limited to 'app/models/merge.rb')
| -rw-r--r-- | app/models/merge.rb | 125 | 
1 files changed, 110 insertions, 15 deletions
| diff --git a/app/models/merge.rb b/app/models/merge.rb index 62bf581d6..7df7aa590 100644 --- a/app/models/merge.rb +++ b/app/models/merge.rb @@ -1,4 +1,4 @@ -class Merge < ActiveRecord::Base +class Merge < ApplicationModel    extend Enumerize    belongs_to :workbench @@ -50,7 +50,7 @@ class Merge < ActiveRecord::Base      new =        if workbench.output.current          Rails.logger.debug "Clone current output" -        Referential.new_from(workbench.output.current, fixme_functional_scope).tap do |clone| +        Referential.new_from(workbench.output.current, workbench.organisation).tap do |clone|            clone.inline_clone = true          end        else @@ -135,10 +135,18 @@ class Merge < ActiveRecord::Base      referential_stop_points_by_route = referential_stop_points.group_by(&:route_id) +    referential_routes_constraint_zones = referential.switch do +      referential.routing_constraint_zones.each_with_object(Hash.new { |h,k| h[k] = [] }) do |routing_constraint_zone, hash| +        hash[routing_constraint_zone.route_id] << routing_constraint_zone +      end +    end +      new.switch do        referential_routes.each do |route|          existing_route = new.routes.find_by line_id: route.line_id, checksum: route.checksum -        unless existing_route +        if existing_route +          existing_route.merge_metadata_from route +        else            objectid = Chouette::Route.where(objectid: route.objectid).exists? ? nil : route.objectid            attributes = route.attributes.merge(              id: nil, @@ -152,21 +160,50 @@ class Merge < ActiveRecord::Base            route_stop_points = referential_stop_points_by_route[route.id]            # Stop Points -          route_stop_points.each do |stop_point| +          route_stop_points.sort_by(&:position).each do |stop_point|              objectid = Chouette::StopPoint.where(objectid: stop_point.objectid).exists? ? nil : stop_point.objectid              attributes = stop_point.attributes.merge(                id: nil,                route_id: nil,                objectid: objectid,              ) -              new_route.stop_points.build attributes            end +          # We need to create StopPoints to known new primary keys +          new_route.save! + +          old_stop_point_ids = route_stop_points.sort_by(&:position).map(&:id) +          new_stop_point_ids = new_route.stop_points.sort_by(&:position).map(&:id) + +          stop_point_ids_mapping = Hash[[old_stop_point_ids, new_stop_point_ids].transpose] + +          # RoutingConstraintZones +          routes_constraint_zones = referential_routes_constraint_zones[route.id] + +          routes_constraint_zones.each do |routing_constraint_zone| +            objectid = new.routing_constraint_zones.where(objectid: routing_constraint_zone.objectid).exists? ? nil : routing_constraint_zone.objectid +            stop_point_ids = routing_constraint_zone.stop_point_ids.map { |id| stop_point_ids_mapping[id] }.compact + +            if stop_point_ids.size != routing_constraint_zone.stop_point_ids.size +              raise "Can't find all required StopPoints for RoutingConstraintZone #{routing_constraint_zone.inspect}" +            end + +            attributes = routing_constraint_zone.attributes.merge( +              id: nil, +              route_id: nil, +              objectid: objectid, +              stop_point_ids: stop_point_ids, +            ) +            new_route.routing_constraint_zones.build attributes + +            # No checksum check. RoutingConstraintZones are always recreated +          end +            new_route.save!            if new_route.checksum != route.checksum -            raise "Checksum has changed: #{route.inspect} #{new_route.inspect}" +            raise "Checksum has changed: \"#{route.checksum}\", \"#{route.checksum_source}\" -> \"#{new_route.checksum}\", \"#{new_route.checksum_source}\""            end          end        end @@ -197,7 +234,9 @@ class Merge < ActiveRecord::Base          existing_journey_pattern = new.journey_patterns.find_by route_id: existing_associated_route.id, checksum: journey_pattern.checksum -        unless existing_journey_pattern +        if existing_journey_pattern +          existing_journey_pattern.merge_metadata_from journey_pattern +        else            objectid = Chouette::JourneyPattern.where(objectid: journey_pattern.objectid).exists? ? nil : journey_pattern.objectid            attributes = journey_pattern.attributes.merge(              id: nil, @@ -221,7 +260,7 @@ class Merge < ActiveRecord::Base            new_journey_pattern = new.journey_patterns.create! attributes            if new_journey_pattern.checksum != journey_pattern.checksum -            raise "Checksum has changed for #{journey_pattern.inspect}: #{journey_pattern.checksum_source} #{new_journey_pattern.checksum_source} " +            raise "Checksum has changed for #{journey_pattern.inspect} (to #{new_journey_pattern.inspect}): \"#{journey_pattern.checksum_source}\" -> \"#{new_journey_pattern.checksum_source}\""            end          end        end @@ -233,16 +272,37 @@ class Merge < ActiveRecord::Base        referential.vehicle_journeys.includes(:vehicle_journey_at_stops).all.to_a      end +    referential_purchase_windows_by_checksum, referential_vehicle_journey_purchase_window_checksums = referential.switch do +      purchase_windows_by_checksum = referential.purchase_windows.each_with_object({}) do |purchase_window, hash| +        hash[purchase_window.checksum] = purchase_window +      end + +      vehicle_journey_purchase_window_checksums = Hash.new { |h,k| h[k] = [] } +      referential.purchase_windows.joins(:vehicle_journeys).pluck("vehicle_journeys.id", :checksum).each do |vehicle_journey_id, checksum| +        vehicle_journey_purchase_window_checksums[vehicle_journey_id] << checksum +      end + +      [purchase_windows_by_checksum, vehicle_journey_purchase_window_checksums] +    end + +    new_vehicle_journey_ids = {} +      new.switch do        referential_vehicle_journeys.each do |vehicle_journey|          # find parent journey pattern by checksum          # TODO add line_id for security +        associated_route_checksum = referential_routes_checksums[vehicle_journey.route_id]          associated_journey_pattern_checksum = referential_journey_patterns_checksums[vehicle_journey.journey_pattern_id] -        existing_associated_journey_pattern = new.journey_patterns.find_by checksum: associated_journey_pattern_checksum + +        existing_associated_route = new.routes.find_by checksum: associated_route_checksum +        existing_associated_journey_pattern = existing_associated_route.journey_patterns.find_by checksum: associated_journey_pattern_checksum          existing_vehicle_journey = new.vehicle_journeys.find_by journey_pattern_id: existing_associated_journey_pattern.id, checksum: vehicle_journey.checksum -        unless existing_vehicle_journey +        if existing_vehicle_journey +          existing_vehicle_journey.merge_metadata_from vehicle_journey +          new_vehicle_journey_ids[vehicle_journey.id] = existing_vehicle_journey.id +        else            objectid = Chouette::VehicleJourney.where(objectid: vehicle_journey.objectid).exists? ? nil : vehicle_journey.objectid            attributes = vehicle_journey.attributes.merge(              id: nil, @@ -264,11 +324,39 @@ class Merge < ActiveRecord::Base              new_vehicle_journey.vehicle_journey_at_stops.build at_stop_attributes            end +          # Associate (and create if needed) PurchaseWindows + +          referential_vehicle_journey_purchase_window_checksums[vehicle_journey.id].each do |purchase_window_checksum| +            associated_purchase_window = new.purchase_windows.find_by(checksum: purchase_window_checksum) + +            unless associated_purchase_window +              purchase_window = referential_purchase_windows_by_checksum[purchase_window_checksum] + +              objectid = new.purchase_windows.where(objectid: purchase_window.objectid).exists? ? nil : purchase_window.objectid +              attributes = purchase_window.attributes.merge( +                id: nil, +                objectid: objectid +              ) +              new_purchase_window = new.purchase_windows.build attributes +              new_purchase_window.save! + +              if new_purchase_window.checksum != purchase_window.checksum +                raise "Checksum has changed: #{purchase_window.checksum_source} #{new_purchase_window.checksum_source}" +              end + +              associated_purchase_window = new_purchase_window +            end + +            new_vehicle_journey.purchase_windows << associated_purchase_window +          end +            new_vehicle_journey.save!            if new_vehicle_journey.checksum != vehicle_journey.checksum              raise "Checksum has changed: #{vehicle_journey.checksum_source} #{new_vehicle_journey.checksum_source}"            end + +          new_vehicle_journey_ids[vehicle_journey.id] = new_vehicle_journey.id          end        end @@ -280,14 +368,14 @@ class Merge < ActiveRecord::Base        time_tables_by_id = Hash[referential.time_tables.includes(:dates, :periods).all.to_a.map { |t| [t.id, t] }]        time_tables_with_associated_lines = -        referential.time_tables.joins(vehicle_journeys: {route: :line}).pluck("lines.id", :id, "vehicle_journeys.checksum") +        referential.time_tables.joins(vehicle_journeys: {route: :line}).pluck("lines.id", :id, "vehicle_journeys.id")        # Because TimeTables will be modified according metadata periods        # we're loading timetables per line (line is associated to a period list)        #        # line_id: [ { time_table.id, vehicle_journey.checksum } ]        time_tables_by_lines = time_tables_with_associated_lines.inject(Hash.new { |h,k| h[k] = [] }) do |hash, row| -        hash[row.shift] << {id: row.first, vehicle_journey_checksum: row.second} +        hash[row.shift] << {id: row.first, vehicle_journey_id: row.second}          hash        end @@ -339,7 +427,9 @@ class Merge < ActiveRecord::Base            existing_time_table = line.time_tables.find_by checksum: candidate_time_table.checksum -          unless existing_time_table +          if existing_time_table +            existing_time_table.merge_metadata_from candidate_time_table +          else              objectid = Chouette::TimeTable.where(objectid: time_table.objectid).exists? ? nil : time_table.objectid              candidate_time_table.objectid = objectid @@ -355,7 +445,12 @@ class Merge < ActiveRecord::Base            # associate VehicleJourney -          associated_vehicle_journey = line.vehicle_journeys.find_by!(checksum: properties[:vehicle_journey_checksum]) +          new_vehicle_journey_id = new_vehicle_journey_ids[properties[:vehicle_journey_id]] +          unless new_vehicle_journey_id +            raise "TimeTable #{existing_time_table.inspect} associated to a not-merged VehicleJourney: #{properties[:vehicle_journey_id]}" +          end + +          associated_vehicle_journey = line.vehicle_journeys.find(new_vehicle_journey_id)            associated_vehicle_journey.time_tables << existing_time_table          end        end @@ -364,7 +459,7 @@ class Merge < ActiveRecord::Base    def save_current      output.update current: new, new: nil -    output.current.update referential_suite: output +    output.current.update referential_suite: output, ready: true      referentials.update_all merged_at: created_at, archived_at: created_at    end | 
