PageRenderTime 54ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/app/models/checkout_statistics.rb

https://github.com/MiraitSystems/enju_trunk
Ruby | 326 lines | 277 code | 25 blank | 24 comment | 57 complexity | c81d94da6b9533cfb41683ea41292ff3 MD5 | raw file
  1. # encoding: utf-8
  2. class CheckoutStatistics
  3. include ActiveModel::Conversion
  4. include ActiveModel::Validations
  5. extend ActiveModel::Naming
  6. extend ActiveModel::Translation
  7. def initialize(attributes = {})
  8. attributes.each do |name, value|
  9. send("#{name}=", value) rescue nil
  10. end
  11. end
  12. def persisted? ; false ; end
  13. attr_accessor :statistical_table_type, :checked_at_from, :checked_at_to, :aggregation_type, :classification_type_id, :first_aggregation, :second_aggregation
  14. validates_presence_of :checked_at_from
  15. validates_presence_of :checked_at_to
  16. validates_presence_of :aggregation_type
  17. validate :checked_at_from_valid?
  18. validate :checked_at_to_valid?
  19. validate :checked_at_range_valid?
  20. def self.first_aggregations
  21. return [[I18n.t('activerecord.models.user_group'), 'user_group']]
  22. end
  23. def self.second_aggregations
  24. return [[I18n.t('activerecord.attributes.agent.grade'), 'grade']]
  25. end
  26. def make_data
  27. # 集計日付を求める
  28. if md = self.checked_at_from.match(/^([0-9]{4})-([0-9]{2})$/)
  29. checked_at_from = "#{md[1]}-#{md[2]}-01"
  30. else
  31. checked_at_from = self.checked_at_from
  32. end
  33. if md = self.checked_at_to.match(/^([0-9]{4})-([0-9]{2})$/)
  34. day = Date.new(md[1].to_i, md[2].to_i, 1).end_of_month
  35. checked_at_to = day.to_s
  36. else
  37. checked_at_to = self.checked_at_to
  38. end
  39. # 集計方法1
  40. if self.aggregation_type == "month"
  41. # 月別
  42. months = []
  43. from_date_obj = Date.parse(checked_at_from)
  44. to_date_obj = Date.parse(checked_at_to)
  45. # 集計月を求める
  46. if from_date_obj.mon == to_date_obj.mon
  47. months << {:display_name => from_date_obj.strftime("%Y-%m"), :from_date => from_date_obj.beginning_of_day, :to_date => to_date_obj.end_of_day}
  48. else
  49. if from_date_obj.day != 1
  50. months << {:display_name => from_date_obj.strftime("%Y-%m"), :from_date => from_date_obj.beginning_of_day, :to_date => from_date_obj.end_of_month.end_of_day}
  51. end
  52. (from_date_obj..to_date_obj).each{|i|
  53. next if i.day != 1
  54. if from_date_obj.year == i.year && from_date_obj.month == i.month
  55. from_date = from_date_obj.beginning_of_day
  56. else
  57. from_date = i.beginning_of_day
  58. end
  59. if to_date_obj.year == i.year && to_date_obj.month == i.month
  60. to_date = to_date_obj.end_of_day
  61. else
  62. to_date = Date.new(i.year, i.month, -1).end_of_day
  63. end
  64. months << {:display_name => i.strftime("%Y-%m"), :from_date => from_date, :to_date => to_date}
  65. }
  66. end
  67. cols = months
  68. elsif self.aggregation_type == "classification_type"
  69. # 分類別
  70. groups = Classification.select("group_identifier").where("classification_type_id = ?", self.classification_type_id).group("group_identifier")
  71. classification_groups = []
  72. groups.each do |group|
  73. if group.group_identifier.present?
  74. classification_ids = Classification.where("group_identifier = ?", group.group_identifier).pluck(:id)
  75. else
  76. # グループ未設定分
  77. classification_ids = Classification.where("group_identifier is null").pluck(:id)
  78. end
  79. classification_groups << {:display_name => group.group_identifier, :classification_ids => classification_ids}
  80. end
  81. cols = classification_groups
  82. end
  83. # 集計方法2
  84. if self.first_aggregation == "user_group"
  85. first_aggregation_column = "users.user_group_id"
  86. first_aggregations = UserGroup.all
  87. end
  88. if self.second_aggregation == "grade"
  89. second_aggregation_column = "agents.grade_id"
  90. second_aggregations = Keycode.where(:name => 'agent.grade')
  91. end
  92. data = []
  93. sum_books = Array.new(cols.length, 0)
  94. sum_persons = Array.new(cols.length, 0)
  95. checkouts = Checkout.joins(:user => :agent)
  96. checkouts = checkouts.joins(:item => {:manifestation => :manifestation_has_classifications})
  97. if self.aggregation_type == "classification_type"
  98. from_date_obj = Date.parse(checked_at_from)
  99. to_date_obj = Date.parse(checked_at_to)
  100. checkouts = checkouts.where("checked_at >= ? and checked_at <= ?", from_date_obj.beginning_of_day, to_date_obj.end_of_day)
  101. end
  102. first_aggregations.each do |first_aggregation|
  103. first_checkouts = checkouts.where("#{first_aggregation_column} = ?", first_aggregation.id)
  104. details = []
  105. second_aggregations.each do |second_aggregation|
  106. if User.joins(:agent).where("#{first_aggregation_column} = ?", first_aggregation.id).where("#{second_aggregation_column} = ?", second_aggregation.id).present?
  107. second_checkouts = first_checkouts.where("#{second_aggregation_column} = ?", second_aggregation.id)
  108. # 冊数
  109. books = []
  110. sum_book = 0
  111. if self.aggregation_type == "month"
  112. months.each do |month|
  113. book = second_checkouts.select("checkouts.id").where("checked_at >= ? and checked_at <= ?", month[:from_date], month[:to_date]).reorder("checkouts.id").uniq
  114. books << book.length
  115. end
  116. elsif self.aggregation_type == "classification_type"
  117. classification_groups.each do |classification_group|
  118. book = second_checkouts.select("checkouts.id").where("manifestation_has_classifications.classification_id in (?)", classification_group[:classification_ids]).reorder("checkouts.id").uniq
  119. books << book.length
  120. end
  121. end
  122. # 人数
  123. persons = []
  124. sum_person = 0
  125. if self.aggregation_type == "month"
  126. months.each do |month|
  127. person = second_checkouts.select("checkouts.user_id").where("checked_at >= ? and checked_at <= ?", month[:from_date], month[:to_date]).reorder(:user_id).uniq
  128. persons << person.length
  129. end
  130. elsif self.aggregation_type == "classification_type"
  131. classification_groups.each do |classification_group|
  132. person = second_checkouts.select("checkouts.user_id").where("manifestation_has_classifications.classification_id in (?)", classification_group[:classification_ids]).reorder(:user_id).uniq
  133. persons << person.length
  134. end
  135. end
  136. details << {:second_aggregation_name => second_aggregation.keyname, :books => books, :persons => persons}
  137. end
  138. end
  139. # 学年未設定分
  140. if User.joins(:agent).where("#{first_aggregation_column} = ?", first_aggregation.id).where("#{second_aggregation_column} is null").present?
  141. second_checkouts = first_checkouts.where("#{second_aggregation_column} is null")
  142. # 冊数
  143. books = []
  144. sum_book = 0
  145. if self.aggregation_type == "month"
  146. months.each do |month|
  147. book = second_checkouts.select("checkouts.id").where("checked_at >= ? and checked_at <= ?", month[:from_date], month[:to_date]).reorder("checkouts.id").uniq
  148. books << book.length
  149. end
  150. elsif self.aggregation_type == "classification_type"
  151. classification_groups.each do |classification_group|
  152. book = second_checkouts.select("checkouts.id").where("manifestation_has_classifications.classification_id in (?)", classification_group[:classification_ids]).reorder("checkouts.id").uniq
  153. books << book.length
  154. end
  155. end
  156. # 人数
  157. persons = []
  158. sum_person = 0
  159. if self.aggregation_type == "month"
  160. months.each do |month|
  161. person = second_checkouts.select("checkouts.user_id").where("checked_at >= ? and checked_at <= ?", month[:from_date], month[:to_date]).reorder(:user_id).uniq
  162. persons << person.length
  163. end
  164. elsif self.aggregation_type == "classification_type"
  165. classification_groups.each do |classification_group|
  166. person = second_checkouts.select("checkouts.user_id").where("manifestation_has_classifications.classification_id in (?)", classification_group[:classification_ids]).reorder(:user_id).uniq
  167. persons << person.length
  168. end
  169. end
  170. details << {:second_aggregation_name => "", :books => books, :persons => persons}
  171. end
  172. # 小計
  173. books = Array.new(cols.length, 0)
  174. persons = Array.new(cols.length, 0)
  175. details.each do |detail|
  176. cols.each_with_index do |col, index|
  177. books[index] += detail[:books][index]
  178. persons[index] += detail[:persons][index]
  179. sum_books[index] += detail[:books][index]
  180. sum_persons[index] += detail[:persons][index]
  181. end
  182. end
  183. # 合計の出力は第2集計が複数の場合のみとする
  184. if details.length > 1
  185. details << {:second_aggregation_name => I18n.t('statistical_table.subtotal'), :books => books, :persons => persons}
  186. end
  187. data << {:first_aggregation_name => first_aggregation.display_name, :details => details}
  188. end
  189. # 総合計
  190. data << {:first_aggregation_name => I18n.t('statistical_table.total'), :details => [{:second_aggregation_name => "", :books => sum_books, :persons => sum_persons}]}
  191. conditions = self
  192. conditions.checked_at_from = checked_at_from
  193. conditions.checked_at_to = checked_at_to
  194. return {:data => data, :cols => cols, :conditions => conditions}
  195. end
  196. def self.output_excelx(output_data, filename = nil)
  197. require 'axlsx'
  198. # initialize
  199. if filename.present?
  200. # 定期作成
  201. out_dir = "#{Rails.root}/private/system/checkout_statistics"
  202. excel_filepath = "#{out_dir}/list#{Time.now.strftime('%s')}#{filename}.xlsx"
  203. else
  204. # 手動作成
  205. out_dir = "#{Rails.root}/private/user_file/checkout_statistics"
  206. excel_filepath = "#{out_dir}/list#{Time.now.strftime('%s')}#{rand(10)}.xlsx"
  207. end
  208. FileUtils.mkdir_p(out_dir) unless FileTest.exist?(out_dir)
  209. Rails.logger.info "output_checkout_statistics_excelx filepath=#{excel_filepath}"
  210. Axlsx::Package.new do |p|
  211. wb = p.workbook
  212. wb.styles do |s|
  213. sheet = wb.add_worksheet(:name => I18n.t('activemodel.models.checkout_statistics'))
  214. top_style = s.add_style :font_name => Setting.checkout_statistics_print_excelx.fontname, :sz => 16, :alignment => {:horizontal => :center, :vertical => :center}
  215. default_style = s.add_style :font_name => Setting.checkout_statistics_print_excelx.fontname
  216. # ヘッダ
  217. sheet.add_row [I18n.t('activemodel.models.checkout_statistics')], :types => :string, :style => top_style
  218. sheet.merge_cells "A1:O1"
  219. condition = []
  220. condition << I18n.t('statistical_table.checkout_period') + ":"
  221. condition << output_data[:conditions].checked_at_from
  222. condition << "~"
  223. condition << output_data[:conditions].checked_at_to
  224. condition << ""
  225. condition << I18n.t('activemodel.attributes.checkout_statistics.aggregation_type') + ":"
  226. condition << I18n.t("statistical_table.checkout_statistics.aggregation_type.#{output_data[:conditions].aggregation_type}")
  227. if output_data[:conditions].aggregation_type == "classification_type"
  228. classification_type = ClassificationType.where("id = ?", output_data[:conditions].classification_type_id).first
  229. condition << classification_type.display_name
  230. else
  231. condition << ""
  232. end
  233. condition << ""
  234. condition << I18n.t('activemodel.attributes.checkout_statistics.first_aggregation') + ":"
  235. condition << I18n.t("statistical_table.checkout_statistics.first_aggregation.#{output_data[:conditions].first_aggregation}")
  236. condition << I18n.t("statistical_table.checkout_statistics.second_aggregation.#{output_data[:conditions].second_aggregation}")
  237. condition << ""
  238. condition << I18n.t('statistical_table.output_date') + ":"
  239. condition << Date.today
  240. sheet.add_row condition, :types => :string, :style => default_style
  241. # 項目名
  242. columns = [I18n.t('activerecord.attributes.user.user_group'), I18n.t('activerecord.attributes.agent.grade'), ""]
  243. output_data[:cols].each do |col|
  244. columns << col[:display_name]
  245. end
  246. columns << I18n.t('statistical_table.total')
  247. sheet.add_row columns, :types => :string, :style => default_style
  248. # データ
  249. output_data[:data].each do |datum|
  250. datum[:details].each_with_index do |detail, index|
  251. # 冊数
  252. if index == 0
  253. books_row = [datum[:first_aggregation_name]]
  254. else
  255. books_row = [""]
  256. end
  257. books_row << detail[:second_aggregation_name]
  258. books_row << I18n.t('statistical_table.books')
  259. books_row += detail[:books]
  260. books_row << detail[:books].inject(:+)
  261. sheet.add_row books_row, :types => :string, :style => default_style
  262. # 人数
  263. persons_row = ["", ""]
  264. persons_row << I18n.t('statistical_table.persons')
  265. persons_row += detail[:persons]
  266. persons_row << detail[:persons].inject(:+)
  267. sheet.add_row persons_row, :types => :string, :style => default_style
  268. end
  269. end
  270. p.serialize(excel_filepath)
  271. end
  272. end
  273. return excel_filepath
  274. end
  275. private
  276. def checked_at_from_valid?
  277. if checked_at_from.present?
  278. unless /^[0-9]{4}-[0-9]{2}$/ =~ checked_at_from || /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/ =~ checked_at_from
  279. errors.add(:checked_at_from)
  280. end
  281. end
  282. end
  283. def checked_at_to_valid?
  284. if checked_at_to.present?
  285. unless /^[0-9]{4}-[0-9]{2}$/ =~ checked_at_to || /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/ =~ checked_at_to
  286. errors.add(:checked_at_to)
  287. end
  288. end
  289. end
  290. def checked_at_range_valid?
  291. if checked_at_from.present? && checked_at_to.present?
  292. if (/^[0-9]{4}-[0-9]{2}$/ =~ checked_at_from && /^[0-9]{4}-[0-9]{2}$/ =~ checked_at_to) || (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/ =~ checked_at_from && /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/ =~ checked_at_to)
  293. if checked_at_from > checked_at_to
  294. errors.add(:checked_at_from)
  295. end
  296. else
  297. errors.add(:checked_at_from)
  298. end
  299. end
  300. end
  301. end