303 lines
9.8 KiB
Ruby
303 lines
9.8 KiB
Ruby
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
|