# frozen_string_literal: true module Submissions module GenerateExportFiles UnknownFormat = Class.new(StandardError) module_function def call(submissions, format: :csv, expires_at: nil) rows = build_table_rows(submissions, expires_at:) if format.to_sym == :csv rows_to_csv(rows) elsif format.to_sym == :xlsx rows_to_xlsx(rows) else raise UnknownFormat end end def rows_to_xlsx(rows) workbook = RubyXL::Workbook.new worksheet = workbook[0] worksheet.sheet_name = Time.current.to_date.to_s headers = build_headers(rows) headers.each_with_index do |column_name, column_index| worksheet.add_cell(0, column_index, column_name) end rows.each.with_index(1) do |row, row_index| extract_columns(row, headers).each_with_index do |value, column_index| worksheet.add_cell(row_index, column_index, value) end end workbook.stream.string end def rows_to_csv(rows) headers = build_headers(rows) CSVSafe.generate do |csv| csv << headers rows.each do |row| csv << extract_columns(row, headers) end end end def build_headers(rows) rows.reduce(Set.new) { |acc, row| acc + row.pluck(:name) } end def extract_columns(row, headers) headers.map { |key| row.find { |e| e[:name] == key }&.dig(:value) } end def build_table_rows(submissions, expires_at: nil) submissions.preload(submitters: [attachments_attachments: :blob, documents_attachments: :blob]) .find_each.map do |submission| submission_data = [] submitters_count = submission.submitters.size submission.submitters.each do |submitter| template_submitters = submission.template_submitters || submission.template.submitters submitter_name = template_submitters.find { |s| s['uuid'] == submitter.uuid }['name'] submission_data += build_submission_data(submitter, submitter_name, submitters_count) submission_data += submitter_formatted_fields(submitter, expires_at:).map do |field| { name: column_name(field[:name], submitter_name, submitters_count), value: field[:value] } end next if submitter != submission.submitters.select(&:completed_at?).max_by(&:completed_at) submission_data += submitter.documents.map.with_index(1) do |attachment, index| { name: "#{I18n.t('document')} #{index}", value: ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:) } end end submission_data end end def build_submission_data(submitter, submitter_name, submitters_count) [ { name: column_name(I18n.t('name'), submitter_name, submitters_count), value: submitter.name }, { name: column_name(I18n.t('email'), submitter_name, submitters_count), value: submitter.email }, { name: column_name(I18n.t('phone'), submitter_name, submitters_count), value: submitter.phone }, { name: column_name(I18n.t('status'), submitter_name, submitters_count), value: submitter.status }, { name: column_name(I18n.t('completed_at'), submitter_name, submitters_count), value: submitter.completed_at.to_s }, { name: column_name(I18n.t('link'), submitter_name, submitters_count), value: submitter.completed_at? ? nil : r.submit_form_url(slug: submitter.slug, **Docuseal.default_url_options) } ].reject { |e| e[:value].blank? } end def column_name(name, submitter_name, submitters_count = 1) submitters_count > 1 ? "#{submitter_name} - #{name}" : name end def submitter_formatted_fields(submitter, expires_at: nil) fields = submitter.submission.template_fields || submitter.submission.template.fields template_fields = fields.select { |f| f['submitter_uuid'] == submitter.uuid } attachments_index = submitter.attachments.index_by(&:uuid) template_field_counters = Hash.new { 0 } template_fields.map do |template_field| submitter_value = submitter.values.fetch(template_field['uuid'], nil) template_field_type = template_field['type'] template_field_counters[template_field_type] += 1 template_field_name = template_field['name'].presence template_field_name ||= "#{I18n.t("#{template_field_type}_field")} #{template_field_counters[template_field_type]}" value = if template_field_type.in?(%w[image signature]) attachment = attachments_index[submitter_value] ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:) if attachment elsif template_field_type == 'file' Array.wrap(submitter_value).compact_blank.filter_map do |e| attachment = attachments_index[e] ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:) if attachment end else submitter_value end { name: template_field_name, uuid: template_field['uuid'], value: } end end def r Rails.application.routes.url_helpers end end end