Move export to multithread.

This commit is contained in:
BoHung Chiu 2022-06-26 15:35:23 +08:00
parent 2178bafe2a
commit a3f6657dfc
6 changed files with 153 additions and 10 deletions

View File

@ -169,14 +169,54 @@ class Admin::AnnouncementsController < OrbitAdminController
end end
def export_excel def export_excel
@announcements = Bulletin.all.desc(:created_at) @thread = Multithread.where(:key=>'export_announcements').first
respond_to do |format| update_flag = true
format.xlsx { if @thread.nil?
response.headers['Content-Disposition'] = 'attachment; filename="announcement_export.xlsx"' @thread = Multithread.create(:key=>'export_announcements',:status=>{:status=>'Processing'})
} else
update_flag = false if @thread.status[:status] == 'Processing' && @thread.respond_to?(:updated_at) && (@thread.updated_at > DateTime.now - 1.minute rescue false)
if update_flag
@thread.update(:status=>{:status=>'Processing'})
end
end
if update_flag
Thread.new do
begin
@announcements = Bulletin.all.desc(:created_at)
last_updated = Bulletin.max(:updated_at).to_i
filename = "public/announcement_export_#{last_updated}.xlsx"
if File.exist?(filename)
@thread.update(:status=>{:status=>'finish','finish_percent'=>100,'info'=>I18n.t('announcement.read_from_cache')})
else
excel_contents = render_to_string( handlers: [:axlsx], formats: [:xlsx] ,partial: 'export_excel.xlsx',locals: {:@announcements=>@announcements,:@thread=>@thread} )
File.open(filename, 'w') do |f|
f.write excel_contents
end
end
@thread.status[:file] = filename
@thread.status[:filename] = "announcement_export_#{DateTime.now.in_time_zone(Time.zone.utc_offset / 3600).strftime('%Y_%m_%d_%H%M')}.xlsx"
@thread.save
rescue => e
@thread.status[:status] = 'error'
# @thread.status[:info] = [e.to_s, e.backtrace]
puts [e.to_s, e.backtrace]
@thread.save
end
end
end
redirect_to admin_announcement_import_path(:thread_id=>@thread.id.to_s)
end
def render_404
render :file => "#{Rails.root}/app/views/errors/404.html", :layout => false, :status => 404, :formats => [:html]
end
def download_file_from_thread
@thread = Multithread.where(:id=>params[:id]).first if params[:id].present?
if @thread && @thread.status[:file]
send_file(@thread.status[:file],:filename=>@thread.status[:filename])
else
render_404
end end
end end
def import_from_xml def import_from_xml
download_tmp_xml params["import_xml"] download_tmp_xml params["import_xml"]
import_from_tmp_xml File.read(File.join(Rails.root, "tmp", "ann_cc_ntu.xml")) import_from_tmp_xml File.read(File.join(Rails.root, "tmp", "ann_cc_ntu.xml"))
@ -184,6 +224,7 @@ class Admin::AnnouncementsController < OrbitAdminController
end end
def import def import
@thread = Multithread.where(:id=>params[:thread_id]).first if params[:thread_id].present?
end end

View File

@ -123,7 +123,13 @@ wb.add_worksheet(name: "Annoucement") do |sheet|
sheet.add_row row, :style => heading sheet.add_row row, :style => heading
sheet.add_row row1 sheet.add_row row1
sheet.add_row row2, :style => example sheet.add_row row2, :style => example
if @thread
all_count = @announcements.count
puts_every_count = [all_count * 3 / 100, 1].max
current_count = 0
finish_percent = 0
@thread.update(:status=>{:status=>'Processing','all_count'=>all_count,'current_count'=>current_count,'finish_percent'=>finish_percent})
end
@announcements.each do |anns| @announcements.each do |anns|
row = [] row = []
row << categories.to_a.index(anns.category) row << categories.to_a.index(anns.category)
@ -168,10 +174,20 @@ wb.add_worksheet(name: "Annoucement") do |sheet|
t = files.collect{|l|l.title_translations["zh_tw"]} t = files.collect{|l|l.title_translations["zh_tw"]}
row << t.join(";") row << t.join(";")
sheet.add_row row sheet.add_row row
if @thread
current_count += 1
if current_count % puts_every_count == 0
finish_percent = (current_count * 100.0 / all_count).round(1)
@thread.update(:status=>{:status=>'Processing','all_count'=>all_count,'current_count'=>current_count,'finish_percent'=>finish_percent})
end
end
end
if @thread
finish_percent = 100
@thread.update(:status=>{:status=>'finish','all_count'=>all_count,'current_count'=>current_count,'finish_percent'=>finish_percent})
end end
end end

View File

@ -1,12 +1,41 @@
<% content_for :page_specific_javascript do %> <% content_for :page_specific_javascript do %>
<script type="text/javascript" src="/assets/validator.js"></script> <script type="text/javascript" src="/assets/validator.js"></script>
<% end %> <% end %>
<% if @thread %>
<div id="threadModal" class="modal hide fade in" tabindex="-1" role="dialog" aria-labelledby="threadModal" aria-hidden="false">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h3 id="threadModal"><%=t("e_paper.#{@thread.key}")%></h3>
</div>
<div class="modal-body">
<div class="thread-status"><%= @thread.status[:status] %></div>
<div class="thread-count-zone <%= 'hide' unless @thread.status[:current_count]%>">
<span class="thread-current-count"><%= @thread.status[:current_count].to_i %></span>/<span class="thread-all-count"><%= @thread.status[:all_count].to_i %></span>
</div>
<span class="thread-finish_percent"><%= @thread.status[:finish_percent].to_i %></span> % finished
<br>
<span class="thread-info"><%= @thread.status[:info].to_s.html_safe %></span>
<br>
<span class="thread-file">
<% if @thread.status[:filename] %>
<a href="<%=admin_announcement_download_file_from_thread_path(:id=>@thread.id.to_s) %>" title="<%= @thread.status[:filename] %>"><%= @thread.status[:filename] %></a>
<% end %>
</span>
</div>
<div class="modal-footer">
<button class="btn" id="modal-close-btn" style="width: 4em;" data-dismiss="modal" aria-hidden="true"><%=t('close')%></button>
</div>
</div>
</div>
</div>
<% end %>
<form action="<%= admin_announcement_importanns_path %>" method="post" class="form-horizontal main-forms" id="import-anns-xls" enctype="multipart/form-data"> <form action="<%= admin_announcement_importanns_path %>" method="post" class="form-horizontal main-forms" id="import-anns-xls" enctype="multipart/form-data">
<h3 style="padding-left: 30px;"><%= t("announcement.export_to_excel") %></h3> <h3 style="padding-left: 30px;"><%= t("announcement.export_to_excel") %></h3>
<div class="control-group"> <div class="control-group">
<div class="controls"> <div class="controls">
<a href="<%= admin_announcement_export_excel_path(:format => "xlsx") %>"><%= t("announcement.export_all_anns") %></a> <a href="<%= admin_announcement_export_excel_path %>"><%= t("announcement.export_all_anns") %></a>
</div> </div>
</div> </div>
<h3 style="padding-left: 30px;"><%= t("announcement.import_from_excel") %></h3> <h3 style="padding-left: 30px;"><%= t("announcement.import_from_excel") %></h3>
<%= hidden_field_tag :authenticity_token, form_authenticity_token %> <%= hidden_field_tag :authenticity_token, form_authenticity_token %>
@ -84,4 +113,58 @@
ext = t[t.length - 1]; ext = t[t.length - 1];
return (ext == "xml") return (ext == "xml")
} }
$(document).ready(function(){
function update_thread(){
$.post("<%=admin_threads_get_status_path%>",{"id": "<%=params[:thread_id]%>"}).done(function(data){
var finish_percent = data["finish_percent"];
var current_count = data["current_count"];
var all_count = data["all_count"];
var is_finish = (data["status"] == "finish" || data["status"] == "error");
var info = data["info"]
if(finish_percent){
$("#threadModal .modal-body .thread-status").text(data["status"]);
if(data["current_count"]){
$("#threadModal .modal-body .thread-count-zone").removeClass('hide');
$("#threadModal .modal-body .thread-current-count").text(current_count);
$("#threadModal .modal-body .thread-all-count").text(all_count);
}else{
$("#threadModal .modal-body .thread-count-zone").addClass('hide');
}
$("#threadModal .modal-body .thread-finish_percent").text(finish_percent);
if(info){
$("#threadModal .modal-body .thread-info").text(info);
}
}
if(!is_finish){
window.time_out_id = window.setTimeout(update_thread, 1000);
}else{
var id = "<%=@thread.id if @thread%>";
var filename = data["filename"];
if(filename){
$("#threadModal .modal-body .thread-file").html(`<a href="<%=admin_announcement_download_file_from_thread_path%>?id=${id}" title="${filename}">${filename}</a>`);
}
if(window.time_out_id)
window.clearTimeout(window.time_out_id);
// window.setTimeout(function(){
// $("#threadModal").modal("hide");
// alert(data["status"]);
// }, 3000);
return;
}
});
}
if($("#threadModal").length != 0){
$("#threadModal").on('hidden.bs.modal',function(){
window.clearTimeout(window.time_out_id);
window.history.replaceState(null,$('title').text(),window.location.pathname);
})
$("#threadModal").on('shown.bs.modal',function(){
window.time_out_id = window.setTimeout(update_thread, 1000);
})
$("#threadModal").modal("show");
$(".show_progress").click(function(){
$("#threadModal").modal("show");
})
}
})
</script> </script>

View File

@ -3,6 +3,7 @@ en:
feed: Feed feed: Feed
import: Import import: Import
announcement: announcement:
read_from_cache: "Read from cache!"
delete_selected: "Delete Selected" delete_selected: "Delete Selected"
expired: This announcement has been expired. expired: This announcement has been expired.
go_back: Go back to the list of announcements. go_back: Go back to the list of announcements.

View File

@ -4,6 +4,7 @@ zh_tw:
import: 匯入 import: 匯入
get_all_anncs_without_subannc: "選擇相關公告" get_all_anncs_without_subannc: "選擇相關公告"
announcement: announcement:
read_from_cache: "從暫存中讀取!"
delete_selected: "刪除所選" delete_selected: "刪除所選"
expired: 此則公告已過期 expired: 此則公告已過期
go_back: 回到公告列表 go_back: 回到公告列表

View File

@ -28,6 +28,7 @@ Rails.application.routes.draw do
get 'announcements/feedform', to: 'announcements#feedform' get 'announcements/feedform', to: 'announcements#feedform'
get 'announcement/settings', to: 'announcements#settings' get 'announcement/settings', to: 'announcements#settings'
get 'announcement/import', to: 'announcements#import' get 'announcement/import', to: 'announcements#import'
get 'announcement/download_file_from_thread', to: 'announcements#download_file_from_thread'
post 'announcement/createsettings', to: 'announcements#createsettings' post 'announcement/createsettings', to: 'announcements#createsettings'
patch 'announcement/updatesettings', to: 'announcements#updatesettings' patch 'announcement/updatesettings', to: 'announcements#updatesettings'
post 'announcement/import_from_wp', to: 'announcements#import_from_wp' post 'announcement/import_from_wp', to: 'announcements#import_from_wp'