From a2066d8c09e302234e20c692e78383562d50762e Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 27 Jul 2016 17:00:06 +0200 Subject: [PATCH] [feature]async generation of users exports --- app/controllers/api/members_controller.rb | 39 +++++++++-- app/models/export.rb | 2 + app/services/users_export_service.rb | 65 +++++++++++++++++++ .../users_members.xlsx.axlsx} | 0 .../users_reservations.xlsx.axlsx} | 0 .../users_subscriptions.xlsx.axlsx} | 0 app/workers/users_export_worker.rb | 27 ++++++++ config/locales/en.yml | 3 + config/locales/fr.yml | 3 + config/locales/mails.en.yml | 3 + config/locales/mails.fr.yml | 3 + 11 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 app/services/users_export_service.rb rename app/views/{api/members/export_members.xlsx.axlsx => exports/users_members.xlsx.axlsx} (100%) rename app/views/{api/members/export_reservations.xlsx.axlsx => exports/users_reservations.xlsx.axlsx} (100%) rename app/views/{api/members/export_subscriptions.xlsx.axlsx => exports/users_subscriptions.xlsx.axlsx} (100%) create mode 100644 app/workers/users_export_worker.rb diff --git a/app/controllers/api/members_controller.rb b/app/controllers/api/members_controller.rb index 2164ed7db..7a5233d89 100644 --- a/app/controllers/api/members_controller.rb +++ b/app/controllers/api/members_controller.rb @@ -91,24 +91,51 @@ class API::MembersController < API::ApiController # export subscriptions def export_subscriptions authorize :export - @subscriptions = Subscription.all.includes(:plan, :user => [:profile]) - render xlsx: 'export_subscriptions.xlsx', filename: "export_subscriptions.xlsx" + export = Export.where({category:'users', export_type: 'subscriptions'}).where('created_at > ?', Subscription.maximum('updated_at')).last + if export.nil? || !FileTest.exist?(export.file) + @export = Export.new({category:'users', export_type: 'subscriptions', user: current_user}) + if @export.save + render json: {export_id: @export.id}, status: :ok + else + render json: @export.errors, status: :unprocessable_entity + end + else + send_file File.join(Rails.root, export.file), :type => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', :disposition => 'attachment' + end end # export reservations def export_reservations authorize :export - @reservations = Reservation.all.includes(:slots, :reservable, :user => [:profile]) - render xlsx: 'export_reservations.xlsx', filename: "export_reservations.xlsx" + export = Export.where({category:'users', export_type: 'reservations'}).where('created_at > ?', Reservation.maximum('updated_at')).last + if export.nil? || !FileTest.exist?(export.file) + @export = Export.new({category:'users', export_type: 'reservations', user: current_user}) + if @export.save + render json: {export_id: @export.id}, status: :ok + else + render json: @export.errors, status: :unprocessable_entity + end + else + send_file File.join(Rails.root, export.file), :type => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', :disposition => 'attachment' + end end def export_members authorize :export - @members = User.with_role(:member).includes(:group, :trainings, :tags, :invoices, :projects, :subscriptions => [:plan], :profile => [:address]) - render xlsx: 'export_members.xlsx', filename: "export_members.xlsx" + export = Export.where({category:'users', export_type: 'members'}).where('created_at > ?', User.with_role(:member).maximum('updated_at')).last + if export.nil? || !FileTest.exist?(export.file) + @export = Export.new({category:'users', export_type: 'members', user: current_user}) + if @export.save + render json: {export_id: @export.id}, status: :ok + else + render json: @export.errors, status: :unprocessable_entity + end + else + send_file File.join(Rails.root, export.file), :type => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', :disposition => 'attachment' + end end def merge diff --git a/app/models/export.rb b/app/models/export.rb index f12b27331..ef44aa74d 100644 --- a/app/models/export.rb +++ b/app/models/export.rb @@ -26,6 +26,8 @@ class Export < ActiveRecord::Base case category when 'statistics' StatisticsExportWorker.perform_async(self.id) + when 'users' + UsersExportWorker.perform_async(self.id) else raise NoMethodError, "Unknown export service for #{category}/#{export_type}" end diff --git a/app/services/users_export_service.rb b/app/services/users_export_service.rb new file mode 100644 index 000000000..ca2c5c214 --- /dev/null +++ b/app/services/users_export_service.rb @@ -0,0 +1,65 @@ +require 'abstract_controller' +require 'action_controller' +require 'action_view' +require 'active_record' + +# require any helpers +require './app/helpers/application_helper' + +class UsersExportService + + # export subscriptions + def export_subscriptions(export) + @subscriptions = Subscription.all.includes(:plan, :user => [:profile]) + + ActionController::Base.prepend_view_path './app/views/' + # place data in view_assigns + view_assigns = {subscriptions: @subscriptions} + av = ActionView::Base.new(ActionController::Base.view_paths, view_assigns) + av.class_eval do + # include any needed helpers (for the view) + include ApplicationHelper + end + + content = av.render template: 'exports/users_subscriptions.xlsx.axlsx' + # write content to file + File.open(export.file,"w+b") {|f| f.puts content } + end + + # export reservations + def export_reservations(export) + @reservations = Reservation.all.includes(:slots, :reservable, :user => [:profile]) + + ActionController::Base.prepend_view_path './app/views/' + # place data in view_assigns + view_assigns = {reservations: @reservations} + av = ActionView::Base.new(ActionController::Base.view_paths, view_assigns) + av.class_eval do + # include any needed helpers (for the view) + include ApplicationHelper + end + + content = av.render template: 'exports/users_reservations.xlsx.axlsx' + # write content to file + File.open(export.file,"w+b") {|f| f.puts content } + end + + # export members + def export_members(export) + @members = User.with_role(:member).includes(:group, :trainings, :tags, :invoices, :projects, :subscriptions => [:plan], :profile => [:address]) + + ActionController::Base.prepend_view_path './app/views/' + # place data in view_assigns + view_assigns = {members: @members} + av = ActionView::Base.new(ActionController::Base.view_paths, view_assigns) + av.class_eval do + # include any needed helpers (for the view) + include ApplicationHelper + end + + content = av.render template: 'exports/users_members.xlsx.axlsx' + # write content to file + File.open(export.file,"w+b") {|f| f.puts content } + end + +end \ No newline at end of file diff --git a/app/views/api/members/export_members.xlsx.axlsx b/app/views/exports/users_members.xlsx.axlsx similarity index 100% rename from app/views/api/members/export_members.xlsx.axlsx rename to app/views/exports/users_members.xlsx.axlsx diff --git a/app/views/api/members/export_reservations.xlsx.axlsx b/app/views/exports/users_reservations.xlsx.axlsx similarity index 100% rename from app/views/api/members/export_reservations.xlsx.axlsx rename to app/views/exports/users_reservations.xlsx.axlsx diff --git a/app/views/api/members/export_subscriptions.xlsx.axlsx b/app/views/exports/users_subscriptions.xlsx.axlsx similarity index 100% rename from app/views/api/members/export_subscriptions.xlsx.axlsx rename to app/views/exports/users_subscriptions.xlsx.axlsx diff --git a/app/workers/users_export_worker.rb b/app/workers/users_export_worker.rb new file mode 100644 index 000000000..95857f94e --- /dev/null +++ b/app/workers/users_export_worker.rb @@ -0,0 +1,27 @@ +class UsersExportWorker + include Sidekiq::Worker + + def perform(export_id) + export = Export.find(export_id) + + unless export.user.is_admin? + raise SecurityError, 'Not allowed to export' + end + + unless export.category == 'users' + raise KeyError, 'Wrong worker called' + end + + service = UsersExportService.new + method_name = "export_#{export.export_type}" + + if %w(members subscriptions reservations).include?(export.export_type) and service.respond_to?(method_name) + service.public_send(method_name, export) + + NotificationCenter.call type: :notify_admin_export_complete, + receiver: export.user, + attached_object: export + end + + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index b26b349b5..6e8a33473 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -259,6 +259,9 @@ en: statistics_project: "of statistics about projects" statistics_subscription: "of subscription statistics" statistics_training: "of statistics about trainings" + users_members: "of the members' list" + users_subscriptions: "of the subscriptions' list" + users_reservations: "of the reservations' list" is_over: "is over." download_here: "Download here" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 15f6dbd37..8c0f6b98f 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -259,6 +259,9 @@ fr: statistics_project: "des statistiques sur les projets" statistics_subscription: "des statistiques d'abonnements" statistics_training: "des statistiques sur les formations" + users_members: "de la liste des membres" + users_subscriptions: "de la liste des abonnements" + users_reservations: "de la liste des réservations" is_over: "est terminé." download_here: "Téléchargez ici" diff --git a/config/locales/mails.en.yml b/config/locales/mails.en.yml index b702e9b5f..b0fa13f02 100644 --- a/config/locales/mails.en.yml +++ b/config/locales/mails.en.yml @@ -261,6 +261,9 @@ en: statistics_project: "of statistics about projects" statistics_subscription: "of subscription statistics" statistics_training: "of statistics about trainings" + users_members: "of the members' list" + users_subscriptions: "of the subscriptions' list" + users_reservations: "of the reservations' list" click_to_download: "Excel file generated successfully. To download it, click" here: "here" diff --git a/config/locales/mails.fr.yml b/config/locales/mails.fr.yml index 6943248ee..faeca7d2c 100644 --- a/config/locales/mails.fr.yml +++ b/config/locales/mails.fr.yml @@ -261,6 +261,9 @@ fr: statistics_project: "des statistiques sur les projets" statistics_subscription: "des statistiques d'abonnements" statistics_training: "des statistiques sur les formations" + users_members: "de la liste des membres" + users_subscriptions: "de la liste des abonnements" + users_reservations: "de la liste des réservations" click_to_download: "La génération est terminée. Pour télécharger le fichier Excel, cliquez" here: "ici"