epaper/app/controllers/admin/e_paper_subscribers_control...

303 lines
9.8 KiB
Ruby
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

require 'rubyXL'
class Admin::EPaperSubscribersController < OrbitAdminController
RackTempMiddleExist = defined?(Rack::TempfileReaper)
RACK_TEMPFILES = defined?(Rack::RACK_TEMPFILES) ? Rack::RACK_TEMPFILES : 'rack.tempfiles'
Is_Rails5 = (Rails.version.to_f >= 5)
if RackTempMiddleExist
if Is_Rails5
def fix_tempfile_reaper
request.set_header(RACK_TEMPFILES, [])
end
else
def fix_tempfile_reaper
env[RACK_TEMPFILES] = nil
end
end
else
def fix_tempfile_reaper; end
end
def initialize
super
@app_title = "e_paper"
end
def index
@table_fields = [t('email'), t('status'), t('language')]
@filter_fields = filter_fields([], [])
@filter_fields.delete(:status)
@filter_fields.delete(:category)
@filter_fields.delete(:tags)
@subscribers = EPaperSubscriber.order_by(sort)
@subscribers = search_data(@subscribers, [:email]).page(params[:page]).per(10)
@thread = (params[:thread_id] ? Multithread.find(params[:thread_id]) : nil rescue nil)
if @thread && @thread.status[:status] == 'finish'
@thread = nil
end
render :partial => "index" if request.xhr?
end
def destroy
subscriber = EPaperSubscriber.find(params[:id]) rescue nil
subscriber.destroy if subscriber
redirect_to admin_e_paper_subscribers_path
end
def export_excel
@epaper_subscribers = EPaperSubscriber.where(:email.nin=>[nil,""]).desc(:created_at)
@subscribers = @epaper_subscribers.where(:subscribed.ne=>false)
@unsubscribers = @epaper_subscribers.where(:subscribed=>false)
respond_to do |format|
format.xlsx {
response.headers['Content-Disposition'] =
'attachment; filename="' +
Site.first.title + '-' +
I18n.t('e_paper.e_paper') + '-' +
I18n.t('e_paper.subscriber') + '.xlsx"'
}
end
end
def get_subscribers_modal
@epaper_subscribers = EPaperSubscriber.where(:email.nin=>[nil,""]).desc(:created_at)
@subscribers = @epaper_subscribers.where(:subscribed.ne=>false)
@unsubscribers = @epaper_subscribers.where(:subscribed=>false)
render :partial => 'modal_select', :layout => false
end
def import_from_excel
thread = Multithread.where(:key=>'import_epaper_subscribers').first
if thread.nil?
thread = Multithread.create(:key=>'import_epaper_subscribers', :status=>{:status=>'Processing'})
else
thread.update(:status=>{:status=>'Processing'})
end
file = params["import_file"]
tempfile = file.tempfile
ObjectSpace.undefine_finalizer(tempfile)
fix_tempfile_reaper
Thread.new do
workbook = RubyXL::Parser.parse(tempfile)
subscribe_sheet = workbook['Subscribe']
unsubscribe_sheet = workbook['Unsubscribe']
all_count =
(subscribe_sheet ? (subscribe_sheet.count - 1) : 0) +
(unsubscribe_sheet ? (unsubscribe_sheet.count - 1) : 0)
puts_every_count = [all_count * 3 / 100, 1].max
current_count = 0
finish_percent = 0
thread.update(
:status=>{
:status=>'Importing',
'all_count'=>all_count,
'current_count'=>current_count,
'finish_percent'=>finish_percent,
'pid'=>Process.pid
}
)
# 修改點:迴圈外先準備一個陣列,收集匯入失敗的列,最後要回報給使用者
failed_rows = []
existing_subscribers = {}
EPaperSubscriber.all.each { |s| existing_subscribers[s.email] = s }
if subscribe_sheet
subscribe_sheet.each_with_index do |row, i|
next if i < 1
next if row.nil?
email = row.cells[0]&.value
next if email.blank?
# 修改點:查詢前先用跟 model 一樣的清洗規則處理 email避免「查詢用原始字串、儲存用清洗後字串」不一致造成誤判成新資料
fixed_email = email.to_s.gsub(/[  ]/,'').sub(/;$/, '').downcase
if fixed_email.count('@') > 1
failed_rows << { row: i + 1, email: email.to_s, reason: '一個欄位包含多個信箱,請拆成單獨一列後重新匯入' }
current_count += 1
next
end
begin
# 修改點:改用記憶體 Hash 查詢,取代資料庫查詢
subscriber = existing_subscribers[fixed_email] || EPaperSubscriber.new(:email=>fixed_email)
subscriber.subscribed = true
subscriber.language = row.cells[1]&.value.presence || I18n.locale.to_s
subscriber.save!
# 修改點:存檔成功後更新 Hash避免同一份檔案裡後面重複出現的同一個email又找不到
existing_subscribers[subscriber.email] = subscriber
rescue => e
failed_rows << { row: i + 1, email: email.to_s, reason: e.message }
end
current_count += 1
if current_count % puts_every_count == 0
finish_percent = (current_count * 100.0 / all_count).round(1)
thread.update(:status=>{:status=>'Importing','all_count'=>all_count,'current_count'=>current_count,'finish_percent'=>finish_percent})
end
end
end
if unsubscribe_sheet
unsubscribe_sheet.each_with_index do |row, i|
next if i < 1
# 修改點對齊Subscribe那段整列空白時row本身是nil要先擋掉
next if row.nil?
email = row.cells[0]&.value
next if email.blank?
# 修改點對齊Subscribe那段查詢前先用同一套清洗規則處理email
fixed_email = email.to_s.gsub(/[  ]/,'').sub(/;$/, '').downcase
# 修改點對齊Subscribe那段擋下一格塞多個信箱的資料
if fixed_email.count('@') > 1
failed_rows << { row: i + 1, email: email.to_s, reason: '一個欄位包含多個信箱,請拆成單獨一列後重新匯入' }
current_count += 1
next
end
# 修改點對齊Subscribe那段改用記憶體Hash查詢取代資料庫查詢
# 修改點save改save!並包begin/rescue例外不再讓整條Thread死掉
begin
subscriber = existing_subscribers[fixed_email] || EPaperSubscriber.new(:email=>fixed_email)
subscriber.subscribed = false
subscriber.language = row.cells[1]&.value.presence || I18n.locale.to_s
subscriber.save!
existing_subscribers[subscriber.email] = subscriber
rescue => e
failed_rows << { row: i + 1, email: email.to_s, reason: e.message }
end
current_count += 1
if current_count % puts_every_count == 0
finish_percent = (current_count * 100.0 / all_count).round(1)
thread.update(:status=>{:status=>'Importing','all_count'=>all_count,'current_count'=>current_count,'finish_percent'=>finish_percent})
end
end
end
thread.update(
:status=>{
:status=>'finish',
'all_count'=>all_count,
'current_count'=>current_count,
'finish_percent'=>100,
'failed_rows'=>failed_rows
}
)
end
redirect_to admin_e_paper_subscribers_path(thread_id: thread.id)
end
def last_import_result
thread = Multithread.where(:key=>'import_epaper_subscribers').first
stalled = false
if thread && thread.status['status'] == 'Importing' && thread.status['pid']
begin
Process.kill(0, thread.status['pid'])
rescue Errno::ESRCH
stalled = true
end
end
render :partial => 'last_import_result', locals: { thread: thread, stalled: stalled }
end
def download_excel_format
@subscribers = []
@unsubscribers = []
respond_to do |format|
format.xlsx {
response.headers['Content-Disposition'] =
'attachment; filename="' +
Site.first.title + '-' +
I18n.t('e_paper.e_paper') + '-' +
I18n.t('e_paper.subscriber') + 'excel_format.xlsx"'
}
end
end
def batch_delete_subscribers
@thread = (params[:thread_id] ? Multithread.find(params[:thread_id]) : nil rescue nil)
if @thread && @thread.status[:status] == 'finish'
@thread = nil
end
end
def delete_subscribers
subscriber_ids = params['subscriber_ids']
thread = Multithread.where(:key=>'delete_epaper_subscribers').first
if thread.nil?
thread = Multithread.create(
:key=>'delete_epaper_subscribers',
:status=>{:status=>'Processing'}
)
else
thread.update(:status=>{:status=>'Processing'})
end
if subscriber_ids.present?
all_count = subscriber_ids.count
puts_every_count = [all_count * 3 / 100, 1].max
current_count = 0
finish_percent = 0
thread.update(
:status=>{
:status=>'Deleting',
'all_count'=>all_count,
'current_count'=>current_count,
'finish_percent'=>finish_percent
}
)
Thread.new do
EPaperSubscriber.where(:id.in=>subscriber_ids).to_a.each do |s|
s.destroy
current_count += 1
if current_count % puts_every_count == 0
finish_percent = (current_count * 100.0 / all_count).round(1)
thread.update(
:status=>{
:status=>'Deleting',
'all_count'=>all_count,
'current_count'=>current_count,
'finish_percent'=>finish_percent
}
)
end
end
thread.update(
:status=>{
:status=>'finish',
'all_count'=>all_count,
'current_count'=>current_count,
'finish_percent'=>100
}
)
end
else
thread.update(:status=>{:status=>'finish'})
end
redirect_to admin_e_paper_subscribers_batch_delete_subscribers_path(thread_id: thread.id)
end
end