From 8b2849408c7f8aecc00789bb3d559308b06cf2e1 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 30 Mar 2021 10:46:21 +0200 Subject: [PATCH] instructeurs can create and download archives --- .../instructeurs/archives_controller.rb | 49 ++++++++++ app/helpers/archive_helper.rb | 5 + app/models/procedure.rb | 14 +++ .../instructeurs/archives/create.js.haml | 1 + .../instructeurs/archives/index.html.haml | 91 +++++++++++++++++++ .../procedures/_download_dossiers.html.haml | 2 + config/locales/views/instructeurs/fr.yml | 1 + config/routes.rb | 2 + .../instructeurs/archives_controller_spec.rb | 54 +++++++++++ .../_download_dossiers.html.haml_spec.rb | 10 ++ 10 files changed, 229 insertions(+) create mode 100644 app/controllers/instructeurs/archives_controller.rb create mode 100644 app/helpers/archive_helper.rb create mode 100644 app/views/instructeurs/archives/create.js.haml create mode 100644 app/views/instructeurs/archives/index.html.haml create mode 100644 spec/controllers/instructeurs/archives_controller_spec.rb diff --git a/app/controllers/instructeurs/archives_controller.rb b/app/controllers/instructeurs/archives_controller.rb new file mode 100644 index 000000000..308f51b80 --- /dev/null +++ b/app/controllers/instructeurs/archives_controller.rb @@ -0,0 +1,49 @@ +module Instructeurs + class ArchivesController < InstructeurController + before_action :ensure_procedure_enabled + + def index + @procedure = procedure + + @archivable_months = archivable_months + @dossiers_termines = @procedure.dossiers.state_termine + @poids_total = ProcedureArchiveService.procedure_files_size(@procedure) + groupe_instructeur = current_instructeur.groupe_instructeurs.where(procedure: @procedure.id).first + @archives = Archive.for_groupe_instructeur(groupe_instructeur) + end + + def create + type = params[:type] + month = Date.strptime(params[:month], '%Y-%m') if params[:month].present? + + ArchiveCreationJob.perform_later(procedure, current_instructeur, type, month) + flash[:notice] = "Votre demande a été prise en compte. Selon le nombre de dossiers, cela peut prendre quelques minutes. Vous recevrez un courriel lorsque le fichier sera disponible." + end + + private + + def ensure_procedure_enabled + if !procedure.publiee? + flash[:alert] = "L'accès aux archives n'est pas disponible pour cette démarche, merci d'en faire la demande à l'équipe de démarches simplifiees" + return redirect_to instructeur_procedure_path(procedure) + end + end + + def archivable_months + start_date = procedure.published_at.to_date + end_date = Time.zone.now.to_date + + (start_date...end_date) + .map(&:beginning_of_month) + .uniq + .reverse + end + + def procedure + current_instructeur + .procedures + .for_download + .find(params[:procedure_id]) + end + end +end diff --git a/app/helpers/archive_helper.rb b/app/helpers/archive_helper.rb new file mode 100644 index 000000000..b349b8793 --- /dev/null +++ b/app/helpers/archive_helper.rb @@ -0,0 +1,5 @@ +module ArchiveHelper + def can_generate_archive?(dossiers_termines, poids_total) + dossiers_termines.count < 100 && poids_total < 1.gigabyte + end +end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index ea0b4e570..b4760c353 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -156,6 +156,20 @@ class Procedure < ApplicationRecord includes(:draft_revision, :published_revision, administrateurs: :user) } + scope :for_download, -> { + includes( + :groupe_instructeurs, + dossiers: { + champs: [ + piece_justificative_file_attachment: :blob, + champs: [ + piece_justificative_file_attachment: :blob + ] + ] + } + ) + } + validates :libelle, presence: true, allow_blank: false, allow_nil: false validates :description, presence: true, allow_blank: false, allow_nil: false validates :administrateurs, presence: true diff --git a/app/views/instructeurs/archives/create.js.haml b/app/views/instructeurs/archives/create.js.haml new file mode 100644 index 000000000..7fe9f7f0b --- /dev/null +++ b/app/views/instructeurs/archives/create.js.haml @@ -0,0 +1 @@ += render_flash(sticky: true) diff --git a/app/views/instructeurs/archives/index.html.haml b/app/views/instructeurs/archives/index.html.haml new file mode 100644 index 000000000..057385e69 --- /dev/null +++ b/app/views/instructeurs/archives/index.html.haml @@ -0,0 +1,91 @@ +- content_for(:title, "Archives pour #{@procedure.libelle}") + += render partial: 'new_administrateur/breadcrumbs', + locals: { steps: [link_to(@procedure.libelle, instructeur_procedure_path(@procedure)), + 'Archives'] } + +.container + %h1 Archives + + .card.featured + .card-title Gestion de vos archives + %p + Vous pouvez télécharger les archives des dossiers terminés depuis la publication de la procédure au format Zip. + + %p + Cet export contient les demande déposée par l'usager et la liste des pièces justificatives transmises. + + %p + Cet export n'est pas possible pour le moment pour les démarches à forte volumétrie. + Nous vous invitons à regarder + = link_to 'la documentation', ARCHIVAGE_DOC_URL + afin de voir les options à votre disposition pour mettre en place un système d'archive. + + %table.table.hoverable + %thead + %tr + %th   + %th Nombre de dossiers terminés + %th Poids estimé + %th Télécharger + + %tbody + - if can_generate_archive?(@dossiers_termines, @poids_total) + %tr + - matching_archive = @archives.find_by(content_type: 'everything') + %td + Tous les dossiers + %td + = @dossiers_termines.count + %td + - if matching_archive.present? && matching_archive.available? + - weight = matching_archive.file.byte_size + - else + - weight = @poids_total + = number_to_human_size(weight) + %td + - if matching_archive.try(&:available?) + = link_to url_for(matching_archive.file), class: 'button primary' do + %span.icon.download-white + Télécharger + - elsif matching_archive.try(&:pending?) + %span.icon.retry + Archive en cours de création + - elsif @dossiers_termines.count > 0 + = link_to instructeur_archives_path(@procedure, type: 'everything'), method: :post, class: "button", remote: true do + %span.icon.new-folder + Demander la création + - else + Rien à télécharger ! + - @archivable_months.each do |month| + - dossiers_termines = @procedure.dossiers.processed_in_month(month) + - nb_dossiers_termines = dossiers_termines.count + - matching_archive = @archives.find_by(content_type: 'monthly', month: month) + %tr + %td + = I18n.l(month, format: "%B %Y") + %td + = nb_dossiers_termines + %td + - if matching_archive.present? && matching_archive.available? + - weight = matching_archive.file.byte_size + - else + - weight = ProcedureArchiveService::dossiers_files_size(dossiers_termines) + = number_to_human_size(weight) + %td + - if nb_dossiers_termines > 0 + - if matching_archive.present? + - if matching_archive.status == 'generated' && matching_archive.file.attached? + = link_to url_for(matching_archive.file), class: 'button primary' do + %span.icon.download-white + Télécharger + - else + %span.icon.retry + Archive en cours de création + - else + = link_to instructeur_archives_path(@procedure, type:'monthly', month: month.strftime('%Y-%m')), method: :post, class: "button", remote: true do + %span.icon.new-folder + Démander la création + - else + Rien à télécharger ! + diff --git a/app/views/instructeurs/procedures/_download_dossiers.html.haml b/app/views/instructeurs/procedures/_download_dossiers.html.haml index 87c2c7417..1e0993605 100644 --- a/app/views/instructeurs/procedures/_download_dossiers.html.haml +++ b/app/views/instructeurs/procedures/_download_dossiers.html.haml @@ -16,3 +16,5 @@ - else %span{ 'data-export-poll-url': download_export_instructeur_procedure_path(procedure, export_format: format, no_progress_notification: true) } = t(:export_pending_html, export_time: time_ago_in_words(export.created_at), export_format: ".#{format}", scope: [:instructeurs, :procedure]) + %li + = link_to t(:download_archive, scope: [:instructeurs, :procedure]), instructeur_archives_path(procedure) diff --git a/config/locales/views/instructeurs/fr.yml b/config/locales/views/instructeurs/fr.yml index 3cc5fabdc..17c7ef429 100644 --- a/config/locales/views/instructeurs/fr.yml +++ b/config/locales/views/instructeurs/fr.yml @@ -8,3 +8,4 @@ fr: ods_html: Demander un export au format .ods export_ready_html: Télécharger l’export au format %{export_format}
(généré il y a %{export_time}) export_pending_html: Un export au format %{export_format} est en train d’être généré
(demandé il y a %{export_time}) + download_archive: Télécharger une archive au format .zip de tous les dossiers et leurs pièces jointes diff --git a/config/routes.rb b/config/routes.rb index bb953ffa4..acdac88ea 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -383,6 +383,8 @@ Rails.application.routes.draw do get 'telecharger_pjs' => 'dossiers#telecharger_pjs' end end + + resources :archives, only: [:index, :create, :show], controller: 'archives' end end get "recherche" => "recherche#index" diff --git a/spec/controllers/instructeurs/archives_controller_spec.rb b/spec/controllers/instructeurs/archives_controller_spec.rb new file mode 100644 index 000000000..d698c95bb --- /dev/null +++ b/spec/controllers/instructeurs/archives_controller_spec.rb @@ -0,0 +1,54 @@ +describe Instructeurs::ArchivesController, type: :controller do + let(:procedure1) { create(:procedure, :published, groupe_instructeurs: [gi1]) } + let(:procedure2) { create(:procedure, :published, groupe_instructeurs: [gi2]) } + let!(:instructeur) { create(:instructeur, groupe_instructeurs: [gi1, gi2]) } + let!(:archive1) { create(:archive, :generated, groupe_instructeurs: [gi1]) } + let!(:archive2) { create(:archive, :generated, groupe_instructeurs: [gi2]) } + let(:gi1) { create(:groupe_instructeur) } + let(:gi2) { create(:groupe_instructeur) } + + before do + sign_in(instructeur.user) + end + + after { Timecop.return } + + describe '#index' do + before do + create_dossier_for_month(procedure1, 2021, 3) + create_dossier_for_month(procedure1, 2021, 3) + create_dossier_for_month(procedure1, 2021, 2) + Timecop.freeze(Time.zone.local(2021, 3, 5)) + end + + it 'displays archives' do + get :index, { params: { procedure_id: procedure1.id } } + + expect(assigns(:dossiers_termines).size).to eq(3) + expect(assigns(:archives)).to eq([archive1]) + end + end + + describe '#create' do + let(:month) { '21-03' } + let(:date_month) { Date.strptime(month, "%Y-%m") } + let(:subject) do + post :create, { + xhr: true, + params: { procedure_id: procedure1.id, type: 'monthly', month: month } + } + end + + it "performs archive creation job" do + expect { subject }.to have_enqueued_job(ArchiveCreationJob).with(procedure1, instructeur, 'monthly', date_month) + expect(flash.notice).to include("Votre demande a été prise en compte") + end + end + + private + + def create_dossier_for_month(procedure, year, month) + Timecop.freeze(Time.zone.local(year, month, 5)) + create(:dossier, :accepte, :with_attestation, procedure: procedure) + end +end diff --git a/spec/views/instructeur/procedures/_download_dossiers.html.haml_spec.rb b/spec/views/instructeur/procedures/_download_dossiers.html.haml_spec.rb index 45b522742..a796905d9 100644 --- a/spec/views/instructeur/procedures/_download_dossiers.html.haml_spec.rb +++ b/spec/views/instructeur/procedures/_download_dossiers.html.haml_spec.rb @@ -16,5 +16,15 @@ describe 'instructeurs/procedures/_download_dossiers.html.haml', type: :view do context "when procedure has at least 1 dossier en construction" do let!(:dossier) { create(:dossier, :en_construction, procedure: procedure) } it { is_expected.to include("Télécharger tous les dossiers") } + + context "With zip archive enabled" do + before { Flipper.enable(:archive_zip_globale, procedure) } + it { is_expected.to include("Télécharger une archive au format .zip") } + end + + context "With zip archive disabled" do + before { Flipper.disable(:archive_zip_globale, procedure) } + it { is_expected.not_to include("Télécharger une archive au format .zip") } + end end end