Newer
Older
dmpopidor / lib / data_cleanup / rules / section / fix_duplicate_number.rb
# frozen_string_literal: true
module DataCleanup
  module Rules
    # Fix duplicate number on Section
    module Section
      class FixDuplicateNumber < Rules::Base

        def description
          "Fix duplicate number on Section"
        end

        def call
          # A set of unique phase_ids that contain Sections with duplicate numbers
          phase_ids = ::Section.group(:number, :phase_id)
                               .count
                               .select { |k,v| v > 1 }
                               .collect { |values, count| values.last }
                               .uniq

          phase_ids.each do |phase_id|
            log("Re-setting number order for Sections in Phase##{phase_id}")
            phase    = ::Phase.find(phase_id)
            sections = phase.sections

            # When every section within a Phase is modifiable, or when none is...
            if sections.all?(&:modifiable?) || sections.all?(&:template?)
              update_homogenous_set(phase)

            # When some of the sections are modifiable then...
            else
              update_heterogenous_set(phase)
            end
          end
        end

        private

        # Re-sort the Phase's Sections by number, then by the ID
        def update_homogenous_set(phase)
          ids = phase.sections.order("number ASC, id ASC").ids
          ::Section.update_numbers!(*ids, parent: phase)
        end

        def update_heterogenous_set(phase)
          # Array of duplicate Section numbers within this Phase
          numbers = phase.sections
                         .group(:number)
                         .count
                         .select { |number, count| count > 1 }
                         .collect(&:first)

          # If there are duplicates in the #1 position
          if numbers.include?(1)
            # There should only be, if any, one prefixed modifiable Section
            prefix  = phase.sections.modifiable.where(number: 1).limit(1).ids

            # In the off-chance that there is more than one prefix Section, stick them
            # after the  unmodifiable block
            erratic = phase.sections.modifiable.where(number: 1).offset(1).ids

            # Collect the unmodifiable Section ids in the order the should be displayed
            unmodifiable = phase.sections.not_modifiable.order('number, id').ids

            # Then any additional Sections that come after the main block...
            modifiable = phase.sections.modifiable.order('number, id').ids

            # Create one Array with all of the ids in the correct order.
            ids = prefix + erratic + unmodifiable + modifiable
            ::Section.update_numbers!(*ids, parent: phase)

          else
            # Sort ids based on the number order. If there are duplicates, then take the
            # earliest first. Unmodifiable sections from the template should be grouped
            # together before the additional, modifiable sections are appended afterwards.
            ids = phase.sections.not_modifiable.order("number, id").ids
            ids += phase.sections.modifiable.order("number, id").ids
            ::Section.update_numbers!(*ids, parent: phase)
          end
        end

      end
    end
  end
end