lib/canvas_sync/processors/provisioning_report_processor.rb



require "csv"
require "activerecord-import"
require "zip"
require_relative "./report_processor"

module CanvasSync
  module Processors
    class ProvisioningReportProcessor < ReportProcessor
      # Processes a provisioning report using the bulk importer.
      #
      # options must contain a models key. If there is only one model
      # Canvas downloads the single report directly as a CSV. If it's
      # more than one model Canvas downloads a ZIP file, so we have to
      # extract that and iterate through it for processing.
      #
      # @param report_file_path [String]
      # @param options [Hash]
      def self.process(report_file_path, options, report_id)
        new(report_file_path, options)
      end

      def initialize(report_file_path, options) # rubocop:disable Metrics/AbcSize
        @options = options
        if options[:models].length == 1
          run_import(options[:models][0], report_file_path)
        else
          unzipped_file_path = extract(report_file_path)
          Dir[unzipped_file_path + "/*.csv"].sort.each do |file_path|
            model_name = file_path.split("/").last.split(".").first
            run_import(model_name, file_path)
          end
        end
      end

      private

      def extract(file_path)
        unzipped_file_path = "#{file_path}_unzipped"

        Zip::File.open(file_path) do |zip_file|
          zip_file.each do |f|
            f_path = File.join(unzipped_file_path, f.name)
            FileUtils.mkdir_p(File.dirname(f_path))
            zip_file.extract(f, f_path) unless File.exist?(f_path)
          end
        end

        unzipped_file_path
      end

      def run_import(model_name, report_file_path)
        if legacy?(model_name)
          CanvasSync::Importers::LegacyImporter.import(
            report_file_path,
            model_name.singularize.capitalize.constantize,
            @options[:account_id],
            @options,
          )
        else
          send("bulk_process_#{model_name}", report_file_path)
        end
      end

      def legacy?(model_name)
        opt = @options[:legacy_support]
        return false if opt == false || opt.nil?
        return true if opt == true
        return opt.include?(model_name)
      end

      def bulk_process_users(report_file_path)
        do_bulk_import(report_file_path, User, options: @options)
      end

      def bulk_process_user_observers(report_file_path)
        do_bulk_import(report_file_path, UserObserver, options: @options)
      end

      def bulk_process_pseudonyms(report_file_path)
        do_bulk_import(report_file_path, Pseudonym, options: @options)
      end

      def bulk_process_accounts(report_file_path)
        do_bulk_import(report_file_path, Account, options: @options)
      end

      def bulk_process_courses(report_file_path)
        do_bulk_import(report_file_path, Course, options: @options)
      end

      def bulk_process_enrollments(report_file_path)
        do_bulk_import(report_file_path, Enrollment, options: @options)
      end

      def bulk_process_sections(report_file_path)
        do_bulk_import(report_file_path, Section, options: @options)
      end

      def bulk_process_xlist(report_file_path)
        do_bulk_import(report_file_path, Section, options: @options, mapping_key: :xlist)
      end

      def bulk_process_groups(report_file_path)
        do_bulk_import(report_file_path, Group, options: @options)
      end

      def bulk_process_grading_periods(report_file_path)
        do_bulk_import(report_file_path, GradingPeriod, options: @options)
      end

      def bulk_process_grading_period_groups(report_file_path)
        do_bulk_import(report_file_path, GradingPeriodGroup, options: @options)
      end

      # Note that group membership is singular because we override the model name param in sync_provisioning_report_job
      def bulk_process_group_membership(report_file_path)
        do_bulk_import(report_file_path, GroupMembership, options: @options)
      end

      def bulk_process_learning_outcomes(report_file_path)
        do_bulk_import(report_file_path, LearningOutcome, options: @options) do |row|
          row[:root_account_ids] = JSON.parse row[:root_account_ids]
          row
        end
      end
    end
  end
end