diff --git a/app/admin/questions.rb b/app/admin/questions.rb index 50eee6c6a..1b4186ea0 100644 --- a/app/admin/questions.rb +++ b/app/admin/questions.rb @@ -5,18 +5,20 @@ member_action :export_xls, method: :get do question = Question.find(params[:id]) - export = ImportExportQuestion.new(question) - export.exporte_donnees + export = ImportExportQuestion.new(question).exporte_donnees + send_data export[:xls], + content_type: export[:content_type], + filename: export[:filename] end controller do def import_xls return if params[:file_xls].blank? - importe_question + question = recupere_question flash[:success] = I18n.t('.layouts.succes.import_question') - redirect_to redirection_apres_import - rescue ImportExportQuestion::Error => e + redirect_to redirection_apres_import(question) + rescue ImportQuestion::Error => e erreur_import(e) rescue ImportXls::Error => e raise ImportExportQuestion::Error, e.message @@ -24,9 +26,9 @@ def import_xls private - def importe_question - import = ImportExportQuestion.new(Question.new(type: params[:type])) - @question = import.importe_donnees(params[:file_xls]) + def recupere_question + question = Question.new(type: params[:type]) + ImportExportQuestion.new(question).importe_donnees(params[:file_xls]) end def erreur_import(error) @@ -36,13 +38,13 @@ def erreur_import(error) private - def redirection_apres_import + def redirection_apres_import(question) redirection_paths = { - 'QuestionClicDansImage' => edit_admin_question_clic_dans_image_path(@question), - 'QuestionGlisserDeposer' => edit_admin_question_glisser_deposer_path(@question), - 'QuestionQcm' => edit_admin_question_qcm_path(@question), - 'QuestionSaisie' => edit_admin_question_saisie_path(@question), - 'QuestionSousConsigne' => edit_admin_question_sous_consigne_path(@question) + 'QuestionClicDansImage' => edit_admin_question_clic_dans_image_path(question), + 'QuestionGlisserDeposer' => edit_admin_question_glisser_deposer_path(question), + 'QuestionQcm' => edit_admin_question_qcm_path(question), + 'QuestionSaisie' => edit_admin_question_saisie_path(question), + 'QuestionSousConsigne' => edit_admin_question_sous_consigne_path(question) } redirection_paths[params[:type]] diff --git a/app/admin/questions_clic_dans_image.rb b/app/admin/questions_clic_dans_image.rb index 2ef14ca96..c2e24ba3f 100644 --- a/app/admin/questions_clic_dans_image.rb +++ b/app/admin/questions_clic_dans_image.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true ActiveAdmin.register QuestionClicDansImage do - before_action :set_question, only: %i[update show] + before_action :set_question, only: %i[update] menu parent: 'Parcours', if: proc { can? :manage, Compte } @@ -40,7 +40,7 @@ action_item :exporter_question, only: :show do link_to 'Exporter le contenu de la question', - admin_question_export_xls_path(question_id: question.id) + admin_question_export_xls_path(question_id: params[:id]) end show do diff --git a/app/models/choix.rb b/app/models/choix.rb index 266306f6c..315a4c8d0 100644 --- a/app/models/choix.rb +++ b/app/models/choix.rb @@ -26,4 +26,16 @@ def audio_type errors.add(:audio, 'doit être un fichier MP3 ou MP4') audio.purge end + + def audio_url + return unless audio.attached? + + cdn_for(audio) + end + + def illustration_url + return unless illustration.attached? + + cdn_for(illustration) + end end diff --git a/app/models/export_question.rb b/app/models/export_question.rb new file mode 100644 index 000000000..5c72a1777 --- /dev/null +++ b/app/models/export_question.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true + +class ExportQuestion + WORKSHEET_NAME = 'Données' + + def initialize(question, headers) + @question = question + @headers = headers + @sheet = sheet + end + + def to_xls + initialise_feuille + remplie_la_feuille + retourne_le_contenu_du_xls + end + + def content_type_xls + 'application/vnd.ms-excel' + end + + def nom_du_fichier + date = DateTime.current.strftime('%Y%m%d') + "#{date}-#{@question.nom_technique}.xls" + end + + private + + def sheet + workbook = Spreadsheet::Workbook.new + workbook.create_worksheet(name: WORKSHEET_NAME) + end + + def initialise_feuille + format_premiere_ligne = Spreadsheet::Format.new(weight: :bold) + @sheet.row(0).default_format = format_premiere_ligne + @headers.each_with_index do |entete, colonne| + @sheet[0, colonne] = entete.to_s.humanize + @sheet.column(colonne).width = 20 + end + end + + def remplie_la_feuille + @headers.each { |_valeur| remplis_champs_commun } + end + + def remplis_champs_commun + @sheet[1, 0] = @question.libelle + @sheet[1, 1] = @question.nom_technique + @sheet[1, 2] = @question.illustration_url + @sheet[1, 3] = @question.transcription_intitule&.ecrit + @sheet[1, 4] = @question.transcription_intitule&.audio_url + remplis_champs_additionnels + end + + def remplis_champs_additionnels + return if @question.sous_consigne? + + @sheet[1, 5] = @question.transcription_modalite_reponse&.ecrit + @sheet[1, 6] = @question.transcription_modalite_reponse&.audio_url + @sheet[1, 7] = @question.description + remplis_champs_specifiques + end + + def remplis_champs_specifiques + case @question.type + when 'QuestionClicDansImage' then remplis_champs_clic_dans_image + when 'QuestionGlisserDeposer' then remplis_champs_glisser_deposer + when 'QuestionQcm' then remplis_champs_qcm + when 'QuestionSaisie' then remplis_champs_saisie + end + end + + def remplis_champs_clic_dans_image + @sheet[1, 8] = @question.zone_cliquable_url + @sheet[1, 9] = @question.image_au_clic_url + end + + def remplis_champs_glisser_deposer + @sheet[1, 8] = @question.zone_depot_url + @question.reponses.each_with_index { |choix, index| ajoute_reponses(choix, 1, index) } + end + + def remplis_champs_saisie + @sheet[1, 8] = @question.suffix_reponse + @sheet[1, 9] = @question.reponse_placeholder + @sheet[1, 10] = @question.type_saisie + return unless @question.bonne_reponse + + @sheet[1, 11] = @question.bonne_reponse.intitule + @sheet[1, 12] = @question.bonne_reponse.nom_technique + end + + def remplis_champs_qcm + @sheet[1, 8] = @question.type_qcm + @question.choix.each_with_index { |choix, index| ajoute_choix(choix, 1, index) } + end + + def ajoute_choix(choix, ligne, index) + columns = %w[intitule nom_technique type_choix audio] + columns.each_with_index do |col, i| + @sheet[0, 9 + (index * 4) + i] = "choix_#{index + 1}_#{col}" + @sheet[ligne, 9 + (index * 4) + i] = choix.send(col) + end + end + + def ajoute_reponses(choix, ligne, index) + columns = %w[nom_technique position_client type_choix illustration] + columns.each_with_index do |col, i| + @sheet[0, 9 + (index * 4) + i] = "reponse_#{index + 1}_#{col}" + @sheet[ligne, 9 + (index * 4) + i] = choix.send(col) + end + end + + def retourne_le_contenu_du_xls + file_contents = StringIO.new + @sheet.workbook.write file_contents + file_contents.string + end +end diff --git a/app/models/import_export_question.rb b/app/models/import_export_question.rb index 50f1cb5ee..5a921bf5b 100644 --- a/app/models/import_export_question.rb +++ b/app/models/import_export_question.rb @@ -16,42 +16,27 @@ class ImportExportQuestion 'QuestionSaisie' => HEADERS_COMMUN + HEADERS_SAISIE, 'QuestionSousConsigne' => HEADERS_SOUS_CONSIGNE }.freeze - delegate :message_erreur_headers, to: :@import - def initialize(question) @question = question @type = question.type - @import = ImportQuestion.new(@question, HEADERS_ATTENDUS[@type]) end def importe_donnees(file) - @import.recupere_data(file) - @import.valide_headers - @import.cree_question + ImportQuestion.new(@question, HEADERS_ATTENDUS[@type]).import_from_xls(file) rescue ActiveRecord::RecordInvalid => e - raise Error, message_erreur_validation(e) + raise ImportQuestion::Error, message_erreur_validation(e) end def exporte_donnees - send_data to_xls, - content_type: content_type_xls, - filename: nom_du_fichier + export = ExportQuestion.new(@question, HEADERS_ATTENDUS[@type]) + { + xls: export.to_xls, + content_type: export.content_type_xls, + filename: export.nom_du_fichier + } end - # def to_xls - # workbook = Spreadsheet::Workbook.new - # sheet = workbook.create_worksheet(name: 'worksheet name') - # initialise_sheet(sheet) - # remplie_la_feuille(sheet) - # retourne_le_contenu_du_xls(workbook) - # end - - # def content_type_xls - # 'application/vnd.ms-excel' - # end - - # def nom_du_fichier - # date = DateTime.current.strftime('%Y%m%d') - # "#{date}-#{@question.nom_technique}.xls" - # end + def message_erreur_validation(exception) + exception.record.errors.full_messages.to_sentence.to_s + end end diff --git a/app/models/import_question.rb b/app/models/import_question.rb index aef32d84c..fdb2d8aa6 100644 --- a/app/models/import_question.rb +++ b/app/models/import_question.rb @@ -7,6 +7,12 @@ def initialize(question, headers_attendus) @type = question.type end + def import_from_xls(file) + recupere_data(file) + valide_headers + cree_question + end + def cree_question ActiveRecord::Base.transaction do intialise_question diff --git a/app/models/import_xls.rb b/app/models/import_xls.rb index c80cda3c3..3f926dec5 100644 --- a/app/models/import_xls.rb +++ b/app/models/import_xls.rb @@ -52,10 +52,6 @@ def telecharge_fichier(url) raise Error, message_erreur_telechargement(@current_download) end - def message_erreur_validation(exception) - exception.record.errors.full_messages.to_sentence.to_s - end - def message_erreur_telechargement(current_download) "Impossible de télécharger un fichier depuis l'url : #{current_download}" end diff --git a/app/models/question.rb b/app/models/question.rb index 3fe3b1cc3..072a9d8b3 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -71,6 +71,12 @@ def sous_consigne? type == 'QuestionSousConsigne' end + def illustration_url + return unless illustration.attached? + + cdn_for(illustration) + end + private def audio_url diff --git a/app/models/question_clic_dans_image.rb b/app/models/question_clic_dans_image.rb index fa70b6870..04873d236 100644 --- a/app/models/question_clic_dans_image.rb +++ b/app/models/question_clic_dans_image.rb @@ -29,6 +29,14 @@ def clic_multiple? svg_contient_class_bonne_reponse?(svg_content, 2) end + def zone_cliquable_url + cdn_for(zone_cliquable) if zone_cliquable.attached? + end + + def image_au_clic_url + cdn_for(image_au_clic) if image_au_clic.attached? + end + private def base_json diff --git a/app/models/question_glisser_deposer.rb b/app/models/question_glisser_deposer.rb index ad69bccfa..846cdb3fc 100644 --- a/app/models/question_glisser_deposer.rb +++ b/app/models/question_glisser_deposer.rb @@ -23,6 +23,10 @@ def as_json(_options = nil) json.merge!(json_audio_fields, reponses_fields) end + def zone_depot_url + cdn_for(zone_depot) if zone_depot.attached? + end + private def base_json diff --git a/spec/models/export_question_spec.rb b/spec/models/export_question_spec.rb new file mode 100644 index 000000000..46bac1352 --- /dev/null +++ b/spec/models/export_question_spec.rb @@ -0,0 +1,207 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ExportQuestion do + describe 'pour tous les types de questions' do + subject(:response_service) do + described_class.new(question_clic, headers) + end + + let(:question_clic) do + create(:question_clic_dans_image, description: 'Ceci est une description', + nom_technique: 'clic') + end + let!(:intitule) do + create(:transcription, :avec_audio, question_id: question_clic.id, categorie: :intitule, + ecrit: 'Ceci est un intitulé') + end + let!(:consigne) do + create(:transcription, :avec_audio, question_id: question_clic.id, + categorie: :modalite_reponse, + ecrit: 'Ceci est une consigne') + end + let(:headers) do + ImportExportQuestion::HEADERS_ATTENDUS[question_clic.type] + end + + describe '#to_xls' do + it 'génére un fichier xls avec les entêtes sur chaque colonnes' do + xls = response_service.to_xls + spreadsheet = Spreadsheet.open(StringIO.new(xls)) + worksheet = spreadsheet.worksheet(0) + + expect(spreadsheet.worksheets.count).to eq(1) + expect(worksheet.row(0)[0]).to eq('Libelle') + expect(worksheet.row(0)[1]).to eq('Nom technique') + expect(worksheet.row(0)[2]).to eq('Illustration') + expect(worksheet.row(0)[3]).to eq('Intitule ecrit') + expect(worksheet.row(0)[4]).to eq('Intitule audio') + expect(worksheet.row(0)[5]).to eq('Consigne ecrit') + expect(worksheet.row(0)[6]).to eq('Consigne audio') + expect(worksheet.row(0)[7]).to eq('Description') + expect(worksheet.row(0)[8]).to eq('Zone cliquable') + expect(worksheet.row(0)[9]).to eq('Image au clic') + end + + it 'génére un fichier xls avec les détails de la question' do + xls = response_service.to_xls + spreadsheet = Spreadsheet.open(StringIO.new(xls)) + worksheet = spreadsheet.worksheet(0) + question = worksheet.row(1) + expect(question[0]).to eq('Question clic dans image') + expect(question[1]).to eq('clic') + expect(question[2]).to be_nil + expect(question[3]).to eq('Ceci est un intitulé') + expect(question[4]).to eq(intitule.audio_url) + expect(question[5]).to eq('Ceci est une consigne') + expect(question[6]).to eq(consigne.audio_url) + expect(question[7]).to eq('Ceci est une description') + expect(question[8]).to eq(question_clic.zone_cliquable_url) + expect(question[9]).to eq(question_clic.image_au_clic_url) + end + end + + describe '#nom_du_fichier' do + it 'genere le nom du fichier' do + date = DateTime.current.strftime('%Y%m%d') + nom_du_fichier_attendu = "#{date}-#{question_clic.nom_technique}.xls" + + expect(response_service.nom_du_fichier).to eq(nom_du_fichier_attendu) + end + end + + describe '#content_type_xls' do + it { expect(response_service.content_type_xls).to eq 'application/vnd.ms-excel' } + end + end + + describe 'pour une question glisser deposer' do + subject(:response_service) do + described_class.new(question_glisser_deposer, headers) + end + + let(:question_glisser_deposer) { create(:question_glisser_deposer) } + let(:headers) do + ImportExportQuestion::HEADERS_ATTENDUS[question_glisser_deposer.type] + end + let!(:reponse) { create(:choix, :bon, question_id: question_glisser_deposer.id) } + let!(:reponse2) { create(:choix, :mauvais, question_id: question_glisser_deposer.id) } + + it 'génére un fichier xls avec les entêtes spécifiques' do + xls = response_service.to_xls + spreadsheet = Spreadsheet.open(StringIO.new(xls)) + worksheet = spreadsheet.worksheet(0) + + expect(spreadsheet.worksheets.count).to eq(1) + expect(worksheet.row(0)[8]).to eq('Zone depot') + expect(worksheet.row(0)[9]).to eq('reponse_1_nom_technique') + expect(worksheet.row(0)[10]).to eq('reponse_1_position_client') + expect(worksheet.row(0)[11]).to eq('reponse_1_type_choix') + expect(worksheet.row(0)[12]).to eq('reponse_1_illustration') + expect(worksheet.row(0)[13]).to eq('reponse_2_nom_technique') + expect(worksheet.row(0)[14]).to eq('reponse_2_position_client') + expect(worksheet.row(0)[15]).to eq('reponse_2_type_choix') + expect(worksheet.row(0)[16]).to eq('reponse_2_illustration') + end + + it 'génére un fichier xls avec les détails de la question' do + xls = response_service.to_xls + spreadsheet = Spreadsheet.open(StringIO.new(xls)) + worksheet = spreadsheet.worksheet(0) + question = worksheet.row(1) + expect(question[8]).to eq(question_glisser_deposer.zone_depot_url) + expect(question[9]).to eq(reponse.nom_technique) + expect(question[10]).to eq(reponse.position_client) + expect(question[11]).to eq(reponse.type_choix) + expect(question[12]).to eq(reponse.illustration_url) + expect(question[13]).to eq(reponse2.nom_technique) + expect(question[14]).to eq(reponse2.position_client) + expect(question[15]).to eq(reponse2.type_choix) + expect(question[16]).to eq(reponse2.illustration_url) + end + end + + describe 'pour une question saisie' do + subject(:response_service) do + described_class.new(question_saisie, headers) + end + + let(:question_saisie) { create(:question_saisie) } + let(:headers) do + ImportExportQuestion::HEADERS_ATTENDUS[question_saisie.type] + end + let!(:reponse) { create(:choix, :bon, question_id: question_saisie.id) } + + it 'génére un fichier xls avec les entêtes spécifiques' do + xls = response_service.to_xls + spreadsheet = Spreadsheet.open(StringIO.new(xls)) + worksheet = spreadsheet.worksheet(0) + + expect(spreadsheet.worksheets.count).to eq(1) + expect(worksheet.row(0)[8]).to eq('Suffix reponse') + expect(worksheet.row(0)[9]).to eq('Reponse placeholder') + expect(worksheet.row(0)[10]).to eq('Type saisie') + expect(worksheet.row(0)[11]).to eq('Bonne reponse intitule') + expect(worksheet.row(0)[12]).to eq('Bonne reponse nom technique') + end + + it 'génére un fichier xls avec les détails de la question' do + xls = response_service.to_xls + spreadsheet = Spreadsheet.open(StringIO.new(xls)) + worksheet = spreadsheet.worksheet(0) + question = worksheet.row(1) + expect(question[8]).to eq(question_saisie.suffix_reponse) + expect(question[9]).to eq(question_saisie.reponse_placeholder) + expect(question[10]).to eq(question_saisie.type_saisie) + expect(question[11]).to eq(question_saisie.bonne_reponse.intitule) + expect(question[12]).to eq(question_saisie.bonne_reponse.nom_technique) + end + end + + describe 'pour une question qcm' do + subject(:response_service) do + described_class.new(question_qcm, headers) + end + + let(:question_qcm) { create(:question_qcm) } + let(:headers) do + ImportExportQuestion::HEADERS_ATTENDUS[question_qcm.type] + end + let!(:reponse) { create(:choix, :bon, question_id: question_qcm.id) } + let!(:reponse2) { create(:choix, :mauvais, question_id: question_qcm.id) } + + it 'génére un fichier xls avec les entêtes spécifiques' do + xls = response_service.to_xls + spreadsheet = Spreadsheet.open(StringIO.new(xls)) + worksheet = spreadsheet.worksheet(0) + + expect(spreadsheet.worksheets.count).to eq(1) + expect(worksheet.row(0)[8]).to eq('Type qcm') + expect(worksheet.row(0)[9]).to eq('choix_1_intitule') + expect(worksheet.row(0)[10]).to eq('choix_1_nom_technique') + expect(worksheet.row(0)[11]).to eq('choix_1_type_choix') + expect(worksheet.row(0)[12]).to eq('choix_1_audio') + expect(worksheet.row(0)[13]).to eq('choix_2_intitule') + expect(worksheet.row(0)[14]).to eq('choix_2_nom_technique') + expect(worksheet.row(0)[15]).to eq('choix_2_type_choix') + expect(worksheet.row(0)[16]).to eq('choix_2_audio') + end + + it 'génére un fichier xls avec les détails de la question' do + xls = response_service.to_xls + spreadsheet = Spreadsheet.open(StringIO.new(xls)) + worksheet = spreadsheet.worksheet(0) + question = worksheet.row(1) + expect(question[8]).to eq(question_qcm.type_qcm) + expect(question[9]).to eq(reponse.intitule) + expect(question[10]).to eq(reponse.nom_technique) + expect(question[11]).to eq(reponse.type_choix) + expect(question[12]).to eq(reponse.audio_url) + expect(question[13]).to eq(reponse2.intitule) + expect(question[14]).to eq(reponse2.nom_technique) + expect(question[15]).to eq(reponse2.type_choix) + expect(question[16]).to eq(reponse2.audio_url) + end + end +end diff --git a/spec/models/import_export_question_spec.rb b/spec/models/import_export_question_spec.rb index 46008518d..565057b35 100644 --- a/spec/models/import_export_question_spec.rb +++ b/spec/models/import_export_question_spec.rb @@ -3,197 +3,33 @@ require 'rails_helper' describe ImportExportQuestion do - describe 'importe les données' do - describe 'pour toutes les questions' do - subject(:service) do - described_class.new(question) - end - - let(:question) { Question.new(type: 'QuestionClicDansImage') } - - describe 'avec un fichier valide' do - let(:file) do - fixture_file_upload('spec/support/import_question_clic.xls', 'text/xls') - end - - it 'importe une nouvelle question' do - expect do - service.importe_donnees(file) - end.to change(Question, :count).by(1) - end - - it 'crée les transcriptions' do - service.importe_donnees(file) - transcriptions = Question.last.transcriptions - expect(transcriptions.count).to eq 2 - expect(transcriptions.first.categorie).to eq 'intitule' - expect(transcriptions.first.ecrit).to eq 'Ceci est un intitulé' - expect(transcriptions.first.audio.attached?).to be true - expect(transcriptions.last.categorie).to eq 'modalite_reponse' - expect(transcriptions.last.ecrit).to eq 'Ceci est une consigne' - expect(transcriptions.last.audio.attached?).to be true - end - - it "importe les données d'une question" do - service.importe_donnees(file) - question = Question.last - expect(question.nom_technique).to eq 'N1Pse5' - expect(question.libelle).to eq 'N1Pse5' - expect(question.description).to eq 'Ceci est une description' - expect(question.illustration.attached?).to be true - end - end - - describe 'avec un fichier invalide' do - let(:file) do - fixture_file_upload('spec/support/import_question_invalide.xls', 'text/xls') - end - - it 'renvoie une erreur avec les headers manquants' do - message = service.send(:message_erreur_headers) - - expect do - service.importe_donnees(file) - end.to raise_error(ImportQuestion::Error, message) - end - end - end - - describe 'pour une question de type qcm' do - subject(:service) do - described_class.new(question) - end - - let(:question) { Question.new(type: 'QuestionQcm') } - - let(:file) do - fixture_file_upload('spec/support/import_question_qcm.xls', 'text/xls') - end - - it 'importe les données spécifiques' do - service.importe_donnees(file) - question = Question.last - expect(question.type_qcm).to eq 'standard' - end - - it 'crée les choix' do - service.importe_donnees(file) - choix = Question.last.choix - expect(choix.count).to eq 2 - choix1 = Choix.first - expect(choix1.intitule).to eq 'Choix1' - expect(choix1.nom_technique).to eq 'Choix1' - expect(choix1.type_choix).to eq 'bon' - expect(choix1.audio.attached?).to be true - choix2 = Choix.last - expect(choix2.intitule).to eq 'Choix2' - expect(choix2.nom_technique).to eq 'Choix2' - expect(choix2.type_choix).to eq 'mauvais' - expect(choix2.audio.attached?).to be true - end - end - - describe 'pour une question de type glisser deposer' do - subject(:service) do - described_class.new(question) - end - - let(:question) { Question.new(type: 'QuestionGlisserDeposer') } - - let(:file) do - fixture_file_upload('spec/support/import_question_glisser.xls', 'text/xls') - end - - it 'importe les données spécifiques' do - service.importe_donnees(file) - question = Question.last - expect(question.zone_depot.attached?).to be true - end - - it 'crée les réponses' do - service.importe_donnees(file) - reponses = Question.last.reponses - expect(reponses.count).to eq 2 - reponse1 = reponses.first - expect(reponse1.nom_technique).to eq 'N2Pon2R1' - expect(reponse1.position_client).to eq 2 - expect(reponse1.type_choix).to eq 'bon' - expect(reponse1.illustration.attached?).to be true - reponse2 = reponses.last - expect(reponse2.nom_technique).to eq 'N2Pon2R2' - expect(reponse2.position_client).to eq 1 - expect(reponse1.type_choix).to eq 'bon' - expect(reponse2.illustration.attached?).to be true - end - end - - describe 'pour une question de type clic dans image' do - subject(:service) do - described_class.new(question) - end - - let(:question) { Question.new(type: 'QuestionClicDansImage') } + subject(:service) do + described_class.new(question) + end - let(:file) do - fixture_file_upload('spec/support/import_question_clic.xls', 'text/xls') - end + describe '#exporte_donnees' do + let!(:question) { create(:question_clic_dans_image) } - it 'importe les données spécifiques' do - service.importe_donnees(file) - question = Question.last - expect(question.image_au_clic.attached?).to be true - expect(question.zone_cliquable.attached?).to be true - end + it 'exporte les données' do + date = DateTime.current.strftime('%Y%m%d') + nom_du_fichier_attendu = "#{date}-#{question.nom_technique}.xls" + expect(service.exporte_donnees[:xls]).not_to be_nil + expect(service.exporte_donnees[:content_type]).to eq 'application/vnd.ms-excel' + expect(service.exporte_donnees[:filename]).to eq nom_du_fichier_attendu end + end - describe 'pour une question de type saisie' do - subject(:service) do - described_class.new(question) - end - - let(:question) { Question.new(type: 'QuestionSaisie') } - - let(:file) do - fixture_file_upload('spec/support/import_question_saisie.xls', 'text/xls') - end - - it 'importe les données spécifiques' do - service.importe_donnees(file) - question = Question.last - expect(question.suffix_reponse).to eq 'suffixe' - expect(question.reponse_placeholder).to eq 'placeholder' - expect(question.type_saisie).to eq 'numerique' - expect(question.bonne_reponse.intitule).to eq '9' - expect(question.bonne_reponse.nom_technique).to eq '9' - end + describe '#importe_donnees' do + let(:question) { create(:question_clic_dans_image) } + let!(:file) do + fixture_file_upload('spec/support/import_question_clic.xls', 'text/xls') end - describe 'pour une question de type sous consigne' do - subject(:service) do - described_class.new(question) - end - - let(:question) { Question.new(type: 'QuestionSousConsigne') } - - let(:file) do - fixture_file_upload('spec/support/import_question_consigne.xls', 'text/xls') - end - - it 'importe les données' do - service.importe_donnees(file) - question = Question.last - expect(question.nom_technique).to eq 'N1Pse5' - expect(question.libelle).to eq 'N1Pse5' - end - - it "créé la transcription de li'intitulé seulement" do + it 'importe une nouvelle question' do + expect(Question.count).to eq 0 + expect do service.importe_donnees(file) - transcriptions = Question.last.transcriptions - expect(transcriptions.count).to eq 1 - expect(transcriptions.first.categorie).to eq 'intitule' - expect(transcriptions.first.ecrit).to eq 'Ceci est un intitulé' - expect(transcriptions.first.audio.attached?).to be true - end + end.to change(Question, :count).by(1) end end end diff --git a/spec/models/import_question_spec.rb b/spec/models/import_question_spec.rb new file mode 100644 index 000000000..e4ad8aed8 --- /dev/null +++ b/spec/models/import_question_spec.rb @@ -0,0 +1,223 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ImportQuestion do + describe 'importe les données' do + describe 'pour toutes les questions' do + subject(:service) do + described_class.new(question_clic, headers) + end + + let(:question_clic) { Question.new(type: 'QuestionClicDansImage') } + + let(:headers) do + ImportExportQuestion::HEADERS_ATTENDUS[question_clic.type] + end + + describe 'avec un fichier valide' do + let(:file) do + fixture_file_upload('spec/support/import_question_clic.xls', 'text/xls') + end + + it 'importe une nouvelle question' do + expect do + service.import_from_xls(file) + end.to change(Question, :count).by(1) + end + + it 'crée les transcriptions' do + service.import_from_xls(file) + transcriptions = Question.last.transcriptions + expect(transcriptions.count).to eq 2 + expect(transcriptions.first.categorie).to eq 'intitule' + expect(transcriptions.first.ecrit).to eq 'Ceci est un intitulé' + expect(transcriptions.first.audio.attached?).to be true + expect(transcriptions.last.categorie).to eq 'modalite_reponse' + expect(transcriptions.last.ecrit).to eq 'Ceci est une consigne' + expect(transcriptions.last.audio.attached?).to be true + end + + it "importe les données d'une question" do + service.import_from_xls(file) + question = Question.last + expect(question.nom_technique).to eq 'N1Pse5' + expect(question.libelle).to eq 'N1Pse5' + expect(question.description).to eq 'Ceci est une description' + expect(question.illustration.attached?).to be true + end + end + + describe 'avec un fichier invalide' do + let(:file) do + fixture_file_upload('spec/support/import_question_invalide.xls', 'text/xls') + end + + it 'renvoie une erreur avec les headers manquants' do + message = service.send(:message_erreur_headers) + + expect do + service.import_from_xls(file) + end.to raise_error(ImportQuestion::Error, message) + end + end + end + + describe 'pour une question de type qcm' do + subject(:service) do + described_class.new(question_qcm, headers) + end + + let(:question_qcm) { Question.new(type: 'QuestionQcm') } + + let(:headers) do + ImportExportQuestion::HEADERS_ATTENDUS[question_qcm.type] + end + + let(:file) do + fixture_file_upload('spec/support/import_question_qcm.xls', 'text/xls') + end + + it 'importe les données spécifiques' do + service.import_from_xls(file) + question = Question.last + expect(question.type_qcm).to eq 'standard' + end + + it 'crée les choix' do + service.import_from_xls(file) + choix = Question.last.choix + expect(choix.count).to eq 2 + choix1 = Choix.first + expect(choix1.intitule).to eq 'Choix1' + expect(choix1.nom_technique).to eq 'Choix1' + expect(choix1.type_choix).to eq 'bon' + expect(choix1.audio.attached?).to be true + choix2 = Choix.last + expect(choix2.intitule).to eq 'Choix2' + expect(choix2.nom_technique).to eq 'Choix2' + expect(choix2.type_choix).to eq 'mauvais' + expect(choix2.audio.attached?).to be true + end + end + + describe 'pour une question de type glisser deposer' do + subject(:service) do + described_class.new(question_glisser, headers) + end + + let(:question_glisser) { Question.new(type: 'QuestionGlisserDeposer') } + + let(:headers) do + ImportExportQuestion::HEADERS_ATTENDUS[question_glisser.type] + end + + let(:file) do + fixture_file_upload('spec/support/import_question_glisser.xls', 'text/xls') + end + + it 'importe les données spécifiques' do + service.import_from_xls(file) + question = Question.last + expect(question.zone_depot.attached?).to be true + end + + it 'crée les réponses' do + service.import_from_xls(file) + reponses = Question.last.reponses + expect(reponses.count).to eq 2 + reponse1 = reponses.first + expect(reponse1.nom_technique).to eq 'N2Pon2R1' + expect(reponse1.position_client).to eq 2 + expect(reponse1.type_choix).to eq 'bon' + expect(reponse1.illustration.attached?).to be true + reponse2 = reponses.last + expect(reponse2.nom_technique).to eq 'N2Pon2R2' + expect(reponse2.position_client).to eq 1 + expect(reponse1.type_choix).to eq 'bon' + expect(reponse2.illustration.attached?).to be true + end + end + + describe 'pour une question de type clic dans image' do + subject(:service) do + described_class.new(question_clic, headers) + end + + let(:question_clic) { Question.new(type: 'QuestionClicDansImage') } + + let(:headers) do + ImportExportQuestion::HEADERS_ATTENDUS[question_clic.type] + end + + let(:file) do + fixture_file_upload('spec/support/import_question_clic.xls', 'text/xls') + end + + it 'importe les données spécifiques' do + service.import_from_xls(file) + question = Question.last + expect(question.image_au_clic.attached?).to be true + expect(question.zone_cliquable.attached?).to be true + end + end + + describe 'pour une question de type saisie' do + subject(:service) do + described_class.new(question_clic, headers) + end + + let(:question_clic) { Question.new(type: 'QuestionSaisie') } + + let(:headers) do + ImportExportQuestion::HEADERS_ATTENDUS[question_clic.type] + end + + let(:file) do + fixture_file_upload('spec/support/import_question_saisie.xls', 'text/xls') + end + + it 'importe les données spécifiques' do + service.import_from_xls(file) + question = Question.last + expect(question.suffix_reponse).to eq 'suffixe' + expect(question.reponse_placeholder).to eq 'placeholder' + expect(question.type_saisie).to eq 'numerique' + expect(question.bonne_reponse.intitule).to eq '9' + expect(question.bonne_reponse.nom_technique).to eq '9' + end + end + + describe 'pour une question de type sous consigne' do + subject(:service) do + described_class.new(question_consigne, headers) + end + + let(:question_consigne) { Question.new(type: 'QuestionSousConsigne') } + + let(:headers) do + ImportExportQuestion::HEADERS_ATTENDUS[question_consigne.type] + end + + let(:file) do + fixture_file_upload('spec/support/import_question_consigne.xls', 'text/xls') + end + + it 'importe les données' do + service.import_from_xls(file) + question = Question.last + expect(question.nom_technique).to eq 'N1Pse5' + expect(question.libelle).to eq 'N1Pse5' + end + + it "créé la transcription de li'intitulé seulement" do + service.import_from_xls(file) + transcriptions = Question.last.transcriptions + expect(transcriptions.count).to eq 1 + expect(transcriptions.first.categorie).to eq 'intitule' + expect(transcriptions.first.ecrit).to eq 'Ceci est un intitulé' + expect(transcriptions.first.audio.attached?).to be true + end + end + end +end