Add Export Model
This commit is contained in:
parent
d0f0533a32
commit
d0939ae1a4
9 changed files with 206 additions and 1 deletions
5
app/jobs/export_job.rb
Normal file
5
app/jobs/export_job.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class ExportJob < ApplicationJob
|
||||||
|
def perform(export)
|
||||||
|
export.compute
|
||||||
|
end
|
||||||
|
end
|
97
app/models/export.rb
Normal file
97
app/models/export.rb
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
class Export < ApplicationRecord
|
||||||
|
MAX_DUREE_CONSERVATION_EXPORT = 15.minutes
|
||||||
|
|
||||||
|
enum format: {
|
||||||
|
csv: 'csv',
|
||||||
|
ods: 'ods',
|
||||||
|
xlsx: 'xlsx'
|
||||||
|
}
|
||||||
|
|
||||||
|
has_and_belongs_to_many :groupe_instructeurs
|
||||||
|
|
||||||
|
has_one_attached :file
|
||||||
|
|
||||||
|
validates :format, :groupe_instructeurs, presence: true
|
||||||
|
|
||||||
|
scope :stale, -> { where('updated_at < ?', (Time.zone.now - MAX_DUREE_CONSERVATION_EXPORT)) }
|
||||||
|
|
||||||
|
after_create :compute_async
|
||||||
|
|
||||||
|
def compute_async
|
||||||
|
ExportJob.perform_later(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def compute
|
||||||
|
file.attach(
|
||||||
|
io: io,
|
||||||
|
filename: filename,
|
||||||
|
content_type: content_type
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def ready?
|
||||||
|
file.attached?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find_or_create_export(format, groupe_instructeurs)
|
||||||
|
export = Export.find_for_format_and_groupe_instructeurs(format, groupe_instructeurs)
|
||||||
|
|
||||||
|
if export.nil?
|
||||||
|
export = Export.create(
|
||||||
|
format: format,
|
||||||
|
groupe_instructeurs: groupe_instructeurs
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
export
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find_for_format_and_groupe_instructeurs(format, groupe_instructeurs)
|
||||||
|
export_including_gis = Export
|
||||||
|
.joins(:exports_groupe_instructeurs)
|
||||||
|
.where(
|
||||||
|
format: format,
|
||||||
|
exports_groupe_instructeurs: { groupe_instructeur: groupe_instructeurs }
|
||||||
|
)
|
||||||
|
|
||||||
|
export_including_gis.find do |export|
|
||||||
|
export.groupe_instructeurs.pluck(:id).sort == groupe_instructeurs.map(&:id).sort
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def filename
|
||||||
|
procedure_identifier = procedure.path || "procedure-#{id}"
|
||||||
|
"dossiers_#{procedure_identifier}_#{Time.zone.now.strftime('%Y-%m-%d_%H-%M')}.#{format}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def io
|
||||||
|
dossiers = Dossier.where(groupe_instructeur: groupe_instructeurs)
|
||||||
|
service = ProcedureExportService.new(procedure, dossiers)
|
||||||
|
|
||||||
|
case format.to_sym
|
||||||
|
when :csv
|
||||||
|
StringIO.new(service.to_csv)
|
||||||
|
when :xlsx
|
||||||
|
StringIO.new(service.to_xlsx)
|
||||||
|
when :ods
|
||||||
|
StringIO.new(service.to_ods)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def content_type
|
||||||
|
case format.to_sym
|
||||||
|
when :csv
|
||||||
|
'text/csv'
|
||||||
|
when :xlsx
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
when :ods
|
||||||
|
'application/vnd.oasis.opendocument.spreadsheet'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def procedure
|
||||||
|
groupe_instructeurs.first.procedure
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,7 @@ class GroupeInstructeur < ApplicationRecord
|
||||||
has_many :assign_tos
|
has_many :assign_tos
|
||||||
has_many :instructeurs, through: :assign_tos, dependent: :destroy
|
has_many :instructeurs, through: :assign_tos, dependent: :destroy
|
||||||
has_many :dossiers
|
has_many :dossiers
|
||||||
|
has_and_belongs_to_many :exports
|
||||||
|
|
||||||
validates :label, presence: { message: 'doit être renseigné' }, allow_nil: false
|
validates :label, presence: { message: 'doit être renseigné' }, allow_nil: false
|
||||||
validates :label, uniqueness: { scope: :procedure, message: 'existe déjà' }
|
validates :label, uniqueness: { scope: :procedure, message: 'existe déjà' }
|
||||||
|
|
9
db/migrate/20191211101608_create_exports.rb
Normal file
9
db/migrate/20191211101608_create_exports.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
class CreateExports < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
create_table :exports do |t|
|
||||||
|
t.string :format, null: false
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,8 @@
|
||||||
|
class CreateExportGroupeInstructeurJoinTable < ActiveRecord::Migration[5.2]
|
||||||
|
create_table "exports_groupe_instructeurs", force: :cascade do |t|
|
||||||
|
t.bigint "export_id", null: false
|
||||||
|
t.bigint "groupe_instructeur_id", null: false
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
15
db/schema.rb
15
db/schema.rb
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2019_12_09_141641) do
|
ActiveRecord::Schema.define(version: 2019_12_11_113341) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -317,6 +317,19 @@ ActiveRecord::Schema.define(version: 2019_12_09_141641) do
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "exports", force: :cascade do |t|
|
||||||
|
t.string "format", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "exports_groupe_instructeurs", force: :cascade do |t|
|
||||||
|
t.bigint "export_id", null: false
|
||||||
|
t.bigint "groupe_instructeur_id", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
end
|
||||||
|
|
||||||
create_table "feedbacks", force: :cascade do |t|
|
create_table "feedbacks", force: :cascade do |t|
|
||||||
t.bigint "user_id"
|
t.bigint "user_id"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
|
|
6
spec/factories/export.rb
Normal file
6
spec/factories/export.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :export do
|
||||||
|
format { :csv }
|
||||||
|
groupe_instructeurs { [create(:groupe_instructeur)] }
|
||||||
|
end
|
||||||
|
end
|
8
spec/factories/groupe_instructeur.rb
Normal file
8
spec/factories/groupe_instructeur.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
FactoryBot.define do
|
||||||
|
sequence(:groupe_label) { |n| "label_#{n}" }
|
||||||
|
|
||||||
|
factory :groupe_instructeur do
|
||||||
|
label { generate(:groupe_label) }
|
||||||
|
procedure { create(:procedure) }
|
||||||
|
end
|
||||||
|
end
|
58
spec/models/export_spec.rb
Normal file
58
spec/models/export_spec.rb
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Export, type: :model do
|
||||||
|
describe 'validations' do
|
||||||
|
let(:groupe_instructeur) { create(:groupe_instructeur) }
|
||||||
|
|
||||||
|
context 'when everything is ok' do
|
||||||
|
let(:export) { build(:export) }
|
||||||
|
|
||||||
|
it { expect(export.save).to be true }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when groupe instructeurs are missing' do
|
||||||
|
let(:export) { build(:export, groupe_instructeurs: []) }
|
||||||
|
|
||||||
|
it { expect(export.save).to be false }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when format is missing' do
|
||||||
|
let(:export) { build(:export, format: nil) }
|
||||||
|
|
||||||
|
it { expect(export.save).to be false }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.stale' do
|
||||||
|
let!(:export) { create(:export) }
|
||||||
|
let(:stale_date) { Time.zone.now() - (Export::MAX_DUREE_CONSERVATION_EXPORT + 1.minute) }
|
||||||
|
let!(:stale_export) { create(:export, updated_at: stale_date) }
|
||||||
|
|
||||||
|
it { expect(Export.stale).to match_array([stale_export]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.destroy' do
|
||||||
|
let!(:groupe_instructeur) { create(:groupe_instructeur) }
|
||||||
|
let!(:export) { create(:export, groupe_instructeurs: [groupe_instructeur]) }
|
||||||
|
|
||||||
|
before { export.destroy! }
|
||||||
|
|
||||||
|
it { expect(Export.count).to eq(0) }
|
||||||
|
it { expect(groupe_instructeur.reload).to be_present }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.find_by groupe_instructeurs' do
|
||||||
|
let!(:procedure) { create(:procedure) }
|
||||||
|
let!(:gi_1) { create(:groupe_instructeur, procedure: procedure) }
|
||||||
|
let!(:gi_2) { create(:groupe_instructeur, procedure: procedure) }
|
||||||
|
let!(:gi_3) { create(:groupe_instructeur, procedure: procedure) }
|
||||||
|
|
||||||
|
context 'when an export is made for one groupe instructeur' do
|
||||||
|
let!(:export) { Export.create(format: :csv, groupe_instructeurs: [gi_1, gi_2]) }
|
||||||
|
|
||||||
|
it { expect(Export.find_for_format_and_groupe_instructeurs(:csv, [gi_1])).to eq(nil) }
|
||||||
|
it { expect(Export.find_for_format_and_groupe_instructeurs(:csv, [gi_2, gi_1])).to eq(export) }
|
||||||
|
it { expect(Export.find_for_format_and_groupe_instructeurs(:csv, [gi_1, gi_2, gi_3])).to eq(nil) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue