141 lines
4.4 KiB
Ruby
141 lines
4.4 KiB
Ruby
class ProcedureExportService
|
|
attr_reader :procedure, :dossiers
|
|
|
|
def initialize(procedure, dossiers)
|
|
@procedure = procedure
|
|
@dossiers = dossiers.downloadable_sorted_batch
|
|
@tables = [:dossiers, :etablissements, :avis] + champs_repetables_options
|
|
end
|
|
|
|
def to_csv
|
|
io = StringIO.new(SpreadsheetArchitect.to_csv(options_for(:dossiers, :csv)))
|
|
create_blob(io, :csv)
|
|
end
|
|
|
|
def to_xlsx
|
|
# We recursively build multi page spreadsheet
|
|
io = @tables.reduce(nil) do |package, table|
|
|
SpreadsheetArchitect.to_axlsx_package(options_for(table, :xlsx), package)
|
|
end.to_stream
|
|
create_blob(io, :xlsx)
|
|
end
|
|
|
|
def to_ods
|
|
# We recursively build multi page spreadsheet
|
|
io = StringIO.new(@tables.reduce(nil) do |spreadsheet, table|
|
|
SpreadsheetArchitect.to_rodf_spreadsheet(options_for(table, :ods), spreadsheet)
|
|
end.bytes)
|
|
create_blob(io, :ods)
|
|
end
|
|
|
|
def to_zip
|
|
attachments = ActiveStorage::DownloadableFile.create_list_from_dossiers(dossiers, true)
|
|
|
|
DownloadableFileService.download_and_zip(procedure, attachments, base_filename) do |zip_filepath|
|
|
ArchiveUploader.new(procedure: procedure, filename: filename(:zip), filepath: zip_filepath).blob
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def create_blob(io, format)
|
|
ActiveStorage::Blob.create_and_upload!(
|
|
io: io,
|
|
filename: filename(format),
|
|
content_type: content_type(format),
|
|
identify: false,
|
|
# We generate the exports ourselves, so they are safe
|
|
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
|
|
)
|
|
end
|
|
|
|
def base_filename
|
|
@base_filename ||= "dossiers_#{procedure_identifier}_#{Time.zone.now.strftime('%Y-%m-%d_%H-%M')}"
|
|
end
|
|
|
|
def filename(format)
|
|
"#{base_filename}.#{format}"
|
|
end
|
|
|
|
def procedure_identifier
|
|
procedure.path || "procedure-#{procedure.id}"
|
|
end
|
|
|
|
def content_type(format)
|
|
case format
|
|
when :csv
|
|
'text/csv'
|
|
when :xlsx
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
when :ods
|
|
'application/vnd.oasis.opendocument.spreadsheet'
|
|
when :zip
|
|
'application/zip'
|
|
end
|
|
end
|
|
|
|
def etablissements
|
|
@etablissements ||= dossiers.flat_map do |dossier|
|
|
[dossier.champs, dossier.champs_private]
|
|
.flatten
|
|
.filter { |champ| champ.is_a?(Champs::SiretChamp) }
|
|
end.filter_map(&:etablissement) + dossiers.filter_map(&:etablissement)
|
|
end
|
|
|
|
def avis
|
|
@avis ||= dossiers.flat_map(&:avis)
|
|
end
|
|
|
|
def champs_repetables_options
|
|
revision = procedure.active_revision
|
|
champs_by_stable_id = dossiers
|
|
.flat_map { |dossier| (dossier.champs + dossier.champs_private).filter(&:repetition?) }
|
|
.group_by(&:stable_id)
|
|
|
|
procedure.types_de_champ_for_procedure_presentation.repetition
|
|
.map { |type_de_champ_repetition| [type_de_champ_repetition, type_de_champ_repetition.types_de_champ_for_revision(revision).to_a] }
|
|
.filter { |(_, types_de_champ)| types_de_champ.present? }
|
|
.map do |(type_de_champ_repetition, types_de_champ)|
|
|
{
|
|
sheet_name: type_de_champ_repetition.libelle_for_export,
|
|
instances: champs_by_stable_id.fetch(type_de_champ_repetition.stable_id, []).flat_map(&:rows_for_export),
|
|
spreadsheet_columns: Proc.new { |instance| instance.spreadsheet_columns(types_de_champ) }
|
|
}
|
|
end
|
|
end
|
|
|
|
DEFAULT_STYLES = {
|
|
header_style: { background_color: "000000", color: "FFFFFF", font_size: 12, bold: true },
|
|
row_style: { background_color: nil, color: "000000", font_size: 12 }
|
|
}
|
|
|
|
def options_for(table, format)
|
|
options = case table
|
|
when :dossiers
|
|
{ instances: dossiers.to_a, sheet_name: 'Dossiers', spreadsheet_columns: spreadsheet_columns(format) }
|
|
when :etablissements
|
|
{ instances: etablissements.to_a, sheet_name: 'Etablissements' }
|
|
when :avis
|
|
{ instances: avis.to_a, sheet_name: 'Avis' }
|
|
when Hash
|
|
table
|
|
end.merge(DEFAULT_STYLES)
|
|
|
|
# transliterate: convert to ASCII characters
|
|
# to ensure truncate respects 30 bytes
|
|
# /\*?[] are invalid Excel worksheet characters
|
|
options[:sheet_name] = I18n.transliterate(options[:sheet_name], locale: :en)
|
|
.delete('/\*?[]')
|
|
.truncate(30, omission: '')
|
|
|
|
options
|
|
end
|
|
|
|
def spreadsheet_columns(format)
|
|
types_de_champ = procedure.types_de_champ_for_procedure_presentation.not_repetition.to_a
|
|
|
|
Proc.new do |instance|
|
|
instance.send(:"spreadsheet_columns_#{format}", types_de_champ: types_de_champ)
|
|
end
|
|
end
|
|
end
|