Merge pull request #6246 from betagouv/6207-optim-poids-archives
6207 optime la page qui liste les archives
This commit is contained in:
commit
7fb86c34e6
11 changed files with 195 additions and 59 deletions
15
app/assets/stylesheets/archive.scss
Normal file
15
app/assets/stylesheets/archive.scss
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
@import "constants";
|
||||||
|
|
||||||
|
table.archive-table {
|
||||||
|
.text-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 3 * $default-spacer $default-spacer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,12 +4,14 @@ module Instructeurs
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@procedure = procedure
|
@procedure = procedure
|
||||||
|
@average_dossier_weight = procedure.average_dossier_weight
|
||||||
|
|
||||||
@archivable_months = archivable_months
|
@count_dossiers_termines_by_month = Traitement.count_dossiers_termines_by_month(groupe_instructeurs)
|
||||||
@dossiers_termines = @procedure.dossiers.state_termine
|
@nb_dossiers_termines = @count_dossiers_termines_by_month.sum { |count_by_month| count_by_month["count"] }
|
||||||
@poids_total = ProcedureArchiveService.procedure_files_size(@procedure)
|
|
||||||
groupe_instructeur = current_instructeur.groupe_instructeurs.where(procedure: @procedure.id).first
|
@archives = Archive
|
||||||
@archives = Archive.for_groupe_instructeur(groupe_instructeur)
|
.for_groupe_instructeur(groupe_instructeurs)
|
||||||
|
.to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -35,21 +37,20 @@ module Instructeurs
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def archivable_months
|
def procedure_id
|
||||||
start_date = procedure.published_at.to_date
|
params[:procedure_id]
|
||||||
end_date = Time.zone.now.to_date
|
end
|
||||||
|
|
||||||
(start_date...end_date)
|
def groupe_instructeurs
|
||||||
.map(&:beginning_of_month)
|
current_instructeur
|
||||||
.uniq
|
.groupe_instructeurs
|
||||||
.reverse
|
.where(procedure_id: procedure_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def procedure
|
def procedure
|
||||||
current_instructeur
|
current_instructeur
|
||||||
.procedures
|
.procedures
|
||||||
.for_download
|
.find(procedure_id)
|
||||||
.find(params[:procedure_id])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,4 +2,12 @@ module ArchiveHelper
|
||||||
def can_generate_archive?(dossiers_termines, poids_total)
|
def can_generate_archive?(dossiers_termines, poids_total)
|
||||||
dossiers_termines.count < 100 && poids_total < 1.gigabyte
|
dossiers_termines.count < 100 && poids_total < 1.gigabyte
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def estimate_weight(archive, nb_dossiers_termines, average_dossier_weight)
|
||||||
|
if archive.present? && archive.available?
|
||||||
|
archive.file.byte_size
|
||||||
|
else
|
||||||
|
nb_dossiers_termines * average_dossier_weight
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -688,6 +688,20 @@ class Procedure < ApplicationRecord
|
||||||
draft_revision.deep_clone(include: [:revision_types_de_champ, :revision_types_de_champ_private])
|
draft_revision.deep_clone(include: [:revision_types_de_champ, :revision_types_de_champ_private])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def average_dossier_weight
|
||||||
|
if dossiers.termine.any?
|
||||||
|
dossiers_sample = dossiers.termine.limit(100)
|
||||||
|
total_size = Champ
|
||||||
|
.includes(piece_justificative_file_attachment: :blob)
|
||||||
|
.where(type: Champs::PieceJustificativeChamp.to_s, dossier: dossiers_sample)
|
||||||
|
.sum('active_storage_blobs.byte_size')
|
||||||
|
|
||||||
|
total_size / dossiers_sample.length
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def before_publish
|
def before_publish
|
||||||
|
|
|
@ -18,4 +18,21 @@ class Traitement < ApplicationRecord
|
||||||
.where('dossiers.state' => Dossier::TERMINE)
|
.where('dossiers.state' => Dossier::TERMINE)
|
||||||
.where("traitements.processed_at + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: Dossier::INTERVAL_BEFORE_EXPIRATION })
|
.where("traitements.processed_at + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: Dossier::INTERVAL_BEFORE_EXPIRATION })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.count_dossiers_termines_by_month(groupe_instructeurs)
|
||||||
|
last_traitements_per_dossier = Traitement
|
||||||
|
.select('max(traitements.processed_at) as processed_at')
|
||||||
|
.where(dossier: Dossier.termine.where(groupe_instructeur: groupe_instructeurs))
|
||||||
|
.group(:dossier_id)
|
||||||
|
.to_sql
|
||||||
|
|
||||||
|
sql = <<~EOF
|
||||||
|
select date_trunc('month', r1.processed_at) as month, count(r1.processed_at)
|
||||||
|
from (#{last_traitements_per_dossier}) as r1
|
||||||
|
group by date_trunc('month', r1.processed_at)
|
||||||
|
order by month desc
|
||||||
|
EOF
|
||||||
|
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
'Archives'] }
|
'Archives'] }
|
||||||
|
|
||||||
.container
|
.container
|
||||||
%h1 Archives
|
%h1.mb-2 Archives
|
||||||
|
|
||||||
.card.featured
|
.card.featured
|
||||||
.card-title Gestion de vos archives
|
.card-title Gestion de vos archives
|
||||||
|
@ -21,29 +21,25 @@
|
||||||
= link_to 'la documentation', ARCHIVAGE_DOC_URL
|
= link_to 'la documentation', ARCHIVAGE_DOC_URL
|
||||||
afin de voir les options à votre disposition pour mettre en place un système d’archive.
|
afin de voir les options à votre disposition pour mettre en place un système d’archive.
|
||||||
|
|
||||||
%table.table.hoverable
|
%table.table.hoverable.archive-table
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th
|
%th
|
||||||
%th Nombre de dossiers terminés
|
%th.text-right Nombre de dossiers terminés
|
||||||
%th Poids estimé
|
%th.text-right Poids estimé
|
||||||
%th Télécharger
|
%th.center Télécharger
|
||||||
|
|
||||||
%tbody
|
%tbody
|
||||||
- if can_generate_archive?(@dossiers_termines, @poids_total)
|
|
||||||
%tr
|
%tr
|
||||||
- matching_archive = @archives.find_by(time_span_type: 'everything')
|
- matching_archive = @archives.find { |archive| archive.time_span_type == 'everything' }
|
||||||
|
- weight = estimate_weight(matching_archive, @nb_dossiers_termines, @average_dossier_weight)
|
||||||
%td
|
%td
|
||||||
Tous les dossiers
|
Tous les dossiers
|
||||||
%td
|
%td.text-right
|
||||||
= @dossiers_termines.count
|
= @nb_dossiers_termines
|
||||||
%td
|
%td.text-right
|
||||||
- if matching_archive.present? && matching_archive.available?
|
|
||||||
- weight = matching_archive.file.byte_size
|
|
||||||
- else
|
|
||||||
- weight = @poids_total
|
|
||||||
= number_to_human_size(weight)
|
= number_to_human_size(weight)
|
||||||
%td
|
%td.center
|
||||||
- if matching_archive.try(&:available?)
|
- if matching_archive.try(&:available?)
|
||||||
= link_to url_for(matching_archive.file), class: 'button primary' do
|
= link_to url_for(matching_archive.file), class: 'button primary' do
|
||||||
%span.icon.download-white
|
%span.icon.download-white
|
||||||
|
@ -51,29 +47,28 @@
|
||||||
- elsif matching_archive.try(&:pending?)
|
- elsif matching_archive.try(&:pending?)
|
||||||
%span.icon.retry
|
%span.icon.retry
|
||||||
= t(:archive_pending_html, created_period: time_ago_in_words(matching_archive.created_at), scope: [:instructeurs, :procedure])
|
= t(:archive_pending_html, created_period: time_ago_in_words(matching_archive.created_at), scope: [:instructeurs, :procedure])
|
||||||
- elsif @dossiers_termines.count > 0
|
- elsif @nb_dossiers_termines == 0
|
||||||
|
Rien à télécharger !
|
||||||
|
- elsif weight < 1.gigabyte
|
||||||
= link_to instructeur_archives_path(@procedure, type: 'everything'), method: :post, class: "button" do
|
= link_to instructeur_archives_path(@procedure, type: 'everything'), method: :post, class: "button" do
|
||||||
%span.icon.new-folder
|
%span.icon.new-folder
|
||||||
Demander la création
|
Demander la création
|
||||||
- else
|
- else
|
||||||
Rien à télécharger !
|
Archive trop volumineuse
|
||||||
- @archivable_months.each do |month|
|
- @count_dossiers_termines_by_month.each do |count_by_month|
|
||||||
- dossiers_termines = @procedure.dossiers.processed_in_month(month)
|
- month = count_by_month["month"].to_date
|
||||||
- nb_dossiers_termines = dossiers_termines.count
|
- nb_dossiers_termines = count_by_month["count"]
|
||||||
- matching_archive = @archives.find_by(time_span_type: 'monthly', month: month)
|
- matching_archive = @archives.find { |archive| archive.time_span_type == 'monthly' && archive.month == month }
|
||||||
|
- weight = estimate_weight(matching_archive, nb_dossiers_termines, @average_dossier_weight)
|
||||||
|
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
= I18n.l(month, format: "%B %Y")
|
= I18n.l(month, format: "%B %Y").capitalize
|
||||||
%td
|
%td.text-right
|
||||||
= nb_dossiers_termines
|
= nb_dossiers_termines
|
||||||
%td
|
%td.text-right
|
||||||
- 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)
|
= number_to_human_size(weight)
|
||||||
%td
|
%td.center
|
||||||
- if nb_dossiers_termines > 0
|
|
||||||
- if matching_archive.present?
|
- if matching_archive.present?
|
||||||
- if matching_archive.status == 'generated' && matching_archive.file.attached?
|
- if matching_archive.status == 'generated' && matching_archive.file.attached?
|
||||||
= link_to url_for(matching_archive.file), class: 'button primary' do
|
= link_to url_for(matching_archive.file), class: 'button primary' do
|
||||||
|
@ -82,10 +77,10 @@
|
||||||
- else
|
- else
|
||||||
%span.icon.retry
|
%span.icon.retry
|
||||||
= t(:archive_pending_html, created_period: time_ago_in_words(matching_archive.created_at), scope: [:instructeurs, :procedure])
|
= t(:archive_pending_html, created_period: time_ago_in_words(matching_archive.created_at), scope: [:instructeurs, :procedure])
|
||||||
- else
|
- elsif weight < 1.gigabyte
|
||||||
= link_to instructeur_archives_path(@procedure, type:'monthly', month: month.strftime('%Y-%m')), method: :post, class: "button" do
|
= link_to instructeur_archives_path(@procedure, type:'monthly', month: month.strftime('%Y-%m')), method: :post, class: "button" do
|
||||||
%span.icon.new-folder
|
%span.icon.new-folder
|
||||||
Démander la création
|
Démander la création
|
||||||
- else
|
- else
|
||||||
Rien à télécharger !
|
Archive trop volumineuse
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ describe Instructeurs::ArchivesController, type: :controller do
|
||||||
it 'displays archives' do
|
it 'displays archives' do
|
||||||
get :index, { params: { procedure_id: procedure1.id } }
|
get :index, { params: { procedure_id: procedure1.id } }
|
||||||
|
|
||||||
expect(assigns(:dossiers_termines).size).to eq(3)
|
expect(assigns(:nb_dossiers_termines).size).to eq(8)
|
||||||
expect(assigns(:archives)).to eq([archive1])
|
expect(assigns(:archives)).to eq([archive1])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -138,8 +138,12 @@ FactoryBot.define do
|
||||||
factory :champ_piece_justificative, class: 'Champs::PieceJustificativeChamp' do
|
factory :champ_piece_justificative, class: 'Champs::PieceJustificativeChamp' do
|
||||||
type_de_champ { association :type_de_champ_piece_justificative, procedure: dossier.procedure }
|
type_de_champ { association :type_de_champ_piece_justificative, procedure: dossier.procedure }
|
||||||
|
|
||||||
after(:build) do |champ, _evaluator|
|
transient do
|
||||||
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
size { 4 }
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:build) do |champ, evaluator|
|
||||||
|
champ.piece_justificative_file.attach(io: StringIO.new("x" * evaluator.size), filename: "toto.txt", content_type: "text/plain")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
24
spec/helpers/archive_helper_spec.rb
Normal file
24
spec/helpers/archive_helper_spec.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
describe ArchiveHelper, type: :helper do
|
||||||
|
describe ".estimate_weight" do
|
||||||
|
let(:nb_dossiers_termines) { 5 }
|
||||||
|
let(:average_dossier_weight) { 2 }
|
||||||
|
|
||||||
|
context 'when archive exist and available' do
|
||||||
|
let(:archive) { build(:archive, :generated) }
|
||||||
|
before do
|
||||||
|
allow_any_instance_of(Archive).to receive(:available?).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns real archive weight' do
|
||||||
|
expect(estimate_weight(archive, nb_dossiers_termines, average_dossier_weight)).to eq nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when archive has not been created' do
|
||||||
|
let(:archive) { nil }
|
||||||
|
it 'returns estimation' do
|
||||||
|
expect(estimate_weight(archive, nb_dossiers_termines, average_dossier_weight)).to eq 10
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1069,4 +1069,25 @@ describe Procedure do
|
||||||
expect(procedure.destroy).to be_truthy
|
expect(procedure.destroy).to be_truthy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#average_dossier_weight' do
|
||||||
|
let(:procedure) { create(:procedure, :published) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create_dossier_with_pj_of_size(4, procedure)
|
||||||
|
create_dossier_with_pj_of_size(5, procedure)
|
||||||
|
create_dossier_with_pj_of_size(6, procedure)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'estimates average dossier weight' do
|
||||||
|
expect(procedure.reload.average_dossier_weight).to eq 5
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_dossier_with_pj_of_size(size, procedure)
|
||||||
|
dossier = create(:dossier, :accepte, procedure: procedure)
|
||||||
|
create(:champ_piece_justificative, size: size, dossier: dossier)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
37
spec/models/traitement_spec.rb
Normal file
37
spec/models/traitement_spec.rb
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
describe Traitement do
|
||||||
|
describe '#count_dossiers_termines_by_month' do
|
||||||
|
let(:procedure) { create(:procedure, :published, groupe_instructeurs: [groupe_instructeurs]) }
|
||||||
|
let(:groupe_instructeurs) { create(:groupe_instructeur) }
|
||||||
|
let(:result) { Traitement.count_dossiers_termines_by_month(groupe_instructeurs) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create_dossier_for_month(procedure, 2021, 3)
|
||||||
|
create_dossier_for_month(procedure, 2021, 3)
|
||||||
|
create_dossier_for_month(procedure, 2021, 2)
|
||||||
|
Timecop.freeze(Time.zone.local(2021, 3, 5))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'count dossiers_termines by month' do
|
||||||
|
expect(count_for_month(result, 3)).to eq 2
|
||||||
|
expect(count_for_month(result, 2)).to eq 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns descending order by month' do
|
||||||
|
expect(result[0]["month"].to_date.month).to eq 3
|
||||||
|
expect(result[1]["month"].to_date.month).to eq 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def count_for_month(count_dossiers_termines_by_month, month)
|
||||||
|
count_dossiers_termines_by_month.find do |count_by_month|
|
||||||
|
count_by_month["month"].to_date.month == month
|
||||||
|
end["count"]
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
Loading…
Reference in a new issue