From a3ef604797f5df143cfd7e917da8db1510d3a4cb Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 2 May 2023 21:13:16 +0100 Subject: [PATCH] Run reports in background --- app/controllers/admin/reports_controller.rb | 51 ++++++++++----------- app/jobs/application_job.rb | 7 +++ app/jobs/report_job.rb | 22 ++++++++- app/views/admin/reports/_download.html.haml | 2 + app/views/admin/reports/_loading.html.haml | 2 + app/views/admin/reports/show.html.haml | 8 +++- app/webpacker/css/admin/reports.scss | 21 +++++++++ config/locales/en.yml | 2 + 8 files changed, 84 insertions(+), 31 deletions(-) create mode 100644 app/views/admin/reports/_download.html.haml create mode 100644 app/views/admin/reports/_loading.html.haml diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index eb24378f4b..e2734c9ff5 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -22,6 +22,13 @@ module Admin def show @report = report_class.new(spree_current_user, params, render: render_data?) + @background_reports = OpenFoodNetwork::FeatureToggle + .enabled?(:background_reports, spree_current_user) + + if @background_reports && request.post? + return background(report_format) + end + if params[:report_format].present? export_report else @@ -34,12 +41,12 @@ module Admin private def export_report - send_data render_report_as(report_format), filename: report_filename + send_data @report.render_as(report_format), filename: report_filename end def show_report assign_view_data - @table = render_report_as(:html) if render_data? + @table = @report.render_as(:html) if render_data? render "show" end @@ -56,21 +63,21 @@ module Admin request.post? end - def render_report_as(format) - if OpenFoodNetwork::FeatureToggle.enabled?(:background_reports, spree_current_user) - @blob = ReportBlob.create_for_upload_later!(report_filename) - ReportJob.perform_later( - report_class, spree_current_user, params, format, @blob - ) - Timeout.timeout(max_wait_time) do - sleep 1 until @blob.content_stored? - end + def background(format) + @blob = ReportBlob.create_for_upload_later!(report_filename) - # This result has been rendered by Rails in safe mode already. - @blob.result.html_safe # rubocop:disable Rails/OutputSafety - else - @report.render_as(format) - end + ReportJob.perform_later( + report_class, spree_current_user, params, format, @blob, SessionChannel.for_request(request) + ) + + render cable_ready: cable_car. + inner_html( + selector: "#report-table", + html: render_to_string(partial: "admin/reports/loading") + ).scroll_into_view( + selector: "#report-table", + block: "start" + ) end def render_timeout_error @@ -84,17 +91,5 @@ module Admin end render "show" end - - def max_wait_time - # This value is used by rack-timeout and nginx, usually 30 seconds in - # staging and production: - server_timeout = ENV.fetch("RACK_TIMEOUT_SERVICE_TIMEOUT", "15").to_f - - # Zero disables the timeout: - return 0 if server_timeout.zero? - - # We want to time out earlier than nginx: - server_timeout - 2.seconds - end end end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index 2b85cac4d7..6411c58c0b 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -7,4 +7,11 @@ class ApplicationJob < ActiveJob::Base # Most jobs are safe to ignore if the underlying records are no longer available # discard_on ActiveJob::DeserializationError + + private + + def enable_active_storage_urls + ActiveStorage::Current.url_options ||= + Rails.application.config.action_controller.default_url_options + end end diff --git a/app/jobs/report_job.rb b/app/jobs/report_job.rb index 390ab18f9c..15c40d79ba 100644 --- a/app/jobs/report_job.rb +++ b/app/jobs/report_job.rb @@ -2,9 +2,14 @@ # Renders a report and stores it in a given blob. class ReportJob < ApplicationJob + include CableReady::Broadcaster + delegate :render, to: ActionController::Base + + before_perform :enable_active_storage_urls + NOTIFICATION_TIME = 5.seconds - def perform(report_class, user, params, format, blob) + def perform(report_class, user, params, format, blob, channel = nil) start_time = Time.zone.now report = report_class.new(user, params, render: true) @@ -14,6 +19,8 @@ class ReportJob < ApplicationJob execution_time = Time.zone.now - start_time email_result(user, blob) if execution_time > NOTIFICATION_TIME + + broadcast_result(channel, format, blob) if channel end def email_result(user, blob) @@ -22,4 +29,17 @@ class ReportJob < ApplicationJob blob: blob, ).report_ready.deliver_later end + + def broadcast_result(channel, format, blob) + cable_ready[channel].inner_html( + selector: "#report-table", + html: actioncable_content(format, blob) + ).broadcast + end + + def actioncable_content(format, blob) + return blob.result if format.to_sym == :html + + render(partial: "admin/reports/download", locals: { file_url: blob.expiring_service_url }) + end end diff --git a/app/views/admin/reports/_download.html.haml b/app/views/admin/reports/_download.html.haml new file mode 100644 index 0000000000..e255c076b0 --- /dev/null +++ b/app/views/admin/reports/_download.html.haml @@ -0,0 +1,2 @@ +.download + = link_to t("admin.reports.download.button"), file_url, target: "_blank", class: "button icon icon-file" diff --git a/app/views/admin/reports/_loading.html.haml b/app/views/admin/reports/_loading.html.haml new file mode 100644 index 0000000000..cad03a9b14 --- /dev/null +++ b/app/views/admin/reports/_loading.html.haml @@ -0,0 +1,2 @@ +.loading + = render partial: "components/admin_spinner" diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 0821ba4835..931aaa7637 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -3,7 +3,9 @@ - content_for :minimal_js, true if @background_reports -= form_for @report.search, :url => url_for do |f| +- options = @background_reports ? { data: { remote: "true" } } : {} + += form_for @report.search, { url: url_for }.merge(options) do |f| %fieldset.no-border-bottom.print-hidden %legend{ align: 'center'}= t(:report_filters) = render partial: "admin/reports/filters/#{@report_type}", locals: { f: f } @@ -24,4 +26,6 @@ %button.btn-print.icon-print{ onclick: "window.print()"}= t(:report_print) = t(@error, link: link_to(t(".report_link_label"), @error_url)) if @error -= @table + +#report-table + = @table diff --git a/app/webpacker/css/admin/reports.scss b/app/webpacker/css/admin/reports.scss index d51d54204b..5d3717788a 100644 --- a/app/webpacker/css/admin/reports.scss +++ b/app/webpacker/css/admin/reports.scss @@ -60,3 +60,24 @@ table.report__table { margin: 0 !important; width: 120px; } + +#report-table { + margin-bottom: 4em; + + .loading, .download { + text-align: center; + height: 2em; + padding: 4em; + width: 100%; + + .spinner { + font-size: 4em; + color: $spree-blue; + opacity: 0.3; + } + + .button { + font-size: 1.25em; + } + } +} diff --git a/config/locales/en.yml b/config/locales/en.yml index 5218012383..4605290f2d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1469,6 +1469,8 @@ en: pack_by_customer: Pack By Customer pack_by_supplier: Pack By Supplier pack_by_product: Pack By Product + download: + button: "Download Report" show: report_taking_longer: > Sorry, this report took too long to process.