excel_sheet.rb 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class ExcelSheet
  3. CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'.freeze
  4. def initialize(title:, header:, records:, locale:, timezone: nil)
  5. require 'write_xlsx' # Only load this gem when it is really used.
  6. @title = title
  7. @header = header
  8. @records = records
  9. @timezone = timezone.presence || Setting.get('timezone_default')
  10. @locale = locale || Locale.default
  11. @tempfile = Tempfile.new('excel-export.xlsx')
  12. @workbook = WriteXLSX.new(@tempfile)
  13. @format_decimal = @workbook.add_format(num_format: '0.00')
  14. @worksheet = @workbook.add_worksheet
  15. @contents = nil
  16. @current_row = 0
  17. @current_column = 0
  18. @lookup_cache = {}
  19. @format_time = @workbook.add_format(num_format: 'yyyy-mm-dd hh:mm:ss')
  20. @format_date = @workbook.add_format(num_format: 'yyyy-mm-dd')
  21. @format_headline = @workbook.add_format
  22. @format_headline.set_bold
  23. @format_headline.set_size(14)
  24. @format_headline.set_color('black')
  25. @format_header = @workbook.add_format
  26. @format_header.set_italic
  27. @format_header.set_bg_color('gray')
  28. @format_header.set_color('white')
  29. @format_footer = @workbook.add_format
  30. @format_footer.set_italic
  31. @format_footer.set_color('gray')
  32. @format_footer.set_size(8)
  33. end
  34. def contents
  35. file = File.new(@tempfile, 'r')
  36. contents = file.read
  37. file.close
  38. contents
  39. end
  40. def content
  41. gen_header
  42. gen_rows
  43. gen_footer
  44. contents
  45. end
  46. def gen_header
  47. @worksheet.write_string(@current_row, @current_column, @title, @format_headline)
  48. @worksheet.set_row(0, 18)
  49. @current_row += 2
  50. @current_column = 0
  51. @header.each do |header|
  52. if header[:width]
  53. @worksheet.set_column(@current_column, @current_column, header[:width])
  54. end
  55. @worksheet.write_string(@current_row, @current_column, header[:display] || header[:name], @format_header)
  56. @current_column += 1
  57. end
  58. end
  59. def gen_rows
  60. @records.each do |record|
  61. gen_row_by_array(record)
  62. end
  63. end
  64. def gen_row_by_array(record)
  65. @current_row += 1
  66. @current_column = 0
  67. record.each do |item|
  68. begin
  69. if item.acts_like?(:time) || item.acts_like?(:date)
  70. value_convert(item, nil, { data_type: 'datetime' })
  71. elsif item.is_a?(Float) || item.is_a?(BigDecimal)
  72. value_convert(item, nil, { data_type: 'float' })
  73. elsif item.is_a?(Integer)
  74. value_convert(item, nil, { data_type: 'integer' })
  75. else
  76. value_convert(item, nil, { data_type: 'string' })
  77. end
  78. rescue => e
  79. Rails.logger.error e
  80. end
  81. @current_column += 1
  82. end
  83. end
  84. def gen_row_by_header(record, additional = {})
  85. @current_row += 1
  86. @current_column = 0
  87. @header.each do |header|
  88. begin
  89. value_convert(record, header[:name], header, additional)
  90. rescue => e
  91. Rails.logger.error e
  92. end
  93. @current_column += 1
  94. end
  95. end
  96. def gen_footer
  97. @current_row += 2
  98. @worksheet.write_string(@current_row, 0, "#{Translation.translate(@locale, 'Timezone')}: #{@timezone}", @format_footer)
  99. @workbook.close
  100. end
  101. def timestamp_in_localtime(time)
  102. return if time.blank?
  103. time.in_time_zone(@timezone).strftime('%F %T') # "2019-08-19 16:21:52"
  104. end
  105. def value_lookup(record, attribute, object, additional)
  106. value = record[attribute.to_sym]
  107. if attribute[-3, 3] == '_id'
  108. ref = attribute[0, attribute.length - 3]
  109. if record.respond_to?(ref.to_sym)
  110. @lookup_cache[attribute] ||= {}
  111. return @lookup_cache[attribute][value] if @lookup_cache[attribute][value]
  112. ref_object = record.send(ref.to_sym)
  113. ref_name = value
  114. if ref_object.respond_to?(:fullname)
  115. ref_name = ref_object.fullname
  116. elsif ref_object.respond_to?(:name)
  117. ref_name = ref_object.name
  118. end
  119. @lookup_cache[attribute][value] = ref_name
  120. return ref_name
  121. end
  122. end
  123. value = record.try(attribute)
  124. # if no value exists, check additional values
  125. if !value && additional && additional[attribute.to_sym]
  126. value = additional[attribute.to_sym]
  127. end
  128. if object[:data_type] !~ %r{^(multi_)?tree_select$} && object[:data_option].present? && object[:data_option]['options'].present?
  129. display_values = ObjectManager::Attribute.data_options_hash(object[:data_option]['options'])
  130. value = Array(value).map { |v| display_values[v] }.join(',')
  131. end
  132. if value.is_a?(Array)
  133. value = value.join(',')
  134. end
  135. value
  136. end
  137. def value_convert(record, attribute, object, additional = {})
  138. value = if attribute
  139. value_lookup(record, attribute, object, additional)
  140. else
  141. record
  142. end
  143. case object[:data_type]
  144. when 'boolean', %r{^(multi)?select$}
  145. @worksheet.write_string(@current_row, @current_column, value) if value.present?
  146. when 'datetime'
  147. @worksheet.write_date_time(@current_row, @current_column, timestamp_in_localtime(value), @format_time) if value.present?
  148. when 'date'
  149. @worksheet.write_date_time(@current_row, @current_column, value.to_s, @format_date) if value.present?
  150. when 'integer'
  151. @worksheet.write_number(@current_row, @current_column, value) if value.present?
  152. when 'float'
  153. @worksheet.write_number(@current_row, @current_column, value, @format_decimal) if value.present?
  154. else
  155. @worksheet.write_string(@current_row, @current_column, value.to_s) if value.present?
  156. end
  157. end
  158. end