ministryofjustice/Claim-for-Crown-Court-Defence

View on GitHub
app/models/stats/stats_report.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
96%
module Stats
  class StatsReport < ApplicationRecord
    Report = Struct.new(:name, :date_required, :hidden, :updatable, keyword_init: true) do
      def initialize(name:, date_required: false, hidden: false, updatable: true)
        super
      end

      delegate :to_s, to: :name

      def date_required?
        date_required
      end

      def hidden?
        hidden
      end

      def updatable?
        updatable
      end
    end

    REPORTS = [Report.new(name: :management_information),
               Report.new(name: :agfs_management_information),
               Report.new(name: :lgfs_management_information),
               Report.new(name: :fee_scheme_usage),
               Report.new(name: :management_information_v2),
               Report.new(name: :agfs_management_information_v2),
               Report.new(name: :lgfs_management_information_v2),
               Report.new(name: :agfs_management_information_statistics, date_required: true),
               Report.new(name: :lgfs_management_information_statistics, date_required: true),
               Report.new(name: :provisional_assessment),
               Report.new(name: :rejections_refusals),
               Report.new(name: :submitted_claims),
               Report.new(name: :reports_access_details, hidden: true, updatable: false)].freeze

    validates :status, inclusion: { in: %w[started completed error] }

    default_scope { order('started_at DESC') }

    scope :completed, -> { where(status: 'completed') }
    scope :errored, -> { where(status: 'error') }
    scope :not_errored, -> { where.not(status: 'error') }

    scope :management_information, -> { not_errored.where(report_name: 'management_information') }
    scope :provisional_assessment, -> { not_errored.where(report_name: 'provisional_assessment') }

    before_destroy -> { document.purge }

    has_one_attached :document

    class << self
      def reports
        @reports ||= REPORTS.index_by(&:name).with_indifferent_access
      end

      def clean_up(report_name)
        destroy_reports_older_than(report_name, 1.month.ago)
        destroy_unfinished_reports_older_than(report_name, 2.hours.ago)
      end

      def most_recent_by_type(report_type)
        where(report_name: report_type).completed.first
      end

      def most_recent_management_information
        management_information.completed.first
      end

      def generation_in_progress?(report_name)
        where('report_name = ? and status = ?', report_name, 'started').any?
      end

      def record_start(report_name)
        create!(report_name:, status: 'started', started_at: Time.zone.now)
      end

      def destroy_reports_older_than(report_name, timestamp)
        where(report_name:, started_at: Time.zone.at(0)..timestamp).destroy_all
      end

      def destroy_unfinished_reports_older_than(report_name, timestamp)
        where(report_name:, status: 'started', started_at: Time.zone.at(0)..timestamp).destroy_all
      end
    end

    def write_report(report_result)
      filename = "#{report_name}_#{started_at.to_fs(:number)}.#{report_result.format}"
      log(:info, :write_report, "Writing report #{report_name} to #{filename}")
      document.attach(io: report_result.io, filename:, content_type: report_result.content_type)
      update(status: 'completed', completed_at: Time.zone.now)
    rescue StandardError => e
      log(:error, :write_report, "error writing report #{report_name}...", e)
      raise
    end

    def download_filename
      "#{report_name}_#{started_at.to_fs(:number)}.csv"
    end

    private

    def log(level, action, message, error = nil)
      LogStuff.send(
        level.to_sym,
        class: self.class.name,
        action:,
        error: error ? "#{error.class} - #{error.message}" : 'false'
      ) do
        message
      end
    end
  end
end