# frozen_string_literal: true class Export < ApplicationRecord include TransientModelsWithPurgeableJobConcern self.ignored_columns += ["procedure_presentation_snapshot"] MAX_DUREE_CONSERVATION_EXPORT = 32.hours MAX_DUREE_GENERATION = 16.hours enum format: { csv: 'csv', ods: 'ods', xlsx: 'xlsx', zip: 'zip', json: 'json' }, _prefix: true enum time_span_type: { everything: 'everything', monthly: 'monthly' } enum statut: { 'a-suivre': 'a-suivre', suivis: 'suivis', traites: 'traites', tous: 'tous', supprimes: 'supprimes', archives: 'archives', expirant: 'expirant' } has_and_belongs_to_many :groupe_instructeurs belongs_to :procedure_presentation, optional: true belongs_to :instructeur, optional: true belongs_to :user_profile, polymorphic: true, optional: true belongs_to :export_template, optional: true has_one_attached :file attribute :sorted_column, :sorted_column attribute :filtered_columns, :filtered_column, array: true validates :format, :groupe_instructeurs, :key, presence: true scope :ante_chronological, -> { order(updated_at: :desc) } after_create_commit :compute_async FORMATS_WITH_TIME_SPAN = [:xlsx, :ods, :csv].flat_map do |format| [{ format: format, time_span_type: 'everything' }] end FORMATS = [:xlsx, :ods, :csv, :zip, :json].map do |format| { format: format } end def compute_async ExportJob.perform_later(self) end def compute self.dossiers_count = dossiers_for_export.count file.attach(blob.signed_id) # attaching a blob directly might run identify/virus scanner and wipe it end def since time_span_type == Export.time_span_types.fetch(:monthly) ? 30.days.ago : nil end def self.find_or_create_fresh_export(format, groupe_instructeurs, user_profile, time_span_type: time_span_types.fetch(:everything), statut: statuts.fetch(:tous), procedure_presentation: nil, export_template: nil) filtered_columns = Array.wrap(procedure_presentation&.filters_for(statut)) sorted_column = procedure_presentation&.sorted_column attributes = { format:, export_template:, time_span_type:, statut:, key: generate_cache_key(groupe_instructeurs.map(&:id), filtered_columns, sorted_column) } recent_export = pending .or(generated.where(updated_at: (5.minutes.ago)..)) .includes(:procedure_presentation) .find_by(attributes) return recent_export if recent_export.present? create!(**attributes, groupe_instructeurs:, user_profile:, filtered_columns:, sorted_column:) end def self.for_groupe_instructeurs(groupe_instructeurs_ids) joins(:groupe_instructeurs).where(groupe_instructeurs: groupe_instructeurs_ids).distinct(:id) end def self.by_key(groupe_instructeurs_ids) where(key: generate_cache_key(groupe_instructeurs_ids)) end def self.generate_cache_key(groupe_instructeurs_ids, filtered_columns = [], sorted_column = nil) columns_key = ([sorted_column] + filtered_columns).compact.map(&:id).sort.join [ groupe_instructeurs_ids.sort.join('-'), Digest::MD5.hexdigest(columns_key) ].join('--') end def count return dossiers_count if !dossiers_count.nil? # export generated return dossiers_for_export.count if built_from_procedure_presentation? nil end def procedure groupe_instructeurs.first.procedure end def built_from_procedure_presentation? sorted_column.present? # hack has we know that procedure_presentation always has a sorted_column end private def dossiers_for_export @dossiers_for_export ||= begin dossiers = Dossier.where(groupe_instructeur: groupe_instructeurs) if since.present? dossiers.visible_by_administration.where('dossiers.depose_at > ?', since) elsif filtered_columns.present? || sorted_column.present? instructeur = instructeur_from(user_profile) filtered_sorted_ids = DossierFilterService.filtered_sorted_ids(dossiers, statut, filtered_columns, sorted_column, instructeur) dossiers.where(id: filtered_sorted_ids) else dossiers.visible_by_administration end end end def instructeur_from(user_profile) case user_profile when Administrateur user_profile.instructeur when Instructeur user_profile end end def blob service = ProcedureExportService.new(procedure, dossiers_for_export, user_profile, export_template) case format.to_sym when :csv service.to_csv when :xlsx service.to_xlsx when :ods service.to_ods when :zip service.to_zip when :json service.to_geo_json end end end